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 +131 -125
- data/lib/normatron.rb +3 -2
- data/lib/normatron/configuration.rb +24 -3
- data/lib/normatron/extensions/active_record.rb +40 -28
- data/lib/normatron/filters.rb +254 -6
- data/spec/configuration_spec.rb +53 -0
- data/spec/extensions/active_record_spec.rb +114 -0
- data/spec/filters_spec.rb +199 -0
- data/spec/normatron_spec.rb +18 -0
- data/spec/spec_helper.rb +0 -3
- data/spec/support/model_model.rb +3 -0
- data/spec/support/schema.rb +4 -7
- metadata +12 -18
- data/lib/normatron/filters/conversions.rb +0 -29
- data/lib/normatron/filters/string_inflections.rb +0 -155
- data/spec/lib/normatron/configuration_spec.rb +0 -45
- data/spec/lib/normatron/extensions/active_record_spec.rb +0 -147
- data/spec/lib/normatron/filters/conversions_spec.rb +0 -40
- data/spec/lib/normatron/filters/string_inflections_spec.rb +0 -268
- data/spec/lib/normatron/filters_spec.rb +0 -22
- data/spec/lib/normatron_spec.rb +0 -22
- data/spec/support/client_model.rb +0 -3
data/README.textile
CHANGED
@@ -39,7 +39,7 @@ class Product < ActiveRecord::Base
|
|
39
39
|
end
|
40
40
|
</pre>
|
41
41
|
|
42
|
-
And
|
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
|
-
|
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
|
-
|
66
|
-
|
67
|
-
|
63
|
+
$ rails console
|
64
|
+
> p1 = Product.create name: " memory card "
|
65
|
+
> p1
|
68
66
|
=> #<Product id: nil, name: "memory card", price: nil>
|
69
|
-
|
70
|
-
|
67
|
+
> p2 = Product.create name: " "
|
68
|
+
> p2
|
71
69
|
=> #<Product id: nil, name: nil, price: nil>
|
72
70
|
</pre>
|
73
71
|
|
74
|
-
|
72
|
+
h3. The _normalize_attributes_ method
|
75
73
|
|
76
|
-
|
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
|
-
|
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
|
-
|
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
|
-
|
85
|
-
|
86
|
-
|
87
|
-
=> "
|
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
|
-
|
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
|
-
|
102
|
-
|
103
|
-
|
104
|
-
=> "HD"
|
106
|
+
$ rails console
|
107
|
+
> Product.normalize_options
|
108
|
+
=> { name: { :squish => [], :blank => [] } }
|
105
109
|
</pre>
|
106
110
|
|
107
|
-
|
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
|
-
|
110
|
-
|
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
|
-
|
129
|
+
h3. The _:with_ option
|
113
130
|
|
114
|
-
|
131
|
+
The _:with_ option allows to bind filters to one attribute or more.
|
115
132
|
|
116
133
|
<pre>
|
117
|
-
|
118
|
-
normalize :
|
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
|
-
|
159
|
+
The same result can be obtained using:
|
160
|
+
|
161
|
+
<pre>normalize :name, :with => [:squish, :blank, :upcase]</pre>
|
122
162
|
|
123
|
-
|
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
|
-
|
127
|
-
|
128
|
-
|
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
|
-
|
131
|
-
|
132
|
-
normalize :
|
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
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
=>
|
143
|
-
|
144
|
-
=>
|
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.
|
198
|
+
h3. Using instance method as filter
|
148
199
|
|
149
|
-
Create an instance method returning the value as you want.
|
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
|
-
|
207
|
+
normalize :phone, :with => [:custom_a, [:custom_b, :a, :b], { :custom_c => [:a, :b, :c] }]
|
155
208
|
|
156
|
-
|
209
|
+
def custom_a(value)
|
210
|
+
# ...
|
211
|
+
end
|
157
212
|
|
158
|
-
def
|
159
|
-
|
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
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
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
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
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.
|
238
|
+
h1. Credits
|
213
239
|
|
214
|
-
This gem was inspired on
|
215
|
-
|
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.
|
244
|
+
h1. License
|
218
245
|
|
219
|
-
|
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.
|
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
|
-
|
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
|
23
|
+
@default_filters = {squish: [], blank: []}
|
7
24
|
end
|
8
25
|
|
9
26
|
def default_filters
|
10
|
-
@default_filters
|
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
|
-
|
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
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
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
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
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
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
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
|
-
|
57
|
+
write_attribute attribute, value
|
58
|
+
end
|
47
59
|
end
|
48
60
|
end
|
49
61
|
end
|