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 +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
|