normatron 0.1.1 → 0.2.0

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