normatron 0.0.3 → 0.0.4
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.rdoc +29 -1
- data/lib/normatron/active_record.rb +37 -13
- data/lib/normatron/conversors.rb +20 -19
- data/lib/normatron/version.rb +1 -1
- data/spec/normatron_spec.rb +78 -52
- data/spec/schema.rb +2 -2
- data/spec/support/test_model.rb +1 -1
- metadata +2 -3
- data/lib/tasks/normatron_tasks.rake +0 -4
data/README.rdoc
CHANGED
@@ -1,3 +1,31 @@
|
|
1
1
|
= Normatron
|
2
2
|
|
3
|
-
|
3
|
+
Normatron is a attribute normalizer for ActiveRecord.
|
4
|
+
|
5
|
+
= Installation
|
6
|
+
|
7
|
+
Add the following into your gemfile:
|
8
|
+
|
9
|
+
gem 'normatron'
|
10
|
+
|
11
|
+
Bundle it up:
|
12
|
+
|
13
|
+
$ bundle install
|
14
|
+
|
15
|
+
= Usage
|
16
|
+
|
17
|
+
Let's take an example model:
|
18
|
+
|
19
|
+
app/models/product.rb
|
20
|
+
class Product < ActiveRecord::Base
|
21
|
+
attr_accessible :name
|
22
|
+
|
23
|
+
normalize :name, :with => :upcase
|
24
|
+
end
|
25
|
+
|
26
|
+
$ rails console
|
27
|
+
> p = Product.new
|
28
|
+
> p.name = " memory card "
|
29
|
+
> p.valid?
|
30
|
+
> p.name
|
31
|
+
=> " MEMORY CARD "
|
@@ -2,7 +2,6 @@ require "active_record"
|
|
2
2
|
|
3
3
|
module Normatron
|
4
4
|
module ActiveRecord
|
5
|
-
include Conversors
|
6
5
|
|
7
6
|
def self.included(base)
|
8
7
|
base.instance_eval do
|
@@ -11,39 +10,63 @@ module Normatron
|
|
11
10
|
before_validation :normalize_attributes
|
12
11
|
|
13
12
|
class << self
|
14
|
-
attr_accessor :
|
13
|
+
attr_accessor :normalization_options
|
15
14
|
end
|
16
15
|
end
|
17
16
|
end
|
18
17
|
|
19
18
|
module ClassMethods
|
19
|
+
# Set an attribute normalization before model validation.
|
20
|
+
# Args uses the attribute names following by a hash with conversion options.
|
21
|
+
#
|
22
|
+
# Example 1:
|
23
|
+
# normalize :attribute_name
|
24
|
+
#
|
25
|
+
# Example 2:
|
26
|
+
# normalize :attribute_name, :with => :upcase
|
27
|
+
#
|
28
|
+
# Example 3:
|
29
|
+
# normalize :attribute_name, :with => [:squish, :downcase]
|
20
30
|
def normalize(*args)
|
21
31
|
# Extract options
|
22
|
-
|
32
|
+
@normalization_options ||= {}
|
23
33
|
options = args.extract_options!
|
24
34
|
|
25
|
-
|
35
|
+
# Set conversors
|
36
|
+
conversors = []
|
26
37
|
if options.empty? # Default standardization
|
27
|
-
|
38
|
+
conversors << [:squish, :strip, :nillify]
|
28
39
|
elsif options.has_key? :with
|
29
|
-
|
40
|
+
conversors << options[:with]
|
30
41
|
else
|
31
|
-
raise "Wrong normalization
|
42
|
+
raise "Wrong normalization key in #{self.name}, use :with instead of #{options.keys.first}"
|
32
43
|
end
|
33
44
|
|
34
45
|
# Make a prettier array
|
35
|
-
|
36
|
-
|
46
|
+
conversors = conversors.flatten.compact
|
47
|
+
conversors.map! { |v| v = v.to_sym }
|
37
48
|
|
38
|
-
#
|
49
|
+
# Check conversors
|
50
|
+
conversors.each do |c|
|
51
|
+
unless Conversors::CALLBACKS.include? c
|
52
|
+
raise "Normalization callback '#{c}' doesn't exist"
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
# Add normalization conversors
|
39
57
|
args.each do |attribute|
|
40
|
-
|
58
|
+
# Check attributes
|
59
|
+
unless self.column_names.include? attribute.to_s
|
60
|
+
raise "Attribute '#{attribute}' doesn't exist in #{self.name}"
|
61
|
+
end
|
62
|
+
|
63
|
+
@normalization_options[attribute] = conversors
|
41
64
|
end
|
42
65
|
end
|
43
66
|
end
|
44
67
|
|
45
68
|
def normalize_attributes
|
46
|
-
options = self.class.
|
69
|
+
options = self.class.normalization_options
|
47
70
|
return unless options
|
48
71
|
|
49
72
|
options.each do |attribute, methods|
|
@@ -51,7 +74,8 @@ module Normatron
|
|
51
74
|
|
52
75
|
methods.each do |method|
|
53
76
|
# Skip if value is nil originally or the method 'nullify' was called before
|
54
|
-
value = convert(method, value) unless value.nil?
|
77
|
+
value = Conversors.convert(method, value) unless value.nil?
|
78
|
+
|
55
79
|
if value == :no_method
|
56
80
|
raise ArgumentError, "Method :#{method} cannot be resolved.
|
57
81
|
Check options for #{attribute} in #{klass}", caller
|
data/lib/normatron/conversors.rb
CHANGED
@@ -4,46 +4,47 @@ require "active_support/all"
|
|
4
4
|
module Normatron
|
5
5
|
module Conversors
|
6
6
|
|
7
|
-
|
8
|
-
|
7
|
+
MB_CHARS_METHODS = [:upcase, :downcase, :capitalize, :lstrip, :rstrip, :strip, :titlecase, :titleize]
|
8
|
+
SELF_METHODS = [:nillify, :nullify, :nil, :squish, :currency, :integer, :float, :postal_code, :phone, :digits, :phrase]
|
9
|
+
CALLBACKS = MB_CHARS_METHODS + SELF_METHODS
|
9
10
|
|
10
|
-
def convert(method, value)
|
11
|
-
if
|
11
|
+
def self.convert(method, value)
|
12
|
+
if MB_CHARS_METHODS.include? method
|
12
13
|
value.mb_chars.send(method).to_s
|
13
|
-
elsif
|
14
|
+
elsif SELF_METHODS.include? method
|
14
15
|
send("convert_#{method}", value)
|
15
16
|
else
|
16
17
|
:no_method
|
17
18
|
end
|
18
19
|
end
|
19
20
|
|
20
|
-
private
|
21
|
-
|
22
21
|
# Return nil if value is blank or else value itself.
|
23
|
-
def convert_nillify(value)
|
22
|
+
def self.convert_nillify(value)
|
24
23
|
value.blank? ? nil : value
|
25
24
|
end
|
26
|
-
|
27
|
-
|
25
|
+
class << self
|
26
|
+
alias :convert_nil :convert_nillify
|
27
|
+
alias :convert_nullify :convert_nillify
|
28
|
+
end
|
28
29
|
|
29
30
|
# Remove repeated spaces from the string.
|
30
|
-
def convert_squish(value)
|
31
|
+
def self.convert_squish(value)
|
31
32
|
value.mb_chars.to_s.gsub(/\p{Zs}+/u, ' ')
|
32
33
|
end
|
33
34
|
|
34
|
-
def convert_currency(value)
|
35
|
+
def self.convert_currency(value)
|
35
36
|
convert_number value, :currency
|
36
37
|
end
|
37
38
|
|
38
|
-
def convert_float(value)
|
39
|
+
def self.convert_float(value)
|
39
40
|
convert_number value, :float
|
40
41
|
end
|
41
42
|
|
42
|
-
def convert_integer(value)
|
43
|
+
def self.convert_integer(value)
|
43
44
|
convert_number value
|
44
45
|
end
|
45
46
|
|
46
|
-
def convert_number(value, type = :integer)
|
47
|
+
def self.convert_number(value, type = :integer)
|
47
48
|
return value unless value.is_a?(String) && value.present?
|
48
49
|
|
49
50
|
# Find the first number in the sequence
|
@@ -70,21 +71,21 @@ module Normatron
|
|
70
71
|
res
|
71
72
|
end
|
72
73
|
|
73
|
-
def convert_postal_code(value)
|
74
|
+
def self.convert_postal_code(value)
|
74
75
|
res = convert_digits value
|
75
76
|
res.size == 8 ? "%s-%s" % [res[0..4], res[5..7]] : value
|
76
77
|
end
|
77
78
|
|
78
|
-
def convert_phone(value)
|
79
|
+
def self.convert_phone(value)
|
79
80
|
res = convert_digits value
|
80
81
|
res.size == 10 ? "(%s) %s-%s" % [res[0..1], res[2..5], res[6..9]] : value
|
81
82
|
end
|
82
83
|
|
83
|
-
def convert_digits(value)
|
84
|
+
def self.convert_digits(value)
|
84
85
|
value.to_s.gsub(/[^\d]/, '')
|
85
86
|
end
|
86
87
|
|
87
|
-
def convert_phrase(value)
|
88
|
+
def self.convert_phrase(value)
|
88
89
|
convert(:squish, convert(:strip, value))
|
89
90
|
end
|
90
91
|
end
|
data/lib/normatron/version.rb
CHANGED
data/spec/normatron_spec.rb
CHANGED
@@ -4,119 +4,145 @@ require "spec_helper"
|
|
4
4
|
|
5
5
|
describe Normatron do
|
6
6
|
before :each do
|
7
|
-
TestModel.
|
7
|
+
TestModel.normalization_options = nil
|
8
|
+
@instance = TestModel.new
|
8
9
|
end
|
9
10
|
|
10
|
-
it "should save without any normalize
|
11
|
+
it "should allow save without any normalize option" do
|
11
12
|
lambda do
|
12
|
-
|
13
|
-
|
14
|
-
a.save!
|
13
|
+
@instance.string_column = "Any string"
|
14
|
+
@instance.save!
|
15
15
|
end.should_not raise_error
|
16
16
|
end
|
17
17
|
|
18
|
+
it "should raise an error if attribute doesn't exist" do
|
19
|
+
lambda do
|
20
|
+
TestModel.normalize :ghost_attribute
|
21
|
+
end.should raise_error "Attribute 'ghost_attribute' doesn't exist in TestModel"
|
22
|
+
end
|
23
|
+
|
24
|
+
it "should raise an error if a wrong option is set" do
|
25
|
+
lambda do
|
26
|
+
TestModel.normalize :string_column, :wrong_option => :upcase
|
27
|
+
end.should raise_error "Wrong normalization key in TestModel, use :with instead of wrong_option"
|
28
|
+
end
|
29
|
+
|
30
|
+
it "should raise an error if wrong conversor is called" do
|
31
|
+
lambda do
|
32
|
+
TestModel.normalize :string_column, :with => :wrong_conversor
|
33
|
+
end.should raise_error "Normalization callback 'wrong_conversor' doesn't exist"
|
34
|
+
end
|
35
|
+
|
36
|
+
it "options should be accessible" do
|
37
|
+
TestModel.normalize :string_column, :with => [:capitalize, :upcase]
|
38
|
+
TestModel.normalize :integer_column, :with => :integer
|
39
|
+
TestModel.normalization_options.should == { :string_column => [:capitalize, :upcase], :integer_column => [:integer] }
|
40
|
+
TestModel.normalization_options.delete :string_column
|
41
|
+
TestModel.normalization_options.should == { :integer_column => [:integer] }
|
42
|
+
end
|
43
|
+
|
18
44
|
describe "Conversors" do
|
19
45
|
it :capitalize do
|
20
|
-
TestModel.normalize :
|
46
|
+
TestModel.normalize :string_column, :with => :capitalize
|
21
47
|
|
22
|
-
m = TestModel.create :
|
23
|
-
m.
|
48
|
+
m = TestModel.create :string_column => "áb c, 1 2 3 DEF GHI i oz "
|
49
|
+
m.string_column.should == "Áb c, 1 2 3 def ghi i oz "
|
24
50
|
end
|
25
51
|
|
26
52
|
it :digits do
|
27
|
-
TestModel.normalize :
|
53
|
+
TestModel.normalize :string_column, :with => :digits
|
28
54
|
|
29
|
-
m = TestModel.create :
|
30
|
-
m.
|
55
|
+
m = TestModel.create :string_column => " 1a 2b 3c 4d 5e 6f "
|
56
|
+
m.string_column.should == "123456"
|
31
57
|
end
|
32
58
|
|
33
59
|
it :downcase do
|
34
|
-
TestModel.normalize :
|
60
|
+
TestModel.normalize :string_column, :with => :downcase
|
35
61
|
|
36
|
-
m = TestModel.create :
|
37
|
-
m.
|
62
|
+
m = TestModel.create :string_column => " a b c, 1 2 3 DEF GHI i oz "
|
63
|
+
m.string_column.should == " a b c, 1 2 3 def ghi i oz "
|
38
64
|
end
|
39
65
|
|
40
66
|
it :phone do
|
41
|
-
TestModel.normalize :
|
67
|
+
TestModel.normalize :string_column, :with => :phone
|
42
68
|
|
43
|
-
m = TestModel.create :
|
44
|
-
m.
|
69
|
+
m = TestModel.create :string_column => "(99) 9999-9999"
|
70
|
+
m.string_column.should == "(99) 9999-9999"
|
45
71
|
|
46
|
-
m = TestModel.create :
|
47
|
-
m.
|
72
|
+
m = TestModel.create :string_column => "9999999999"
|
73
|
+
m.string_column.should == "(99) 9999-9999"
|
48
74
|
|
49
|
-
m = TestModel.create :
|
50
|
-
m.
|
75
|
+
m = TestModel.create :string_column => "999999999"
|
76
|
+
m.string_column.should == "999999999"
|
51
77
|
end
|
52
78
|
|
53
79
|
it :phrase do
|
54
|
-
TestModel.normalize :
|
80
|
+
TestModel.normalize :string_column, :with => :phrase
|
55
81
|
|
56
|
-
m = TestModel.create :
|
57
|
-
m.
|
82
|
+
m = TestModel.create :string_column => " a b c, 1 2 3 DEF GHI i oz "
|
83
|
+
m.string_column.should == "a b c, 1 2 3 DEF GHI i oz"
|
58
84
|
end
|
59
85
|
|
60
86
|
it :postal_code do
|
61
|
-
TestModel.normalize :
|
87
|
+
TestModel.normalize :string_column, :with => :postal_code
|
62
88
|
|
63
|
-
m = TestModel.create :
|
64
|
-
m.
|
89
|
+
m = TestModel.create :string_column => "88888-888"
|
90
|
+
m.string_column.should == "88888-888"
|
65
91
|
|
66
|
-
m = TestModel.create :
|
67
|
-
m.
|
92
|
+
m = TestModel.create :string_column => "88888888"
|
93
|
+
m.string_column.should == "88888-888"
|
68
94
|
|
69
|
-
m = TestModel.create :
|
70
|
-
m.
|
95
|
+
m = TestModel.create :string_column => "8888888"
|
96
|
+
m.string_column.should == "8888888"
|
71
97
|
end
|
72
98
|
|
73
99
|
it :squish do
|
74
|
-
TestModel.normalize :
|
100
|
+
TestModel.normalize :string_column, :with => :squish
|
75
101
|
|
76
|
-
m = TestModel.create :
|
77
|
-
m.
|
102
|
+
m = TestModel.create :string_column => " a b c, 1 2 3 DEF GHI i oz "
|
103
|
+
m.string_column.should == " a b c, 1 2 3 DEF GHI i oz "
|
78
104
|
end
|
79
105
|
|
80
106
|
it :strip do
|
81
|
-
TestModel.normalize :
|
107
|
+
TestModel.normalize :string_column, :with => :strip
|
82
108
|
|
83
|
-
m = TestModel.create :
|
84
|
-
m.
|
109
|
+
m = TestModel.create :string_column => " a b c, 1 2 3 DEF GHI i oz "
|
110
|
+
m.string_column.should == "a b c, 1 2 3 DEF GHI i oz"
|
85
111
|
end
|
86
112
|
|
87
113
|
it :titlecase do
|
88
|
-
TestModel.normalize :
|
114
|
+
TestModel.normalize :string_column, :with => :titlecase
|
89
115
|
|
90
|
-
m = TestModel.create :
|
91
|
-
m.
|
116
|
+
m = TestModel.create :string_column => " aé áb c, 1 2 3 DEF GHI i oz "
|
117
|
+
m.string_column.should == " Aé Áb C, 1 2 3 Def Ghi I Oz "
|
92
118
|
end
|
93
119
|
|
94
120
|
it :upcase do
|
95
|
-
TestModel.normalize :
|
121
|
+
TestModel.normalize :string_column, :with => :upcase
|
96
122
|
|
97
|
-
m = TestModel.create :
|
98
|
-
m.
|
123
|
+
m = TestModel.create :string_column => " a b c, 1 2 3 DEF GHI i oz "
|
124
|
+
m.string_column.should == " A B C, 1 2 3 DEF GHI I OZ "
|
99
125
|
end
|
100
126
|
|
101
127
|
it :nillify do
|
102
|
-
TestModel.normalize :
|
128
|
+
TestModel.normalize :string_column, :with => :nillify
|
103
129
|
|
104
|
-
m = TestModel.create :
|
105
|
-
m.
|
130
|
+
m = TestModel.create :string_column => " "
|
131
|
+
m.string_column.should == nil
|
106
132
|
end
|
107
133
|
|
108
134
|
it :lstrip do
|
109
|
-
TestModel.normalize :
|
135
|
+
TestModel.normalize :string_column, :with => :lstrip
|
110
136
|
|
111
|
-
m = TestModel.create :
|
112
|
-
m.
|
137
|
+
m = TestModel.create :string_column => " a b c, 1 2 3 DEF GHI i oz "
|
138
|
+
m.string_column.should == "a b c, 1 2 3 DEF GHI i oz "
|
113
139
|
end
|
114
140
|
|
115
141
|
it :rstrip do
|
116
|
-
TestModel.normalize :
|
142
|
+
TestModel.normalize :string_column, :with => :rstrip
|
117
143
|
|
118
|
-
m = TestModel.create :
|
119
|
-
m.
|
144
|
+
m = TestModel.create :string_column => " a b c, 1 2 3 DEF GHI i oz "
|
145
|
+
m.string_column.should == " a b c, 1 2 3 DEF GHI i oz"
|
120
146
|
end
|
121
147
|
end
|
122
148
|
end
|
data/spec/schema.rb
CHANGED
data/spec/support/test_model.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: normatron
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.4
|
5
5
|
prerelease:
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
@@ -9,7 +9,7 @@ authors:
|
|
9
9
|
autorequire:
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
|
-
date: 2012-08-
|
12
|
+
date: 2012-08-28 00:00:00.000000000 Z
|
13
13
|
dependencies:
|
14
14
|
- !ruby/object:Gem::Dependency
|
15
15
|
name: rails
|
@@ -71,7 +71,6 @@ files:
|
|
71
71
|
- lib/normatron/active_record.rb
|
72
72
|
- lib/normatron/conversors.rb
|
73
73
|
- lib/normatron/version.rb
|
74
|
-
- lib/tasks/normatron_tasks.rake
|
75
74
|
- lib/normatron.rb
|
76
75
|
- MIT-LICENSE
|
77
76
|
- Rakefile
|