normatron 0.2.1 → 0.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README.textile +35 -48
- data/Rakefile +6 -7
- data/lib/generators/normatron/install_generator.rb +23 -0
- data/lib/generators/normatron/templates/normatron.rb +4 -0
- data/lib/normatron.rb +22 -8
- data/lib/normatron/configuration.rb +26 -22
- data/lib/normatron/extensions.rb +8 -0
- data/lib/normatron/extensions/active_record.rb +20 -15
- data/lib/normatron/filters.rb +26 -379
- data/lib/normatron/filters/blank_filter.rb +29 -0
- data/lib/normatron/filters/camelize_filter.rb +50 -0
- data/lib/normatron/filters/capitalize_filter.rb +29 -0
- data/lib/normatron/filters/chomp_filter.rb +34 -0
- data/lib/normatron/filters/dasherize_filter.rb +25 -0
- data/lib/normatron/filters/downcase_filter.rb +29 -0
- data/lib/normatron/filters/dump_filter.rb +27 -0
- data/lib/normatron/filters/helpers.rb +44 -0
- data/lib/normatron/filters/keep_filter.rb +100 -0
- data/lib/normatron/filters/remove_filter.rb +37 -0
- data/lib/normatron/filters/squeeze_filter.rb +30 -0
- data/lib/normatron/filters/squish_filter.rb +28 -0
- data/lib/normatron/filters/strip_filter.rb +33 -0
- data/lib/normatron/filters/swapcase_filter.rb +30 -0
- data/lib/normatron/filters/titleize_filter.rb +29 -0
- data/lib/normatron/filters/underscore_filter.rb +45 -0
- data/lib/normatron/filters/upcase_filter.rb +29 -0
- data/lib/normatron/version.rb +3 -0
- data/spec/normatron/configuration_spec.rb +60 -0
- data/spec/normatron/extensions/active_record_spec.rb +96 -0
- data/spec/normatron/filters/blank_filter_spec.rb +15 -0
- data/spec/normatron/filters/camelize_filter_spec.rb +42 -0
- data/spec/normatron/filters/capitalize_filter_spec.rb +14 -0
- data/spec/normatron/filters/chomp_filter_spec.rb +15 -0
- data/spec/normatron/filters/dasherize_filter_spec.rb +9 -0
- data/spec/normatron/filters/downcase_filter_spec.rb +10 -0
- data/spec/normatron/filters/dump_filter_spec.rb +10 -0
- data/spec/normatron/filters/keep_filter_spec.rb +86 -0
- data/spec/normatron/filters/remove_filter_spec.rb +86 -0
- data/spec/normatron/filters/squeeze_filter_spec.rb +10 -0
- data/spec/normatron/filters/squish_filter_spec.rb +12 -0
- data/spec/normatron/filters/strip_filter_spec.rb +12 -0
- data/spec/normatron/filters/swapcase_filter_spec.rb +12 -0
- data/spec/normatron/filters/titleize_filter_spec.rb +12 -0
- data/spec/normatron/filters/underscore_filter_spec.rb +26 -0
- data/spec/normatron/filters/upcase_filter_spec.rb +10 -0
- data/spec/normatron_spec.rb +28 -2
- data/spec/spec_helper.rb +37 -4
- data/spec/support/my_filters.rb +7 -0
- data/spec/support/user_model.rb +14 -0
- metadata +64 -13
- data/spec/configuration_spec.rb +0 -53
- data/spec/extensions/active_record_spec.rb +0 -114
- data/spec/filters_spec.rb +0 -442
- data/spec/support/model_model.rb +0 -3
- data/spec/support/schema.rb +0 -7
data/README.textile
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
h1. Normatron
|
2
2
|
|
3
|
-
Normatron is
|
3
|
+
Normatron is a Ruby On Rails plugin that perform attribute normalizations for ActiveRecord objects.
|
4
4
|
With it you can normalize attributes to the desired format before saving them in the database.
|
5
5
|
This gem inhibits the work of having to override attributes or create a specific method to perform most of the normalizations.
|
6
6
|
|
@@ -18,6 +18,10 @@ Or install it by yourself:
|
|
18
18
|
|
19
19
|
pre. $ gem install normatron
|
20
20
|
|
21
|
+
Then run the generator:
|
22
|
+
|
23
|
+
pre. $ rails generator normatron:install
|
24
|
+
|
21
25
|
h2. The problem
|
22
26
|
|
23
27
|
Suppose you have a product model as the following:
|
@@ -41,15 +45,15 @@ And we want the _name_ attribute be uppercased before saving it into the databas
|
|
41
45
|
The most usual approach to do this includes:
|
42
46
|
|
43
47
|
* Override the _name_ setter and convert the value to an uppercased string.
|
44
|
-
*
|
48
|
+
* Write a method or block and bind it to the "before_validation":http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html callback.
|
45
49
|
|
46
50
|
Both ways are ilenegant, boring, error prone and very expensive.
|
47
51
|
What led me to make this gem and offer a third way to solve this issue:
|
48
52
|
|
49
53
|
h2. Usage
|
50
54
|
|
51
|
-
Normatron uses "
|
52
|
-
These filters are applied to all attributes
|
55
|
+
Normatron uses ":squish":http://rubydoc.info/gems/normatron/Normatron/Filters/SquishFilter and ":blank":http://rubydoc.info/gems/normatron/Normatron/Filters/BlankFilter as default filters.
|
56
|
+
These filters are applied to all attributes through "normalize":http://rubydoc.info/gems/normatron/Normatron/Extensions/ActiveRecord/ClassMethods#normalize-instance_method function, since no options is given.
|
53
57
|
|
54
58
|
pre. # ./app/models/products.rb
|
55
59
|
class Product < ActiveRecord::Base
|
@@ -58,48 +62,30 @@ class Product < ActiveRecord::Base
|
|
58
62
|
end
|
59
63
|
|
60
64
|
pre. $ rails console
|
61
|
-
>
|
65
|
+
> memory = Product.create name: " memory card "
|
62
66
|
=> #<Product id: nil, name: "memory card", price: nil>
|
63
|
-
>
|
67
|
+
> null = Product.create name: " "
|
64
68
|
=> #<Product id: nil, name: nil, price: nil>
|
65
69
|
|
66
|
-
h3.
|
70
|
+
h3. Configurations
|
67
71
|
|
68
|
-
|
69
|
-
This is the method that invokes all filters to their attributes. So you can perform the normalizations without necessarily having to perform the model validations.
|
72
|
+
"normatron:install":http://rubydoc.info/gems/normatron/Normatron/InstallGenerator generator creates a configuration file located at @./config/initializers/normatron.rb@.
|
70
73
|
|
71
|
-
|
72
|
-
> p = Product.new name: " hard drive"
|
73
|
-
=> #<Product id: nil, name: " hard drive", price: nil>
|
74
|
-
> p.normalize_attributes
|
75
|
-
> p
|
76
|
-
=> #<Product id: nil, name: "hard drive", price: nil>
|
74
|
+
For now, you can set the default normalization filters or disable normatron by commenting the @add_orm@ line.
|
77
75
|
|
78
|
-
h3.
|
76
|
+
h3. Applying normalizations
|
79
77
|
|
80
|
-
|
78
|
+
Methods like "create":http://api.rubyonrails.org/classes/ActiveRecord/Persistence/ClassMethods.html#method-i-create, "valid?":http://api.rubyonrails.org/classes/ActiveRecord/Validations.html#method-i-valid-3F or "save":http://api.rubyonrails.org/classes/ActiveRecord/Validations.html#method-i-valid-3F always call the "apply_normalizations":http://rubydoc.info/gems/normatron/Normatron/Extensions/ActiveRecord/InstanceMethods#apply_normalizations-instance_method method, thought "before_validation":http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html callback.
|
79
|
+
This is the method that invokes all filters to their attributes. So you can perform the normalizations without necessarily having to perform the model validations.
|
81
80
|
|
82
81
|
pre. $ rails console
|
83
|
-
> Product.
|
84
|
-
=>
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
The following example is a data structure returned by the @normalize_options@ method:
|
82
|
+
> product = Product.new name: " hard drive"
|
83
|
+
=> #<Product id: nil, name: " hard drive", price: nil>
|
84
|
+
> product.apply_normalizations
|
85
|
+
> product
|
86
|
+
=> #<Product id: nil, name: "hard drive", price: nil>
|
89
87
|
|
90
|
-
|
91
|
-
> MyModel.normalize_options
|
92
|
-
=> { :attribute_a => { :filter_a => [] },
|
93
|
-
:attribute_b => { :filter_a => [],
|
94
|
-
:filter_b => [arg_a, arg_b] },
|
95
|
-
:attribute_c => { :filter_b => [arg_a, arg_b],
|
96
|
-
:filter_c => [arg],
|
97
|
-
:filter_d => [] },
|
98
|
-
:attribute_d => { :filter_a => [],
|
99
|
-
:filter_c => [arg],
|
100
|
-
:filter_d => [arg_a, arg_b, arg_c, arg_d] } }
|
101
|
-
|
102
|
-
h3. The _:with_ option
|
88
|
+
h3. Setting normalization rules
|
103
89
|
|
104
90
|
The @:with@ option allows to bind filters to one or more attribute.
|
105
91
|
|
@@ -108,11 +94,11 @@ pre. class Product < ActiveRecord::Base
|
|
108
94
|
end
|
109
95
|
|
110
96
|
pre. $ rails console
|
111
|
-
> Product.
|
97
|
+
> Product.normalize_filters
|
112
98
|
=> { :name => { :upcase => [] } }
|
113
99
|
|
114
100
|
The filters passed throught @:with@ option will not stack with default filters.
|
115
|
-
When
|
101
|
+
When "normalize":http://rubydoc.info/gems/normatron/Normatron/Extensions/ActiveRecord/ClassMethods#normalize-instance_method method is used multiple times for the same attribute, it will stack the filter bindings.
|
116
102
|
|
117
103
|
pre. class Product < ActiveRecord::Base
|
118
104
|
normalize :name
|
@@ -120,7 +106,7 @@ pre. class Product < ActiveRecord::Base
|
|
120
106
|
end
|
121
107
|
|
122
108
|
pre. $ rails console
|
123
|
-
> Product.
|
109
|
+
> Product.normalize_filters
|
124
110
|
=> { :name => { :squish => [],
|
125
111
|
:blank => [],
|
126
112
|
:upcase => [] } }
|
@@ -137,7 +123,7 @@ a) Using a Hash where the key is the filter name and the value is an arguments A
|
|
137
123
|
pre. class Product < ActiveRecord::Base
|
138
124
|
normalize :name, :with => [ { :keep => [:Latin], :remove => [:Nd, :Zs] } ]
|
139
125
|
normalize :description, :with => :squeeze
|
140
|
-
normalize :brand, :with =>
|
126
|
+
normalize :brand, :with => { :squeeze => ["a-z"] }
|
141
127
|
end
|
142
128
|
|
143
129
|
b) Using an Array where the first element if the attribute name and rest is the filter arguments.
|
@@ -145,18 +131,17 @@ b) Using an Array where the first element if the attribute name and rest is the
|
|
145
131
|
pre. class Product < ActiveRecord::Base
|
146
132
|
normalize :name, :with => [ [:keep, :Latin], [:remove, :Nd, :Zs] ]
|
147
133
|
normalize :description, :with => :squeeze
|
148
|
-
normalize :brand, :with => [ [:squeeze, "a-z"]
|
134
|
+
normalize :brand, :with => [ [:squeeze, "a-z"] ]
|
149
135
|
end
|
150
136
|
|
151
137
|
Both ways will produce the same result:
|
152
138
|
|
153
139
|
pre. $ rails console
|
154
|
-
> Product.
|
140
|
+
> Product.normalize_filters
|
155
141
|
=> { :name => { :keep => [:Latin],
|
156
142
|
:remove => [:Nd, :Zs] },
|
157
143
|
:description => { :squeeze => [] },
|
158
|
-
:brand => { :squeeze => ["a-z"]
|
159
|
-
:keep => [:Word] } }
|
144
|
+
:brand => { :squeeze => ["a-z"] } }
|
160
145
|
|
161
146
|
h3. Using instance method as filter
|
162
147
|
|
@@ -186,7 +171,7 @@ end
|
|
186
171
|
|
187
172
|
h2. Filters
|
188
173
|
|
189
|
-
Information about native filters and how to use them can be found in "Normatron::Filters(Normatron::Filters Rubydoc)":http://rubydoc.info/gems/normatron/Normatron/Filters.
|
174
|
+
Information about native filters and how to use them can be found in "Normatron::Filters(Normatron::Filters Rubydoc)":http://rubydoc.info/gems/normatron/Normatron/Filters.
|
190
175
|
|
191
176
|
h1. Contributing
|
192
177
|
|
@@ -204,9 +189,11 @@ There are several ways to make this gem even better:
|
|
204
189
|
|
205
190
|
h1. Credits
|
206
191
|
|
207
|
-
This gem was initially inspired on
|
208
|
-
* "normalize_attributes (normalize_attributes Gem)":https://github.com/fnando/normalize_attributes -
|
209
|
-
* "attribute_normalizer (attribute_normalizer Gem)":https://github.com/mdeering/attribute_normalizer -
|
192
|
+
This gem was initially inspired on:
|
193
|
+
* "normalize_attributes (normalize_attributes Gem)":https://github.com/fnando/normalize_attributes - I liked the cleaner code and simplicity of this gem.
|
194
|
+
* "attribute_normalizer (attribute_normalizer Gem)":https://github.com/mdeering/attribute_normalizer - Very powerful.
|
195
|
+
|
196
|
+
The idea is to mix the good things of both gems, adding some features and changing something to fit my taste.
|
210
197
|
|
211
198
|
h1. License
|
212
199
|
|
data/Rakefile
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
#!/usr/bin/env rake
|
2
|
+
=begin
|
2
3
|
begin
|
3
4
|
require 'bundler/setup'
|
4
5
|
rescue LoadError
|
@@ -22,13 +23,11 @@ end
|
|
22
23
|
|
23
24
|
Bundler::GemHelper.install_tasks
|
24
25
|
|
25
|
-
|
26
|
+
=end
|
27
|
+
require 'rspec/core/rake_task'
|
26
28
|
|
27
|
-
|
28
|
-
|
29
|
-
t.libs << 'test'
|
30
|
-
t.pattern = 'test/**/*_test.rb'
|
31
|
-
t.verbose = false
|
29
|
+
RSpec::Core::RakeTask.new(:spec) do |task|
|
30
|
+
task.rspec_opts = '--color -f d'
|
32
31
|
end
|
33
32
|
|
34
|
-
task :default => :
|
33
|
+
task :default => :spec
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Normatron
|
2
|
+
##
|
3
|
+
# Creates all necessary files to run Normatron in your Ruby On Rails project.
|
4
|
+
#
|
5
|
+
# These files are:
|
6
|
+
# * config/initializers/normatron.rb
|
7
|
+
#
|
8
|
+
# *Usage:*
|
9
|
+
# $ rails generator normatron:install
|
10
|
+
class InstallGenerator < Rails::Generators::Base
|
11
|
+
source_root File.expand_path('../templates', __FILE__)
|
12
|
+
desc "Create all necessary files to run Normatron in your Ruby On Rails project."
|
13
|
+
|
14
|
+
##
|
15
|
+
# Copy files from templates to their respective destination
|
16
|
+
#
|
17
|
+
# These files are:
|
18
|
+
# * config/initializers/normatron.rb
|
19
|
+
def copy_files
|
20
|
+
copy_file "normatron.rb", "config/initializers/normatron.rb"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/normatron.rb
CHANGED
@@ -1,20 +1,34 @@
|
|
1
1
|
require "normatron/configuration"
|
2
|
-
require "normatron/extensions
|
2
|
+
require "normatron/extensions"
|
3
3
|
|
4
4
|
module Normatron
|
5
|
-
VERSION = "0.2.1"
|
6
|
-
|
7
5
|
class << self
|
8
6
|
def configuration
|
9
7
|
@configuration ||= Configuration.new
|
10
8
|
end
|
11
9
|
alias :config :configuration
|
12
10
|
|
13
|
-
def
|
11
|
+
def setup
|
14
12
|
yield(configuration)
|
15
13
|
end
|
16
|
-
alias :setup :configure
|
17
|
-
end
|
18
|
-
end
|
19
14
|
|
20
|
-
|
15
|
+
def build_hash(*terms)
|
16
|
+
terms.flatten!(1)
|
17
|
+
|
18
|
+
filters_hash = {}
|
19
|
+
terms.each do |term|
|
20
|
+
case term
|
21
|
+
when Array
|
22
|
+
key, value = term.first, term.drop(1)
|
23
|
+
filters_hash[key.to_sym] = value.kind_of?(Array) && value.empty? ? nil : value
|
24
|
+
when Hash
|
25
|
+
term.each { |key, value| filters_hash[key.to_sym] = *value }
|
26
|
+
else
|
27
|
+
filters_hash[term.to_sym] = nil
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
filters_hash
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -1,34 +1,38 @@
|
|
1
|
+
require 'normatron/filters'
|
2
|
+
|
1
3
|
module Normatron
|
2
4
|
class Configuration
|
3
|
-
|
4
|
-
|
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
|
5
|
+
attr_reader :default_filters
|
6
|
+
attr_accessor :filters
|
21
7
|
|
22
8
|
def initialize
|
23
|
-
@default_filters = {
|
9
|
+
@default_filters = { blank: nil, squish: nil }
|
10
|
+
|
11
|
+
@filters = {}
|
12
|
+
@filters[:blank] = Normatron::Filters::BlankFilter
|
13
|
+
@filters[:camelize] = Normatron::Filters::CamelizeFilter
|
14
|
+
@filters[:capitalize] = Normatron::Filters::CapitalizeFilter
|
15
|
+
@filters[:chomp] = Normatron::Filters::ChompFilter
|
16
|
+
@filters[:dasherize] = Normatron::Filters::DasherizeFilter
|
17
|
+
@filters[:downcase] = Normatron::Filters::DowncaseFilter
|
18
|
+
@filters[:dump] = Normatron::Filters::DumpFilter
|
19
|
+
@filters[:keep] = Normatron::Filters::KeepFilter
|
20
|
+
@filters[:remove] = Normatron::Filters::RemoveFilter
|
21
|
+
@filters[:squeeze] = Normatron::Filters::SqueezeFilter
|
22
|
+
@filters[:squish] = Normatron::Filters::SquishFilter
|
23
|
+
@filters[:strip] = Normatron::Filters::StripFilter
|
24
|
+
@filters[:swapcase] = Normatron::Filters::SwapcaseFilter
|
25
|
+
@filters[:titleize] = Normatron::Filters::TitleizeFilter
|
26
|
+
@filters[:underscore] = Normatron::Filters::UnderscoreFilter
|
27
|
+
@filters[:upcase] = Normatron::Filters::UpcaseFilter
|
24
28
|
end
|
25
29
|
|
26
|
-
def
|
27
|
-
|
30
|
+
def add_orm(extension)
|
31
|
+
extension::ORM_CLASS.send(:include, extension)
|
28
32
|
end
|
29
33
|
|
30
34
|
def default_filters=(filters)
|
31
|
-
@default_filters =
|
35
|
+
@default_filters = Normatron.build_hash(filters)
|
32
36
|
end
|
33
37
|
end
|
34
38
|
end
|
@@ -1,16 +1,19 @@
|
|
1
|
-
require
|
1
|
+
require 'active_record'
|
2
2
|
|
3
3
|
module Normatron
|
4
4
|
module Extensions
|
5
5
|
module ActiveRecord
|
6
|
+
|
7
|
+
ORM_CLASS = ::ActiveRecord::Base
|
8
|
+
|
6
9
|
def self.included(base)
|
7
10
|
base.instance_eval do
|
8
11
|
extend ClassMethods
|
9
12
|
include InstanceMethods
|
10
|
-
before_validation :
|
13
|
+
before_validation :apply_normalizations
|
11
14
|
|
12
15
|
class << self
|
13
|
-
attr_accessor :
|
16
|
+
attr_accessor :normalize_filters
|
14
17
|
end
|
15
18
|
end
|
16
19
|
end
|
@@ -19,38 +22,40 @@ module Normatron
|
|
19
22
|
def normalize(*args)
|
20
23
|
# Check existence of all attributes
|
21
24
|
options = args.extract_options!
|
22
|
-
|
23
|
-
raise
|
25
|
+
filters, columns = args.map(&:to_s), column_names
|
26
|
+
raise UnknownAttributeError if (columns & filters).size != filters.size
|
24
27
|
|
25
28
|
# Specify the use of default filters or not
|
26
29
|
if options[:with].nil? || options[:with].blank?
|
27
30
|
new_filters = Normatron.config.default_filters
|
28
31
|
else
|
29
|
-
new_filters = Normatron
|
32
|
+
new_filters = Normatron.build_hash(options[:with])
|
30
33
|
end
|
31
34
|
|
32
35
|
# Append to older filters hash
|
33
|
-
@
|
34
|
-
@
|
35
|
-
args.reduce(@
|
36
|
-
filters = (@
|
36
|
+
@normalize_filters ||= {}
|
37
|
+
@normalize_filters =
|
38
|
+
args.reduce(@normalize_filters) do |hash, att|
|
39
|
+
filters = (@normalize_filters[att] || {}).merge(new_filters)
|
37
40
|
hash.merge({att => filters})
|
38
41
|
end
|
39
42
|
end
|
40
43
|
end
|
41
44
|
|
42
45
|
module InstanceMethods
|
43
|
-
def
|
44
|
-
|
46
|
+
def apply_normalizations
|
47
|
+
named_filters = Normatron.configuration.filters
|
48
|
+
|
49
|
+
self.class.normalize_filters.each do |attribute, filters|
|
45
50
|
value = send("#{attribute}_before_type_cast") || send(attribute)
|
46
51
|
|
47
52
|
filters.each do |filter, args|
|
48
53
|
if self.respond_to? filter
|
49
54
|
value = send(filter, value, *args)
|
50
|
-
elsif
|
51
|
-
value =
|
55
|
+
elsif !named_filters[filter].nil?
|
56
|
+
value = named_filters[filter].evaluate(value, *args)
|
52
57
|
else
|
53
|
-
raise
|
58
|
+
raise UnknownFilterError
|
54
59
|
end
|
55
60
|
end
|
56
61
|
|
data/lib/normatron/filters.rb
CHANGED
@@ -1,384 +1,31 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
require '
|
4
|
-
require '
|
5
|
-
require '
|
1
|
+
require 'normatron/filters/blank_filter'
|
2
|
+
require 'normatron/filters/camelize_filter'
|
3
|
+
require 'normatron/filters/capitalize_filter'
|
4
|
+
require 'normatron/filters/chomp_filter'
|
5
|
+
require 'normatron/filters/dasherize_filter'
|
6
|
+
require 'normatron/filters/downcase_filter'
|
7
|
+
require 'normatron/filters/dump_filter'
|
8
|
+
require 'normatron/filters/keep_filter'
|
9
|
+
require 'normatron/filters/remove_filter'
|
10
|
+
require 'normatron/filters/squeeze_filter'
|
11
|
+
require 'normatron/filters/squish_filter'
|
12
|
+
require 'normatron/filters/strip_filter'
|
13
|
+
require 'normatron/filters/swapcase_filter'
|
14
|
+
require 'normatron/filters/titleize_filter'
|
15
|
+
require 'normatron/filters/underscore_filter'
|
16
|
+
require 'normatron/filters/upcase_filter'
|
6
17
|
|
7
18
|
module Normatron
|
19
|
+
# Top-Level namespace of all native Normatron filters.
|
20
|
+
#
|
21
|
+
# All filters share some characteristics:
|
22
|
+
# * They have the <code>Filter</code> suffix in the name.
|
23
|
+
# * Has a class method called <code>evaluate</code>, which runs what the filter claims to do.
|
24
|
+
# * The first argument of the method <code>evaluate</code> always will be the variable to be filtered.
|
25
|
+
# * They returns a different object from the input variable, ie, even if the value remains unchanged, the <code>object_id</code> of both variables will be different.
|
26
|
+
# * They treat accented characters and not just <code>/[a-zA-Z]/</code> interval.
|
8
27
|
module Filters
|
9
|
-
|
10
|
-
|
11
|
-
##
|
12
|
-
# Returns a <tt>Nil</tt> object for a blank string or the string itself otherwise.
|
13
|
-
#
|
14
|
-
# @example
|
15
|
-
# blank("") #=> nil
|
16
|
-
# blank(" ") #=> nil
|
17
|
-
# blank(" \n ") #=> nil
|
18
|
-
# blank("1") #=> "1"
|
19
|
-
# blank("It's blank?") #=> "It's blank?"
|
20
|
-
# blank(123) #=> 123
|
21
|
-
#
|
22
|
-
# # ActiveRecord normalizer usage
|
23
|
-
# normalize :attribute_a, :with => :blank
|
24
|
-
# normalize :attribute_b, :with => [:custom_filter, :blank]
|
25
|
-
# @param [String] value A character sequence
|
26
|
-
# @return [String, Nil] The object itself or nil
|
27
|
-
# @see http://api.rubyonrails.org/classes/String.html#method-i-blank-3F String#blank?
|
28
|
-
def blank(value)
|
29
|
-
return value unless string?(value) && value.to_s.blank?
|
30
|
-
nil
|
31
|
-
end
|
32
|
-
|
33
|
-
##
|
34
|
-
# Converts strings to UpperCamelCase.
|
35
|
-
# If the argument to camelize is set to <tt>:lower</tt> then camelize produces lowerCamelCase.
|
36
|
-
# camelize will also convert <tt>'/'</tt> to <tt>'::'</tt> which is useful for converting paths to namespaces.
|
37
|
-
#
|
38
|
-
# @example
|
39
|
-
# camelize("active_record/errors") #=> "ActiveRecord::Errors"
|
40
|
-
# camelize("active_record/errors", :upper) #=> "ActiveRecord::Errors"
|
41
|
-
# camelize("active_record/errors", :lower) #=> "activeRecord::Errors"
|
42
|
-
# camelize(123) #=> 123
|
43
|
-
#
|
44
|
-
# # ActiveRecord normalizer usage
|
45
|
-
# normalize :attribute_a, :with => :camelize
|
46
|
-
# normalize :attribute_b, :with => [:custom_filter, :camelize]
|
47
|
-
# normalize :attribute_c, :with => [[:camelize, :lower]]
|
48
|
-
# normalize :attribute_d, :with => [{:camelize => :lower}]
|
49
|
-
# normalize :attribute_e, :with => [:custom_filter, [:camelize, :lower]]
|
50
|
-
# normalize :attribute_f, :with => [:custom_filter, {:camelize => :lower}]
|
51
|
-
# @param [String] value A character sequence
|
52
|
-
# @param [Symbol] first_letter_case <tt>:lower</tt> for lowerCamelCase or <tt>:upper</tt> for UpperCamelCase
|
53
|
-
# @return [String, Object] The camelized String or the object itself
|
54
|
-
def camelize(value, first_letter_case = :upper)
|
55
|
-
case value
|
56
|
-
when String
|
57
|
-
string = value
|
58
|
-
when ActiveSupport::Multibyte::Chars
|
59
|
-
string = value.to_s
|
60
|
-
else
|
61
|
-
return value
|
62
|
-
end
|
63
|
-
|
64
|
-
inflections = ActiveSupport::Inflector::Inflections.instance
|
65
|
-
if first_letter_case == :upper
|
66
|
-
string = string.sub(/^[\p{L}\d]*/u) { inflections.acronyms[$&] || capitalize($&) }
|
67
|
-
elsif first_letter_case == :lower
|
68
|
-
string = string.sub(/^(?:#{inflections.acronym_regex}(?=\b|[\p{L}_])|\p{Word}*_)/u) { downcase($&) }
|
69
|
-
else
|
70
|
-
raise "Use options :upper or :lower for Normatron::Filters#camelize"
|
71
|
-
end
|
72
|
-
string.gsub!(/(?:_|(\/))([\p{L}\d]*)/iu) { "#{$1}#{inflections.acronyms[$2] || capitalize($2)}" }.gsub!('/', '::')
|
73
|
-
|
74
|
-
(value.is_a?(String) && string) || string.mb_chars
|
75
|
-
end
|
76
|
-
|
77
|
-
##
|
78
|
-
# Makes the first character uppercase and lowercase others.
|
79
|
-
#
|
80
|
-
# @example
|
81
|
-
# capitalize("jESSE PINK") #=> "Jesse pink"
|
82
|
-
# capitalize("wALTER WHITE") #=> "Walter white"
|
83
|
-
# capitalize(" mr. Fring") #=> " mr. fring"
|
84
|
-
# capitalize(123) #=> 123
|
85
|
-
#
|
86
|
-
# # ActiveRecord normalizer usage
|
87
|
-
# normalize :attribute_a, :with => :capitalize
|
88
|
-
# normalize :attribute_b, :with => [:custom_filter, :capitalize]
|
89
|
-
# @param [String] value A character sequence
|
90
|
-
# @return [String, Object] The capitalized String or the object itself
|
91
|
-
# @see http://www.ruby-doc.org/core-1.9.3/String.html#method-i-capitalize String#capitalize
|
92
|
-
# @see http://api.rubyonrails.org/classes/ActiveSupport/Multibyte/Chars.html#method-i-capitalize ActiveSupport::Multibyte::Chars#capitalize
|
93
|
-
def capitalize(value)
|
94
|
-
(string?(value) && eval_send(:capitalize, value)) || value
|
95
|
-
end
|
96
|
-
|
97
|
-
##
|
98
|
-
# Remove the given record separator from the end of the string (If present).
|
99
|
-
# If <tt>$/</tt> has not been changed from the default Ruby record separator,
|
100
|
-
# then chomp also removes carriage return characters (that is it will remove <tt>\n</tt>, <tt>\r</tt>, and <tt>\r\n</tt>).
|
101
|
-
#
|
102
|
-
# @example
|
103
|
-
# chomp("Bon \n Scott\n") #=> "Bon \n Scott"
|
104
|
-
# chomp("Bon \n Scott\r") #=> "Bon \n Scott"
|
105
|
-
# chomp("Bon \n Scott\r\n") #=> "Bon \n Scott"
|
106
|
-
# chomp("Bon \n Scott\n\r") #=> "Bon \n Scott\n"
|
107
|
-
# chomp("Bon \n Scott", "t") #=> "Bon \n Scot"
|
108
|
-
# chomp("Bon \n Scott", "Scott") #=> "Bon \n "
|
109
|
-
# chomp("Bon \n Scott", " \n Scott") #=> "Bon"
|
110
|
-
# chomp(100) #=> 100
|
111
|
-
#
|
112
|
-
# # ActiveRecord normalizer usage
|
113
|
-
# normalize :attribute_a, :with => :chomp
|
114
|
-
# normalize :attribute_b, :with => [:custom_filter, :chomp]
|
115
|
-
# normalize :attribute_c, :with => [[:chomp, "x"]]
|
116
|
-
# normalize :attribute_d, :with => [{:chomp => "y"}]
|
117
|
-
# normalize :attribute_e, :with => [:custom_filter, [:chomp, "z"]]
|
118
|
-
# normalize :attribute_f, :with => [:custom_filter, {:chomp => "\t"}]
|
119
|
-
# @param [String] value A character sequence
|
120
|
-
# @param [String] separator A character sequence
|
121
|
-
# @return [String, Object] The chopped String or the object itself
|
122
|
-
# @see http://www.ruby-doc.org/core-1.9.3/String.html#method-i-chomp String#chomp
|
123
|
-
def chomp(value, separator=$/)
|
124
|
-
(string?(value) && eval_send(:chomp, value, separator)) || value
|
125
|
-
end
|
126
|
-
|
127
|
-
##
|
128
|
-
# Replaces all underscores with dashes.
|
129
|
-
#
|
130
|
-
# @example
|
131
|
-
# dasherize("monty_python") #=> "monty-python"
|
132
|
-
# dasherize("_.·-'*'-·._") #=> "-.·-'*'-·.-"
|
133
|
-
# dasherize(123) #=> 123
|
134
|
-
#
|
135
|
-
# # ActiveRecord normalizer usage
|
136
|
-
# normalize :attribute_a, :with => :dasherize
|
137
|
-
# normalize :attribute_b, :with => [:custom_filter, :dasherize]
|
138
|
-
# @param [String] value A character sequence
|
139
|
-
# @return [String, Object] The dasherized String or the object itself
|
140
|
-
# @see http://api.rubyonrails.org/classes/String.html#method-i-dasherize String#dasherize
|
141
|
-
def dasherize(value)
|
142
|
-
(string?(value) && eval_send(:dasherize, value)) || value
|
143
|
-
end
|
144
|
-
|
145
|
-
##
|
146
|
-
# Lowercase all characters.
|
147
|
-
#
|
148
|
-
# @example
|
149
|
-
# downcase("VEGETA!!!") #=> "vegeta!!!"
|
150
|
-
# downcase(123) #=> 123
|
151
|
-
#
|
152
|
-
# # ActiveRecord normalizer usage
|
153
|
-
# normalize :attribute_a, :with => :downcase
|
154
|
-
# normalize :attribute_b, :with => [:custom_filter, :downcase]
|
155
|
-
# @param [String] value A character sequence
|
156
|
-
# @return [String, Object] The lowercased String or the object itself
|
157
|
-
# @see http://www.ruby-doc.org/core-1.9.3/String.html#method-i-downcase String#downcase
|
158
|
-
# @see http://api.rubyonrails.org/classes/ActiveSupport/Multibyte/Chars.html#method-i-downcase ActiveSupport::Multibyte::Chars#downcase
|
159
|
-
def downcase(value)
|
160
|
-
(string?(value) && eval_send(:downcase, value)) || value
|
161
|
-
end
|
162
|
-
|
163
|
-
##
|
164
|
-
# Creates a literal string representation with all nonprinting characters
|
165
|
-
# replaced by <tt>\\n</tt> notation and all special characters escaped.
|
166
|
-
#
|
167
|
-
# @example
|
168
|
-
# dump("I'm not\na \"clubber\"...") #=> "\"I'm not\\na \\\"clubber\\\"...\""
|
169
|
-
# dump("I'm not\na \"clubber\"...") #== '"I\'m not\na \"clubber\"..."'
|
170
|
-
# dump('I\'m not\na "clubber"...') #=> "\"I'm not\\\\na \\\"clubber\\\"...\""
|
171
|
-
# dump('I\'m not\na "clubber"...') #== '"I\'m not\\\na \"clubber\"..."'
|
172
|
-
# dump(100) #=> 100
|
173
|
-
#
|
174
|
-
# # ActiveRecord normalizer usage
|
175
|
-
# normalize :attribute_a, :with => :dump
|
176
|
-
# normalize :attribute_b, :with => [:custom_filter, :dump]
|
177
|
-
# @param [String] value A character sequence
|
178
|
-
# @return [String, Object] The dumpped String or the object itself
|
179
|
-
# @see http://www.ruby-doc.org/core-1.9.3/String.html#method-i-dump String#dump
|
180
|
-
def dump(value)
|
181
|
-
(string?(value) && eval_send(:dump, value)) || value
|
182
|
-
end
|
183
|
-
|
184
|
-
##
|
185
|
-
# Keep only the specified characters.
|
186
|
-
# Details about the options can be found in the Regexp class documentation.
|
187
|
-
#
|
188
|
-
# @example
|
189
|
-
# keep("Doom 3", :L) #=> "Doom" equivalent to /\p{L}/u
|
190
|
-
# keep("Doom 3", :N) #=> "3" equivalent to /\p{N}/u
|
191
|
-
# keep("Doom 3", :L, :N) #=> "Doom3" equivalent to /\p{L}\p{N}/u
|
192
|
-
# keep("Doom 3", :Lu, :N) #=> "D3" equivalent to /\p{Lu}\p{N}/u
|
193
|
-
# keep("Doom ˩", :Latin) #=> "Doom" equivalent to /\p{Latin}/u
|
194
|
-
#
|
195
|
-
# # For normalizations
|
196
|
-
# normalize :attribute_a, :with => [[:keep, :Lu, :N]]
|
197
|
-
# normalize :attribute_b, :with => [:custom_filter, [:keep, :L, :Nd]]
|
198
|
-
# normalize :attribute_c, :with => [:custom_filter, {:keep => [:Latin, :Z]}]
|
199
|
-
# @param [String] value A character sequence
|
200
|
-
# @param [[Symbol]*] args Array of Symbols equivalent to Regexp for \\p{} construct.
|
201
|
-
# @return [String, Object] The clean character sequence or the object itself
|
202
|
-
# @see http://www.ruby-doc.org/core-1.9.3/Regexp.html Regexp
|
203
|
-
def keep(value, *args)
|
204
|
-
eval_regexp(value, true, args)
|
205
|
-
end
|
206
|
-
|
207
|
-
##
|
208
|
-
# Remove trailing spaces.
|
209
|
-
#
|
210
|
-
# @example
|
211
|
-
# lstrip(" copyleft ") #=> "copyleft "
|
212
|
-
#
|
213
|
-
# # For normalizations
|
214
|
-
# normalize :attribute, :with => [:custom_filter, :lstrip]
|
215
|
-
# @param [String] value A character sequence
|
216
|
-
# @return [String, Object] The character sequence without trailing spaces or the object itself
|
217
|
-
# @see http://www.ruby-doc.org/core-1.9.3/String.html#method-i-lstrip String#lstrip
|
218
|
-
def lstrip(value)
|
219
|
-
(string?(value) && eval_strip(value, true, false)) || value
|
220
|
-
end
|
221
|
-
|
222
|
-
##
|
223
|
-
# Remove only the specified characters.
|
224
|
-
# Details about the options can be found in the Regexp class documentation.
|
225
|
-
#
|
226
|
-
# @example
|
227
|
-
# remove("Quake 3", :L) #=> "3" equivalent to /\p{L}/u
|
228
|
-
# remove("Quake 3", :N) #=> "Quake " equivalent to /\p{N}/u
|
229
|
-
# remove("Quake 3", :L, :N) #=> " " equivalent to /\p{L}\p{N}/u
|
230
|
-
# remove("Quake 3", :Lu, :N) #=> "uake " equivalent to /\p{Lu}\p{N}/u
|
231
|
-
# remove("Quake ˩", :Latin) #=> "Quake" equivalent to /\p{Latin}/u
|
232
|
-
#
|
233
|
-
# # For normalizations
|
234
|
-
# normalize :attribute_a, :with => [[:remove, :Lu, :N]]
|
235
|
-
# normalize :attribute_b, :with => [:custom_filter, [:remove, :L, :Nd]]
|
236
|
-
# normalize :attribute_c, :with => [:custom_filter, {:remove => [:Latin, :Z]}]
|
237
|
-
# @param [String] value A character sequence
|
238
|
-
# @param [[Symbol]*] args Array of Symbols equivalent to Regexp for \\p{} construct.
|
239
|
-
# @return [String, Object] The clean character sequence or the object itself
|
240
|
-
# @see http://www.ruby-doc.org/core-1.9.3/Regexp.html Regexp
|
241
|
-
def remove(value, *args)
|
242
|
-
eval_regexp(value, false, args)
|
243
|
-
end
|
244
|
-
|
245
|
-
##
|
246
|
-
# Remove leading spaces.
|
247
|
-
#
|
248
|
-
# @example
|
249
|
-
# rstrip(" copyright ") #=> " copyright"
|
250
|
-
#
|
251
|
-
# # For normalizations
|
252
|
-
# normalize :attribute, :with => [:custom_filter, :rstrip]
|
253
|
-
# @param [String] value A character sequence
|
254
|
-
# @return [String, Object] The character sequence without leading spaces or the object itself
|
255
|
-
# @see http://www.ruby-doc.org/core-1.9.3/String.html#method-i-rstrip String#rstrip
|
256
|
-
def rstrip(value)
|
257
|
-
(string?(value) && eval_strip(value, false, true)) || value
|
258
|
-
end
|
259
|
-
|
260
|
-
##
|
261
|
-
# Remove multiple occurences of the same character.
|
262
|
-
# If no option are given, all runs of identical characters are replaced by a single character.
|
263
|
-
#
|
264
|
-
# @example
|
265
|
-
# squeeze("yellow moon") #=> "yelow mon"
|
266
|
-
# squeeze(" now is the", " ") #=> " now is the"
|
267
|
-
# squeeze("putters shoot balls", "m-z") #=> "puters shot balls"
|
268
|
-
#
|
269
|
-
# # For normalizations
|
270
|
-
# normalize :attribute_a, :with => [:custom_filter, :squeeze]
|
271
|
-
# normalize :attribute_b, :with => [:custom_filter, [:squeeze, "a-f"]]
|
272
|
-
# normalize :attribute_c, :with => [:custom_filter, {:squeeze => ["a-f"]}]
|
273
|
-
# @param [String] value A character sequence
|
274
|
-
# @param [[String]*] args Chars to be affected
|
275
|
-
# @return [String, Object] The clean character sequence or the object itself
|
276
|
-
# @see http://www.ruby-doc.org/core-1.9.3/String.html#method-i-squeeze String#squeeze
|
277
|
-
def squeeze(value, *args)
|
278
|
-
return value unless string?(value)
|
279
|
-
(args.any? && value.squeeze(args.first)) || value.squeeze
|
280
|
-
end
|
281
|
-
|
282
|
-
##
|
283
|
-
# Strip and remove multiple spaces.
|
284
|
-
#
|
285
|
-
# @example
|
286
|
-
# squish(" the \n simpsons ") #=> "the simpsons"
|
287
|
-
#
|
288
|
-
# # For normalizations
|
289
|
-
# normalize :attribute, :with => [:custom_filter, :squish]
|
290
|
-
# @param [String] value A character sequence
|
291
|
-
# @return [String, Object] The clean character sequence or the object itself
|
292
|
-
#
|
293
|
-
# @see http://api.rubyonrails.org/classes/String.html#method-i-squish String#squish
|
294
|
-
def squish(value)
|
295
|
-
(string?(value) && value.squish) || value
|
296
|
-
end
|
297
|
-
|
298
|
-
##
|
299
|
-
# Remove traling and leading spaces from the string.
|
300
|
-
#
|
301
|
-
# @example
|
302
|
-
# strip(" copy ") #=> "copy"
|
303
|
-
#
|
304
|
-
# # For normalizations
|
305
|
-
# normalize :attribute, :with => [:custom_filter, :strip]
|
306
|
-
# @param [String] value A character sequence
|
307
|
-
# @return [String, Object] The stripped character sequence or the object itself
|
308
|
-
def strip(value)
|
309
|
-
(string?(value) && eval_strip(value, true, true)) || value
|
310
|
-
end
|
311
|
-
|
312
|
-
##
|
313
|
-
# Replaces uppercased characters by lowercased and vice versa.
|
314
|
-
#
|
315
|
-
# @example
|
316
|
-
# swapcase("AiNn Kmo éH BOM ser v1d4 l0k4") #=> "aInN kMO Éh bom SER V1D4 L0K4"
|
317
|
-
# swapcase(100) #=> 100
|
318
|
-
#
|
319
|
-
# # ActiveRecord normalizer usage
|
320
|
-
# normalize :attribute_a, :with => :swapcase
|
321
|
-
# normalize :attribute_b, :with => [:custom_filter, :swapcase]
|
322
|
-
# @param [String] value A character sequence
|
323
|
-
# @return [String, Object] The String with case swapped or the object itself
|
324
|
-
def swapcase(value)
|
325
|
-
return value unless string?(value)
|
326
|
-
value.gsub(/./) { |c| (/\p{Lu}/.match(c) && downcase(c) || (/\p{Ll}/.match(c) && upcase(c))) || c }
|
327
|
-
end
|
328
|
-
|
329
|
-
##
|
330
|
-
# Converts all characters to uppercase.
|
331
|
-
#
|
332
|
-
# @example
|
333
|
-
# downcase("kakarotto!!!") #=> "KAKAROTTO!!!"
|
334
|
-
# downcase(123) #=> 123
|
335
|
-
#
|
336
|
-
# # For normalizations
|
337
|
-
# normalize :attribute, :with => [:custom_filter, :upcase]
|
338
|
-
# @param [String] value A character sequence
|
339
|
-
# @return [String, Object] The lowercased String or the object itself
|
340
|
-
# @see http://www.ruby-doc.org/core-1.9.3/String.html#method-i-upcase String#upcase
|
341
|
-
# @see http://api.rubyonrails.org/classes/ActiveSupport/Multibyte/Chars.html#method-i-upcase ActiveSupport::Multibyte::Chars#upcase
|
342
|
-
def upcase(value)
|
343
|
-
(string?(value) && eval_send(:upcase, value)) || value
|
344
|
-
end
|
345
|
-
|
346
|
-
protected
|
347
|
-
|
348
|
-
def eval_regexp(value, keep, *args)
|
349
|
-
return value unless string?(value)
|
350
|
-
|
351
|
-
options = args.flatten.compact.uniq
|
352
|
-
|
353
|
-
regex = options.map{ |s| "\\p{#{s.to_s}}" } * ""
|
354
|
-
regex.prepend('^') if keep
|
355
|
-
regex = eval "/[#{regex}]/u"
|
356
|
-
|
357
|
-
value.gsub(regex, "")
|
358
|
-
end
|
359
|
-
|
360
|
-
def eval_send(method, value, option=nil)
|
361
|
-
type = value.class
|
362
|
-
value = value.mb_chars if type == String
|
363
|
-
if option
|
364
|
-
value = value.send(method, option)
|
365
|
-
else
|
366
|
-
value = value.send(method)
|
367
|
-
end
|
368
|
-
(type == String && value.to_s) || value
|
369
|
-
end
|
370
|
-
|
371
|
-
def eval_strip(value, start, ending)
|
372
|
-
regex = []
|
373
|
-
regex << '\A\p{Zs}*' if start
|
374
|
-
regex << '\p{Zs}*\z' if ending
|
375
|
-
regex = eval "/#{regex * '|'}/u"
|
28
|
+
end
|
29
|
+
end
|
376
30
|
|
377
|
-
value.gsub(regex, '')
|
378
|
-
end
|
379
31
|
|
380
|
-
def string?(value)
|
381
|
-
value.is_a?(ActiveSupport::Multibyte::Chars) || value.is_a?(String)
|
382
|
-
end
|
383
|
-
end
|
384
|
-
end
|