normalizr 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: 46e20b8bf0d6a0371b2d02e709f83f64f530febc
4
+ data.tar.gz: 03815de2a257c27aa115e25601658eed6c6d76c6
5
+ SHA512:
6
+ metadata.gz: 300fc2ccb51ad7808ebb8fd24baa6b9a9124d4a796df908d6a9d7653b386bec23a7f14ee44c44228b52c71ec55d48aac44b70331c58301996e3489491bdb7982
7
+ data.tar.gz: 53347f657d9352371bea8b9d3d2025a51e819ddf8786fa70f4650e21313fbe84f90c7fe3b860e39027867207a01febd75e67d83eb46c5913592fb01756e9338e
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/.rspec ADDED
@@ -0,0 +1 @@
1
+ --format Fuubar --color --backtrace
data/.travis.yml ADDED
@@ -0,0 +1,9 @@
1
+ language: ruby
2
+ rvm:
3
+ - 2.0.0
4
+ - 2.1.0
5
+ addons:
6
+ code_climate:
7
+ repo_token: 1cac54041089115a5bf218169b62c76c8b87a5896f5aa973f89637a8553c0376
8
+ matrix:
9
+ fast_finish: true
data/Gemfile ADDED
@@ -0,0 +1,7 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gemspec
4
+
5
+ group :test do
6
+ gem 'codeclimate-test-reporter', require: false
7
+ end
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2014 DM
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,127 @@
1
+ ## Normalizr
2
+
3
+ [![Travic CI](http://img.shields.io/travis/dimko/normalizr.svg)](https://travis-ci.org/dimko/normalizr) [![Code Climate](http://img.shields.io/codeclimate/github/dimko/normalizr.svg)](https://codeclimate.com/github/dimko/normalizr) [![Coverage](http://img.shields.io/codeclimate/coverage/github/dimko/normalizr.svg)](https://codeclimate.com/github/dimko/normalizr)
4
+
5
+ The [attribute_normalizer](https://github.com/mdeering/attribute_normalizer) replacement.
6
+
7
+ ### Synopsis
8
+
9
+ Attribute normalizer doesn't normalize overloaded writer methods correctly. Example:
10
+
11
+ ```ruby
12
+ class Currency
13
+ include Normalizr::Concern
14
+
15
+ attr_accessor :value
16
+ normalize_attribute :value
17
+
18
+ def value=(*)
19
+ # ...
20
+ super
21
+ end
22
+ end
23
+ ```
24
+
25
+ `value` will never be normalized as expected. Normalizr resolves this problem and doesn't generate any extra instance methods.
26
+
27
+ Magic based on ruby's `prepend` feature, so it requires 2.0 or higher version.
28
+
29
+ ### Installation
30
+
31
+ Add this line to your application's Gemfile:
32
+
33
+ gem 'normalizr'
34
+
35
+ And then execute:
36
+
37
+ $ bundle
38
+
39
+ Or install it yourself as:
40
+
41
+ $ gem install normalizr
42
+
43
+ ### Usage
44
+
45
+ Specify default normalizers:
46
+
47
+ ```ruby
48
+ Normalizr.configure do
49
+ default :strip, :blank
50
+ end
51
+ ```
52
+
53
+ Add a custom normalizer:
54
+
55
+ ```ruby
56
+ Normalizr.configure do
57
+ add :titleize do |value|
58
+ String === value ? value.titleize : value
59
+ end
60
+
61
+ add :truncate do |value, options|
62
+ if String === value
63
+ options.reverse_merge!(length: 30, omission: '...')
64
+ l = options[:length] - options[:omission].mb_chars.length
65
+ chars = value.mb_chars
66
+ (chars.length > options[:length] ? chars[0...l] + options[:omission] : value).to_s
67
+ else
68
+ value
69
+ end
70
+ end
71
+
72
+ add :indent do |value, amount = 2|
73
+ value.indent!(amount) if String === value
74
+ value
75
+ end
76
+ end
77
+ ```
78
+
79
+ Add attributes normalization:
80
+
81
+ ```ruby
82
+ class User < ActiveRecord::Base
83
+ normalize :first_name, :last_name, :about # with default normalizers
84
+ normalize :email, with: :downcase
85
+
86
+ # supports `normalize_attribute` and `normalize_attributes` as well
87
+ normalize_attribute :skype
88
+ end
89
+
90
+ class Currency
91
+ include Normalizr::Concern
92
+ normalize :value
93
+ end
94
+ ```
95
+
96
+ Normalize value outside class:
97
+
98
+ ```ruby
99
+ Normalizr.do(value)
100
+ Normalizr.do(value, :strip, :blank)
101
+ Normalizr.do(value, :strip, truncate: { length: 20 })
102
+ ```
103
+
104
+ ### Built-in normalizers
105
+
106
+ - blank
107
+ - boolean
108
+ - capitalize
109
+ - control_chars
110
+ - downcase
111
+ - phone
112
+ - squish
113
+ - strip
114
+ - upcase
115
+ - whitespace
116
+
117
+ ### Contributing
118
+
119
+ 1. Fork it
120
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
121
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
122
+ 4. Push to the branch (`git push origin my-new-feature`)
123
+ 5. Create new Pull Request
124
+
125
+ ### Thanks
126
+
127
+ Special thanks to [Michael Deering](https://github.com/mdeering) for original idea.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require 'bundler/gem_tasks'
2
+ require 'rspec/core/rake_task'
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task default: :spec
data/lib/normalizr.rb ADDED
@@ -0,0 +1,43 @@
1
+ require 'normalizr/concern'
2
+ require 'normalizr/configuration'
3
+ require 'normalizr/exceptions'
4
+ require 'normalizr/options_parser'
5
+ require 'normalizr/version'
6
+
7
+ module Normalizr
8
+ extend self
9
+
10
+ module RSpec
11
+ autoload :Matcher, 'normalizr/rspec/matcher'
12
+ end
13
+
14
+ def configuration
15
+ @configuration ||= Configuration.new
16
+ end
17
+
18
+ def configure
19
+ configuration.instance_eval &Proc.new
20
+ end
21
+
22
+ def find(name)
23
+ unless name.respond_to?(:call)
24
+ configuration.normalizers.fetch(name) { raise MissingNormalizer.new(name) }
25
+ else
26
+ name
27
+ end
28
+ end
29
+
30
+ def do(value, *normalizers)
31
+ normalizers = configuration.default_normalizers if normalizers.empty?
32
+ normalizers.each do |name|
33
+ name, options = name.first if Hash === name
34
+ value = find(name).call(value, options)
35
+ end
36
+ value
37
+ end
38
+ end
39
+
40
+ require 'normalizr/normalizers'
41
+ ActiveSupport.on_load(:active_record) do
42
+ ActiveRecord::Base.send(:include, Normalizr::Concern)
43
+ end
@@ -0,0 +1,31 @@
1
+ module Normalizr
2
+ module Concern
3
+ def self.included(base)
4
+ base.extend(ClassMethods)
5
+
6
+ base.instance_eval do
7
+ class << self
8
+ # attribute normalizer replacement
9
+ alias_method :normalize_attribute, :normalize
10
+ alias_method :normalize_attributes, :normalize
11
+ end
12
+ end
13
+ end
14
+
15
+ module ClassMethods
16
+ def normalize(*args, &block)
17
+ options = Normalizr::OptionsParser.new(args, block)
18
+
19
+ prepend Module.new {
20
+ options.attributes.each do |method|
21
+ define_method :"#{method}=" do |value|
22
+ value = Normalizr.do(value, *options.pre)
23
+ value = Normalizr.do(value, *options.post) if options.post.any?
24
+ super(value)
25
+ end
26
+ end
27
+ }
28
+ end
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,21 @@
1
+ module Normalizr
2
+ class Configuration
3
+ attr_accessor :default_normalizers
4
+
5
+ def normalizers
6
+ @normalizers ||= {}
7
+ end
8
+
9
+ def normalizer_names
10
+ normalizers.keys
11
+ end
12
+
13
+ def default(*normalizers)
14
+ self.default_normalizers = normalizers
15
+ end
16
+
17
+ def add(name)
18
+ normalizers[name] = Proc.new
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,7 @@
1
+ module Normalizr
2
+ class MissingNormalizer < StandardError
3
+ def initialize(name)
4
+ super "undefined normalizer #{name}"
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,55 @@
1
+ require 'set'
2
+
3
+ Normalizr.configure do
4
+ default :strip, :blank
5
+
6
+ add :blank do |value|
7
+ value unless /\A[[:space:]]*\z/ === value
8
+ end
9
+
10
+ TRUE_VALUES = [true, 1, '1', 't', 'T', 'true', 'TRUE', 'on', 'ON'].to_set
11
+
12
+ add :boolean do |value|
13
+ unless String === value && value.blank?
14
+ TRUE_VALUES.include?(value)
15
+ end
16
+ end
17
+
18
+ add :control_chars do |value|
19
+ value.gsub!(/[[:cntrl:]&&[^[:space:]]]/, '') if String === value
20
+ value
21
+ end
22
+
23
+ add :phone do |value|
24
+ if String === value
25
+ value.gsub!(/[^0-9]+/, '')
26
+ value unless value.empty?
27
+ else
28
+ value
29
+ end
30
+ end
31
+
32
+ add :squish do |value|
33
+ if String === value
34
+ value.strip!
35
+ value.gsub!(/\s+/, ' ')
36
+ end
37
+ value
38
+ end
39
+
40
+ add :whitespace do |value|
41
+ if String === value
42
+ value.gsub!(/[^\S\n]+/, ' ')
43
+ value.gsub!(/\s?\n\s?/, "\n")
44
+ value.strip!
45
+ end
46
+ value
47
+ end
48
+
49
+ [:capitalize, :downcase, :strip, :upcase].each do |name|
50
+ add name do |value|
51
+ value.send(:"#{name}!") if String === value
52
+ value
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,26 @@
1
+ module Normalizr
2
+ class OptionsParser
3
+ attr_reader :attributes, :options, :block
4
+
5
+ def initialize(args, block)
6
+ @options = Hash === args.last ? args.pop : {}
7
+ @attributes = args
8
+ @block = block
9
+ end
10
+
11
+ def pre
12
+ options_at(:with, :before) { block }
13
+ end
14
+
15
+ def post
16
+ options_at(:after)
17
+ end
18
+
19
+ private
20
+
21
+ def options_at(*keys)
22
+ block = yield if block_given?
23
+ [ *options.values_at(*keys), block ].flatten.compact
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,48 @@
1
+ module Normalizr
2
+ module RSpec
3
+ module Matcher
4
+ def normalize(attribute)
5
+ Normalization.new(attribute)
6
+ end
7
+
8
+ alias_method :normalize_attribute, :normalize
9
+
10
+ class Normalization
11
+ def initialize(attribute)
12
+ @attribute = attribute
13
+ @from = ''
14
+ end
15
+
16
+ def description
17
+ "normalize #{@attribute} from #{@from.nil? ? 'nil' : "\"#{@from}\""} to #{@to.nil? ? 'nil' : "\"#{@to}\""}"
18
+ end
19
+
20
+ def failure_message
21
+ "#{@attribute} did not normalize as expected! \"#{@subject.send(@attribute)}\" != #{@to.nil? ? 'nil' : "\"#{@to}\""}"
22
+ end
23
+
24
+ def failure_message_when_negated
25
+ "expected #{@attribute} to not be normalized from #{@from.nil? ? 'nil' : "\"#{@from}\""} to #{@to.nil? ? 'nil' : "\"#{@to}\""}"
26
+ end
27
+
28
+ alias negative_failure_message failure_message_when_negated
29
+
30
+ def from(value)
31
+ @from = value
32
+ self
33
+ end
34
+
35
+ def to(value)
36
+ @to = value
37
+ self
38
+ end
39
+
40
+ def matches?(subject)
41
+ @subject = subject
42
+ @subject.send("#{@attribute}=", @from)
43
+ @subject.send(@attribute) == @to
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,3 @@
1
+ module Normalizr
2
+ VERSION = '0.0.1'
3
+ end
data/normalizr.gemspec ADDED
@@ -0,0 +1,29 @@
1
+ lib = File.expand_path('../lib', __FILE__)
2
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
3
+ require 'normalizr/version'
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = 'normalizr'
7
+ spec.version = Normalizr::VERSION
8
+ spec.authors = ['Dimko']
9
+ spec.email = ['deemox@gmail.com']
10
+ spec.description = 'Writer methods parameters normalization'
11
+ spec.summary = 'Writer methods parameters normalization'
12
+ spec.homepage = 'https://github.com/dimko/normalizr'
13
+ spec.license = 'MIT'
14
+
15
+ spec.files = `git ls-files`.split($/)
16
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
17
+ spec.require_paths = ['lib']
18
+
19
+ spec.required_ruby_version = '>= 2.0'
20
+
21
+ spec.add_development_dependency 'pry'
22
+ spec.add_development_dependency 'bundler', '~> 1.5'
23
+ spec.add_development_dependency 'rake'
24
+ spec.add_development_dependency 'rspec', '~> 3.0'
25
+ spec.add_development_dependency 'rspec-its'
26
+ spec.add_development_dependency 'fuubar'
27
+ spec.add_development_dependency 'activerecord', '>= 3.0'
28
+ spec.add_development_dependency 'sqlite3'
29
+ end
@@ -0,0 +1,9 @@
1
+ require 'spec_helper'
2
+
3
+ describe Normalizr::Concern do
4
+ subject { Class.new { include Normalizr::Concern }}
5
+
6
+ it { should respond_to(:normalize) }
7
+ it { should respond_to(:normalize_attributes) }
8
+ it { should respond_to(:normalize_attribute) }
9
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Normalizr::Configuration do
4
+ let(:configuration) { described_class.new }
5
+ subject { configuration }
6
+
7
+ describe '#default' do
8
+ before { configuration.default(:blank) }
9
+ its(:default_normalizers) { should == [:blank] }
10
+ end
11
+
12
+ describe '#normalizer_names' do
13
+ before { allow(configuration).to receive(:normalizers) {{ blank: proc {}}}}
14
+ its(:normalizer_names) { should == [:blank] }
15
+ end
16
+
17
+ describe '#add' do
18
+ before { configuration.add(:blank) {}}
19
+ its(:normalizer_names) { should include(:blank) }
20
+ end
21
+ end
@@ -0,0 +1,80 @@
1
+ require 'spec_helper'
2
+
3
+ describe Normalizr do
4
+ let(:configuration) { Normalizr.configuration }
5
+
6
+ describe '#find' do
7
+ subject { described_class.find(normalizer) }
8
+
9
+ context 'existed normalizer' do
10
+ let(:normalizer) { :blank }
11
+
12
+ it 'returns normalizer' do
13
+ expect(subject).to be_a(Proc)
14
+ end
15
+ end
16
+
17
+ context 'not existed normalizer' do
18
+ let(:normalizer) { :not_existed }
19
+
20
+ it 'raise exception with normalizer in message' do
21
+ expect { subject }.to raise_error(Normalizr::MissingNormalizer, /not_existed/)
22
+ end
23
+ end
24
+
25
+ context 'proc' do
26
+ let(:normalizer) { proc {} }
27
+
28
+ it 'returns parameter' do
29
+ expect(subject).to eq(normalizer)
30
+ end
31
+ end
32
+ end
33
+
34
+ describe '#do' do
35
+ before { configuration.default_normalizers = [:strip, :blank] }
36
+ subject { described_class.do(value, *normalizers) }
37
+
38
+ context 'normalizers is empty' do
39
+ let(:normalizers) { [] }
40
+ let(:value) { ' text ' }
41
+ it { should == 'text' }
42
+ end
43
+
44
+ context 'normalizers is not empty' do
45
+ let(:normalizers) { [truncate: { length: 8, omission: '...' }] }
46
+ let(:value) { 'Lorem ipsum dolor sit amet' }
47
+ it { should == 'Lorem...' }
48
+ end
49
+
50
+ context 'proc' do
51
+ let(:normalizers) { [proc { |v| v.first(5) }] }
52
+ let(:value) { 'Lorem ipsum dolor sit amet' }
53
+ it { should == 'Lorem' }
54
+ end
55
+ end
56
+
57
+
58
+ describe '#do' do
59
+ before { configuration.default_normalizers = [:strip, :blank] }
60
+ subject { described_class.do(value, *normalizers) }
61
+
62
+ context 'normalizers is empty' do
63
+ let(:normalizers) { [] }
64
+ let(:value) { ' text ' }
65
+
66
+ it 'use default normalizers' do
67
+ expect(subject).to eq('text')
68
+ end
69
+ end
70
+
71
+ context 'normalizers is not empty' do
72
+ let(:normalizers) { [:strip, { truncate: { length: 8, omission: '...' }}, proc { |v| v.first(6) }] }
73
+ let(:value) { ' Lorem ipsum dolor sit amet' }
74
+
75
+ it 'normalize value' do
76
+ expect(subject).to eq('Lorem.')
77
+ end
78
+ end
79
+ end
80
+ end
@@ -0,0 +1,49 @@
1
+ require 'spec_helper'
2
+
3
+ describe Article do
4
+ it { should normalize_attribute(:title).from(' Social Life at the Edge of Chaos ').to('Social Life at the Edge of Chaos') }
5
+ it { should normalize_attribute(:slug) }
6
+ it { should normalize_attribute(:slug).from(' Social Life at the Edge of Chaos ').to('social-life-at-the-edge-of-chaos') }
7
+ it { should normalize_attribute(:limited_slug) }
8
+ it { should normalize_attribute(:limited_slug).from(' Social Life at the Edge of Chaos ').to('social-life') }
9
+
10
+ context 'normalization should not interfere with other hooks and aliases on the attribute assignment' do
11
+ before do
12
+ @article = Article.create!(title: 'Original Title')
13
+ end
14
+
15
+ it 'should still reflect that the attribute has been changed through the call to super' do
16
+ expect{ @article.title = 'New Title' }.to change(@article, :title).from('Original Title').to('New Title')
17
+ end
18
+ end
19
+
20
+ context 'when another instance of the same saved record has been changed' do
21
+ before do
22
+ @article = Article.create!(title: 'Original Title')
23
+ @article2 = Article.find(@article.id)
24
+ @article2.update_attributes(title: 'New Title')
25
+ end
26
+
27
+ it "should reflect the change when the record is reloaded" do
28
+ expect{ @article.reload }.to change(@article, :title).from('Original Title').to('New Title')
29
+ end
30
+ end
31
+
32
+ context 'normalization should work with multiple attributes at the same time' do
33
+ before do
34
+ @article = Article.new(slug: ' Bad Slug ', limited_slug: ' Bad Limited Slug ')
35
+ end
36
+
37
+ it "should apply normalizations to both attributes" do
38
+ expect(@article.slug).to eq 'bad-slug'
39
+ expect(@article.limited_slug).to eq 'bad-limited'
40
+ end
41
+ end
42
+
43
+ context 'with the default normalizer changed' do
44
+ it "only strips leading and trailing whitespace" do
45
+ @book = Book.new(author: ' testing the default normalizer ')
46
+ expect(@book.author).to eq('testing the default normalizer')
47
+ end
48
+ end
49
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe Author do
4
+ context 'Testing the built in normalizers' do
5
+ # default normalization [ :strip, :blank ]
6
+ it { should normalize_attribute(:name) }
7
+ it { should normalize_attribute(:name).from(' this ').to('this') }
8
+ it { should normalize_attribute(:name).from(' ').to(nil) }
9
+
10
+ # :strip normalizer
11
+ it { should normalize_attribute(:first_name).from(' this ').to('this') }
12
+ it { should normalize_attribute(:first_name).from(' ').to('') }
13
+
14
+ # :squish normalizer
15
+ it { should normalize_attribute(:nickname).from(' this nickname ').to('this nickname') }
16
+
17
+ # :blank normalizer
18
+ it { should normalize_attribute(:last_name).from('').to(nil) }
19
+ it { should normalize_attribute(:last_name).from(' ').to(nil) }
20
+ it { should normalize_attribute(:last_name).from(' this ').to(' this ') }
21
+
22
+ # :whitespace normalizer
23
+ it { should normalize_attribute(:biography).from(" this line\nbreak ").to("this line\nbreak") }
24
+ it { should normalize_attribute(:biography).from("\tthis\tline\nbreak ").to("this line\nbreak") }
25
+ it { should normalize_attribute(:biography).from(" \tthis \tline \nbreak \t \nthis").to("this line\nbreak\nthis") }
26
+ it { should normalize_attribute(:biography).from(' ').to('') }
27
+
28
+ # :control_chars normalizer
29
+ it { should normalize_attribute(:bibliography).from("No \bcontrol\u0003 chars").to("No control chars") }
30
+ it { should normalize_attribute(:bibliography).from("Except for\tspaces.\r\nAll kinds").to("Except for\tspaces.\r\nAll kinds") }
31
+ end
32
+
33
+ context 'on default attribute with the default normalizer changed' do
34
+ it { should normalize_attribute(:phone_number).from('no-numbers-here').to(nil) }
35
+ it { should normalize_attribute(:phone_number).from('1.877.987.9875').to('18779879875') }
36
+ it { should normalize_attribute(:phone_number).from('+ 1 (877) 987-9875').to('18779879875') }
37
+ end
38
+ end
@@ -0,0 +1,50 @@
1
+ require 'spec_helper'
2
+
3
+ describe Book do
4
+ it { should normalize_attribute(:author).from(' Michael Deering ').to('Michael Deering') }
5
+ it { should normalize_attribute(:us_price).from('$3.50').to(3.50) }
6
+ it { should normalize_attribute(:cnd_price).from('$3,450.98').to(3450.98) }
7
+ it { should normalize_attribute(:summary).from(' Here is my summary that is a little to long ').to('Here is m...') }
8
+
9
+ it { should normalize_attribute(:title).from('pick up chicks with magic tricks').to('Pick Up Chicks With Magic Tricks') }
10
+
11
+ context 'normalization should not interfere with other hooks and aliases on the attribute assignment' do
12
+ before do
13
+ @book = Book.create!(title: 'Original Title')
14
+ end
15
+
16
+ it 'should still reflect that the attribute has been changed through the call to super' do
17
+ expect { @book.title = 'New Title' }.to change(@book, :title_changed?).from(false).to(true)
18
+ end
19
+ end
20
+
21
+ context 'when another instance of the same saved record has been changed' do
22
+ before do
23
+ @book = Book.create!(title: 'Original Title')
24
+ @book2 = Book.find(@book.id)
25
+ @book2.update_attributes(title: 'New Title')
26
+ end
27
+
28
+ it "should reflect the change when the record is reloaded" do
29
+ expect { @book.reload }.to change(@book, :title).from('Original Title').to('New Title')
30
+ end
31
+ end
32
+
33
+ context 'normalization should work with multiple attributes at the same time' do
34
+ before do
35
+ @book = Book.new(title: ' Bad Title ', author: ' Bad Author ')
36
+ end
37
+
38
+ it "should apply normalizations to both attributes" do
39
+ expect(@book.title).to eq('Bad Title')
40
+ expect(@book.author).to eq('Bad Author')
41
+ end
42
+ end
43
+
44
+ context 'with the default normalizer changed' do
45
+ it "only strips leading and trailing whitespace" do
46
+ @book = Book.new(author: ' testing the default normalizer ')
47
+ expect(@book.author).to eq('testing the default normalizer')
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,10 @@
1
+ require 'spec_helper'
2
+
3
+ describe Journal do
4
+ context 'Testing the built in normalizers' do
5
+ # default normalization [ :strip, :blank ]
6
+ it { should normalize_attribute(:name) }
7
+ it { should normalize_attribute(:name).from(' Physical Review ').to('Physical Review') }
8
+ it { should normalize_attribute(:name).from(' ').to(nil) }
9
+ end
10
+ end
@@ -0,0 +1,17 @@
1
+ require 'spec_helper'
2
+
3
+ describe Magazine do
4
+ it { should normalize_attribute(:name).from(' Plain Old Ruby Objects ').to('Plain Old Ruby Objects') }
5
+ it { should normalize_attribute(:us_price).from('$3.50').to('3.50') }
6
+ it { should normalize_attribute(:cnd_price).from('$3,450.98').to('3450.98') }
7
+ it { should normalize_attribute(:sold).from('true').to(true) }
8
+ it { should normalize_attribute(:sold).from('0').to(false) }
9
+ it { should normalize_attribute(:sold).from('').to(nil) }
10
+
11
+ it { should normalize_attribute(:summary).from(' Here is my summary that is a little to long ').
12
+ to('Here is m...') }
13
+
14
+ it { should normalize_attribute(:title).from('some really interesting title').
15
+ to('Some Really Interesting Title') }
16
+
17
+ end
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe Publisher do
4
+ context 'using the built in phone normalizer' do
5
+ it { should normalize_attribute(:phone_number).from('no-numbers-here').to(nil) }
6
+ it { should normalize_attribute(:phone_number).from('1.877.987.9875').to('18779879875') }
7
+ it { should normalize_attribute(:phone_number).from('+ 1 (877) 987-9875').to('18779879875') }
8
+ end
9
+
10
+ context 'overloaded writer method' do
11
+ subject { Publisher.new(name: 'Mike') }
12
+ it { should normalize_attribute(:name).from('').to(nil) }
13
+
14
+ describe 'custom_writer' do
15
+ subject { Publisher.new(name: 'Mike').custom_writer }
16
+ it { should be(true) }
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,7 @@
1
+ require 'spec_helper'
2
+
3
+ describe User do
4
+ context 'on default attribute with the default normalizer changed' do
5
+ it { should normalize_attribute(:firstname).from(' here ').to('here') }
6
+ end
7
+ end
@@ -0,0 +1,51 @@
1
+ require 'support/codeclimate'
2
+ require 'pry'
3
+ require 'rspec'
4
+ require 'rspec/its'
5
+ require 'active_record'
6
+ require 'normalizr'
7
+
8
+ ActiveRecord::Migration.suppress_messages do
9
+ require 'support/connection'
10
+ require 'support/schema'
11
+ end
12
+
13
+ require 'support/models/article'
14
+ require 'support/models/author'
15
+ require 'support/models/book'
16
+ require 'support/models/journal'
17
+ require 'support/models/magazine'
18
+ require 'support/models/person'
19
+ require 'support/models/publisher'
20
+ require 'support/models/user'
21
+
22
+ RSpec.configure do |config|
23
+ config.include Normalizr::RSpec::Matcher
24
+ end
25
+
26
+ Normalizr.configure do |config|
27
+ default :strip, :special_normalizer, :blank
28
+
29
+ add :currency do |value|
30
+ String === value ? value.gsub(/[^0-9\.]+/, '') : value
31
+ end
32
+
33
+ add :special_normalizer do |value|
34
+ if String === value && /testing the default normalizer/ === value
35
+ 'testing the default normalizer'
36
+ else
37
+ value
38
+ end
39
+ end
40
+
41
+ add :truncate do |value, options|
42
+ if String === value
43
+ options.reverse_merge!(length: 30, omission: '...')
44
+ l = options[:length] - options[:omission].mb_chars.length
45
+ chars = value.mb_chars
46
+ (chars.length > options[:length] ? chars[0...l] + options[:omission] : value).to_s
47
+ else
48
+ value
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,6 @@
1
+ require 'codeclimate-test-reporter'
2
+ CodeClimate::TestReporter.configure do |config|
3
+ config.logger.level = Logger::WARN
4
+ end
5
+
6
+ CodeClimate::TestReporter.start
@@ -0,0 +1,4 @@
1
+ ActiveRecord::Base.establish_connection \
2
+ database: ':memory:',
3
+ adapter: 'sqlite3',
4
+ timeout: 500
@@ -0,0 +1,10 @@
1
+ class Article < ActiveRecord::Base
2
+ normalize_attribute :limited_slug, before: [:strip, :blank], after: [{ truncate: { length: 11, omission: '' }}] do |value|
3
+ value.present? && value.is_a?(String) ? value.downcase.gsub(/\s+/, '-') : value
4
+ end
5
+
6
+ normalize_attribute :title
7
+ normalize_attribute :slug, with: [:strip, :blank] do |value|
8
+ value.present? && value.is_a?(String) ? value.downcase.gsub(/\s+/, '-') : value
9
+ end
10
+ end
@@ -0,0 +1,9 @@
1
+ class Author < ActiveRecord::Base
2
+ normalize_attribute :name
3
+ normalize_attribute :nickname, with: :squish
4
+ normalize_attribute :first_name, with: :strip
5
+ normalize_attribute :last_name, with: :blank
6
+ normalize_attribute :phone_number, with: :phone
7
+ normalize_attribute :biography, with: :whitespace
8
+ normalize_attribute :bibliography, with: :control_chars
9
+ end
@@ -0,0 +1,8 @@
1
+ class Book < ActiveRecord::Base
2
+ normalize :author
3
+ normalize :us_price, :cnd_price, with: :currency
4
+ normalize_attributes :summary, with: [:strip, { truncate: { length: 12 }}, :blank]
5
+ normalize_attributes :title do |value|
6
+ String === value ? value.titleize.strip : value
7
+ end
8
+ end
@@ -0,0 +1,3 @@
1
+ class Journal < ActiveRecord::Base
2
+ normalize_attribute :name
3
+ end
@@ -0,0 +1,18 @@
1
+ class Magazine
2
+ include Normalizr::Concern
3
+
4
+ attr_accessor :name,
5
+ :cnd_price,
6
+ :us_price,
7
+ :summary,
8
+ :title,
9
+ :sold
10
+
11
+ normalize :name
12
+ normalize :us_price, :cnd_price, with: :currency
13
+ normalize :summary, with: [:strip, { truncate: { length: 12 }}, :blank]
14
+ normalize :sold, with: :boolean
15
+ normalize :title do |value|
16
+ String === value ? value.titleize.strip : value
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ class Person < ActiveRecord::Base
2
+ self.abstract_class = true
3
+ end
@@ -0,0 +1,11 @@
1
+ class Publisher < ActiveRecord::Base
2
+ attr_accessor :custom_writer
3
+
4
+ normalize :name, with: :blank
5
+ normalize :phone_number, with: :phone
6
+
7
+ def name=(_)
8
+ self.custom_writer = true
9
+ super
10
+ end
11
+ end
@@ -0,0 +1,3 @@
1
+ class User < Person
2
+ normalize_attribute :firstname
3
+ end
@@ -0,0 +1,40 @@
1
+ ActiveRecord::Schema.define do
2
+ create_table :publishers, force: true do |t|
3
+ t.string :name
4
+ t.string :phone_number
5
+ end
6
+
7
+ create_table :authors, force: true do |t|
8
+ t.string :name
9
+ t.string :nickname
10
+ t.string :first_name
11
+ t.string :last_name
12
+ t.string :phone_number
13
+ t.text :biography
14
+ t.text :bibliography
15
+ end
16
+
17
+ create_table :books, force: true do |t|
18
+ t.string :author
19
+ t.string :isbn
20
+ t.decimal :cnd_price
21
+ t.decimal :us_price
22
+ t.string :summary
23
+ t.string :title
24
+ end
25
+
26
+ create_table :journals, force: true do |t|
27
+ t.string :name
28
+ end
29
+
30
+ create_table :articles, force: true do |t|
31
+ t.string :title
32
+ t.string :slug
33
+ t.string :limited_slug
34
+ end
35
+
36
+ create_table :users, force: true do |t|
37
+ t.string :firstname
38
+ t.string :lastname
39
+ end
40
+ end
metadata ADDED
@@ -0,0 +1,216 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: normalizr
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Dimko
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-08-22 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: pry
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: bundler
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '1.5'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '1.5'
41
+ - !ruby/object:Gem::Dependency
42
+ name: rake
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: rspec
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '3.0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '3.0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: rspec-its
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
83
+ - !ruby/object:Gem::Dependency
84
+ name: fuubar
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - ">="
88
+ - !ruby/object:Gem::Version
89
+ version: '0'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - ">="
95
+ - !ruby/object:Gem::Version
96
+ version: '0'
97
+ - !ruby/object:Gem::Dependency
98
+ name: activerecord
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - ">="
102
+ - !ruby/object:Gem::Version
103
+ version: '3.0'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '3.0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: sqlite3
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ description: Writer methods parameters normalization
126
+ email:
127
+ - deemox@gmail.com
128
+ executables: []
129
+ extensions: []
130
+ extra_rdoc_files: []
131
+ files:
132
+ - ".gitignore"
133
+ - ".rspec"
134
+ - ".travis.yml"
135
+ - Gemfile
136
+ - LICENSE.txt
137
+ - README.md
138
+ - Rakefile
139
+ - lib/normalizr.rb
140
+ - lib/normalizr/concern.rb
141
+ - lib/normalizr/configuration.rb
142
+ - lib/normalizr/exceptions.rb
143
+ - lib/normalizr/normalizers.rb
144
+ - lib/normalizr/options_parser.rb
145
+ - lib/normalizr/rspec/matcher.rb
146
+ - lib/normalizr/version.rb
147
+ - normalizr.gemspec
148
+ - spec/lib/normalizr/concern_spec.rb
149
+ - spec/lib/normalizr/configuration_spec.rb
150
+ - spec/lib/normalizr_spec.rb
151
+ - spec/models/article_spec.rb
152
+ - spec/models/author_spec.rb
153
+ - spec/models/book_spec.rb
154
+ - spec/models/journal_spec.rb
155
+ - spec/models/magazine_spec.rb
156
+ - spec/models/publisher_spec.rb
157
+ - spec/models/user_spec.rb
158
+ - spec/spec_helper.rb
159
+ - spec/support/codeclimate.rb
160
+ - spec/support/connection.rb
161
+ - spec/support/models/article.rb
162
+ - spec/support/models/author.rb
163
+ - spec/support/models/book.rb
164
+ - spec/support/models/journal.rb
165
+ - spec/support/models/magazine.rb
166
+ - spec/support/models/person.rb
167
+ - spec/support/models/publisher.rb
168
+ - spec/support/models/user.rb
169
+ - spec/support/schema.rb
170
+ homepage: https://github.com/dimko/normalizr
171
+ licenses:
172
+ - MIT
173
+ metadata: {}
174
+ post_install_message:
175
+ rdoc_options: []
176
+ require_paths:
177
+ - lib
178
+ required_ruby_version: !ruby/object:Gem::Requirement
179
+ requirements:
180
+ - - ">="
181
+ - !ruby/object:Gem::Version
182
+ version: '2.0'
183
+ required_rubygems_version: !ruby/object:Gem::Requirement
184
+ requirements:
185
+ - - ">="
186
+ - !ruby/object:Gem::Version
187
+ version: '0'
188
+ requirements: []
189
+ rubyforge_project:
190
+ rubygems_version: 2.2.2
191
+ signing_key:
192
+ specification_version: 4
193
+ summary: Writer methods parameters normalization
194
+ test_files:
195
+ - spec/lib/normalizr/concern_spec.rb
196
+ - spec/lib/normalizr/configuration_spec.rb
197
+ - spec/lib/normalizr_spec.rb
198
+ - spec/models/article_spec.rb
199
+ - spec/models/author_spec.rb
200
+ - spec/models/book_spec.rb
201
+ - spec/models/journal_spec.rb
202
+ - spec/models/magazine_spec.rb
203
+ - spec/models/publisher_spec.rb
204
+ - spec/models/user_spec.rb
205
+ - spec/spec_helper.rb
206
+ - spec/support/codeclimate.rb
207
+ - spec/support/connection.rb
208
+ - spec/support/models/article.rb
209
+ - spec/support/models/author.rb
210
+ - spec/support/models/book.rb
211
+ - spec/support/models/journal.rb
212
+ - spec/support/models/magazine.rb
213
+ - spec/support/models/person.rb
214
+ - spec/support/models/publisher.rb
215
+ - spec/support/models/user.rb
216
+ - spec/support/schema.rb