normalizr 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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