usecasing_validations 0.5.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 +15 -0
  2. data/.gitignore +19 -0
  3. data/.rspec +3 -0
  4. data/.rvmrc +1 -0
  5. data/Gemfile +4 -0
  6. data/Gemfile.lock +40 -0
  7. data/LICENSE.txt +22 -0
  8. data/README.md +1 -0
  9. data/Rakefile +6 -0
  10. data/lib/usecasing/depends_all.rb +21 -0
  11. data/lib/usecasing/validator.rb +31 -0
  12. data/lib/usecasing_validations/custom_validator.rb +21 -0
  13. data/lib/usecasing_validations/each_validator.rb +32 -0
  14. data/lib/usecasing_validations/errors.rb +364 -0
  15. data/lib/usecasing_validations/helpers.rb +50 -0
  16. data/lib/usecasing_validations/target.rb +44 -0
  17. data/lib/usecasing_validations/validations/format.rb +116 -0
  18. data/lib/usecasing_validations/validations/helper_methods.rb +16 -0
  19. data/lib/usecasing_validations/validations/length.rb +126 -0
  20. data/lib/usecasing_validations/validations/presence.rb +17 -0
  21. data/lib/usecasing_validations/validations/uniqueness.rb +56 -0
  22. data/lib/usecasing_validations/validator.rb +20 -0
  23. data/lib/usecasing_validations/version.rb +3 -0
  24. data/lib/usecasing_validations.rb +122 -0
  25. data/spec/spec_helper.rb +17 -0
  26. data/spec/support/models/ruby_post.rb +9 -0
  27. data/spec/support/models/ruby_post_with_comments.rb +28 -0
  28. data/spec/support/usecases/validate_comments.rb +34 -0
  29. data/spec/support/usecases/validate_comments_custom_target.rb +14 -0
  30. data/spec/support/usecases/validate_depends_all.rb +24 -0
  31. data/spec/support/usecases/validate_post.rb +18 -0
  32. data/spec/support/usecases/validate_post_clear_errors.rb +9 -0
  33. data/spec/support/usecases/validate_uniq_comments.rb +77 -0
  34. data/spec/usecasing/validate_comments_custom_target_spec.rb +36 -0
  35. data/spec/usecasing/validate_comments_spec.rb +77 -0
  36. data/spec/usecasing/validate_depends_all_spec.rb +22 -0
  37. data/spec/usecasing/validate_post_clear_errors_spec.rb +48 -0
  38. data/spec/usecasing/validates_uniqueness_of_spec.rb +124 -0
  39. data/usecasing_validations.gemspec +29 -0
  40. metadata +166 -0
checksums.yaml ADDED
@@ -0,0 +1,15 @@
1
+ ---
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ MGUyMzJjMTM2MDg0NWFhNzBhNjI2MWZjODc2NmUzYjA1MDlmMDI5Zg==
5
+ data.tar.gz: !binary |-
6
+ NWEyZTM5MTVlNjc3MThkYmUyOTg0NmE1MGUxYTA1ZjVmNzg2MTlkNQ==
7
+ SHA512:
8
+ metadata.gz: !binary |-
9
+ MmExMzUyN2U1OWI0NWUwOGVjOGY3YmI1NjMwMjhhYjcwN2RjYWM4ZWQ5MzE2
10
+ OTJkMjcyMmFiZjc5MmE1YWUwNWIxMjUzYzExZmVjZWIzZmUxMjQyZTk1ZDAy
11
+ N2I5ODk2MTBiODkxYWEwZTM1ODA1YzA5YjUwYWE3Y2YxNjRlNTg=
12
+ data.tar.gz: !binary |-
13
+ MzZhZTk0MWM4Y2IxZWQ1YzI3ZmRmNzcxMTY2MzRjZjAxNzE4Y2E4ZDliMjA1
14
+ NzIwOWQ0OTBhYmVkZjFmZDBlODc2MmU1Yjg3NTQ5MzFjOWVkZTZlNGUzZDhh
15
+ MmY1NzY1OGJiNjEzNjY0NGVjMzE0MDZmYmUyMDk3MDE3YTQ5MDY=
data/.gitignore ADDED
@@ -0,0 +1,19 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp/*
18
+ *.DS_Store
19
+ db/test.sqlite3
data/.rspec ADDED
@@ -0,0 +1,3 @@
1
+ --tty
2
+ --color
3
+ --format documentation
data/.rvmrc ADDED
@@ -0,0 +1 @@
1
+ rvm 1.9.3@usecasing_validations --create
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in usecasing.gemspec
4
+ gemspec
data/Gemfile.lock ADDED
@@ -0,0 +1,40 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ usecasing_validations (0.5.0)
5
+ usecasing (= 0.1.4)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ coderay (1.1.0)
11
+ diff-lcs (1.2.5)
12
+ metaclass (0.0.2)
13
+ method_source (0.8.2)
14
+ mocha (1.0.0)
15
+ metaclass (~> 0.0.1)
16
+ pry (0.9.12.4)
17
+ coderay (~> 1.0)
18
+ method_source (~> 0.8)
19
+ slop (~> 3.4)
20
+ rake (10.1.1)
21
+ rspec (2.14.1)
22
+ rspec-core (~> 2.14.0)
23
+ rspec-expectations (~> 2.14.0)
24
+ rspec-mocks (~> 2.14.0)
25
+ rspec-core (2.14.7)
26
+ rspec-expectations (2.14.4)
27
+ diff-lcs (>= 1.1.3, < 2.0)
28
+ rspec-mocks (2.14.4)
29
+ slop (3.4.7)
30
+ usecasing (0.1.4)
31
+
32
+ PLATFORMS
33
+ ruby
34
+
35
+ DEPENDENCIES
36
+ mocha
37
+ pry
38
+ rake
39
+ rspec
40
+ usecasing_validations!
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Thiago Dantas
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1 @@
1
+ Nothing to add for now
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+
3
+ require 'rspec/core/rake_task'
4
+
5
+ task :default => :test
6
+ RSpec::Core::RakeTask.new(:test)
@@ -0,0 +1,21 @@
1
+ module UseCase
2
+
3
+ class DependsAll < Base
4
+
5
+ private ######################### PRIVATE ######################
6
+
7
+ def self.tx(execution_order, context)
8
+ ctx = Context.new(context)
9
+ executed = []
10
+ execution_order.each do |usecase|
11
+ # break unless ctx.success?
12
+ executed.push(usecase)
13
+ yield usecase, ctx
14
+ end
15
+ rollback(executed, ctx) unless ctx.success?
16
+ ctx
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,31 @@
1
+ module UseCase
2
+
3
+ class Validator < Base
4
+
5
+ include UseCaseValidations
6
+
7
+ def perform
8
+ targets = [*target]
9
+
10
+ if target.nil?
11
+ all_validations_green = false
12
+
13
+ elsif targets.empty?
14
+ all_validations_green = true
15
+
16
+ else
17
+ all_validations_green = targets.map do |object_to_validate|
18
+ if Helpers._marked_for_destruction?(object_to_validate)
19
+ true
20
+ else
21
+ valid?(object_to_validate)
22
+ end
23
+ end.all?
24
+ end
25
+
26
+ failure(self.class.to_s.downcase.to_sym, :failed) unless all_validations_green
27
+ end
28
+
29
+ end
30
+
31
+ end
@@ -0,0 +1,21 @@
1
+ module UseCaseValidations
2
+
3
+ class CustomValidator < Validator
4
+
5
+ attr_reader :methods
6
+
7
+ def initialize(args)
8
+ options = Helpers._extract_options!(args)
9
+ @methods = args
10
+ super(options)
11
+ end
12
+
13
+ def validate(record)
14
+ [*methods].map do |method|
15
+ base.send(method, record)
16
+ end.all?
17
+ end
18
+
19
+ end
20
+
21
+ end
@@ -0,0 +1,32 @@
1
+ module UseCaseValidations
2
+
3
+ class EachValidator < Validator
4
+
5
+ attr_reader :attributes
6
+
7
+ def initialize(options)
8
+ @attributes = Array(options.delete(:attributes))
9
+ raise ArgumentError, ":attributes cannot be blank" if @attributes.empty?
10
+ super
11
+ check_validity!
12
+ end
13
+
14
+ def validate(record)
15
+ attributes.each do |attribute|
16
+ value = record.respond_to?(attribute) ? record.send(attribute) : nil
17
+ next if (value.nil? && options[:allow_nil]) || (Helpers._blank?(value) && options[:allow_blank])
18
+ validate_each(record, attribute, value)
19
+ end
20
+ end
21
+
22
+ # Override this method in subclasses with the validation logic, adding
23
+ # errors to the records +errors+ array where necessary.
24
+ def validate_each(record, attribute, value)
25
+ raise NotImplementedError, "Subclasses must implement a validate_each(record, attribute, value) method"
26
+ end
27
+
28
+ def check_validity!; end
29
+
30
+ end
31
+
32
+ end
@@ -0,0 +1,364 @@
1
+ module UseCaseValidations
2
+
3
+ class Errors
4
+ include Enumerable
5
+
6
+ CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank, :strict]
7
+
8
+ attr_reader :messages
9
+
10
+ # Pass in the instance of the object that is using the errors object.
11
+ #
12
+ # class Person
13
+ # def initialize
14
+ # @errors = ActiveModel::Errors.new(self)
15
+ # end
16
+ # end
17
+ def initialize(base)
18
+ @base = base
19
+ @messages = {}
20
+ end
21
+
22
+ def initialize_dup(other) # :nodoc:
23
+ @messages = other.messages.dup
24
+ super
25
+ end
26
+
27
+ # Clear the error messages.
28
+ #
29
+ # person.errors.full_messages # => ["name can not be nil"]
30
+ # person.errors.clear
31
+ # person.errors.full_messages # => []
32
+ def clear
33
+ messages.clear
34
+ end
35
+
36
+ # Returns +true+ if the error messages include an error for the given key
37
+ # +attribute+, +false+ otherwise.
38
+ #
39
+ # person.errors.messages # => {:name=>["can not be nil"]}
40
+ # person.errors.include?(:name) # => true
41
+ # person.errors.include?(:age) # => false
42
+ def include?(attribute)
43
+ (v = messages[attribute]) && v.any?
44
+ end
45
+ # aliases include?
46
+ alias :has_key? :include?
47
+
48
+ # Get messages for +key+.
49
+ #
50
+ # person.errors.messages # => {:name=>["can not be nil"]}
51
+ # person.errors.get(:name) # => ["can not be nil"]
52
+ # person.errors.get(:age) # => nil
53
+ def get(key)
54
+ messages[key]
55
+ end
56
+
57
+ # Set messages for +key+ to +value+.
58
+ #
59
+ # person.errors.get(:name) # => ["can not be nil"]
60
+ # person.errors.set(:name, ["can't be nil"])
61
+ # person.errors.get(:name) # => ["can't be nil"]
62
+ def set(key, value)
63
+ messages[key] = value
64
+ end
65
+
66
+ # Delete messages for +key+. Returns the deleted messages.
67
+ #
68
+ # person.errors.get(:name) # => ["can not be nil"]
69
+ # person.errors.delete(:name) # => ["can not be nil"]
70
+ # person.errors.get(:name) # => nil
71
+ def delete(key)
72
+ messages.delete(key)
73
+ end
74
+
75
+ # When passed a symbol or a name of a method, returns an array of errors
76
+ # for the method.
77
+ #
78
+ # person.errors[:name] # => ["can not be nil"]
79
+ # person.errors['name'] # => ["can not be nil"]
80
+ def [](attribute)
81
+ get(attribute.to_sym) || set(attribute.to_sym, [])
82
+ end
83
+
84
+ # Adds to the supplied attribute the supplied error message.
85
+ #
86
+ # person.errors[:name] = "must be set"
87
+ # person.errors[:name] # => ['must be set']
88
+ def []=(attribute, error)
89
+ self[attribute] << error
90
+ end
91
+
92
+ # Iterates through each error key, value pair in the error messages hash.
93
+ # Yields the attribute and the error for that attribute. If the attribute
94
+ # has more than one error message, yields once for each error message.
95
+ #
96
+ # person.errors.add(:name, "can't be blank")
97
+ # person.errors.each do |attribute, error|
98
+ # # Will yield :name and "can't be blank"
99
+ # end
100
+ #
101
+ # person.errors.add(:name, "must be specified")
102
+ # person.errors.each do |attribute, error|
103
+ # # Will yield :name and "can't be blank"
104
+ # # then yield :name and "must be specified"
105
+ # end
106
+ def each
107
+ messages.each_key do |attribute|
108
+ self[attribute].each { |error| yield attribute, error }
109
+ end
110
+ end
111
+
112
+ # Returns the number of error messages.
113
+ #
114
+ # person.errors.add(:name, "can't be blank")
115
+ # person.errors.size # => 1
116
+ # person.errors.add(:name, "must be specified")
117
+ # person.errors.size # => 2
118
+ def size
119
+ values.flatten.size
120
+ end
121
+
122
+ # Returns all message values.
123
+ #
124
+ # person.errors.messages # => {:name=>["can not be nil", "must be specified"]}
125
+ # person.errors.values # => [["can not be nil", "must be specified"]]
126
+ def values
127
+ messages.values
128
+ end
129
+
130
+ # Returns all message keys.
131
+ #
132
+ # person.errors.messages # => {:name=>["can not be nil", "must be specified"]}
133
+ # person.errors.keys # => [:name]
134
+ def keys
135
+ messages.keys
136
+ end
137
+
138
+ # Returns an array of error messages, with the attribute name included.
139
+ #
140
+ # person.errors.add(:name, "can't be blank")
141
+ # person.errors.add(:name, "must be specified")
142
+ # person.errors.to_a # => ["name can't be blank", "name must be specified"]
143
+ def to_a
144
+ full_messages
145
+ end
146
+
147
+ # Returns the number of error messages.
148
+ #
149
+ # person.errors.add(:name, "can't be blank")
150
+ # person.errors.count # => 1
151
+ # person.errors.add(:name, "must be specified")
152
+ # person.errors.count # => 2
153
+ def count
154
+ to_a.size
155
+ end
156
+
157
+ # Returns +true+ if no errors are found, +false+ otherwise.
158
+ # If the error message is a string it can be empty.
159
+ #
160
+ # person.errors.full_messages # => ["name can not be nil"]
161
+ # person.errors.empty? # => false
162
+ def empty?
163
+ all? { |k, v| v && v.empty? && !v.is_a?(String) }
164
+ end
165
+ # aliases empty?
166
+ alias_method :blank?, :empty?
167
+
168
+ # Returns an xml formatted representation of the Errors hash.
169
+ #
170
+ # person.errors.add(:name, "can't be blank")
171
+ # person.errors.add(:name, "must be specified")
172
+ # person.errors.to_xml
173
+ # # =>
174
+ # # <?xml version=\"1.0\" encoding=\"UTF-8\"?>
175
+ # # <errors>
176
+ # # <error>name can't be blank</error>
177
+ # # <error>name must be specified</error>
178
+ # # </errors>
179
+ def to_xml(options={})
180
+ to_a.to_xml({ root: "errors", skip_types: true }.merge!(options))
181
+ end
182
+
183
+ # Returns a Hash that can be used as the JSON representation for this
184
+ # object. You can pass the <tt>:full_messages</tt> option. This determines
185
+ # if the json object should contain full messages or not (false by default).
186
+ #
187
+ # person.as_json # => {:name=>["can not be nil"]}
188
+ # person.as_json(full_messages: true) # => {:name=>["name can not be nil"]}
189
+ def as_json(options=nil)
190
+ to_hash(options && options[:full_messages])
191
+ end
192
+
193
+ # Returns a Hash of attributes with their error messages. If +full_messages+
194
+ # is +true+, it will contain full messages (see +full_message+).
195
+ #
196
+ # person.to_hash # => {:name=>["can not be nil"]}
197
+ # person.to_hash(true) # => {:name=>["name can not be nil"]}
198
+ def to_hash(full_messages = false)
199
+ if full_messages
200
+ messages = {}
201
+ self.messages.each do |attribute, array|
202
+ messages[attribute] = array.map { |message| full_message(attribute, message) }
203
+ end
204
+ messages
205
+ else
206
+ self.messages.dup
207
+ end
208
+ end
209
+
210
+ # Adds +message+ to the error messages on +attribute+. More than one error
211
+ # can be added to the same +attribute+. If no +message+ is supplied,
212
+ # <tt>:invalid</tt> is assumed.
213
+ #
214
+ # person.errors.add(:name)
215
+ # # => ["is invalid"]
216
+ # person.errors.add(:name, 'must be implemented')
217
+ # # => ["is invalid", "must be implemented"]
218
+ #
219
+ # person.errors.messages
220
+ # # => {:name=>["must be implemented", "is invalid"]}
221
+ #
222
+ # If +message+ is a symbol, it will be translated using the appropriate
223
+ # scope (see +generate_message+).
224
+ #
225
+ # If +message+ is a proc, it will be called, allowing for things like
226
+ # <tt>Time.now</tt> to be used within an error.
227
+ #
228
+ # person.errors.messages # => {}
229
+ def add(attribute, message = nil, options = {})
230
+ message = normalize_message(attribute, message, options)
231
+ self[attribute] << message
232
+ end
233
+
234
+ # Returns +true+ if an error on the attribute with the given message is
235
+ # present, +false+ otherwise. +message+ is treated the same as for +add+.
236
+ #
237
+ # person.errors.add :name, :blank
238
+ # person.errors.added? :name, :blank # => true
239
+ def added?(attribute, message = nil, options = {})
240
+ message = normalize_message(attribute, message, options)
241
+ self[attribute].include? message
242
+ end
243
+
244
+ # Returns all the full error messages in an array.
245
+ #
246
+ # class Person
247
+ # validates_presence_of :name, :address, :email
248
+ # validates_length_of :name, in: 5..30
249
+ # end
250
+ #
251
+ # person = Person.create(address: '123 First St.')
252
+ # person.errors.full_messages
253
+ # # => ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Email can't be blank"]
254
+ def full_messages
255
+ map { |attribute, message| full_message(attribute, message) }
256
+ end
257
+
258
+ # Returns all the full error messages for a given attribute in an array.
259
+ #
260
+ # class Person
261
+ # validates_presence_of :name, :email
262
+ # validates_length_of :name, in: 5..30
263
+ # end
264
+ #
265
+ # person = Person.create()
266
+ # person.errors.full_messages_for(:name)
267
+ # # => ["Name is too short (minimum is 5 characters)", "Name can't be blank"]
268
+ def full_messages_for(attribute)
269
+ (get(attribute) || []).map { |message| full_message(attribute, message) }
270
+ end
271
+
272
+ # Returns a full message for a given attribute.
273
+ #
274
+ # person.errors.full_message(:name, 'is invalid') # => "Name is invalid"
275
+ def full_message(attribute, message)
276
+ return message if attribute == :base || !@base.class.respond_to?(:model_name)
277
+
278
+ attr_name = attribute.to_s.tr('.', '_').humanize
279
+ attr_name = @base.class.human_attribute_name(attribute, default: attr_name)
280
+ I18n.t(:"errors.format", {
281
+ default: "%{attribute} %{message}",
282
+ attribute: attr_name,
283
+ message: message
284
+ })
285
+ end
286
+
287
+ # Translates an error message in its default scope
288
+ # (<tt>activemodel.errors.messages</tt>).
289
+ #
290
+ # Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>,
291
+ # if it's not there, it's looked up in <tt>models.MODEL.MESSAGE</tt> and if
292
+ # that is not there also, it returns the translation of the default message
293
+ # (e.g. <tt>activemodel.errors.messages.MESSAGE</tt>). The translated model
294
+ # name, translated attribute name and the value are available for
295
+ # interpolation.
296
+ #
297
+ # When using inheritance in your models, it will check all the inherited
298
+ # models too, but only if the model itself hasn't been found. Say you have
299
+ # <tt>class Admin < User; end</tt> and you wanted the translation for
300
+ # the <tt>:blank</tt> error message for the <tt>title</tt> attribute,
301
+ # it looks for these translations:
302
+ #
303
+ # * <tt>activemodel.errors.models.admin.attributes.title.blank</tt>
304
+ # * <tt>activemodel.errors.models.admin.blank</tt>
305
+ # * <tt>activemodel.errors.models.user.attributes.title.blank</tt>
306
+ # * <tt>activemodel.errors.models.user.blank</tt>
307
+ # * any default you provided through the +options+ hash (in the <tt>activemodel.errors</tt> scope)
308
+ # * <tt>activemodel.errors.messages.blank</tt>
309
+ # * <tt>errors.attributes.title.blank</tt>
310
+ # * <tt>errors.messages.blank</tt>
311
+ def generate_message(attribute, type = :invalid, options = {})
312
+ type = options.delete(:message) if options[:message].is_a?(Symbol)
313
+
314
+ if !@base.class.respond_to?(:model_name)
315
+ return options.key?(:message) ? options[:message] : type
316
+ end
317
+
318
+ if @base.class.respond_to?(:i18n_scope)
319
+ defaults = @base.class.lookup_ancestors.map do |klass|
320
+ [ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
321
+ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ]
322
+ end
323
+ else
324
+ defaults = []
325
+ end
326
+
327
+ defaults << options.delete(:message)
328
+ defaults << :"#{@base.class.i18n_scope}.errors.messages.#{type}" if @base.class.respond_to?(:i18n_scope)
329
+ defaults << :"errors.attributes.#{attribute}.#{type}"
330
+ defaults << :"errors.messages.#{type}"
331
+
332
+ defaults.compact!
333
+ defaults.flatten!
334
+
335
+ key = defaults.shift
336
+ value = (attribute != :base ? @base.send(attribute) : nil)
337
+
338
+ options = {
339
+ default: defaults,
340
+ model: @base.class.model_name.human,
341
+ attribute: @base.class.human_attribute_name(attribute),
342
+ value: value
343
+ }.merge!(options)
344
+
345
+ I18n.translate(key, options)
346
+ end
347
+
348
+ private
349
+ def normalize_message(attribute, message, options)
350
+ message ||= :invalid
351
+
352
+ case message
353
+ when Symbol
354
+ generate_message(attribute, message, Helpers._except(options, *CALLBACKS_OPTIONS))
355
+ when Proc
356
+ message.call
357
+ else
358
+ message
359
+ end
360
+ end
361
+
362
+ end
363
+
364
+ end
@@ -0,0 +1,50 @@
1
+ module UseCaseValidations
2
+
3
+ module Helpers
4
+
5
+ extend self
6
+
7
+ def _blank?(object)
8
+ if object.is_a?(String)
9
+ object !~ /[^[:space:]]/
10
+ else
11
+ object.respond_to?(:empty?) ? object.empty? : !object
12
+ end
13
+ end
14
+
15
+ def _marked_for_destruction?(object)
16
+ object.respond_to?(:marked_for_destruction?) ? object.marked_for_destruction? : false
17
+ end
18
+
19
+ def _extract_options!(array)
20
+ if array.last.is_a?(Hash) && array.last.instance_of?(Hash)
21
+ array.pop
22
+ else
23
+ {}
24
+ end
25
+ end
26
+
27
+ def _symbolyze_keys(hash)
28
+ hash.keys.reduce({ }) do |acc, key|
29
+ acc[key.to_sym] = hash[key]
30
+ acc
31
+ end
32
+ end
33
+
34
+ def _except(hash, *keys)
35
+ _hash = hash.dup
36
+ keys.each { |key| _hash.delete(key) }
37
+ _hash
38
+ end
39
+
40
+ def _call_proc_or_method(base, proc_or_method, object = nil)
41
+ if object.nil?
42
+ proc_or_method.is_a?(Proc) ? base.instance_exec(&proc_or_method) : base.send(proc_or_method)
43
+ else
44
+ proc_or_method.is_a?(Proc) ? base.instance_exec(object, &proc_or_method) : base.send(proc_or_method, object)
45
+ end
46
+ end
47
+
48
+ end
49
+
50
+ end
@@ -0,0 +1,44 @@
1
+ module UseCaseValidations
2
+
3
+ module Target
4
+
5
+ def self.included(base)
6
+ base.extend(ClassMethods)
7
+ end
8
+
9
+ def target_sym
10
+ self.class.target_sym
11
+ end
12
+
13
+ def parent_target_sym
14
+ self.class.options.key?(:in) ? self.class.options[:in] : nil
15
+ end
16
+
17
+ def target
18
+ send(target_sym)
19
+ end
20
+
21
+ def parent_target
22
+ parent_target_sym ? send(parent_target_sym) : nil
23
+ end
24
+
25
+ module ClassMethods
26
+
27
+ attr_reader :target_sym, :options
28
+
29
+ def target(target_sym, options = {})
30
+ @target_sym, @options = target_sym, options
31
+
32
+ if options.key?(:in)
33
+ define_method(options[:in]) { context.send(options[:in]) }
34
+ define_method(target_sym) { send(options[:in]).send(target_sym) }
35
+ else
36
+ define_method(target_sym) { context.send(target_sym) }
37
+ end
38
+ end
39
+
40
+ end
41
+
42
+ end
43
+
44
+ end