README.md
## Normalizr
[![Gem Version](https://badge.fury.io/rb/normalizr.svg)](https://badge.fury.io/rb/normalizr)
[![Build Status](https://github.com/dmeremyanin/normalizr/actions/workflows/rspec.yml/badge.svg?branch=main)](https://github.com/dmeremyanin/normalizr/actions/workflows/rspec.yml)
[![Code Climate](https://codeclimate.com/github/dmeremyanin/normalizr/badges/gpa.svg)](https://codeclimate.com/github/dmeremyanin/normalizr)
[![Test Coverage](https://codeclimate.com/github/dmeremyanin/normalizr/badges/coverage.svg)](https://codeclimate.com/github/dmeremyanin/normalizr/coverage)
The [attribute_normalizer](https://github.com/mdeering/attribute_normalizer) replacement.
### Synopsis
Attribute normalizer doesn't normalize overloaded methods correctly. Example:
```ruby
class Phone
include AttributeNormalizer
attr_accessor :number
normalize_attribute :number
def number=(value)
@number = value
end
end
```
`number` will never be normalized as expected. Normalizr resolves this problem and doesn't pollute target object namespace.
Magic based on ruby's `prepend` feature, so it requires 2.0 or higher version.
### Installation
Add this line to your application's Gemfile:
gem 'normalizr'
And then execute:
$ bundle
Or install it yourself as:
$ gem install normalizr
### Usage
Specify default normalizers:
```ruby
Normalizr.configure do
default :strip, :blank
end
```
Register custom normalizer:
```ruby
Normalizr.configure do
add :titleize do |value|
String === value ? value.titleize : value
end
add :truncate do |value, options|
if String === value
options.reverse_merge!(length: 30, omission: '...')
l = options[:length] - options[:omission].mb_chars.length
chars = value.mb_chars
(chars.length > options[:length] ? chars[0...l] + options[:omission] : value).to_s
else
value
end
end
add :indent do |value, amount = 2|
if String === value
value.indent(amount)
else
value
end
end
end
```
Add attributes normalization:
```ruby
class User < ActiveRecord::Base
normalize :first_name, :last_name, :about # with default normalizers
normalize :email, with: :downcase
# you can use default and custom normalizers together
normalize :middle_name, with: [:default, :titleize]
# supports `normalize_attribute` and `normalize_attributes` as well
normalize_attribute :skype
# array normalization is supported too
normalize :skills
end
user = User.new(first_name: '', last_name: '', middle_name: 'elizabeth ', skills: [nil, '', ' ruby'])
user.email = "ADDRESS@example.com"
user.first_name
#=> nil
user.last_name
#=> nil
user.middle_name
#=> "Elizabeth"
user.email
#=> "address@example.com"
user.skills
#=> ["ruby"]
```
```ruby
class SMS
include Normalizr::Concern
attr_accessor :phone, :message
normalize :phone, with: :phone
normalize :message
def initialize(phone, message)
self.phone = phone
self.message = message
end
end
sms = SMS.new("+1 (810) 555-0000", "It works \n")
sms.phone
#=> "18105550000"
sms.message
#=> "It works"
```
You can also use if/unless options (they accept a symbol (method name) or proc):
```ruby
class Book
include Normalizr::Concerns
attr_accessor :author, :description, :date
normalize :author, if: :author_should_be_normalized?
normalize :description, unless: :description_should_not_be_normalized?
normalize :author, if: -> { date.today? }
end
```
Normalize values outside of class:
```ruby
Normalizr.normalize(value)
Normalizr.normalize(value, :strip, :blank)
Normalizr.normalize(value, :strip, truncate: { length: 20 })
```
### ORMs
Normalizr automatically loads into:
* ActiveRecord
* Mongoid
### RSpec matcher
```ruby
describe User do
it { should normalize(:name) }
it { should normalize(:phone).from('+1 (810) 555-0000').to('18105550000') }
it { should normalize(:email).from('ADDRESS@example.com').to('address@example.com') }
end
```
### Built-in normalizers
- blank
- boolean
- capitalize
- control_chars
- downcase
- phone
- squish
- strip
- upcase
- whitespace
### Contributing
1. Fork it
2. Create your feature branch (`git checkout -b my-new-feature`)
3. Commit your changes (`git commit -am 'Add some feature'`)
4. Push to the branch (`git push origin my-new-feature`)
5. Create new Pull Request
### Thanks
Special thanks to [Michael Deering](https://github.com/mdeering) for original idea.