normalizy 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (40) hide show
  1. checksums.yaml +7 -0
  2. data/CHANGELOG.md +3 -0
  3. data/LICENSE +21 -0
  4. data/README.md +341 -0
  5. data/lib/generators/normalizy/install_generator.rb +13 -0
  6. data/lib/generators/normalizy/templates/config/initializers/normalizy.rb +7 -0
  7. data/lib/normalizy/config.rb +35 -0
  8. data/lib/normalizy/extensions.rb +105 -0
  9. data/lib/normalizy/filters/number.rb +17 -0
  10. data/lib/normalizy/filters/strip.rb +19 -0
  11. data/lib/normalizy/filters.rb +8 -0
  12. data/lib/normalizy/rspec/matcher.rb +115 -0
  13. data/lib/normalizy/version.rb +5 -0
  14. data/lib/normalizy.rb +20 -0
  15. data/spec/normalizy/config/add_spec.rb +17 -0
  16. data/spec/normalizy/config/alias_spec.rb +63 -0
  17. data/spec/normalizy/config/default_filters_spec.rb +19 -0
  18. data/spec/normalizy/config/initialize_spec.rb +12 -0
  19. data/spec/normalizy/config/normalizy_aliases_spec.rb +9 -0
  20. data/spec/normalizy/config/normalizy_raws_spec.rb +9 -0
  21. data/spec/normalizy/extensions/apply_normalizations_spec.rb +163 -0
  22. data/spec/normalizy/extensions/normalizy_rules_spec.rb +244 -0
  23. data/spec/normalizy/extensions/normalizy_spec.rb +11 -0
  24. data/spec/normalizy/filters/number_spec.rb +15 -0
  25. data/spec/normalizy/filters/strip_spec.rb +13 -0
  26. data/spec/normalizy/normalizy/configure_spec.rb +11 -0
  27. data/spec/normalizy/rspec/matcher/description_spec.rb +26 -0
  28. data/spec/normalizy/rspec/matcher/failure_message_spec.rb +70 -0
  29. data/spec/normalizy/rspec/matcher/failure_message_when_negated_spec.rb +32 -0
  30. data/spec/normalizy/rspec/matcher/from_spec.rb +18 -0
  31. data/spec/normalizy/rspec/matcher/matchers_spec.rb +97 -0
  32. data/spec/normalizy/rspec/matcher/to_spec.rb +18 -0
  33. data/spec/normalizy/rspec/normalizy_spec.rb +8 -0
  34. data/spec/rails_helper.rb +9 -0
  35. data/spec/support/common.rb +11 -0
  36. data/spec/support/db/schema.rb +13 -0
  37. data/spec/support/filters/blacklist_filter.rb +15 -0
  38. data/spec/support/models/clean.rb +4 -0
  39. data/spec/support/models/user.rb +9 -0
  40. metadata +210 -0
data/lib/normalizy.rb ADDED
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Normalizy
4
+ class << self
5
+ def config
6
+ @config ||= Config.new
7
+ end
8
+
9
+ def configure
10
+ yield config
11
+ end
12
+ end
13
+ end
14
+
15
+ require 'active_record'
16
+ require 'normalizy/config'
17
+ require 'normalizy/extensions'
18
+ require 'normalizy/rspec/matcher'
19
+
20
+ ActiveRecord::Base.send :include, Normalizy::Extension
@@ -0,0 +1,17 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe Normalizy::Config, '#add' do
6
+ it 'adds filters to the built-in filters' do
7
+ Normalizy.configure do |config|
8
+ config.add :blacklist, :blacklist_filter
9
+ end
10
+
11
+ expect(Normalizy.config.filters).to eq(
12
+ blacklist: :blacklist_filter,
13
+ number: Normalizy::Filters::Number,
14
+ strip: Normalizy::Filters::Strip
15
+ )
16
+ end
17
+ end
@@ -0,0 +1,63 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe Normalizy::Config, '#alias' do
6
+ context 'with no raw type' do
7
+ let!(:object) { User.new name: 'Washington Botelho' }
8
+
9
+ before do
10
+ object.class.normalizy_rules = {}
11
+
12
+ Normalizy.configure do |config|
13
+ config.alias :email, :downcase
14
+ end
15
+ end
16
+
17
+ it 'alias one filter to others' do
18
+ object.class.normalizy :name, with: :email
19
+
20
+ object.save
21
+
22
+ expect(object.name).to eq 'washington botelho'
23
+ end
24
+ end
25
+
26
+ context 'with raw type' do
27
+ let!(:object) { User.new amount: 'R$ 4.200,00' }
28
+
29
+ before { object.class.normalizy_rules = {} }
30
+
31
+ context 'configured on setup' do
32
+ before do
33
+ Normalizy.configure do |config|
34
+ config.alias :money, :number, raw: true
35
+ end
36
+ end
37
+
38
+ it 'alias one filter to others' do
39
+ object.class.normalizy :amount, with: :money
40
+
41
+ object.save
42
+
43
+ expect(object.amount).to eq 420_000
44
+ end
45
+ end
46
+
47
+ context 'configured on normalizy' do
48
+ before do
49
+ Normalizy.configure do |config|
50
+ config.alias :money, :number
51
+ end
52
+ end
53
+
54
+ it 'alias one filter to others' do
55
+ object.class.normalizy :amount, with: :money, raw: true
56
+
57
+ object.save
58
+
59
+ expect(object.amount).to eq 420_000
60
+ end
61
+ end
62
+ end
63
+ end
@@ -0,0 +1,19 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe Normalizy::Config, 'default_filters' do
6
+ context 'when get' do
7
+ it 'returns the default filters' do
8
+ expect(subject.default_filters).to eq({})
9
+ end
10
+ end
11
+
12
+ context 'when set' do
13
+ before { subject.default_filters = :blank }
14
+
15
+ it 'keeps the original' do
16
+ expect(subject.default_filters).to eq :blank
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,12 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe Normalizy::Config, 'filters' do
6
+ it 'loads some filters' do
7
+ expect(subject.filters).to eq(
8
+ number: Normalizy::Filters::Number,
9
+ strip: Normalizy::Filters::Strip
10
+ )
11
+ end
12
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe Normalizy::Config, 'normalizy_aliases' do
6
+ it 'has the right defaults' do
7
+ expect(subject.normalizy_aliases).to eq({})
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe Normalizy::Config, 'normalizy_raws' do
6
+ it 'has the right defaults' do
7
+ expect(subject.normalizy_raws).to eq [:number]
8
+ end
9
+ end
@@ -0,0 +1,163 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe '#apply_normalizations' do
6
+ context 'when object has no normalizy' do
7
+ let!(:object) { Clean.create name: ' washington Botelho ' }
8
+
9
+ it 'does not normalize' do
10
+ expect(object.name).to eq ' washington Botelho '
11
+ end
12
+ end
13
+
14
+ context 'when object has normalizy' do
15
+ let!(:object) { User.new name: ' Washington Fuck Botelho ' }
16
+
17
+ before { object.class.normalizy_rules = {} }
18
+
19
+ context 'when a rule is not given' do
20
+ context 'and no block' do
21
+ before do
22
+ Normalizy.configure do |config|
23
+ config.default_filters = :squish
24
+ end
25
+
26
+ object.class.normalizy :name
27
+ end
28
+
29
+ it 'uses the default' do
30
+ object.save
31
+
32
+ expect(object.name).to eq 'Washington Fuck Botelho'
33
+ end
34
+ end
35
+
36
+ context 'but block is' do
37
+ let!(:block) { ->(value) { value.upcase } }
38
+
39
+ before do
40
+ Normalizy.configure do |config|
41
+ config.add :blacklist, Normalizy::Filters::Blacklist
42
+ end
43
+ end
44
+
45
+ it 'executes the block' do
46
+ object.class.normalizy :name, with: block
47
+
48
+ object.save
49
+
50
+ expect(object.name).to eq ' WASHINGTON FUCK BOTELHO '
51
+ end
52
+ end
53
+ end
54
+
55
+ context 'when a rule is given' do
56
+ context 'as symbol format' do
57
+ before { object.class.normalizy :name, with: :squish }
58
+
59
+ specify do
60
+ object.save
61
+
62
+ expect(object.name).to eq 'Washington Fuck Botelho'
63
+ end
64
+ end
65
+
66
+ context 'as array of symbol format' do
67
+ before { object.class.normalizy :name, with: [:squish] }
68
+
69
+ specify do
70
+ object.save
71
+
72
+ expect(object.name).to eq 'Washington Fuck Botelho'
73
+ end
74
+ end
75
+
76
+ context 'as a hash format' do
77
+ context 'and filter does not receives options' do
78
+ before { object.class.normalizy :name, with: { squish: :options } }
79
+
80
+ specify do
81
+ object.save
82
+
83
+ expect(object.name).to eq 'Washington Fuck Botelho'
84
+ end
85
+ end
86
+
87
+ context 'and filter receives options' do
88
+ before { object.class.normalizy :name, with: { strip: { side: :left } } }
89
+
90
+ specify do
91
+ object.save
92
+
93
+ expect(object.name).to eq 'Washington Fuck Botelho '
94
+ end
95
+ end
96
+ end
97
+
98
+ context 'as a module' do
99
+ before { object.class.normalizy :name, with: Normalizy::Filters::Blacklist }
100
+
101
+ specify do
102
+ object.save
103
+
104
+ expect(object.name).to eq ' Washington filtered Botelho '
105
+ end
106
+ end
107
+
108
+ context 'and block too' do
109
+ let!(:block) { ->(value) { value.upcase } }
110
+
111
+ before do
112
+ Normalizy.configure do |config|
113
+ config.add :blacklist, Normalizy::Filters::Blacklist
114
+ end
115
+ end
116
+
117
+ it 'runs after rules' do
118
+ object.class.normalizy :name, with: :blacklist, &block
119
+
120
+ object.save
121
+
122
+ expect(object.name).to eq ' WASHINGTON FILTERED BOTELHO '
123
+ end
124
+ end
125
+ end
126
+
127
+ context 'when no filter match' do
128
+ context 'and object has a method that responds' do
129
+ context 'with no options on rule' do
130
+ it 'runs the native method with empty options' do
131
+ object.class.normalizy :name, with: :custom_reverse
132
+
133
+ object.save
134
+
135
+ expect(object.name).to eq ' ohletoB kcuF notgnihsaW .{}.custom'
136
+ end
137
+ end
138
+
139
+ context 'with no options on rule' do
140
+ it 'runs the native method with given options' do
141
+ object.class.normalizy :name, with: { custom_reverse: :options }
142
+
143
+ object.save
144
+
145
+ expect(object.name).to eq ' ohletoB kcuF notgnihsaW .options.custom'
146
+ end
147
+ end
148
+ end
149
+
150
+ context 'and attribute has not a method that responds' do
151
+ context 'but attribute has a native method like it' do
152
+ it 'runs the native method' do
153
+ object.class.normalizy :name, with: :split
154
+
155
+ object.save
156
+
157
+ expect(object.name).to eq %(["Washington", "Fuck", "Botelho"])
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
163
+ end
@@ -0,0 +1,244 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe Normalizy::Extension, '#normalizy_rules' do
6
+ let!(:model) { User }
7
+
8
+ before { model.normalizy_rules = {} }
9
+
10
+ context 'with default' do
11
+ before do
12
+ Normalizy.configure do |config|
13
+ config.default_filters = :squish
14
+ end
15
+ end
16
+
17
+ context 'with one field' do
18
+ context 'with no rules' do
19
+ before { model.normalizy :name }
20
+
21
+ it 'appends default' do
22
+ expect(model.normalizy_rules).to eq(name: [{ block: nil, options: {}, rules: :squish }])
23
+ end
24
+ end
25
+
26
+ context 'with one rule' do
27
+ before { model.normalizy :name, with: :upcase }
28
+
29
+ specify do
30
+ expect(model.normalizy_rules).to eq(name: [{ block: nil, options: {}, rules: :upcase }])
31
+ end
32
+ end
33
+
34
+ context 'with multiple rules' do
35
+ before { model.normalizy :name, with: [:upcase, 'blank', { trim: { side: :left } }] }
36
+
37
+ specify do
38
+ expect(model.normalizy_rules).to eq(name: [{ block: nil, options: {}, rules: [:upcase, 'blank', { trim: { side: :left } }] }])
39
+ end
40
+ end
41
+
42
+ context 'with multiple normalizes including repeated rules' do
43
+ before do
44
+ model.normalizy :name, with: [:upcase, { trim: { side: :left } }]
45
+ model.normalizy :name, with: :squish
46
+ model.normalizy :name, with: [:upcase, { trim: { side: :right } }]
47
+ model.normalizy :name, with: :squish
48
+ end
49
+
50
+ it 'includes all' do
51
+ expect(model.normalizy_rules).to eq(
52
+ name: [
53
+ { block: nil, options: {}, rules: [:upcase, { trim: { side: :left } }] },
54
+ { block: nil, options: {}, rules: :squish },
55
+ { block: nil, options: {}, rules: [:upcase, { trim: { side: :right } }] },
56
+ { block: nil, options: {}, rules: :squish }
57
+ ]
58
+ )
59
+ end
60
+ end
61
+ end
62
+
63
+ context 'with multiple fields' do
64
+ context 'with no rule' do
65
+ before { model.normalizy :email, :name }
66
+
67
+ it 'appends default' do
68
+ expect(model.normalizy_rules).to eq(
69
+ email: [{ block: nil, options: {}, rules: :squish }],
70
+ name: [{ block: nil, options: {}, rules: :squish }]
71
+ )
72
+ end
73
+ end
74
+
75
+ context 'with one rule' do
76
+ before { model.normalizy :email, :name, with: :upcase }
77
+
78
+ specify do
79
+ expect(model.normalizy_rules).to eq(
80
+ name: [{ block: nil, options: {}, rules: :upcase }],
81
+ email: [{ block: nil, options: {}, rules: :upcase }]
82
+ )
83
+ end
84
+ end
85
+
86
+ context 'with multiple rules' do
87
+ before { model.normalizy :email, :name, with: [:upcase, :blank, { trim: { side: :left } }] }
88
+
89
+ specify do
90
+ expect(model.normalizy_rules).to eq(
91
+ email: [{ block: nil, options: {}, rules: [:upcase, :blank, { trim: { side: :left } }] }],
92
+ name: [{ block: nil, options: {}, rules: [:upcase, :blank, { trim: { side: :left } }] }]
93
+ )
94
+ end
95
+ end
96
+
97
+ context 'with multiple normalizes including repeated rules' do
98
+ before do
99
+ model.normalizy :email, :name, with: [:upcase, { trim: { side: :left } }]
100
+ model.normalizy :email, :name, with: :squish
101
+ model.normalizy :email, :name, with: [:upcase, { trim: { side: :right } }]
102
+ model.normalizy :email, :name, with: :squish
103
+ end
104
+
105
+ it 'merges all as unique with the last one having priority in deep levels too' do
106
+ expect(model.normalizy_rules).to eq(
107
+ email: [
108
+ { block: nil, options: {}, rules: [:upcase, { trim: { side: :left } }] },
109
+ { block: nil, options: {}, rules: :squish },
110
+ { block: nil, options: {}, rules: [:upcase, { trim: { side: :right } }] },
111
+ { block: nil, options: {}, rules: :squish }
112
+ ],
113
+ name: [
114
+ { block: nil, options: {}, rules: [:upcase, { trim: { side: :left } }] },
115
+ { block: nil, options: {}, rules: :squish },
116
+ { block: nil, options: {}, rules: [:upcase, { trim: { side: :right } }] },
117
+ { block: nil, options: {}, rules: :squish }
118
+ ]
119
+ )
120
+ end
121
+ end
122
+ end
123
+ end
124
+
125
+ context 'with no default' do
126
+ before do
127
+ Normalizy.configure do |config|
128
+ config.default_filters = []
129
+ end
130
+ end
131
+
132
+ context 'with one field' do
133
+ context 'with no rules' do
134
+ before { model.normalizy :name }
135
+
136
+ specify do
137
+ expect(model.normalizy_rules).to eq(name: [{ block: nil, options: {}, rules: [] }])
138
+ end
139
+ end
140
+
141
+ context 'with one rule' do
142
+ before { model.normalizy :name, with: :upcase }
143
+
144
+ specify do
145
+ expect(model.normalizy_rules).to eq(name: [{ block: nil, options: {}, rules: :upcase }])
146
+ end
147
+ end
148
+
149
+ context 'with multiple rules' do
150
+ before { model.normalizy :name, with: %i[upcase blank] }
151
+
152
+ specify do
153
+ expect(model.normalizy_rules).to eq(name: [{ block: nil, options: {}, rules: %i[upcase blank] }])
154
+ end
155
+ end
156
+
157
+ context 'with multiple normalizes including repeated rules' do
158
+ before do
159
+ model.normalizy :name, with: :upcase
160
+ model.normalizy :name, with: :squish
161
+ model.normalizy :name, with: :upcase
162
+ end
163
+
164
+ it 'merges all as unique' do
165
+ expect(model.normalizy_rules).to eq(
166
+ name: [
167
+ { block: nil, options: {}, rules: :upcase },
168
+ { block: nil, options: {}, rules: :squish },
169
+ { block: nil, options: {}, rules: :upcase }
170
+ ]
171
+ )
172
+ end
173
+ end
174
+ end
175
+
176
+ context 'with multiple fields' do
177
+ context 'with no rule' do
178
+ before { model.normalizy :email, :name }
179
+
180
+ specify do
181
+ expect(model.normalizy_rules).to eq(
182
+ email: [{ block: nil, options: {}, rules: [] }],
183
+ name: [{ block: nil, options: {}, rules: [] }]
184
+ )
185
+ end
186
+ end
187
+
188
+ context 'with one rule' do
189
+ before { model.normalizy :email, :name, with: :upcase }
190
+
191
+ specify do
192
+ expect(model.normalizy_rules).to eq(
193
+ email: [{ block: nil, options: {}, rules: :upcase }],
194
+ name: [{ block: nil, options: {}, rules: :upcase }]
195
+ )
196
+ end
197
+ end
198
+
199
+ context 'with multiple rules' do
200
+ before { model.normalizy :email, :name, with: %i[upcase blank] }
201
+
202
+ specify do
203
+ expect(model.normalizy_rules).to eq(
204
+ email: [{ block: nil, options: {}, rules: %i[upcase blank] }],
205
+ name: [{ block: nil, options: {}, rules: %i[upcase blank] }]
206
+ )
207
+ end
208
+ end
209
+
210
+ context 'with multiple normalizes including repeated rules' do
211
+ before do
212
+ model.normalizy :email, :name, with: :upcase
213
+ model.normalizy :email, :name, with: :squish
214
+ model.normalizy :email, :name, with: :upcase
215
+ end
216
+
217
+ it 'merges all as unique' do
218
+ expect(model.normalizy_rules).to eq(
219
+ email: [
220
+ { block: nil, options: {}, rules: :upcase },
221
+ { block: nil, options: {}, rules: :squish },
222
+ { block: nil, options: {}, rules: :upcase }
223
+ ],
224
+ name: [
225
+ { block: nil, options: {}, rules: :upcase },
226
+ { block: nil, options: {}, rules: :squish },
227
+ { block: nil, options: {}, rules: :upcase }
228
+ ]
229
+ )
230
+ end
231
+ end
232
+ end
233
+ end
234
+
235
+ context 'when block is given' do
236
+ let!(:block) { ->(value) { value.downcase } }
237
+
238
+ before { model.normalizy(:name, &block) }
239
+
240
+ specify do
241
+ expect(model.normalizy_rules[:name][0][:block]).to eq block
242
+ end
243
+ end
244
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe User, 'active_record' do
6
+ xit 'norm the object' do
7
+ matcher.matches?(object)
8
+
9
+ expect(matcher.instance_variable_get(:@subject)).to eq object
10
+ end
11
+ end
@@ -0,0 +1,15 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe Normalizy::Filters::Number do
6
+ it { expect(subject.call('abcdefghijklmnopkrstuvxyz')).to eq nil }
7
+ it { expect(subject.call('ABCDEFGHIJKLMNOPKRSTUVXYZ')).to eq nil }
8
+ it { expect(subject.call('áéíóúàâêôãõ')).to eq nil }
9
+ it { expect(subject.call('0123456789')).to eq 123_456_789 }
10
+ it { expect(subject.call("\n")).to eq nil }
11
+ it { expect(subject.call(42)).to eq 42 }
12
+ it { expect(subject.call(nil)).to eq nil }
13
+ it { expect(subject.call('nil')).to eq nil }
14
+ it { expect(subject.call('')).to eq nil }
15
+ end
@@ -0,0 +1,13 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe Normalizy::Filters::Strip do
6
+ it { expect(subject.call(' Some Text ')).to eq 'Some Text' }
7
+ it { expect(subject.call(' Some Text ', side: :left)).to eq 'Some Text ' }
8
+ it { expect(subject.call(' Some Text ', side: :right)).to eq ' Some Text' }
9
+ it { expect(subject.call(' Some Text ', side: :both)).to eq 'Some Text' }
10
+ it { expect(subject.call("\n")).to eq '' }
11
+ it { expect(subject.call(42)).to eq 42 }
12
+ it { expect(subject.call(nil)).to eq nil }
13
+ end
@@ -0,0 +1,11 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe Normalizy, '#configure' do
6
+ it 'yields the default config' do
7
+ described_class.configure do |conf|
8
+ expect(conf).to be_a_instance_of Normalizy::Config
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,26 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails_helper'
4
+
5
+ RSpec.describe Normalizy::RSpec::Matcher, '.description' do
6
+ let!(:matcher) { described_class.new :name }
7
+
8
+ before do
9
+ matcher.from :from
10
+ matcher.to :to
11
+ end
12
+
13
+ context 'with no :with expectation' do
14
+ specify do
15
+ expect(matcher.description).to eq 'normalizy name from "from" to "to"'
16
+ end
17
+ end
18
+
19
+ context 'with :with expectation' do
20
+ before { matcher.with :blank }
21
+
22
+ specify do
23
+ expect(matcher.description).to eq 'normalizy name with blank'
24
+ end
25
+ end
26
+ end