normalizy 0.1.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 (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