attribute_normalizer 0.2.0 → 0.2.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.
- data/README.textile +24 -6
- data/lib/attribute_normalizer/model_inclusions.rb +36 -0
- data/lib/attribute_normalizer/rspec_matcher.rb +45 -0
- data/lib/attribute_normalizer.rb +10 -85
- data/rails/init.rb +1 -1
- data/spec/book_spec.rb +13 -5
- data/spec/connection_and_schema.rb +16 -0
- data/spec/models/author.rb +13 -0
- data/spec/models/book.rb +13 -0
- data/spec/test_helper.rb +5 -27
- metadata +17 -4
data/README.textile
CHANGED
@@ -4,6 +4,10 @@ p. A little normalization goes a long way in helping maintain data integrity.
|
|
4
4
|
|
5
5
|
h2. Recent Changes
|
6
6
|
|
7
|
+
* 0.2.1
|
8
|
+
** ActiveModel Support Built-in
|
9
|
+
** Fix for :with option getting dropped when normalizing several attributes
|
10
|
+
|
7
11
|
* 0.2.0
|
8
12
|
** Removed the normalization on reads.
|
9
13
|
** Added out of the box support for CassandraObjects
|
@@ -11,11 +15,9 @@ h2. Recent Changes
|
|
11
15
|
** Added the ability to define global normalizers
|
12
16
|
** *Strings are no longer get 'strip' called on them before getting passed on to your defined normalization blocks*
|
13
17
|
|
14
|
-
* 0.1.2
|
15
|
-
** Changed post-normalization attribute assignment to call super to avoid preventing plugins/gems/hooks to work on the same attribute.
|
16
|
-
|
17
18
|
h2. Supported ORMs
|
18
19
|
|
20
|
+
* _Active Model_
|
19
21
|
* Active Record
|
20
22
|
* CassandraObjects
|
21
23
|
|
@@ -31,12 +33,18 @@ h2. Install
|
|
31
33
|
|
32
34
|
p. Then just required it. Rails usages is as follows.
|
33
35
|
|
36
|
+
h3. Rails 2
|
37
|
+
|
34
38
|
<pre><code># config/environment.rb
|
35
|
-
|
39
|
+
config.gem 'attribute_normalizer'</code></pre>
|
40
|
+
|
41
|
+
h3. Rails 3
|
42
|
+
<pre><code># Gemfile
|
43
|
+
gem 'attribute_normalizer'</code></pre>
|
36
44
|
|
37
45
|
p. It also still works as a traditional Rails plugin.
|
38
46
|
|
39
|
-
|
47
|
+
<pre><code>./script/plugin install git://github.com/mdeering/attribute_normalizer.git</code></pre>
|
40
48
|
|
41
49
|
h2. Usage
|
42
50
|
|
@@ -109,14 +117,24 @@ h2. Test Helpers
|
|
109
117
|
|
110
118
|
p. If you are running RSpec there is matcher available for use. Usage can been seen above. Include it as follows.
|
111
119
|
|
120
|
+
h3. Rails 2
|
121
|
+
|
112
122
|
<pre><code># spec/spec_helper.rb
|
113
123
|
# ...
|
114
|
-
|
115
124
|
Spec::Runner.configure do |config|
|
116
125
|
# ...
|
117
126
|
config.include AttributeNormalizer::RSpecMatcher, :type => :models
|
118
127
|
end</code></pre>
|
119
128
|
|
129
|
+
h3. Rails 3
|
130
|
+
|
131
|
+
<pre><code># spec/spec_helper.rb
|
132
|
+
# ...
|
133
|
+
Spec::Runner.configure do |config|
|
134
|
+
# ...
|
135
|
+
config.include AttributeNormalizer::RSpecMatcher # Models group type is missing at this time rspec-rails 2.0.0.beta.11
|
136
|
+
end</code></pre>
|
137
|
+
|
120
138
|
p. _I will gladly take a patch to add a macro to Test::Unit if someone submits it._
|
121
139
|
|
122
140
|
h2. Credits
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module AttributeNormalizer
|
2
|
+
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def normalize_attributes(*attributes, &block)
|
9
|
+
options = attributes.extract_options!
|
10
|
+
with = options.delete(:with)
|
11
|
+
|
12
|
+
attributes.each do |attribute|
|
13
|
+
define_method "normalize_#{attribute}" do |value|
|
14
|
+
normalized = if block_given? && !value.blank?
|
15
|
+
yield(value)
|
16
|
+
elsif !with.nil? && !value.blank?
|
17
|
+
unless AttributeNormalizer.configuration.normalizers.has_key?(with)
|
18
|
+
raise AttributeNormalizer::MissingNormalizer.new("No normalizer was found for #{with}")
|
19
|
+
end
|
20
|
+
AttributeNormalizer.configuration.normalizers[with].call(value, options)
|
21
|
+
else
|
22
|
+
value.is_a?(String) ? value.strip : value
|
23
|
+
end
|
24
|
+
normalized.nil? || (normalized.is_a?(String) && normalized == '') ? nil : normalized
|
25
|
+
end
|
26
|
+
self.send :private, "normalize_#{attribute}"
|
27
|
+
|
28
|
+
define_method "#{attribute}=" do |value|
|
29
|
+
super(self.send(:"normalize_#{attribute}", value))
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
alias :normalize_attribute :normalize_attributes
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
module AttributeNormalizer
|
2
|
+
|
3
|
+
module RSpecMatcher
|
4
|
+
|
5
|
+
def normalize_attribute(attribute)
|
6
|
+
NormalizeAttribute.new(attribute)
|
7
|
+
end
|
8
|
+
|
9
|
+
class NormalizeAttribute
|
10
|
+
|
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 from(value)
|
25
|
+
@from = value
|
26
|
+
self
|
27
|
+
end
|
28
|
+
|
29
|
+
def to(value)
|
30
|
+
@to = value
|
31
|
+
self
|
32
|
+
end
|
33
|
+
|
34
|
+
def matches?(subject)
|
35
|
+
@subject = subject
|
36
|
+
@subject.send("#{@attribute}=", @from)
|
37
|
+
|
38
|
+
@subject.send(@attribute) == @to
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
42
|
+
|
43
|
+
end
|
44
|
+
|
45
|
+
end
|
data/lib/attribute_normalizer.rb
CHANGED
@@ -11,102 +11,27 @@ module AttributeNormalizer
|
|
11
11
|
yield(configuration)
|
12
12
|
end
|
13
13
|
|
14
|
-
def self.included(base)
|
15
|
-
base.extend ClassMethods
|
16
|
-
end
|
17
|
-
|
18
14
|
class Configuration
|
19
|
-
|
20
15
|
attr_accessor :normalizers
|
21
|
-
|
22
16
|
def initialize
|
23
17
|
@normalizers = {}
|
24
18
|
end
|
25
|
-
|
26
19
|
end
|
27
20
|
|
28
|
-
|
29
|
-
|
30
|
-
def normalize_attributes(*attributes, &block)
|
31
|
-
options = attributes.extract_options!
|
32
|
-
|
33
|
-
attributes.each do |attribute|
|
34
|
-
|
35
|
-
klass = class << self; self end
|
36
|
-
|
37
|
-
klass.send :define_method, "normalize_#{attribute}" do |value|
|
38
|
-
normalized = if block_given? && !value.blank?
|
39
|
-
yield(value)
|
40
|
-
elsif !options[:with].nil? && !value.blank?
|
41
|
-
raise AttributeNormalizer::MissingNormalizer.new("No normalizer was found for #{options[:with]}") unless AttributeNormalizer.configuration.normalizers.has_key?(options[:with])
|
42
|
-
this = AttributeNormalizer.configuration.normalizers[options.delete(:with)].call(value, options)
|
43
|
-
else
|
44
|
-
value.is_a?(String) ? value.strip : value
|
45
|
-
end
|
46
|
-
normalized.nil? || (normalized.is_a?(String) && normalized == '') ? nil : normalized
|
47
|
-
end
|
48
|
-
|
49
|
-
klass.send :private, "normalize_#{attribute}"
|
50
|
-
|
51
|
-
src = <<-end_src
|
52
|
-
def #{attribute}=(value)
|
53
|
-
super(self.class.send(:normalize_#{attribute}, value))
|
54
|
-
end
|
55
|
-
end_src
|
56
|
-
|
57
|
-
module_eval src, __FILE__, __LINE__
|
58
|
-
|
59
|
-
end
|
60
|
-
|
61
|
-
end
|
62
|
-
|
63
|
-
alias :normalize_attribute :normalize_attributes
|
64
|
-
|
65
|
-
end
|
66
|
-
|
67
|
-
module RSpecMatcher
|
68
|
-
|
69
|
-
def normalize_attribute(attribute)
|
70
|
-
NormalizeAttribute.new(attribute)
|
71
|
-
end
|
72
|
-
|
73
|
-
class NormalizeAttribute
|
74
|
-
|
75
|
-
def description
|
76
|
-
"normalize #{@attribute} from #{@from.nil? ? 'nil' : "\"#{@from}\""} to #{@to.nil? ? 'nil' : "\"#{@to}\""}"
|
77
|
-
end
|
78
|
-
|
79
|
-
def failure_message
|
80
|
-
"#{@attribute} did not normalize as expected! \"#{@subject.send(@attribute)}\" != #{@to.nil? ? 'nil' : "\"#{@to}\""}"
|
81
|
-
end
|
82
|
-
|
83
|
-
def from(value)
|
84
|
-
@from = value
|
85
|
-
self
|
86
|
-
end
|
87
|
-
|
88
|
-
def initialize(attribute)
|
89
|
-
@attribute = attribute
|
90
|
-
@from = ''
|
91
|
-
end
|
21
|
+
end
|
92
22
|
|
93
|
-
def matches?(subject)
|
94
|
-
@subject = subject
|
95
|
-
@subject.send("#{@attribute}=", @from)
|
96
|
-
return false unless @subject.send(@attribute) == @to
|
97
|
-
true
|
98
|
-
end
|
99
23
|
|
100
|
-
|
101
|
-
|
102
|
-
self
|
103
|
-
end
|
24
|
+
require 'attribute_normalizer/model_inclusions'
|
25
|
+
require 'attribute_normalizer/rspec_matcher'
|
104
26
|
|
105
|
-
end
|
106
27
|
|
28
|
+
def include_attribute_normalizer(class_or_module)
|
29
|
+
return if class_or_module.include?(AttributeNormalizer)
|
30
|
+
class_or_module.class_eval do
|
31
|
+
extend AttributeNormalizer::ClassMethods
|
107
32
|
end
|
108
|
-
|
109
33
|
end
|
110
34
|
|
111
|
-
|
112
|
-
|
35
|
+
include_attribute_normalizer(ActiveModel::Base) if defined?(ActiveModel::Base)
|
36
|
+
include_attribute_normalizer(ActiveRecord::Base) if defined?(ActiveRecord::Base)
|
37
|
+
include_attribute_normalizer(CassandraObject::Base) if defined?(CassandraObject::Base)
|
data/rails/init.rb
CHANGED
@@ -1 +1 @@
|
|
1
|
-
require
|
1
|
+
require 'attribute_normalizer'
|
data/spec/book_spec.rb
CHANGED
@@ -4,14 +4,14 @@ describe Book do
|
|
4
4
|
|
5
5
|
it { should normalize_attribute(:author).from(' Michael Deering ').to('Michael Deering') }
|
6
6
|
|
7
|
-
it { should normalize_attribute(:
|
7
|
+
it { should normalize_attribute(:us_price).from('$3.50').to(3.50) }
|
8
|
+
it { should normalize_attribute(:cnd_price).from('$3,450.98').to(3450.98) }
|
8
9
|
|
9
10
|
it { should normalize_attribute(:summary).from('Here is my summary that is a little to long').to('Here is m...') }
|
10
11
|
|
11
|
-
it { should normalize_attribute(:title).from('pick up chicks with magic tricks').to('Pick Up Chicks With Magic Tricks')}
|
12
|
+
it { should normalize_attribute(:title).from('pick up chicks with magic tricks').to('Pick Up Chicks With Magic Tricks') }
|
12
13
|
|
13
14
|
context 'normalization should not interfear with other hooks and aliases on the attribute assignment' do
|
14
|
-
|
15
15
|
before do
|
16
16
|
@book = Book.create!(:title => 'Original Title')
|
17
17
|
end
|
@@ -19,11 +19,9 @@ describe Book do
|
|
19
19
|
it 'should still reflect that the attribute has been changed through the call to super' do
|
20
20
|
lambda { @book.title = 'New Title' }.should change(@book, :title_changed?).from(false).to(true)
|
21
21
|
end
|
22
|
-
|
23
22
|
end
|
24
23
|
|
25
24
|
context 'when another instance of the same saved record has been changed' do
|
26
|
-
|
27
25
|
before do
|
28
26
|
@book = Book.create!(:title => 'Original Title')
|
29
27
|
@book2 = Book.find(@book.id)
|
@@ -33,7 +31,17 @@ describe Book do
|
|
33
31
|
it "should reflect the change when the record is reloaded" do
|
34
32
|
lambda { @book.reload }.should change(@book, :title).from('Original Title').to('New Title')
|
35
33
|
end
|
34
|
+
end
|
35
|
+
|
36
|
+
context 'normalization should work with multiple attributes at the same time' do
|
37
|
+
before do
|
38
|
+
@book = Book.new(:title => ' Bad Title ', :author => ' Bad Author ')
|
39
|
+
end
|
36
40
|
|
41
|
+
it "should apply normalizations to both attributes" do
|
42
|
+
@book.title.should == 'Bad Title'
|
43
|
+
@book.author.should == 'Bad Author'
|
44
|
+
end
|
37
45
|
end
|
38
46
|
|
39
47
|
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
ActiveRecord::Base.establish_connection({
|
2
|
+
:database => ":memory:",
|
3
|
+
:adapter => 'sqlite3',
|
4
|
+
:timeout => 500
|
5
|
+
})
|
6
|
+
|
7
|
+
ActiveRecord::Schema.define do
|
8
|
+
create_table :books, :force => true do |t|
|
9
|
+
t.string :author
|
10
|
+
t.string :isbn
|
11
|
+
t.decimal :cnd_price
|
12
|
+
t.decimal :us_price
|
13
|
+
t.string :summary
|
14
|
+
t.string :title
|
15
|
+
end
|
16
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Author < ActiveModel::Base
|
2
|
+
|
3
|
+
normalize_attribute :name
|
4
|
+
|
5
|
+
normalize_attribute :hourly_cost, :with => :currency
|
6
|
+
|
7
|
+
normalize_attributes :biography, :with => :truncate, :length => 12
|
8
|
+
|
9
|
+
normalize_attributes :email_address do |value|
|
10
|
+
value.is_a?(String) ? value.titleize.strip : value
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
data/spec/models/book.rb
ADDED
@@ -0,0 +1,13 @@
|
|
1
|
+
class Book < ActiveRecord::Base
|
2
|
+
|
3
|
+
normalize_attribute :author
|
4
|
+
|
5
|
+
normalize_attribute :us_price, :cnd_price, :with => :currency
|
6
|
+
|
7
|
+
normalize_attributes :summary, :with => :truncate, :length => 12
|
8
|
+
|
9
|
+
normalize_attributes :title do |value|
|
10
|
+
value.is_a?(String) ? value.titleize.strip : value
|
11
|
+
end
|
12
|
+
|
13
|
+
end
|
data/spec/test_helper.rb
CHANGED
@@ -1,11 +1,11 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'spec'
|
3
|
-
require 'active_support'
|
4
3
|
require 'active_record'
|
5
4
|
|
6
5
|
$LOAD_PATH.unshift(File.dirname(__FILE__) + '/../lib')
|
7
6
|
require 'attribute_normalizer'
|
8
7
|
|
8
|
+
|
9
9
|
AttributeNormalizer.configure do |config|
|
10
10
|
|
11
11
|
config.normalizers[:currency] = lambda do |value, options|
|
@@ -25,33 +25,11 @@ AttributeNormalizer.configure do |config|
|
|
25
25
|
|
26
26
|
end
|
27
27
|
|
28
|
-
ActiveRecord::Base.establish_connection({ :database => ":memory:", :adapter => 'sqlite3', :timeout => 500 })
|
29
|
-
|
30
|
-
ActiveRecord::Schema.define do
|
31
|
-
create_table :books, :force => true do |t|
|
32
|
-
t.string :author
|
33
|
-
t.string :isbn
|
34
|
-
t.decimal :price
|
35
|
-
t.string :summary
|
36
|
-
t.string :title
|
37
|
-
end
|
38
|
-
end
|
39
|
-
|
40
|
-
class Book < ActiveRecord::Base
|
41
|
-
|
42
|
-
normalize_attribute :author
|
43
28
|
|
44
|
-
|
29
|
+
require 'connection_and_schema'
|
30
|
+
require 'models/book'
|
45
31
|
|
46
|
-
normalize_attributes :summary, :with => :truncate, :length => 12
|
47
|
-
|
48
|
-
normalize_attributes :title do |value|
|
49
|
-
value.is_a?(String) ? value.titleize.strip : value
|
50
|
-
end
|
51
|
-
|
52
|
-
end
|
53
32
|
|
54
33
|
Spec::Runner.configure do |config|
|
55
|
-
config.include AttributeNormalizer::RSpecMatcher
|
56
|
-
end
|
57
|
-
|
34
|
+
config.include AttributeNormalizer::RSpecMatcher
|
35
|
+
end
|
metadata
CHANGED
@@ -1,12 +1,13 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: attribute_normalizer
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
+
hash: 21
|
4
5
|
prerelease: false
|
5
6
|
segments:
|
6
7
|
- 0
|
7
8
|
- 2
|
8
|
-
-
|
9
|
-
version: 0.2.
|
9
|
+
- 1
|
10
|
+
version: 0.2.1
|
10
11
|
platform: ruby
|
11
12
|
authors:
|
12
13
|
- Michael Deering
|
@@ -14,7 +15,7 @@ autorequire:
|
|
14
15
|
bindir: bin
|
15
16
|
cert_chain: []
|
16
17
|
|
17
|
-
date: 2010-
|
18
|
+
date: 2010-08-18 00:00:00 -06:00
|
18
19
|
default_executable:
|
19
20
|
dependencies: []
|
20
21
|
|
@@ -33,9 +34,14 @@ files:
|
|
33
34
|
- install.rb
|
34
35
|
- install.txt
|
35
36
|
- lib/attribute_normalizer.rb
|
37
|
+
- lib/attribute_normalizer/model_inclusions.rb
|
38
|
+
- lib/attribute_normalizer/rspec_matcher.rb
|
36
39
|
- rails/init.rb
|
37
40
|
- spec/attribute_normalizer_spec.rb
|
38
41
|
- spec/book_spec.rb
|
42
|
+
- spec/connection_and_schema.rb
|
43
|
+
- spec/models/author.rb
|
44
|
+
- spec/models/book.rb
|
39
45
|
- spec/test_helper.rb
|
40
46
|
has_rdoc: true
|
41
47
|
homepage: http://github.com/mdeering/attribute_normalizer
|
@@ -49,27 +55,34 @@ rdoc_options:
|
|
49
55
|
require_paths:
|
50
56
|
- lib
|
51
57
|
required_ruby_version: !ruby/object:Gem::Requirement
|
58
|
+
none: false
|
52
59
|
requirements:
|
53
60
|
- - ">="
|
54
61
|
- !ruby/object:Gem::Version
|
62
|
+
hash: 3
|
55
63
|
segments:
|
56
64
|
- 0
|
57
65
|
version: "0"
|
58
66
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
67
|
+
none: false
|
59
68
|
requirements:
|
60
69
|
- - ">="
|
61
70
|
- !ruby/object:Gem::Version
|
71
|
+
hash: 3
|
62
72
|
segments:
|
63
73
|
- 0
|
64
74
|
version: "0"
|
65
75
|
requirements: []
|
66
76
|
|
67
77
|
rubyforge_project:
|
68
|
-
rubygems_version: 1.3.
|
78
|
+
rubygems_version: 1.3.7
|
69
79
|
signing_key:
|
70
80
|
specification_version: 3
|
71
81
|
summary: Attribute normalizer that excepts code blocks.
|
72
82
|
test_files:
|
73
83
|
- spec/attribute_normalizer_spec.rb
|
74
84
|
- spec/book_spec.rb
|
85
|
+
- spec/connection_and_schema.rb
|
86
|
+
- spec/models/author.rb
|
87
|
+
- spec/models/book.rb
|
75
88
|
- spec/test_helper.rb
|