rest_my_case 1.6.0 → 1.7.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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: d82932cb42f7f06eaa0a2ffd09fc866ae7217047
4
- data.tar.gz: 35df440b5058dde4f650d7cc20a1f4f116c9c45f
3
+ metadata.gz: 4fbc100a76e2fe8ef3a5b546121c072662e41864
4
+ data.tar.gz: 335942040cc881c507bf65a3a437f50f1436c3b0
5
5
  SHA512:
6
- metadata.gz: 0495e5467c6f6331d5dd19878fc74171903f915ba5de464b3770cfdaa26df83df943b2773f857d23aec381995237ac5e5279ac912a6726bdc56b3b42829486bb
7
- data.tar.gz: 5a28d8b8550466280cddb7352c12135b0f3f5d6bf8d90cdc5418eedbfa1908996321cb4a13d1c2d4441c23e143fea495e979354eb807a7222b9405d6123f9514
6
+ metadata.gz: 2872b4f50c0d422921083b393fda8d3d81010a7e7268985d966bcab5961aa5c398506af5494f2fd6889bcebeb551f2a3ecac4c09e74df57a3d4012914952254c
7
+ data.tar.gz: b464f45b1152e7916cc847d0b8c34e3e26a57ee0393b6b33896c831ce950e7f3b3ea4d65abba3e1ead99ee3d5c042bc0e341b0532698c147a87eeb0da8ad87df
data/.rubocop.yml CHANGED
@@ -10,13 +10,6 @@ Style/EmptyLinesAroundModuleBody:
10
10
  Style/EmptyLinesAroundClassBody:
11
11
  Enabled: false
12
12
 
13
- Style/DotPosition:
14
- Enabled: false
15
-
16
- Style/Encoding:
17
- Enabled: true
18
- EnforcedStyle: when_needed
19
-
20
13
  AllCops:
21
14
  Exclude:
22
15
  - '**/Rakefile'
@@ -25,6 +18,8 @@ AllCops:
25
18
  - 'spec/**/*'
26
19
  - 'tmp/**/*'
27
20
  - 'pkg/**/*'
21
+ - 'lib/rest_my_case/accusation_attorneys/**/*'
22
+ - 'lib/rest_my_case/validator/errors.rb'
28
23
 
29
24
  Documentation:
30
25
  Enabled: false
data/lib/rest_my_case.rb CHANGED
@@ -1,21 +1,31 @@
1
1
  require 'ostruct'
2
2
 
3
3
  require 'rest_my_case/version'
4
+ require 'rest_my_case/helpers'
4
5
  require 'rest_my_case/errors'
5
-
6
6
  require 'rest_my_case/config/base'
7
7
  require 'rest_my_case/config/general'
8
-
9
8
  require 'rest_my_case/defense_attorney/base'
10
9
  require 'rest_my_case/context/base'
11
10
  require 'rest_my_case/judge/base'
12
-
13
- require 'rest_my_case/trial/defendant'
14
11
  require 'rest_my_case/trial/case'
15
12
  require 'rest_my_case/trial/court'
16
13
 
17
14
  require 'rest_my_case/base'
18
15
 
16
+ require 'rest_my_case/accusation_attorneys/helper_methods'
17
+ require 'rest_my_case/accusation_attorneys/base'
18
+ require 'rest_my_case/accusation_attorneys/each'
19
+ require 'rest_my_case/accusation_attorneys/custom'
20
+ # require 'rest_my_case/accusation_attorneys/format'
21
+ # require 'rest_my_case/accusation_attorneys/length'
22
+ require 'rest_my_case/accusation_attorneys/presence'
23
+ # require 'rest_my_case/accusation_attorneys/numericality'
24
+
25
+ require 'rest_my_case/validator/class_methods'
26
+ require 'rest_my_case/validator/errors'
27
+ require 'rest_my_case/validator/base'
28
+
19
29
  module RestMyCase
20
30
 
21
31
  def self.configure
@@ -0,0 +1,23 @@
1
+ module RestMyCase
2
+ module AccusationAttorneys
3
+
4
+ class Base
5
+
6
+ attr_accessor :base
7
+
8
+ attr_reader :options
9
+
10
+ def initialize(options = {})
11
+ @options = Helpers.except(options, :class).freeze
12
+ end
13
+
14
+ # Override this method in subclasses with validation logic, adding errors
15
+ # to the records +errors+ array where necessary.
16
+ def validate(record)
17
+ fail NotImplementedError, "Subclasses must implement a validate(record) method."
18
+ end
19
+
20
+ end
21
+
22
+ end
23
+ end
@@ -0,0 +1,21 @@
1
+ module RestMyCase
2
+ module AccusationAttorneys
3
+
4
+ class Custom < Base
5
+
6
+ attr_reader :methods
7
+
8
+ def initialize(args)
9
+ @methods = args
10
+
11
+ super Helpers.extract_options!(args)
12
+ end
13
+
14
+ def validate(record)
15
+ [*methods].map { |method| base.send(method, record) }.all?
16
+ end
17
+
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,34 @@
1
+ module RestMyCase
2
+ module AccusationAttorneys
3
+
4
+ class Each < Base
5
+
6
+ attr_reader :attributes
7
+
8
+ def initialize(options)
9
+ @attributes = Array(options.delete(:attributes))
10
+ fail ArgumentError, ":attributes cannot be blank" if @attributes.empty?
11
+ super
12
+ check_validity!
13
+ end
14
+
15
+ def validate(record)
16
+ attributes.each do |attribute|
17
+ value = record.respond_to?(attribute) ? record.send(attribute) : nil
18
+ next if (value.nil? && options[:allow_nil]) || (Helpers.blank?(value) && options[:allow_blank])
19
+ validate_each(record, attribute, value)
20
+ end
21
+ end
22
+
23
+ # Override this method in subclasses with the validation logic, adding
24
+ # errors to the records +errors+ array where necessary.
25
+ def validate_each(record, attribute, value)
26
+ fail NotImplementedError, "Subclasses must implement a validate_each(record, attribute, value) method"
27
+ end
28
+
29
+ def check_validity!; end
30
+
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,16 @@
1
+ module RestMyCase
2
+ module AccusationAttorneys
3
+
4
+ module HelperMethods
5
+
6
+ def _merge_attributes(attr_names)
7
+ options = Helpers.symbolyze_keys(Helpers.extract_options!(attr_names))
8
+ attr_names.flatten!
9
+ options[:attributes] = attr_names
10
+ options
11
+ end
12
+
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,17 @@
1
+ module RestMyCase
2
+ module AccusationAttorneys
3
+
4
+ class Presence < Each
5
+ def validate_each(record, attr_name, value)
6
+ record.errors.add(attr_name, :blank, options) if Helpers.blank?(value)
7
+ end
8
+ end
9
+
10
+ module HelperMethods
11
+ def validates_presence_of(*attr_names)
12
+ validates_with Presence, _merge_attributes(attr_names)
13
+ end
14
+ end
15
+
16
+ end
17
+ end
@@ -4,7 +4,10 @@ module RestMyCase
4
4
 
5
5
  extend Config::Base
6
6
 
7
- TRIAL_COURT = Trial::Court.new Judge::Base, DefenseAttorney::Base
7
+ def self.trial_court
8
+ @trial_court ||= Trial::Court.new \
9
+ Judge::Base, DefenseAttorney::Base, RestMyCase::Base
10
+ end
8
11
 
9
12
  def self.depends(*use_case_classes)
10
13
  dependencies.push(*use_case_classes)
@@ -21,7 +24,7 @@ module RestMyCase
21
24
  fail ArgumentError, 'Must respond_to method #to_hash'
22
25
  end
23
26
 
24
- TRIAL_COURT.execute([self], attributes.to_hash).context
27
+ trial_court.execute([self], attributes.to_hash).context
25
28
  end
26
29
 
27
30
  def self.context_accessor(*methods)
@@ -58,11 +61,11 @@ module RestMyCase
58
61
  def final; end
59
62
 
60
63
  def invoke(*use_case_classes)
61
- TRIAL_COURT.execute(use_case_classes, context.to_hash).context
64
+ trial_court.execute(use_case_classes, context.to_hash).context
62
65
  end
63
66
 
64
67
  def invoke!(*use_case_classes)
65
- TRIAL_COURT.execute(use_case_classes, context).tap do |trial_case|
68
+ trial_court.execute(use_case_classes, context).tap do |trial_case|
66
69
  abort if trial_case.aborted
67
70
  end.context
68
71
  end
@@ -93,10 +96,15 @@ module RestMyCase
93
96
 
94
97
  protected ######################## PROTECTED ###############################
95
98
 
99
+ def trial_court
100
+ self.class.trial_court
101
+ end
102
+
96
103
  def silent_abort?
97
104
  return false if dependent_use_case.nil?
98
105
 
99
- RestMyCase.get_config :silence_dependencies_abort, dependent_use_case.class
106
+ RestMyCase.get_config \
107
+ :silence_dependencies_abort, dependent_use_case.class
100
108
  end
101
109
 
102
110
  end
@@ -10,11 +10,25 @@ module RestMyCase
10
10
  end
11
11
 
12
12
  def get(attribute, use_case_class)
13
- custom_config = use_case_class.send(attribute)
13
+ custom_config = use_case_class_config(attribute, use_case_class)
14
14
 
15
15
  custom_config.nil? ? send(attribute) : custom_config
16
16
  end
17
17
 
18
+ protected ######################## PROTECTED #############################
19
+
20
+ def use_case_class_config(attribute, use_case_class)
21
+ return nil unless use_case_class.respond_to? attribute
22
+
23
+ custom_config = use_case_class.send(attribute)
24
+
25
+ if custom_config.nil?
26
+ use_case_class_config(attribute, use_case_class.superclass)
27
+ else
28
+ custom_config
29
+ end
30
+ end
31
+
18
32
  end
19
33
 
20
34
  end
@@ -9,7 +9,7 @@ module RestMyCase
9
9
 
10
10
  def build_case_for_the_defendant
11
11
  @trial_case.use_cases =
12
- @trial_case.defendant.use_case_classes.map do |use_case_class|
12
+ @trial_case.use_case_classes.map do |use_case_class|
13
13
  all_dependencies(use_case_class)
14
14
  end.flatten
15
15
  end
@@ -17,17 +17,15 @@ module RestMyCase
17
17
  protected ######################## PROTECTED #############################
18
18
 
19
19
  def all_dependencies(use_case_class)
20
- return [] unless use_case_class.respond_to? :dependencies
20
+ return [] if use_case_class == @trial_case.last_ancestor
21
21
 
22
22
  all_dependencies(use_case_class.superclass) |
23
- dependencies_including_itself_last(use_case_class, nil)
23
+ dependencies_including_itself(use_case_class, @trial_case.defendant)
24
24
  end
25
25
 
26
26
  private ########################### PRIVATE ##############################
27
27
 
28
- def dependencies_including_itself_last(use_case_class, dependent_use_case)
29
- return [] unless use_case_class.superclass.respond_to? :dependencies
30
-
28
+ def dependencies_including_itself(use_case_class, dependent_use_case)
31
29
  use_case = use_case_class.new(@trial_case.context, dependent_use_case)
32
30
 
33
31
  dependencies(use_case).push use_case
@@ -35,7 +33,7 @@ module RestMyCase
35
33
 
36
34
  def dependencies(use_case)
37
35
  use_case.class.dependencies.map do |dependency|
38
- dependencies_including_itself_last dependency, use_case
36
+ dependencies_including_itself dependency, use_case
39
37
  end
40
38
  end
41
39
 
@@ -0,0 +1,63 @@
1
+ module RestMyCase
2
+
3
+ module Helpers
4
+
5
+ module_function
6
+
7
+ def super_method(object, method_name, *args)
8
+ return nil unless object.superclass.respond_to? method_name
9
+
10
+ object.superclass.send method_name, *args
11
+ end
12
+
13
+ def blank?(object)
14
+ if object.is_a?(String)
15
+ object !~ /[^[:space:]]/
16
+ else
17
+ object.respond_to?(:empty?) ? object.empty? : !object
18
+ end
19
+ end
20
+
21
+ def marked_for_destruction?(object)
22
+ return false unless object.respond_to?(:marked_for_destruction?)
23
+
24
+ object.marked_for_destruction?
25
+ end
26
+
27
+ def extract_options!(array)
28
+ if array.last.is_a?(Hash) && array.last.instance_of?(Hash)
29
+ array.pop
30
+ else
31
+ {}
32
+ end
33
+ end
34
+
35
+ def symbolyze_keys(hash)
36
+ hash.keys.each_with_object({}) do |key, symbolyzed_hash|
37
+ symbolyzed_hash[key.to_sym] = hash[key]
38
+ end
39
+ end
40
+
41
+ def except(hash, *keys)
42
+ hash = hash.dup
43
+ keys.each { |key| hash.delete(key) }
44
+ hash
45
+ end
46
+
47
+ def slice(hash, *keys)
48
+ keys.each_with_object({}) do |key, sliced_hash|
49
+ sliced_hash[key] = hash[key]
50
+ end
51
+ end
52
+
53
+ def call_proc_or_method(base, proc_or_method, object = nil)
54
+ if proc_or_method.is_a?(Proc)
55
+ base.instance_exec(object, &proc_or_method)
56
+ else
57
+ base.send(proc_or_method, object)
58
+ end
59
+ end
60
+
61
+ end
62
+
63
+ end
@@ -15,7 +15,7 @@ module RestMyCase
15
15
  run_rollback_methods
16
16
  run_final_methods
17
17
 
18
- @trial_case.aborted = !@use_case_that_aborted.nil?
18
+ @trial_case.should_abort = !@use_case_that_aborted.nil?
19
19
  end
20
20
 
21
21
  protected ######################## PROTECTED #############################
@@ -3,15 +3,19 @@ module RestMyCase
3
3
 
4
4
  class Case
5
5
 
6
- attr_accessor :use_cases, :aborted
6
+ attr_accessor :use_cases, :should_abort
7
7
 
8
- attr_reader :context, :defendant
8
+ attr_reader :context, :defendant, :last_ancestor, :use_case_classes
9
9
 
10
- def initialize(use_case_classes, attributes)
11
- @aborted = false
12
- @context = build_context(attributes)
13
- @defendant = Defendant.new use_case_classes
14
- @use_cases = []
10
+ def initialize(last_ancestor, use_case_classes, attributes)
11
+ @context = build_context attributes
12
+ @defendant = build_defendant(last_ancestor, use_case_classes)
13
+ @last_ancestor = last_ancestor
14
+ @use_case_classes = use_case_classes
15
+ end
16
+
17
+ def aborted
18
+ @should_abort || defendant.options[:should_abort]
15
19
  end
16
20
 
17
21
  protected ######################## PROTECTED #############################
@@ -22,6 +26,12 @@ module RestMyCase
22
26
  Context::Base.new attributes
23
27
  end
24
28
 
29
+ def build_defendant(defendant_class, use_case_classes)
30
+ Class.new(defendant_class) do
31
+ depends(*use_case_classes)
32
+ end.new(@context)
33
+ end
34
+
25
35
  end
26
36
 
27
37
  end
@@ -1,10 +1,10 @@
1
1
  module RestMyCase
2
2
  module Trial
3
3
 
4
- Court = Struct.new(:judge_class, :defense_attorney_class) do
4
+ Court = Struct.new(:judge_class, :defense_attorney_class, :last_ancestor) do
5
5
 
6
6
  def execute(use_case_classes, attributes = {})
7
- trial_case = Case.new(use_case_classes, attributes)
7
+ trial_case = Case.new(last_ancestor, use_case_classes, attributes)
8
8
 
9
9
  defense_attorney_class.new(trial_case).build_case_for_the_defendant
10
10
 
@@ -0,0 +1,82 @@
1
+ module RestMyCase
2
+ module Validator
3
+
4
+ class Base < RestMyCase::Base
5
+
6
+ extend ClassMethods
7
+
8
+ self.silence_dependencies_abort = true
9
+
10
+ extend AccusationAttorneys::HelperMethods
11
+
12
+ def target_name
13
+ self.class.target_name
14
+ end
15
+
16
+ def target
17
+ respond_to?(target_name) ? send(target_name) : context.send(target_name)
18
+ end
19
+
20
+ def perform
21
+ targets = [*target]
22
+
23
+ skip! if targets.empty?
24
+
25
+ if target.nil?
26
+ error('no target to validate!')
27
+ else
28
+ error('unprocessable_entity') unless all_validations_green? targets
29
+ end
30
+ end
31
+
32
+ protected ######################## PROTECTED #############################
33
+
34
+ def all_validations_green?(targets)
35
+ targets.map do |object_to_validate|
36
+ if Helpers.marked_for_destruction?(object_to_validate)
37
+ true
38
+ else
39
+ extend_errors_and_run_validations(object_to_validate)
40
+
41
+ object_to_validate.errors.empty?
42
+ end
43
+ end.all?
44
+ end
45
+
46
+ private ########################### PRIVATE ##############################
47
+
48
+ def extend_errors_and_run_validations(object_to_validate)
49
+ extend_errors_if_necessary object_to_validate
50
+
51
+ object_to_validate.errors.clear if self.class.clear_errors
52
+
53
+ self.class.validators.values.flatten.uniq.each do |validator|
54
+ next if validator_condition_fails(validator, object_to_validate)
55
+
56
+ validator.base = self
57
+
58
+ validator.validate object_to_validate
59
+ end
60
+ end
61
+
62
+ def extend_errors_if_necessary(object_to_validate)
63
+ return true if object_to_validate.respond_to?(:errors)
64
+
65
+ object_to_validate.instance_eval do
66
+ def errors
67
+ @errors ||= Errors.new(self)
68
+ end
69
+ end
70
+ end
71
+
72
+ def validator_condition_fails(validator, object_to_validate)
73
+ return false unless validator.options.key?(:if)
74
+
75
+ !Helpers.call_proc_or_method \
76
+ self, validator.options[:if], object_to_validate
77
+ end
78
+
79
+ end
80
+
81
+ end
82
+ end
@@ -0,0 +1,58 @@
1
+ module RestMyCase
2
+ module Validator
3
+
4
+ module ClassMethods
5
+
6
+ attr_reader :validators, :clear_errors
7
+
8
+ def trial_court
9
+ @trial_court ||= Trial::Court.new \
10
+ Judge::Base, DefenseAttorney::Base, RestMyCase::Validator::Base
11
+ end
12
+
13
+ def target_name
14
+ @target_name || Helpers.super_method(self, :target_name)
15
+ end
16
+
17
+ def target(target_name)
18
+ @target_name = target_name
19
+ end
20
+
21
+ def clear_errors!
22
+ @clear_errors = true
23
+ end
24
+
25
+ def validators
26
+ @validators ||= Hash.new { |hash, key| hash[key] = [] }
27
+ end
28
+
29
+ def validate(*args, &block)
30
+ validators[nil] << CustomValidator.new(args, &block)
31
+ end
32
+
33
+ def validates_with(*args, &block)
34
+ options = Helpers.extract_options!(args)
35
+
36
+ options[:class] = self
37
+
38
+ args.each do |klass|
39
+ store_validators_by_attribute klass.new(options, &block)
40
+ end
41
+ end
42
+
43
+ protected ######################## PROTECTED #############################
44
+
45
+ def store_validators_by_attribute(validator)
46
+ if validator.respond_to?(:attributes) && !validator.attributes.empty?
47
+ validator.attributes.each do |attribute|
48
+ validators[attribute.to_sym] << validator
49
+ end
50
+ else
51
+ validators[nil] << validator
52
+ end
53
+ end
54
+
55
+ end
56
+
57
+ end
58
+ end
@@ -0,0 +1,366 @@
1
+ module RestMyCase
2
+ module Validator
3
+
4
+ class Errors
5
+ include Enumerable
6
+
7
+ CALLBACKS_OPTIONS = [:if, :unless, :on, :allow_nil, :allow_blank, :strict]
8
+
9
+ attr_reader :messages
10
+
11
+ # Pass in the instance of the object that is using the errors object.
12
+ #
13
+ # class Person
14
+ # def initialize
15
+ # @errors = ActiveModel::Errors.new(self)
16
+ # end
17
+ # end
18
+ def initialize(base)
19
+ @base = base
20
+ @messages = {}
21
+ end
22
+
23
+ def initialize_dup(other) # :nodoc:
24
+ @messages = other.messages.dup
25
+ super
26
+ end
27
+
28
+ # Clear the error messages.
29
+ #
30
+ # person.errors.full_messages # => ["name can not be nil"]
31
+ # person.errors.clear
32
+ # person.errors.full_messages # => []
33
+ def clear
34
+ messages.clear
35
+ end
36
+
37
+ # Returns +true+ if the error messages include an error for the given key
38
+ # +attribute+, +false+ otherwise.
39
+ #
40
+ # person.errors.messages # => {:name=>["can not be nil"]}
41
+ # person.errors.include?(:name) # => true
42
+ # person.errors.include?(:age) # => false
43
+ def include?(attribute)
44
+ (v = messages[attribute]) && v.any?
45
+ end
46
+ # aliases include?
47
+ alias :has_key? :include?
48
+
49
+ # Get messages for +key+.
50
+ #
51
+ # person.errors.messages # => {:name=>["can not be nil"]}
52
+ # person.errors.get(:name) # => ["can not be nil"]
53
+ # person.errors.get(:age) # => nil
54
+ def get(key)
55
+ messages[key]
56
+ end
57
+
58
+ # Set messages for +key+ to +value+.
59
+ #
60
+ # person.errors.get(:name) # => ["can not be nil"]
61
+ # person.errors.set(:name, ["can't be nil"])
62
+ # person.errors.get(:name) # => ["can't be nil"]
63
+ def set(key, value)
64
+ messages[key] = value
65
+ end
66
+
67
+ # Delete messages for +key+. Returns the deleted messages.
68
+ #
69
+ # person.errors.get(:name) # => ["can not be nil"]
70
+ # person.errors.delete(:name) # => ["can not be nil"]
71
+ # person.errors.get(:name) # => nil
72
+ def delete(key)
73
+ messages.delete(key)
74
+ end
75
+
76
+ # When passed a symbol or a name of a method, returns an array of errors
77
+ # for the method.
78
+ #
79
+ # person.errors[:name] # => ["can not be nil"]
80
+ # person.errors['name'] # => ["can not be nil"]
81
+ def [](attribute)
82
+ get(attribute.to_sym) || set(attribute.to_sym, [])
83
+ end
84
+
85
+ # Adds to the supplied attribute the supplied error message.
86
+ #
87
+ # person.errors[:name] = "must be set"
88
+ # person.errors[:name] # => ['must be set']
89
+ def []=(attribute, error)
90
+ self[attribute] << error
91
+ end
92
+
93
+ # Iterates through each error key, value pair in the error messages hash.
94
+ # Yields the attribute and the error for that attribute. If the attribute
95
+ # has more than one error message, yields once for each error message.
96
+ #
97
+ # person.errors.add(:name, "can't be blank")
98
+ # person.errors.each do |attribute, error|
99
+ # # Will yield :name and "can't be blank"
100
+ # end
101
+ #
102
+ # person.errors.add(:name, "must be specified")
103
+ # person.errors.each do |attribute, error|
104
+ # # Will yield :name and "can't be blank"
105
+ # # then yield :name and "must be specified"
106
+ # end
107
+ def each
108
+ messages.each_key do |attribute|
109
+ self[attribute].each { |error| yield attribute, error }
110
+ end
111
+ end
112
+
113
+ # Returns the number of error messages.
114
+ #
115
+ # person.errors.add(:name, "can't be blank")
116
+ # person.errors.size # => 1
117
+ # person.errors.add(:name, "must be specified")
118
+ # person.errors.size # => 2
119
+ def size
120
+ values.flatten.size
121
+ end
122
+
123
+ # Returns all message values.
124
+ #
125
+ # person.errors.messages # => {:name=>["can not be nil", "must be specified"]}
126
+ # person.errors.values # => [["can not be nil", "must be specified"]]
127
+ def values
128
+ messages.values
129
+ end
130
+
131
+ # Returns all message keys.
132
+ #
133
+ # person.errors.messages # => {:name=>["can not be nil", "must be specified"]}
134
+ # person.errors.keys # => [:name]
135
+ def keys
136
+ messages.keys
137
+ end
138
+
139
+ # Returns an array of error messages, with the attribute name included.
140
+ #
141
+ # person.errors.add(:name, "can't be blank")
142
+ # person.errors.add(:name, "must be specified")
143
+ # person.errors.to_a # => ["name can't be blank", "name must be specified"]
144
+ def to_a
145
+ full_messages
146
+ end
147
+
148
+ # Returns the number of error messages.
149
+ #
150
+ # person.errors.add(:name, "can't be blank")
151
+ # person.errors.count # => 1
152
+ # person.errors.add(:name, "must be specified")
153
+ # person.errors.count # => 2
154
+ def count
155
+ to_a.size
156
+ end
157
+
158
+ # Returns +true+ if no errors are found, +false+ otherwise.
159
+ # If the error message is a string it can be empty.
160
+ #
161
+ # person.errors.full_messages # => ["name can not be nil"]
162
+ # person.errors.empty? # => false
163
+ def empty?
164
+ all? { |k, v| v && v.empty? && !v.is_a?(String) }
165
+ end
166
+ # aliases empty?
167
+ alias_method :blank?, :empty?
168
+
169
+ # Returns an xml formatted representation of the Errors hash.
170
+ #
171
+ # person.errors.add(:name, "can't be blank")
172
+ # person.errors.add(:name, "must be specified")
173
+ # person.errors.to_xml
174
+ # # =>
175
+ # # <?xml version=\"1.0\" encoding=\"UTF-8\"?>
176
+ # # <errors>
177
+ # # <error>name can't be blank</error>
178
+ # # <error>name must be specified</error>
179
+ # # </errors>
180
+ def to_xml(options={})
181
+ to_a.to_xml({ root: "errors", skip_types: true }.merge!(options))
182
+ end
183
+
184
+ # Returns a Hash that can be used as the JSON representation for this
185
+ # object. You can pass the <tt>:full_messages</tt> option. This determines
186
+ # if the json object should contain full messages or not (false by default).
187
+ #
188
+ # person.as_json # => {:name=>["can not be nil"]}
189
+ # person.as_json(full_messages: true) # => {:name=>["name can not be nil"]}
190
+ def as_json(options=nil)
191
+ to_hash(options && options[:full_messages])
192
+ end
193
+
194
+ # Returns a Hash of attributes with their error messages. If +full_messages+
195
+ # is +true+, it will contain full messages (see +full_message+).
196
+ #
197
+ # person.to_hash # => {:name=>["can not be nil"]}
198
+ # person.to_hash(true) # => {:name=>["name can not be nil"]}
199
+ def to_hash(full_messages = false)
200
+ if full_messages
201
+ messages = {}
202
+ self.messages.each do |attribute, array|
203
+ messages[attribute] = array.map { |message| full_message(attribute, message) }
204
+ end
205
+ messages
206
+ else
207
+ self.messages.dup
208
+ end
209
+ end
210
+
211
+ # Adds +message+ to the error messages on +attribute+. More than one error
212
+ # can be added to the same +attribute+. If no +message+ is supplied,
213
+ # <tt>:invalid</tt> is assumed.
214
+ #
215
+ # person.errors.add(:name)
216
+ # # => ["is invalid"]
217
+ # person.errors.add(:name, 'must be implemented')
218
+ # # => ["is invalid", "must be implemented"]
219
+ #
220
+ # person.errors.messages
221
+ # # => {:name=>["must be implemented", "is invalid"]}
222
+ #
223
+ # If +message+ is a symbol, it will be translated using the appropriate
224
+ # scope (see +generate_message+).
225
+ #
226
+ # If +message+ is a proc, it will be called, allowing for things like
227
+ # <tt>Time.now</tt> to be used within an error.
228
+ #
229
+ # person.errors.messages # => {}
230
+ def add(attribute, message = nil, options = {})
231
+ message = normalize_message(attribute, message, options)
232
+ self[attribute] << message
233
+ end
234
+
235
+ # Returns +true+ if an error on the attribute with the given message is
236
+ # present, +false+ otherwise. +message+ is treated the same as for +add+.
237
+ #
238
+ # person.errors.add :name, :blank
239
+ # person.errors.added? :name, :blank # => true
240
+ def added?(attribute, message = nil, options = {})
241
+ message = normalize_message(attribute, message, options)
242
+ self[attribute].include? message
243
+ end
244
+
245
+ # Returns all the full error messages in an array.
246
+ #
247
+ # class Person
248
+ # validates_presence_of :name, :address, :email
249
+ # validates_length_of :name, in: 5..30
250
+ # end
251
+ #
252
+ # person = Person.create(address: '123 First St.')
253
+ # person.errors.full_messages
254
+ # # => ["Name is too short (minimum is 5 characters)", "Name can't be blank", "Email can't be blank"]
255
+ def full_messages
256
+ map { |attribute, message| full_message(attribute, message) }
257
+ end
258
+
259
+ # Returns all the full error messages for a given attribute in an array.
260
+ #
261
+ # class Person
262
+ # validates_presence_of :name, :email
263
+ # validates_length_of :name, in: 5..30
264
+ # end
265
+ #
266
+ # person = Person.create()
267
+ # person.errors.full_messages_for(:name)
268
+ # # => ["Name is too short (minimum is 5 characters)", "Name can't be blank"]
269
+ def full_messages_for(attribute)
270
+ (get(attribute) || []).map { |message| full_message(attribute, message) }
271
+ end
272
+
273
+ # Returns a full message for a given attribute.
274
+ #
275
+ # person.errors.full_message(:name, 'is invalid') # => "Name is invalid"
276
+ def full_message(attribute, message)
277
+ return message if attribute == :base || !@base.class.respond_to?(:model_name)
278
+
279
+ attr_name = attribute.to_s.tr('.', '_').humanize
280
+ attr_name = @base.class.human_attribute_name(attribute, default: attr_name)
281
+ I18n.t(:"errors.format", {
282
+ default: "%{attribute} %{message}",
283
+ attribute: attr_name,
284
+ message: message
285
+ })
286
+ end
287
+
288
+ # Translates an error message in its default scope
289
+ # (<tt>activemodel.errors.messages</tt>).
290
+ #
291
+ # Error messages are first looked up in <tt>models.MODEL.attributes.ATTRIBUTE.MESSAGE</tt>,
292
+ # if it's not there, it's looked up in <tt>models.MODEL.MESSAGE</tt> and if
293
+ # that is not there also, it returns the translation of the default message
294
+ # (e.g. <tt>activemodel.errors.messages.MESSAGE</tt>). The translated model
295
+ # name, translated attribute name and the value are available for
296
+ # interpolation.
297
+ #
298
+ # When using inheritance in your models, it will check all the inherited
299
+ # models too, but only if the model itself hasn't been found. Say you have
300
+ # <tt>class Admin < User; end</tt> and you wanted the translation for
301
+ # the <tt>:blank</tt> error message for the <tt>title</tt> attribute,
302
+ # it looks for these translations:
303
+ #
304
+ # * <tt>activemodel.errors.models.admin.attributes.title.blank</tt>
305
+ # * <tt>activemodel.errors.models.admin.blank</tt>
306
+ # * <tt>activemodel.errors.models.user.attributes.title.blank</tt>
307
+ # * <tt>activemodel.errors.models.user.blank</tt>
308
+ # * any default you provided through the +options+ hash (in the <tt>activemodel.errors</tt> scope)
309
+ # * <tt>activemodel.errors.messages.blank</tt>
310
+ # * <tt>errors.attributes.title.blank</tt>
311
+ # * <tt>errors.messages.blank</tt>
312
+ def generate_message(attribute, type = :invalid, options = {})
313
+ type = options.delete(:message) if options[:message].is_a?(Symbol)
314
+
315
+ if !@base.class.respond_to?(:model_name)
316
+ return options.key?(:message) ? options[:message] : type
317
+ end
318
+
319
+ if @base.class.respond_to?(:i18n_scope)
320
+ defaults = @base.class.lookup_ancestors.map do |klass|
321
+ [ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.attributes.#{attribute}.#{type}",
322
+ :"#{@base.class.i18n_scope}.errors.models.#{klass.model_name.i18n_key}.#{type}" ]
323
+ end
324
+ else
325
+ defaults = []
326
+ end
327
+
328
+ defaults << options.delete(:message)
329
+ defaults << :"#{@base.class.i18n_scope}.errors.messages.#{type}" if @base.class.respond_to?(:i18n_scope)
330
+ defaults << :"errors.attributes.#{attribute}.#{type}"
331
+ defaults << :"errors.messages.#{type}"
332
+
333
+ defaults.compact!
334
+ defaults.flatten!
335
+
336
+ key = defaults.shift
337
+ value = (attribute != :base ? @base.send(attribute) : nil)
338
+
339
+ options = {
340
+ default: defaults,
341
+ model: @base.class.model_name.human,
342
+ attribute: @base.class.human_attribute_name(attribute),
343
+ value: value
344
+ }.merge!(options)
345
+
346
+ I18n.translate(key, options)
347
+ end
348
+
349
+ private
350
+ def normalize_message(attribute, message, options)
351
+ message ||= :invalid
352
+
353
+ case message
354
+ when Symbol
355
+ generate_message(attribute, message, Helpers.except(options, *CALLBACKS_OPTIONS))
356
+ when Proc
357
+ message.call
358
+ else
359
+ message
360
+ end
361
+ end
362
+
363
+ end
364
+
365
+ end
366
+ end
@@ -1,5 +1,5 @@
1
1
  module RestMyCase
2
2
 
3
- VERSION = '1.6.0'
3
+ VERSION = '1.7.0'
4
4
 
5
5
  end
@@ -295,7 +295,7 @@ describe RestMyCase::Base do
295
295
  it "context prove that only the correct method have ran" do
296
296
  expect(@context.setup.length).to be 7
297
297
  expect(@context.perform.length).to be 7
298
- expect(@context.rollback.length).to be 7
298
+ expect(@context.rollback.length).to be 0
299
299
  expect(@context.final.length).to be 7
300
300
  end
301
301
 
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  describe RestMyCase::DefenseAttorney::Base do
4
4
 
5
5
  let(:use_cases) do
6
- trial_case = RestMyCase::Trial::Case.new(use_case_classes, id: 1)
6
+ trial_case = RestMyCase::Trial::Case.new(RestMyCase::Base, use_case_classes, id: 1)
7
7
 
8
8
  described_class.new(trial_case).build_case_for_the_defendant
9
9
 
@@ -4,12 +4,14 @@ describe RestMyCase::Trial::Court do
4
4
 
5
5
  context "When using Judge::Base and DefenseAttorney::Base" do
6
6
  before do
7
- @trial_court = described_class.new RestMyCase::Judge::Base, RestMyCase::DefenseAttorney::Base
7
+ @use_case_class = Class.new(RestMyCase::Base)
8
+ @trial_court = described_class.new \
9
+ RestMyCase::Judge::Base, RestMyCase::DefenseAttorney::Base, RestMyCase::Base
8
10
  end
9
11
 
10
12
  context "When something that responds to #to_hash is passed down" do
11
13
 
12
- let(:context) { @trial_court.execute([Object], id: 1, name: '2').context }
14
+ let(:context) { @trial_court.execute([@use_case_class], id: 1, name: '2').context }
13
15
 
14
16
  it "should create a context with it" do
15
17
  expect(context).to be_a RestMyCase::Context::Base
@@ -0,0 +1,23 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Testing hierarchy capabilities' do
4
+
5
+ it "HierarchyValidation::Son should be able to inherit Father's validations and alter them" do
6
+ # post_for_father = RubyPost.new
7
+ # father_context = HierarchyValidation::Father.perform(post: post_for_father)
8
+
9
+ # expect(father_context.ok?).to be false
10
+ # expect(post_for_father.errors.added?(:title, :blank)).to be true
11
+ # expect(post_for_father.errors.size).to be 1
12
+
13
+ post_for_son = RubyPost.new
14
+ son_context = HierarchyValidation::Son.perform(post: post_for_son)
15
+
16
+ expect(son_context.ok?).to be false
17
+ expect(post_for_son.errors.added?(:title, :blank)).to be true
18
+ expect(post_for_son.errors.added?(:email, :blank)).to be true
19
+ expect(post_for_son.errors.size).to be 2
20
+ end
21
+
22
+ end
23
+
@@ -0,0 +1,9 @@
1
+ class RubyPost
2
+
3
+ attr_accessor :title, :body, :phone_number
4
+
5
+ def initialize(attributes = {})
6
+ (attributes || {}).each { |name, value| send("#{name}=", value) }
7
+ end
8
+
9
+ end
@@ -0,0 +1,28 @@
1
+ class RubyPostWithComments
2
+
3
+ class RubyComment
4
+
5
+ attr_accessor :title, :email, :post_id
6
+
7
+ def initialize(attributes = {})
8
+ (attributes || {}).each { |name, value| send("#{name}=", value) }
9
+ end
10
+
11
+ end
12
+
13
+
14
+ attr_reader :comments
15
+
16
+ def initialize(comments = {})
17
+ @comments = comments.map { |comment| RubyComment.new(comment) }
18
+ @comments = [] if @comments.nil?
19
+ end
20
+
21
+ def first_two_comments
22
+ [
23
+ comments[0],
24
+ comments[1]
25
+ ]
26
+ end
27
+
28
+ end
@@ -0,0 +1,17 @@
1
+ module HierarchyValidation
2
+
3
+ class Father < RestMyCase::Validator::Base
4
+
5
+ target :post
6
+
7
+ validates_presence_of :title
8
+
9
+ end
10
+
11
+ class Son < Father
12
+
13
+ validates_presence_of :email
14
+
15
+ end
16
+
17
+ end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rest_my_case
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.6.0
4
+ version: 1.7.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - goncalvesjoao
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-04-16 00:00:00.000000000 Z
11
+ date: 2015-04-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: pry
@@ -112,25 +112,37 @@ files:
112
112
  - Rakefile
113
113
  - console
114
114
  - lib/rest_my_case.rb
115
+ - lib/rest_my_case/accusation_attorneys/base.rb
116
+ - lib/rest_my_case/accusation_attorneys/custom.rb
117
+ - lib/rest_my_case/accusation_attorneys/each.rb
118
+ - lib/rest_my_case/accusation_attorneys/helper_methods.rb
119
+ - lib/rest_my_case/accusation_attorneys/presence.rb
115
120
  - lib/rest_my_case/base.rb
116
121
  - lib/rest_my_case/config/base.rb
117
122
  - lib/rest_my_case/config/general.rb
118
123
  - lib/rest_my_case/context/base.rb
119
124
  - lib/rest_my_case/defense_attorney/base.rb
120
125
  - lib/rest_my_case/errors.rb
126
+ - lib/rest_my_case/helpers.rb
121
127
  - lib/rest_my_case/judge/base.rb
122
128
  - lib/rest_my_case/trial/case.rb
123
129
  - lib/rest_my_case/trial/court.rb
124
- - lib/rest_my_case/trial/defendant.rb
130
+ - lib/rest_my_case/validator/base.rb
131
+ - lib/rest_my_case/validator/class_methods.rb
132
+ - lib/rest_my_case/validator/errors.rb
125
133
  - lib/rest_my_case/version.rb
126
134
  - rest_my_case.gemspec
127
135
  - spec/rest_my_case/base_spec.rb
128
136
  - spec/rest_my_case/defense_attorney/base_spec.rb
129
137
  - spec/rest_my_case/trial_case/court_spec.rb
138
+ - spec/rest_my_case/validator/hierarchy_spec.rb
130
139
  - spec/spec_helper.rb
131
140
  - spec/support/defense_attorney.rb
132
141
  - spec/support/perform.rb
133
142
  - spec/support/rest_my_case_base.rb
143
+ - spec/support/validator/models/ruby_post.rb
144
+ - spec/support/validator/models/ruby_post_with_comments.rb
145
+ - spec/support/validator/usecases/hierarchy_validation.rb
134
146
  homepage: https://github.com/goncalvesjoao/rest_my_case
135
147
  licenses:
136
148
  - MIT
@@ -159,7 +171,11 @@ test_files:
159
171
  - spec/rest_my_case/base_spec.rb
160
172
  - spec/rest_my_case/defense_attorney/base_spec.rb
161
173
  - spec/rest_my_case/trial_case/court_spec.rb
174
+ - spec/rest_my_case/validator/hierarchy_spec.rb
162
175
  - spec/spec_helper.rb
163
176
  - spec/support/defense_attorney.rb
164
177
  - spec/support/perform.rb
165
178
  - spec/support/rest_my_case_base.rb
179
+ - spec/support/validator/models/ruby_post.rb
180
+ - spec/support/validator/models/ruby_post_with_comments.rb
181
+ - spec/support/validator/usecases/hierarchy_validation.rb
@@ -1,7 +0,0 @@
1
- module RestMyCase
2
- module Trial
3
-
4
- Defendant = Struct.new(:use_case_classes)
5
-
6
- end
7
- end