normatron 0.2.1 → 0.3.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.
Files changed (55) hide show
  1. data/README.textile +35 -48
  2. data/Rakefile +6 -7
  3. data/lib/generators/normatron/install_generator.rb +23 -0
  4. data/lib/generators/normatron/templates/normatron.rb +4 -0
  5. data/lib/normatron.rb +22 -8
  6. data/lib/normatron/configuration.rb +26 -22
  7. data/lib/normatron/extensions.rb +8 -0
  8. data/lib/normatron/extensions/active_record.rb +20 -15
  9. data/lib/normatron/filters.rb +26 -379
  10. data/lib/normatron/filters/blank_filter.rb +29 -0
  11. data/lib/normatron/filters/camelize_filter.rb +50 -0
  12. data/lib/normatron/filters/capitalize_filter.rb +29 -0
  13. data/lib/normatron/filters/chomp_filter.rb +34 -0
  14. data/lib/normatron/filters/dasherize_filter.rb +25 -0
  15. data/lib/normatron/filters/downcase_filter.rb +29 -0
  16. data/lib/normatron/filters/dump_filter.rb +27 -0
  17. data/lib/normatron/filters/helpers.rb +44 -0
  18. data/lib/normatron/filters/keep_filter.rb +100 -0
  19. data/lib/normatron/filters/remove_filter.rb +37 -0
  20. data/lib/normatron/filters/squeeze_filter.rb +30 -0
  21. data/lib/normatron/filters/squish_filter.rb +28 -0
  22. data/lib/normatron/filters/strip_filter.rb +33 -0
  23. data/lib/normatron/filters/swapcase_filter.rb +30 -0
  24. data/lib/normatron/filters/titleize_filter.rb +29 -0
  25. data/lib/normatron/filters/underscore_filter.rb +45 -0
  26. data/lib/normatron/filters/upcase_filter.rb +29 -0
  27. data/lib/normatron/version.rb +3 -0
  28. data/spec/normatron/configuration_spec.rb +60 -0
  29. data/spec/normatron/extensions/active_record_spec.rb +96 -0
  30. data/spec/normatron/filters/blank_filter_spec.rb +15 -0
  31. data/spec/normatron/filters/camelize_filter_spec.rb +42 -0
  32. data/spec/normatron/filters/capitalize_filter_spec.rb +14 -0
  33. data/spec/normatron/filters/chomp_filter_spec.rb +15 -0
  34. data/spec/normatron/filters/dasherize_filter_spec.rb +9 -0
  35. data/spec/normatron/filters/downcase_filter_spec.rb +10 -0
  36. data/spec/normatron/filters/dump_filter_spec.rb +10 -0
  37. data/spec/normatron/filters/keep_filter_spec.rb +86 -0
  38. data/spec/normatron/filters/remove_filter_spec.rb +86 -0
  39. data/spec/normatron/filters/squeeze_filter_spec.rb +10 -0
  40. data/spec/normatron/filters/squish_filter_spec.rb +12 -0
  41. data/spec/normatron/filters/strip_filter_spec.rb +12 -0
  42. data/spec/normatron/filters/swapcase_filter_spec.rb +12 -0
  43. data/spec/normatron/filters/titleize_filter_spec.rb +12 -0
  44. data/spec/normatron/filters/underscore_filter_spec.rb +26 -0
  45. data/spec/normatron/filters/upcase_filter_spec.rb +10 -0
  46. data/spec/normatron_spec.rb +28 -2
  47. data/spec/spec_helper.rb +37 -4
  48. data/spec/support/my_filters.rb +7 -0
  49. data/spec/support/user_model.rb +14 -0
  50. metadata +64 -13
  51. data/spec/configuration_spec.rb +0 -53
  52. data/spec/extensions/active_record_spec.rb +0 -114
  53. data/spec/filters_spec.rb +0 -442
  54. data/spec/support/model_model.rb +0 -3
  55. data/spec/support/schema.rb +0 -7
@@ -1,6 +1,6 @@
1
1
  h1. Normatron
2
2
 
3
- Normatron is an Ruby On Rails plugin that perform attribute normalizations for ActiveRecord objects.
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
- * Use the "_before_validation_":http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html callback to run a method or block doing the task.
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 "_: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.
52
- These filters are applied to all attributes in @normalize@ function, since no options is given.
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
- > p1 = Product.create name: " memory card "
65
+ > memory = Product.create name: " memory card "
62
66
  => #<Product id: nil, name: "memory card", price: nil>
63
- > p2 = Product.create name: " "
67
+ > null = Product.create name: " "
64
68
  => #<Product id: nil, name: nil, price: nil>
65
69
 
66
- h3. The _normalize_attributes_ method
70
+ h3. Configurations
67
71
 
68
- 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 _normalize_attributes_ method, thought "before_validation":http://api.rubyonrails.org/classes/ActiveRecord/Callbacks.html callback.
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
- pre. $ rails console
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. The _normalize_options_ method
76
+ h3. Applying normalizations
79
77
 
80
- To read the normalization filters set for a model, just call the @normalize_options@ method.
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.normalize_options
84
- => { name: { :squish => [], :blank => [] } }
85
-
86
- This method returns a Hash, where the keys are the attribute names and values are another Hash with filter options.
87
- In the filter options Hash, keys represent the filter names and the values are Arrays containing arguments to be passed to filter methods.
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
- pre. $ rails console
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.normalize_options
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 @normalize@ method is used multiple times for the same attribute, it will stack the filter bindings.
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.normalize_options
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 => [ { :squeeze => ["a-z"] }, { :keep => [:Word] } ]
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"], [:keep, :Word] ]
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.normalize_options
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. All methods have a short description of what they do and some examples of how to use them.
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 these gems:
208
- * "normalize_attributes (normalize_attributes Gem)":https://github.com/fnando/normalize_attributes - "fnando (fnando's Github)":https://github.com/fnando
209
- * "attribute_normalizer (attribute_normalizer Gem)":https://github.com/mdeering/attribute_normalizer - "mdeering (mdeering's Github)":https://github.com/mdeering
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
- require 'rake/testtask'
26
+ =end
27
+ require 'rspec/core/rake_task'
26
28
 
27
- Rake::TestTask.new(:test) do |t|
28
- t.libs << 'lib'
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 => :test
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
@@ -0,0 +1,4 @@
1
+ Normatron.setup do |config|
2
+ config.default_filters = :blank, :squish
3
+ config.add_orm Normatron::Extensions::ActiveRecord
4
+ end
@@ -1,20 +1,34 @@
1
1
  require "normatron/configuration"
2
- require "normatron/extensions/active_record"
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 configure
11
+ def setup
14
12
  yield(configuration)
15
13
  end
16
- alias :setup :configure
17
- end
18
- end
19
14
 
20
- ActiveRecord::Base.send(:include, Normatron::Extensions::ActiveRecord) if defined?(ActiveRecord::Base)
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
- 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
5
+ attr_reader :default_filters
6
+ attr_accessor :filters
21
7
 
22
8
  def initialize
23
- @default_filters = { squish: [], blank: [] }
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 default_filters
27
- @default_filters
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 = self.class.clean_filters(filters)
35
+ @default_filters = Normatron.build_hash(filters)
32
36
  end
33
37
  end
34
38
  end
@@ -0,0 +1,8 @@
1
+ require 'normatron/extensions/active_record'
2
+
3
+ module Normatron
4
+ module Extensions
5
+ class UnknownAttributeError < RuntimeError ; end
6
+ class UnknownFilterError < RuntimeError ; end
7
+ end
8
+ end
@@ -1,16 +1,19 @@
1
- require "normatron/filters"
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 :normalize_attributes
13
+ before_validation :apply_normalizations
11
14
 
12
15
  class << self
13
- attr_accessor :normalize_options
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
- args, columns = args.map(&:to_sym), column_names.map(&:to_sym)
23
- raise "attribute" if (columns & args).size != args.size
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::Configuration.clean_filters(options[:with])
32
+ new_filters = Normatron.build_hash(options[:with])
30
33
  end
31
34
 
32
35
  # 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)
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 normalize_attributes
44
- self.class.normalize_options.each do |attribute, filters|
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 Normatron::Filters.respond_to? filter
51
- value = Normatron::Filters.send(filter, value, *args)
55
+ elsif !named_filters[filter].nil?
56
+ value = named_filters[filter].evaluate(value, *args)
52
57
  else
53
- raise "Filter '#{filter}' wasn't found."
58
+ raise UnknownFilterError
54
59
  end
55
60
  end
56
61
 
@@ -1,384 +1,31 @@
1
- # encoding: UTF-8
2
-
3
- require 'active_support/multibyte/chars'
4
- require 'active_support/core_ext/string'
5
- require 'active_support/inflector/inflections'
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
- extend self
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