rest_my_case 1.6.0 → 1.7.0

Sign up to get free protection for your applications and to get access to all the features.
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