normatron 0.1.1 → 0.2.0

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 CHANGED
@@ -39,7 +39,7 @@ class Product < ActiveRecord::Base
39
39
  end
40
40
  </pre>
41
41
 
42
- And you want the _name_ attribute be uppercased before saving it into the database.
42
+ And we want the _name_ attribute be uppercased before saving it into the database.
43
43
  The most usual approach to do this includes:
44
44
 
45
45
  * Override the _name_ setter and convert the value to an uppercased string.
@@ -50,9 +50,7 @@ What led me to make this gem and offer a third way to solve the problem:
50
50
 
51
51
  h2. Usage
52
52
 
53
- h3. The default filters
54
-
55
- Normatron uses *:squish* and *:blank* as default filters.
53
+ Normatron uses ":squish":http://rubydoc.info/gems/normatron/Normatron/Filters#squish-class_method and ":blank":http://rubydoc.info/gems/normatron/Normatron/Filters#blank-class_method as default filters.
56
54
  These filters are applied to all attributes in *normalize* function, since no options is given.
57
55
 
58
56
  <pre>
@@ -62,144 +60,172 @@ class Product < ActiveRecord::Base
62
60
  normalize :name
63
61
  end
64
62
 
65
- $ rails console
66
- > p = Product.create name: " memory card "
67
- > p
63
+ $ rails console
64
+ > p1 = Product.create name: " memory card "
65
+ > p1
68
66
  => #<Product id: nil, name: "memory card", price: nil>
69
- > q = Product.create name: " "
70
- > q
67
+ > p2 = Product.create name: " "
68
+ > p2
71
69
  => #<Product id: nil, name: nil, price: nil>
72
70
  </pre>
73
71
 
74
- You can also apply the default filters to multiple attributes as following:
72
+ h3. The _normalize_attributes_ method
75
73
 
76
- <pre>normalize :login, :email, :address, :city</pre>
74
+ All filters are automatically applied in the "before_validation":http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html callback.
75
+ So, the changes occur when you call any method that validates the model.
77
76
 
78
- h3. Changes occur before validation
77
+ <pre>
78
+ $ rails console
79
+ > p = Product.create name: " "
80
+ > p.name
81
+ => nil
82
+ > p = Product.new name: " keyboard \n "
83
+ > p.valid?
84
+ > p.name
85
+ => "keyboard"
86
+ > p = Product.new name: "\n\nblu ray\n\n"
87
+ > p.save
88
+ > p.name
89
+ => "blu ray"
90
+ </pre>
79
91
 
80
- All filters are automatically applyied in the *before_validation* callback.
81
- So, the changes occur when you call any method that validates de model.
92
+ We can call the *normalize_attributes* method to perform the normalization without do the validations.
82
93
 
83
94
  <pre>
84
- $ rails console
85
- > p = Product.create name: "mouse"
86
- > p.name
87
- => "MOUSE"
88
- > p = Product.new name: "keyboard"
89
- > p.valid?
90
- > p.name
91
- => "KEYBOARD"
92
- > p = Product.new name: "dvd"
93
- > p.save
94
- > p.name
95
- => "DVD"
95
+ > p = Product.new name: " hard drive"
96
+ > p.normalize_attributes
97
+ > p.name
98
+ => "hard drive"
96
99
  </pre>
97
100
 
98
- It's possible to call the *apply_normalizations* method. That perform the normalization without do the validations.
101
+ h3. The _normalize_options_ method
102
+
103
+ To read the normalization filters set for a model, just call the *normalize_options* method.
99
104
 
100
105
  <pre>
101
- > p = Product.new name: "hd"
102
- > p.apply_normalizations
103
- > p.name
104
- => "HD"
106
+ $ rails console
107
+ > Product.normalize_options
108
+ => { name: { :squish => [], :blank => [] } }
105
109
  </pre>
106
110
 
107
- h3. Specifying filters
111
+ This method returns a Hash, where the keys are the attribute names and values ​​are another Hash with filter options.
112
+ In Hash with filter options, keys represent the filter names and the values ​​are Arrays with the arguments that are passed to the methods of filtering.
113
+ The following example is a data structure returned by the _normalize_options_ method.
108
114
 
109
- Just add the *:with* option and pass the filters you want.
110
- All Symbols outside the *:with* Hash are flagged as an attribute.
115
+ <pre>
116
+ $ rails console
117
+ > MyModel.normalize_options
118
+ => { :attribute_a => { :filter_a => [] },
119
+ :attribute_b => { :filter_a => [],
120
+ :filter_b => [arg_a, arg_b] },
121
+ :attribute_c => { :filter_b => [arg_a, arg_b],
122
+ :filter_c => [arg],
123
+ :filter_d => [] },
124
+ :attribute_d => { :filter_a => [],
125
+ :filter_c => [arg],
126
+ :filter_d => [arg_a, arg_b, arg_c, arg_d] } }
127
+ </pre>
111
128
 
112
- <pre>normalize :login, :address, :city, :with => :upcase</pre>
129
+ h3. The _:with_ option
113
130
 
114
- To apply multiple filters, pass it as an array:
131
+ The _:with_ option allows to bind filters to one attribute or more.
115
132
 
116
133
  <pre>
117
- normalize :login, :address, :city, :with => [:upcase, :blank, :strip]
118
- normalize :email, :with => [:downcase, :blank, :strip]
134
+ class Product < ActiveRecord::Base
135
+ normalize :name, :with => :upcase
136
+ end
137
+
138
+ $ rails console
139
+ > Product.normalize_options
140
+ => { :name => { :upcase => [] } }
141
+ </pre>
142
+
143
+ The filters passed throught _:with_ option will not stack with default filters.
144
+ When _normalize_ method is used multiple times for the same attribute, it will stack the filter bindings.
145
+
146
+ <pre>
147
+ class Product < ActiveRecord::Base
148
+ normalize :name
149
+ normalize :name, :with => :upcase
150
+ end
151
+
152
+ $ rails console
153
+ > Product.normalize_options
154
+ => { :name => { :squish => [],
155
+ :blank => [],
156
+ :upcase => [] } }
119
157
  </pre>
120
158
 
121
- h3. Stacking Filters
159
+ The same result can be obtained using:
160
+
161
+ <pre>normalize :name, :with => [:squish, :blank, :upcase]</pre>
122
162
 
123
- You can stack filters to an attribute calling the *normalize* method multiple times.
163
+ Some filters may use arguments to perferm normalizations.
164
+ There are two approaches to deal with filter arguments in Normatron:
165
+
166
+ a) Using a Hash where the key is the filter name and the value is the filter arguments as an Array.
124
167
 
125
168
  <pre>
126
- # ./app/models/user.rb
127
- class User < ActiveRecord::Base
128
- attr_accessible :name, :surname, :login, :password, :email
169
+ class Product < ActiveRecord::Base
170
+ normalize :name, :with => [ { :keep => [:Latin], :remove => [:Nd, :Zs] } ]
171
+ normalize :description, :with => :squeeze
172
+ normalize :brand, :with => [ { :squeeze => ["a-z"] }, { :keep => [:Word] } ]
173
+ end
174
+ </pre>
175
+
176
+ b) Using an Array where the first element if the attribute name and rest is the filter arguments.
129
177
 
130
- normalize :name, :surname, :email, :login, :with => [:blank, :squish]
131
- normalize :name, :surname, :with => :upcase
132
- normalize :email, :with => :downcase
178
+ <pre>
179
+ class Product < ActiveRecord::Base
180
+ normalize :name, :with => [ [:keep, :Latin], [:remove, :Nd, :Zs] ]
181
+ normalize :description, :with => :squeeze
182
+ normalize :brand, :with => [ [:squeeze, "a-z"], [:keep, :Word] ]
133
183
  end
184
+ </pre>
134
185
 
135
- $ rails console
136
- > u = User.new
137
- > u.name = " \n \f \r \t "
138
- > u.surname = " norris "
139
- > u.login = " iKICKasses"
140
- > u.email = "GMAIL@CHUCKNORRIS.COM"
141
- > u.valid?
142
- => true
143
- > u
144
- => #<User id: nil, name: nil, surname: "NORRIS", login: "iKICKasses", email: "gmail@chucknorris.com">
186
+ Both ways will produce the same result:
187
+
188
+ <pre>
189
+ $ rails console
190
+ > Product.normalize_options
191
+ => { :name => { :keep => [:Latin],
192
+ :remove => [:Nd, :Zs] },
193
+ :description => { :squeeze => [] },
194
+ :brand => { :squeeze => ["a-z"],
195
+ :keep => [:Word] } }
145
196
  </pre>
146
197
 
147
- h3. Model instance filter
198
+ h3. Using instance method as filter
148
199
 
149
- Create an instance method returning the value as you want. The method must have an unique mandatory parameter.
200
+ Create an instance method returning the value as you want.
201
+ The first argument is mandatory, and will receive the original value of the attribute.
202
+ If you need to use aditional arguments or varargs, just add them after the first argument.
150
203
 
151
204
  <pre>
152
205
  # ./app/models/client.rb
153
206
  class Client < ActiveRecord::Base
154
- attr_accessible :name, :phone
207
+ normalize :phone, :with => [:custom_a, [:custom_b, :a, :b], { :custom_c => [:a, :b, :c] }]
155
208
 
156
- normalize :phone, :with => :my_phone_filter
209
+ def custom_a(value)
210
+ # ...
211
+ end
157
212
 
158
- def my_phone_filter(value)
159
- digits = value.gsub(/\D/, '')
160
- "(%s) %s-%s" % [digits[0..1], digits[2..5], digits[6..9]]
213
+ def custom_b(value, *args)
214
+ # ...
161
215
  end
162
- end
163
216
 
164
- $ rails console
165
- > p = Client.create :name => "Jim Raynor", :phone => "!a1b@c2d#e3f$g4h i5j%k6l&m7n*o8p(q9r)s1t "
166
- > p.phone
167
- => "(12) 3456-7891"
217
+ def custom_c(value, arg_a, arg_b, arg_c)
218
+ # ...
219
+ end
220
+ end
168
221
  </pre>
169
222
 
170
223
  h2. Filters
171
224
 
172
- table.
173
- |_. FILTER|_. DESCRIPTION|
174
- |:alphas|Remove all non letter characters|
175
- |:blank|Convert a blank String into a Nil object|
176
- |:capitalize|The first character will be uppercased, all others will be lowercased|
177
- |:dasherize|Replace all underscores with dashes|
178
- |:digits|Remove all non digit characters|
179
- |:downcase|Lowercase all characters on the sequence|
180
- |:lstrip|Remove trailing spaces|
181
- |:rstrip|Remove leading spaces|
182
- |:squeeze|Replace followed occurrences of the same character by a single one|
183
- |:squish|Remove trailing, leading, surplus spaces and scape characters|
184
- |:strip|Remove trailing and leading spaces|
185
- |:title|Downcase all characters in the sequence and upcase the first character of each word|
186
- |:upcase|Upcase all characters in the sequence|
187
-
188
- h1. Changelog
189
-
190
- - 0.1.1 :=
191
- * Two new filters: alphas and digits
192
- * Blank filter extracted from StringInflections to Conversors
193
- * Fixed bug filter stack to same attribute
194
- * Nil attributes will be ignored
195
- =:
196
- - 0.1.0 :=
197
- * Two new filters: dasherize and squeeze
198
- * Native filters replaced by StringInflections
199
- =:
200
- - 0.0.7 :=
201
- * Ability to use an instance method as a filter
202
- =:
225
+ Information about native filters and how to use them can be found in:
226
+
227
+ * "Normatron::Filters Rubydoc (Normatron::Filters Rubydoc)":http://rubydoc.info/gems/normatron/Normatron/Filters
228
+ * "Normatron::Filters Source (Normatron::Filters Source)":https://github.com/fernandors87/normatron/blob/master/lib/normatron/filters.rb
203
229
 
204
230
  h1. Contributing
205
231
 
@@ -209,32 +235,12 @@ h1. Contributing
209
235
  * Send me your feedback.
210
236
  * Offer me a job or pay me a beer. =]
211
237
 
212
- h1. Special Thanks
238
+ h1. Credits
213
239
 
214
- This gem was inspired on work of "fnando (fnando's Github)":https://github.com/fnando called "normalize_attributes (normalize_attributes gem)":https://github.com/fnando/normalize_attributes.
215
- He have a lot of interesting gems on his repo, please take a look.
240
+ This gem was initially inspired on these gems:
241
+ * "normalize_attributes (normalize_attributes Gem)":https://github.com/fnando/normalize_attributes - "fnando (fnando's Github)":https://github.com/fnando
242
+ * "attribute_normalizer (attribute_normalizer Gem)":https://github.com/mdeering/attribute_normalizer - "mdeering (mdeering's Github)":https://github.com/mdeering
216
243
 
217
- h1. MIT Licence
244
+ h1. License
218
245
 
219
- <pre>
220
- Copyright 2012 Fernando Rodrigues da Silva
221
-
222
- Permission is hereby granted, free of charge, to any person obtaining
223
- a copy of this software and associated documentation files (the
224
- "Software"), to deal in the Software without restriction, including
225
- without limitation the rights to use, copy, modify, merge, publish,
226
- distribute, sublicense, and/or sell copies of the Software, and to
227
- permit persons to whom the Software is furnished to do so, subject to
228
- the following conditions:
229
-
230
- The above copyright notice and this permission notice shall be
231
- included in all copies or substantial portions of the Software.
232
-
233
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
234
- EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
235
- MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
236
- NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
237
- LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
238
- OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
239
- WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
240
- </pre>
246
+ See file attached to source code or click "here":https://github.com/fernandors87/normatron/blob/master/MIT-LICENSE.
data/lib/normatron.rb CHANGED
@@ -2,12 +2,13 @@ require "normatron/configuration"
2
2
  require "normatron/extensions/active_record"
3
3
 
4
4
  module Normatron
5
- VERSION = "0.1.1"
5
+ VERSION = "0.2.0"
6
6
 
7
7
  class << self
8
8
  def configuration
9
9
  @configuration ||= Configuration.new
10
- end
10
+ end
11
+ alias :config :configuration
11
12
 
12
13
  def configure
13
14
  yield(configuration)
@@ -1,13 +1,34 @@
1
1
  module Normatron
2
2
  class Configuration
3
- attr_writer :default_filters
3
+ def self.clean_filters(*with)
4
+ elements = with.flatten(1)
5
+ raise "empty" unless elements.any?
6
+
7
+ result = {}
8
+ elements.each do |e|
9
+ case e
10
+ when Array
11
+ result[e[0].to_sym] = e[1..-1]
12
+ when Hash
13
+ e.each { |key, value| result[key] = *value.flatten(1) }
14
+ else
15
+ result[e.to_sym] = []
16
+ end
17
+ end
18
+
19
+ result
20
+ end
4
21
 
5
22
  def initialize
6
- @default_filters ||= [:squish, :blank]
23
+ @default_filters = {squish: [], blank: []}
7
24
  end
8
25
 
9
26
  def default_filters
10
- @default_filters = [@default_filters].flatten.compact.uniq
27
+ @default_filters
28
+ end
29
+
30
+ def default_filters=(filters)
31
+ @default_filters = self.class.clean_filters(filters)
11
32
  end
12
33
  end
13
34
  end
@@ -5,45 +5,57 @@ module Normatron
5
5
  module ActiveRecord
6
6
  def self.included(base)
7
7
  base.instance_eval do
8
- before_validation :apply_normalizations
8
+ extend ClassMethods
9
+ include InstanceMethods
10
+ before_validation :normalize_attributes
9
11
 
10
12
  class << self
11
13
  attr_accessor :normalize_options
14
+ end
15
+ end
16
+ end
12
17
 
13
- def normalize(*args)
14
- options = args.extract_options!
15
- return nil unless args
16
-
17
- options[:with] = [options[:with]].flatten.compact
18
- options[:with] = Normatron.configuration.default_filters if options[:with].empty?
18
+ module ClassMethods
19
+ def normalize(*args)
20
+ # Check existence of all attributes
21
+ options = args.extract_options!
22
+ args, columns = args.map(&:to_sym), column_names.map(&:to_sym)
23
+ raise "attribute" if (columns & args).size != args.size
24
+
25
+ # Specify the use of default filters or not
26
+ if options[:with].nil? || options[:with].blank?
27
+ new_filters = Normatron.config.default_filters
28
+ else
29
+ new_filters = Normatron::Configuration.clean_filters(options[:with])
30
+ end
19
31
 
20
- @normalize_options ||= {}
21
- args.each do |attribute|
22
- @normalize_options[attribute] ||= []
23
- @normalize_options[attribute] += options[:with]
24
- @normalize_options[attribute] = @normalize_options[attribute].uniq
25
- end
26
- @normalize_options
27
- end
32
+ # Append to older filters hash
33
+ @normalize_options ||= {}
34
+ @normalize_options =
35
+ args.reduce(@normalize_options) do |hash, att|
36
+ filters = (@normalize_options[att] || {}).merge(new_filters)
37
+ hash.merge({att => filters})
28
38
  end
29
39
  end
30
40
  end
31
41
 
32
- def apply_normalizations
33
- self.class.normalize_options.each do |attribute, filters|
34
- value = send("#{attribute}_before_type_cast") || send(attribute)
35
-
36
- filters.each do |filter|
37
- if self.respond_to? filter
38
- value = send(filter, value)
39
- elsif Normatron::Filters::StringInflections.respond_to? filter
40
- value = Normatron::Filters::StringInflections.send(filter, value)
41
- else
42
- value = Normatron::Filters::Conversions.send(filter, value)
42
+ module InstanceMethods
43
+ def normalize_attributes
44
+ self.class.normalize_options.each do |attribute, filters|
45
+ value = send("#{attribute}_before_type_cast") || send(attribute)
46
+
47
+ filters.each do |filter, args|
48
+ if self.respond_to? filter
49
+ value = send(filter, value, *args)
50
+ elsif Normatron::Filters.respond_to? filter
51
+ value = Normatron::Filters.send(filter, value, *args)
52
+ else
53
+ raise "Filter '#{filter}' wasn't found."
54
+ end
43
55
  end
44
- end
45
56
 
46
- write_attribute attribute, value
57
+ write_attribute attribute, value
58
+ end
47
59
  end
48
60
  end
49
61
  end