mutations 0.5.10 → 0.5.11

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.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- mutations (0.5.9)
4
+ mutations (0.5.11)
5
5
  activesupport
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -114,7 +114,7 @@ That being said, you can pass multiple hashes to run, and they are merged togeth
114
114
  ```ruby
115
115
  # A user comments on an article
116
116
  class CreateComment < Mutations::Command
117
- requried do
117
+ required do
118
118
  model :user
119
119
  model :article
120
120
  string :comment, max_length: 500
@@ -138,7 +138,7 @@ Here, we pass two hashes to CreateComment. Even if the params[:comment] hash has
138
138
  1. Subclass Mutations::Command
139
139
 
140
140
  ```ruby
141
- class YourMutation < Mutatons::Command
141
+ class YourMutation < Mutations::Command
142
142
  # ...
143
143
  end
144
144
  ```
data/TODO CHANGED
@@ -1,2 +1,14 @@
1
1
  - Document default behavior discarding of nils for optional params
2
2
  - document string.discard_empty
3
+
4
+ - protected parameters:
5
+ optional do
6
+ boolean :skip_confirmation, protected: true
7
+ end
8
+ Given the above, skip_confirmation is only accepted as a parameter if it's passed in a later hash, eg this would make it take:
9
+ User::ChangeEmail.run!(params, user: current_user, skip_confirmation: true)
10
+ But this would not:
11
+ params = {user: current_user, skip_confirmation: true}
12
+ User::ChangeEmail.run!(params)
13
+
14
+
@@ -1,25 +1,10 @@
1
- # IDEA i just had (protected parameters):
2
- # optional do
3
- # boolean :skip_confirmation, protected: true
4
- # end
5
- # Given the above, skip_confirmation is only accepted as a parameter if it's passed in a later hash, eg this would make it take:
6
- # User::ChangeEmail.run!(params, user: current_user, skip_confirmation: true)
7
- # But this would not:
8
- # params = {user: current_user, skip_confirmation: true}
9
- # User::ChangeEmail.run!(params)
10
-
11
-
12
1
  module Mutations
13
2
  class Command
14
-
15
- ##
16
- ##
17
- ##
18
3
  class << self
19
- def required(&block)
20
- self.input_filters.required(&block)
21
-
22
- self.input_filters.required_keys.each do |key|
4
+ def create_attr_methods(meth, &block)
5
+ self.input_filters.send(meth, &block)
6
+ keys = self.input_filters.send("#{meth}_keys")
7
+ keys.each do |key|
23
8
  define_method(key) do
24
9
  @filtered_input[key]
25
10
  end
@@ -33,41 +18,27 @@ module Mutations
33
18
  end
34
19
  end
35
20
  end
21
+ private :create_attr_methods
36
22
 
37
- def optional(&block)
38
- self.input_filters.optional(&block)
39
-
40
- self.input_filters.optional_keys.each do |key|
41
- define_method(key) do
42
- @filtered_input[key]
43
- end
44
-
45
- define_method("#{key}_present?") do
46
- @filtered_input.has_key?(key)
47
- end
23
+ def required(&block)
24
+ create_attr_methods(:required, &block)
25
+ end
48
26
 
49
- define_method("#{key}=") do |v|
50
- @filtered_input[key] = v
51
- end
52
- end
27
+ def optional(&block)
28
+ create_attr_methods(:optional, &block)
53
29
  end
54
30
 
55
31
  def run(*args)
56
- new(*args).execute!
32
+ new(*args).run
57
33
  end
58
34
 
59
35
  def run!(*args)
60
- m = run(*args)
61
- if m.success?
62
- m.result
63
- else
64
- raise ValidationException.new(m.errors)
65
- end
36
+ new(*args).run!
66
37
  end
67
38
 
68
39
  # Validates input, but doesn't call execute. Returns an Outcome with errors anyway.
69
40
  def validate(*args)
70
- new(*args).validation_outcome
41
+ new(*args).validate
71
42
  end
72
43
 
73
44
  def input_filters
@@ -84,19 +55,10 @@ module Mutations
84
55
 
85
56
  # Instance methods
86
57
  def initialize(*args)
87
- if args.length == 0
88
- @original_hash = {}
89
- else
90
- @original_hash = args.shift
91
- raise ArgumentError.new("All arguments must be hashes") unless @original_hash.is_a?(Hash)
92
- @original_hash = @original_hash.with_indifferent_access
58
+ @original_hash = args.each_with_object({}.with_indifferent_access) do |arg, h|
59
+ raise ArgumentError.new("All arguments must be hashes") unless arg.is_a?(Hash)
60
+ h.merge!(arg)
93
61
  end
94
-
95
- args.each do |a|
96
- raise ArgumentError.new("All arguments must be hashes") unless a.is_a?(Hash)
97
- @original_hash.merge!(a)
98
- end
99
-
100
62
  @filtered_input, @errors = self.input_filters.filter(@original_hash)
101
63
  end
102
64
 
@@ -104,26 +66,37 @@ module Mutations
104
66
  self.class.input_filters
105
67
  end
106
68
 
107
- def execute!
108
- return Outcome.new(false, nil, @errors) if @errors
69
+ def has_errors?
70
+ !@errors.nil?
71
+ end
109
72
 
110
- # IDEA/TODO: run validate block
73
+ def run
74
+ return validation_outcome if has_errors?
75
+ validation_outcome(execute)
76
+ end
111
77
 
112
- r = execute
113
- if @errors # Execute can add errors
114
- return Outcome.new(false, nil, @errors)
78
+ def run!
79
+ outcome = run
80
+ if outcome.success?
81
+ outcome.result
115
82
  else
116
- return Outcome.new(true, r, nil)
83
+ raise ValidationException.new(outcome.errors)
117
84
  end
118
85
  end
119
86
 
87
+ def validate
88
+ validation_outcome
89
+ end
90
+
120
91
  # Runs input thru the filter and sets @filtered_input and @errors
121
- def validation_outcome
122
- if @errors
123
- Outcome.new(false, nil, @errors)
124
- else
125
- Outcome.new(true, nil, nil)
126
- end
92
+ def validation_outcome(result = nil)
93
+ Outcome.new(!has_errors?, has_errors? ? nil : result, @errors, @filtered_input)
94
+ end
95
+
96
+ protected
97
+
98
+ def execute
99
+ # Meant to be overridden
127
100
  end
128
101
 
129
102
  # add_error("name", :too_short)
@@ -134,18 +107,13 @@ module Mutations
134
107
  raise ArgumentError.new("Invalid kind") unless kind.is_a?(Symbol)
135
108
 
136
109
  @errors ||= ErrorHash.new
137
- cur_errors = @errors
138
- parts = key.to_s.split(".")
139
- while part = parts.shift
140
- part = part.to_sym
141
- if parts.length > 0
142
- cur_errors[part] = ErrorHash.new unless cur_errors[part].is_a?(ErrorHash)
143
- cur_errors = cur_errors[part]
144
- else
145
- cur_errors[part] = ErrorAtom.new(key, kind, message: message)
110
+ @errors.tap do |errs|
111
+ *path, last = key.to_s.split(".")
112
+ inner = path.inject(errs) do |cut_errors,part|
113
+ cur_errors[part.to_sym] ||= ErrorHash.new
146
114
  end
115
+ inner[last] = ErrorAtom.new(key, kind, message: message)
147
116
  end
148
- @errors
149
117
  end
150
118
 
151
119
  def merge_errors(hash)
@@ -156,9 +124,5 @@ module Mutations
156
124
  def inputs
157
125
  @filtered_input
158
126
  end
159
-
160
- def execute
161
- # Meant to be overridden
162
- end
163
127
  end
164
128
  end
@@ -2,7 +2,6 @@ module Mutations
2
2
  class FloatFilter < InputFilter
3
3
  @default_options = {
4
4
  nils: false, # true allows an explicit nil to be valid. Overrides any other options
5
- # TODO: add strict
6
5
  min: nil, # lowest value, inclusive
7
6
  max: nil # highest value, inclusive
8
7
  }
@@ -31,13 +31,11 @@ module Mutations
31
31
  end
32
32
 
33
33
  def required(&block)
34
- # TODO: raise if nesting is wrong
35
34
  @current_inputs = @required_inputs
36
35
  instance_eval &block
37
36
  end
38
37
 
39
38
  def optional(&block)
40
- # TODO: raise if nesting is wrong
41
39
  @current_inputs = @optional_inputs
42
40
  instance_eval &block
43
41
  end
@@ -2,7 +2,6 @@ module Mutations
2
2
  class IntegerFilter < InputFilter
3
3
  @default_options = {
4
4
  nils: false, # true allows an explicit nil to be valid. Overrides any other options
5
- # TODO: add strict
6
5
  min: nil, # lowest value, inclusive
7
6
  max: nil # highest value, inclusive
8
7
  }
@@ -10,18 +10,26 @@ module Mutations
10
10
  def initialize(name, opts = {})
11
11
  super(opts)
12
12
  @name = name
13
-
14
- class_const = options[:class] || @name.to_s.camelize
15
- class_const = class_const.constantize if class_const.is_a?(String)
16
- self.options[:class] = class_const
17
-
18
- if options[:builder]
19
- options[:builder] = options[:builder].constantize if options[:builder].is_a?(String)
13
+ end
14
+
15
+ # Initialize the model class and builder
16
+ def initialize_constants!
17
+ @initialize_constants ||= begin
18
+ class_const = options[:class] || @name.to_s.camelize
19
+ class_const = class_const.constantize if class_const.is_a?(String)
20
+ self.options[:class] = class_const
21
+
22
+ if options[:builder]
23
+ options[:builder] = options[:builder].constantize if options[:builder].is_a?(String)
24
+ end
25
+
26
+ true
20
27
  end
21
28
  end
22
29
 
23
30
  def filter(data)
24
-
31
+ initialize_constants!
32
+
25
33
  # Handle nil case
26
34
  if data.nil?
27
35
  return [nil, nil] if options[:nils]
@@ -49,4 +57,4 @@ module Mutations
49
57
  end
50
58
 
51
59
  end
52
- end
60
+ end
@@ -1,19 +1,13 @@
1
1
  module Mutations
2
2
  class Outcome
3
- def initialize(is_success, result, errors)
4
- @success, @result, @errors = is_success, result, errors
3
+ attr_reader :result, :errors, :inputs
4
+
5
+ def initialize(is_success, result, errors, inputs)
6
+ @success, @result, @errors, @inputs = is_success, result, errors, inputs
5
7
  end
6
8
 
7
9
  def success?
8
10
  @success
9
11
  end
10
-
11
- def result
12
- @result
13
- end
14
-
15
- def errors
16
- @errors
17
- end
18
12
  end
19
13
  end
@@ -1,3 +1,3 @@
1
1
  module Mutations
2
- VERSION = "0.5.10"
2
+ VERSION = "0.5.11"
3
3
  end
data/spec/command_spec.rb CHANGED
@@ -72,11 +72,20 @@ describe "Command" do
72
72
  assert_raises ArgumentError do
73
73
  outcome = SimpleCommand.run(1)
74
74
  end
75
+
76
+ assert_raises ArgumentError do
77
+ outcome = SimpleCommand.run({name: "John"}, 1)
78
+ end
75
79
  end
76
80
 
77
81
  it "should accept nothing at all" do
78
82
  SimpleCommand.run # make sure nothing is raised
79
83
  end
84
+
85
+ it "should return the filtered inputs in the outcome" do
86
+ outcome = SimpleCommand.run(name: " John ", email: "john@gmail.com", amount: "5")
87
+ assert_equal ({name: "John", email: "john@gmail.com", amount: 5}).stringify_keys, outcome.inputs
88
+ end
80
89
  end
81
90
 
82
91
  describe "EigenCommand" do
data/spec/default_spec.rb CHANGED
@@ -0,0 +1,33 @@
1
+ require_relative 'spec_helper'
2
+ require 'simple_command'
3
+
4
+ describe 'Mutations - defaults' do
5
+
6
+ class DefaultCommand < Mutations::Command
7
+ required do
8
+ string :name, default: "Bob Jones"
9
+ end
10
+
11
+ def execute
12
+ inputs
13
+ end
14
+ end
15
+
16
+ it "should have a default if no value is passed" do
17
+ outcome = DefaultCommand.run
18
+ assert_equal true, outcome.success?
19
+ assert_equal ({"name" => "Bob Jones"}), outcome.result
20
+ end
21
+
22
+ it "should have the passed value if a value is passed" do
23
+ outcome = DefaultCommand.run(name: "Fred")
24
+ assert_equal true, outcome.success?
25
+ assert_equal ({"name" => "Fred"}), outcome.result
26
+ end
27
+
28
+ it "should be an error if nil is passed on a required field with a default" do
29
+ outcome = DefaultCommand.run(name: nil)
30
+ assert_equal false, outcome.success?
31
+ end
32
+
33
+ end
@@ -27,21 +27,24 @@ describe "Mutations::ModelFilter" do
27
27
  # it "disallows different types of models" do
28
28
  # end
29
29
 
30
- it "raises an exception during initialization if constantization fails" do
30
+ it "raises an exception during filtering if constantization fails" do
31
+ f = Mutations::ModelFilter.new(:complex_model)
31
32
  assert_raises NameError do
32
- Mutations::ModelFilter.new(:complex_model)
33
+ f.filter(nil)
33
34
  end
34
35
  end
35
36
 
36
- it "raises an exception during initialization if constantization of class fails" do
37
+ it "raises an exception during filtering if constantization of class fails" do
38
+ f = Mutations::ModelFilter.new(:simple_model, class: "ComplexModel")
37
39
  assert_raises NameError do
38
- Mutations::ModelFilter.new(:simple_model, class: "ComplexModel")
40
+ f.filter(nil)
39
41
  end
40
42
  end
41
43
 
42
- it "raises an exception during initialization if constantization of builder fails" do
44
+ it "raises an exception during filtering if constantization of builder fails" do
45
+ f = Mutations::ModelFilter.new(:simple_model, builder: "ComplexModel")
43
46
  assert_raises NameError do
44
- Mutations::ModelFilter.new(:simple_model, builder: "ComplexModel")
47
+ f.filter(nil)
45
48
  end
46
49
  end
47
50
 
@@ -89,4 +92,4 @@ describe "Mutations::ModelFilter" do
89
92
  # it "makes sure that if you build a record from a hash, it still has to be of the right class" do
90
93
  # end
91
94
 
92
- end
95
+ end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: mutations
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.10
4
+ version: 0.5.11
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-02-28 00:00:00.000000000 Z
12
+ date: 2013-03-02 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activesupport
@@ -123,7 +123,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
123
123
  version: '0'
124
124
  requirements: []
125
125
  rubyforge_project:
126
- rubygems_version: 1.8.24
126
+ rubygems_version: 1.8.23
127
127
  signing_key:
128
128
  specification_version: 3
129
129
  summary: Compose your business logic into commands that sanitize and validate input.