trailblazer 1.1.2 → 2.0.0.beta1
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 +4 -4
- data/.travis.yml +10 -7
- data/CHANGES.md +108 -0
- data/COMM-LICENSE +91 -0
- data/Gemfile +18 -4
- data/LICENSE.txt +7 -20
- data/README.md +55 -15
- data/Rakefile +21 -2
- data/draft-1.2.rb +7 -0
- data/lib/trailblazer.rb +17 -4
- data/lib/trailblazer/dsl.rb +47 -0
- data/lib/trailblazer/operation/auto_inject.rb +47 -0
- data/lib/trailblazer/operation/builder.rb +18 -18
- data/lib/trailblazer/operation/callback.rb +31 -38
- data/lib/trailblazer/operation/contract.rb +46 -0
- data/lib/trailblazer/operation/controller.rb +45 -27
- data/lib/trailblazer/operation/guard.rb +24 -0
- data/lib/trailblazer/operation/model.rb +41 -33
- data/lib/trailblazer/operation/nested.rb +43 -0
- data/lib/trailblazer/operation/params.rb +13 -0
- data/lib/trailblazer/operation/persist.rb +13 -0
- data/lib/trailblazer/operation/policy.rb +26 -72
- data/lib/trailblazer/operation/present.rb +19 -0
- data/lib/trailblazer/operation/procedural/contract.rb +15 -0
- data/lib/trailblazer/operation/procedural/validate.rb +22 -0
- data/lib/trailblazer/operation/pundit.rb +42 -0
- data/lib/trailblazer/operation/representer.rb +25 -92
- data/lib/trailblazer/operation/rescue.rb +23 -0
- data/lib/trailblazer/operation/resolver.rb +18 -24
- data/lib/trailblazer/operation/validate.rb +50 -0
- data/lib/trailblazer/operation/wrap.rb +37 -0
- data/lib/trailblazer/version.rb +1 -1
- data/test/{operation/controller_test.rb → controller_test.rb} +8 -4
- data/test/docs/auto_inject_test.rb +30 -0
- data/test/docs/contract_test.rb +429 -0
- data/test/docs/dry_test.rb +31 -0
- data/test/docs/guard_test.rb +143 -0
- data/test/docs/nested_test.rb +117 -0
- data/test/docs/policy_test.rb +2 -0
- data/test/docs/pundit_test.rb +109 -0
- data/test/docs/representer_test.rb +268 -0
- data/test/docs/rescue_test.rb +153 -0
- data/test/docs/wrap_test.rb +174 -0
- data/test/gemfiles/Gemfile.ruby-1.9 +3 -0
- data/test/gemfiles/Gemfile.ruby-2.0 +12 -0
- data/test/gemfiles/Gemfile.ruby-2.3 +12 -0
- data/test/module_test.rb +22 -15
- data/test/operation/builder_test.rb +66 -18
- data/test/operation/callback_test.rb +70 -0
- data/test/operation/contract_test.rb +385 -15
- data/test/operation/dsl/callback_test.rb +18 -30
- data/test/operation/dsl/contract_test.rb +209 -19
- data/test/operation/dsl/representer_test.rb +42 -15
- data/test/operation/guard_test.rb +1 -147
- data/test/operation/model_test.rb +105 -0
- data/test/operation/params_test.rb +36 -0
- data/test/operation/persist_test.rb +44 -0
- data/test/operation/pipedream_test.rb +59 -0
- data/test/operation/pipetree_test.rb +104 -0
- data/test/operation/present_test.rb +24 -0
- data/test/operation/pundit_test.rb +104 -0
- data/test/{representer_test.rb → operation/representer_test.rb} +58 -42
- data/test/operation/resolver_test.rb +34 -70
- data/test/operation_test.rb +57 -189
- data/test/test_helper.rb +23 -3
- data/trailblazer.gemspec +8 -7
- metadata +91 -59
- data/gemfiles/Gemfile.rails.lock +0 -130
- data/gemfiles/Gemfile.reform-2.0 +0 -6
- data/gemfiles/Gemfile.reform-2.1 +0 -7
- data/lib/trailblazer/autoloading.rb +0 -15
- data/lib/trailblazer/endpoint.rb +0 -31
- data/lib/trailblazer/operation.rb +0 -175
- data/lib/trailblazer/operation/collection.rb +0 -6
- data/lib/trailblazer/operation/dispatch.rb +0 -3
- data/lib/trailblazer/operation/model/dsl.rb +0 -29
- data/lib/trailblazer/operation/model/external.rb +0 -34
- data/lib/trailblazer/operation/policy/guard.rb +0 -35
- data/lib/trailblazer/operation/uploaded_file.rb +0 -77
- data/test/callback_test.rb +0 -104
- data/test/collection_test.rb +0 -57
- data/test/model_test.rb +0 -148
- data/test/operation/external_model_test.rb +0 -71
- data/test/operation/policy_test.rb +0 -97
- data/test/operation/reject_test.rb +0 -34
- data/test/rollback_test.rb +0 -47
@@ -0,0 +1,24 @@
|
|
1
|
+
require "trailblazer/operation/policy"
|
2
|
+
require "uber/option"
|
3
|
+
|
4
|
+
class Trailblazer::Operation
|
5
|
+
module Policy
|
6
|
+
module Guard
|
7
|
+
def self.import!(operation, import, user_proc, options={})
|
8
|
+
Policy.add!(operation, import, options) { Guard.build(user_proc) }
|
9
|
+
end
|
10
|
+
|
11
|
+
def self.build(callable)
|
12
|
+
value = Uber::Option[callable]
|
13
|
+
|
14
|
+
# call'ing the Uber::Option will run either proc or block.
|
15
|
+
# this gets wrapped in a Operation::Result object.
|
16
|
+
->(options) { Result.new( !!value.(options), {} ) }
|
17
|
+
end
|
18
|
+
end # Guard
|
19
|
+
|
20
|
+
def self.Guard(*args, &block)
|
21
|
+
[ Guard, args, block ]
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -1,50 +1,58 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
base.extend DSL
|
1
|
+
class Trailblazer::Operation
|
2
|
+
module Model
|
3
|
+
Step = ->(operation, options) { options["model"] = operation.model!(options["params"]) }
|
4
|
+
|
5
|
+
def self.import!(operation, import, model_class, action=nil)
|
6
|
+
if import.inheriting? # not sure how to do overrides!
|
7
|
+
# FIXME: prototyping inheritance. should we handle that here?
|
8
|
+
return operation["model.action"] = model_class
|
10
9
|
end
|
11
10
|
|
12
|
-
|
13
|
-
module BuildModel
|
14
|
-
def model!(params)
|
15
|
-
instantiate_model(params)
|
16
|
-
end
|
11
|
+
import.(:&, Step, name: "model.build")
|
17
12
|
|
18
|
-
|
19
|
-
|
20
|
-
end
|
13
|
+
operation["model.class"] = model_class
|
14
|
+
operation["model.action"] = action
|
21
15
|
|
22
|
-
|
23
|
-
|
24
|
-
end
|
16
|
+
operation.send :include, BuildMethods
|
17
|
+
end
|
25
18
|
|
26
|
-
|
27
|
-
|
28
|
-
|
19
|
+
# Methods to create the model according to class configuration and params.
|
20
|
+
module BuildMethods
|
21
|
+
def model_class
|
22
|
+
self["model.class"] or raise "[Trailblazer] You didn't call Operation::model."
|
23
|
+
end
|
29
24
|
|
30
|
-
|
25
|
+
def action_name
|
26
|
+
self["model.action"] or :new
|
31
27
|
end
|
32
28
|
|
29
|
+
def model!(params)
|
30
|
+
instantiate_model(params)
|
31
|
+
end
|
33
32
|
|
34
|
-
|
35
|
-
|
36
|
-
super(params, model, *args)
|
33
|
+
def instantiate_model(params)
|
34
|
+
send("#{action_name}_model", params)
|
37
35
|
end
|
38
36
|
|
39
|
-
|
40
|
-
|
37
|
+
def new_model(params)
|
38
|
+
model_class.new
|
39
|
+
end
|
41
40
|
|
42
|
-
def
|
43
|
-
|
41
|
+
def update_model(params)
|
42
|
+
model_class.find(params[:id])
|
44
43
|
end
|
45
|
-
|
46
|
-
|
44
|
+
|
45
|
+
alias_method :find_model, :update_model
|
46
|
+
|
47
|
+
# Doesn't throw an exception and will return false to divert to Left.
|
48
|
+
def find_by_model(params)
|
49
|
+
model = model_class.find_by(id: params[:id])
|
50
|
+
|
51
|
+
self["result.model"] = Result.new(!model.nil?, {})
|
52
|
+
model
|
47
53
|
end
|
48
54
|
end
|
49
55
|
end
|
56
|
+
|
57
|
+
DSL.macro!(:Model, Model)
|
50
58
|
end
|
@@ -0,0 +1,43 @@
|
|
1
|
+
class Trailblazer::Operation
|
2
|
+
module Nested
|
3
|
+
# Please note that the instance_variable_get are here on purpose since the
|
4
|
+
# superinternal API is not entirely decided, yet.
|
5
|
+
def self.import!(operation, import, step)
|
6
|
+
import.(:&, ->(input, options) {
|
7
|
+
result = step._call(*options.to_runtime_data)
|
8
|
+
|
9
|
+
result.instance_variable_get(:@data).to_mutable_data.each do |k,v|
|
10
|
+
options[k] = v
|
11
|
+
end
|
12
|
+
|
13
|
+
result.success? # DISCUSS: what if we could simply return the result object here?
|
14
|
+
}, {} )
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
DSL.macro!(:Nested, Nested)
|
19
|
+
|
20
|
+
module Rescue
|
21
|
+
def self.import!(_operation, import, *exceptions, handler:->(*){}, &block)
|
22
|
+
exceptions = [StandardError] unless exceptions.any?
|
23
|
+
handler = Pipetree::DSL::Option.(handler)
|
24
|
+
|
25
|
+
rescue_block = ->(options, operation, *, &nested_pipe) {
|
26
|
+
begin
|
27
|
+
res = nested_pipe.call
|
28
|
+
res.first == ::Pipetree::Flow::Right # FIXME.
|
29
|
+
rescue *exceptions => exception
|
30
|
+
handler.call(operation, exception, options)
|
31
|
+
#options["result.model.find"] = "argh! because #{exception.class}"
|
32
|
+
false
|
33
|
+
end
|
34
|
+
}
|
35
|
+
|
36
|
+
# operation.| operation.Wrap(rescue_block, &block), name: "Rescue:#{block.source_location.last}"
|
37
|
+
Wrap.import! _operation, import, rescue_block, name: "Rescue:#{block.source_location.last}", &block
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
DSL.macro!(:Rescue, Rescue)
|
42
|
+
end
|
43
|
+
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Trailblazer::Operation
|
2
|
+
module Params
|
3
|
+
def self.included(includer)
|
4
|
+
includer.> Replace, after: New
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
# Returned object will replace "params". Original is saved in "params.original".
|
9
|
+
Params::Replace = ->(input, options) {
|
10
|
+
options["params.original"] = original = options["params"]
|
11
|
+
options["params"] = input.params!(original)
|
12
|
+
}
|
13
|
+
end
|
@@ -0,0 +1,13 @@
|
|
1
|
+
class Trailblazer::Operation
|
2
|
+
module Persist
|
3
|
+
def self.import!(operation, import, options={})
|
4
|
+
save_method = options[:method] || :save
|
5
|
+
contract_name = options[:name] || "contract.default"
|
6
|
+
|
7
|
+
import.(:&, ->(input, options) { options[contract_name].send(save_method) }, # TODO: test me.
|
8
|
+
name: "persist.save")
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
DSL.macro!(:Persist, Persist)
|
13
|
+
end
|
@@ -1,85 +1,39 @@
|
|
1
|
-
|
1
|
+
class Trailblazer::Operation
|
2
|
+
module Policy
|
3
|
+
# Step: This generically `call`s a policy and then pushes its result to `options`.
|
4
|
+
# You can use any callable object as a policy with this step.
|
5
|
+
# :private:
|
6
|
+
class Eval
|
7
|
+
include Uber::Callable
|
2
8
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
# Adds #evaluate_policy to #setup!, and ::policy.
|
8
|
-
module Operation::Policy
|
9
|
-
def self.included(includer)
|
10
|
-
includer.extend DSL
|
11
|
-
end
|
12
|
-
|
13
|
-
module DSL
|
14
|
-
def self.extended(extender)
|
15
|
-
extender.inheritable_attr :policy_config
|
16
|
-
extender.policy_config = lambda { |*| true } # return true per default.
|
9
|
+
def initialize(name:nil, path:nil)
|
10
|
+
@name = name
|
11
|
+
@path = path
|
17
12
|
end
|
18
13
|
|
19
|
-
def
|
20
|
-
|
21
|
-
|
14
|
+
def call(input, options)
|
15
|
+
condition = options[@path] # this allows dependency injection.
|
16
|
+
result = condition.(options)
|
22
17
|
|
23
|
-
|
24
|
-
|
25
|
-
end
|
26
|
-
end
|
18
|
+
options["policy.#{@name}"] = result["policy"] # assign the policy as a skill.
|
19
|
+
options["result.policy.#{@name}"] = result
|
27
20
|
|
28
|
-
|
29
|
-
|
30
|
-
private
|
31
|
-
module Setup
|
32
|
-
def setup!(params)
|
33
|
-
evaluate_policy(super)
|
21
|
+
# flow control
|
22
|
+
result.success? # since we & this, it's only executed OnRight and the return boolean decides the direction, input is passed straight through.
|
34
23
|
end
|
35
24
|
end
|
36
|
-
include Setup
|
37
|
-
|
38
|
-
def evaluate_policy(params)
|
39
|
-
user = params[:current_user]
|
40
25
|
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
end
|
26
|
+
# Adds the `yield` result to the pipe and treats it like a policy-compatible object at runtime.
|
27
|
+
def self.add!(operation, import, options)
|
28
|
+
name = options[:name] || :default
|
45
29
|
|
46
|
-
|
47
|
-
|
48
|
-
end
|
30
|
+
# configure class level.
|
31
|
+
operation[path = "policy.#{name}.eval"] = yield
|
49
32
|
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
@policy_class, @action = policy_class, action
|
55
|
-
end
|
56
|
-
|
57
|
-
# Without a block, return the policy object (which is usually a Pundit-style class).
|
58
|
-
# When block is passed evaluate the default rule and run block when false.
|
59
|
-
def call(user, model, external_policy=nil)
|
60
|
-
policy = build_policy(user, model, external_policy)
|
61
|
-
|
62
|
-
policy.send(@action) || yield(policy, @action) if block_given?
|
63
|
-
policy
|
64
|
-
end
|
65
|
-
|
66
|
-
private
|
67
|
-
def build_policy(user, model, policy)
|
68
|
-
policy or @policy_class.new(user, model)
|
69
|
-
end
|
70
|
-
end
|
71
|
-
end
|
72
|
-
|
73
|
-
|
74
|
-
module Operation::Deny
|
75
|
-
def self.included(includer)
|
76
|
-
includer.extend ClassMethods
|
77
|
-
end
|
78
|
-
|
79
|
-
module ClassMethods
|
80
|
-
def deny!
|
81
|
-
raise NotAuthorizedError
|
82
|
-
end
|
33
|
+
# add step.
|
34
|
+
import.(:&, Eval.new( name: name, path: path ),
|
35
|
+
name: path
|
36
|
+
)
|
83
37
|
end
|
84
38
|
end
|
85
39
|
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
class Trailblazer::Operation
|
2
|
+
module Present
|
3
|
+
def self.included(includer)
|
4
|
+
includer.extend PresentMethod
|
5
|
+
includer.& Stop, before: Call
|
6
|
+
end
|
7
|
+
|
8
|
+
module PresentMethod
|
9
|
+
def present(params={}, options={}, *args)
|
10
|
+
call(params, options.merge("present.stop?" => true), *args)
|
11
|
+
end
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
# Stops the pipeline if "present.stop?" is set, which usually happens in Operation::present.
|
16
|
+
Present::Stop = ->(input, options) { ! options["present.stop?"] } # false returns Left.
|
17
|
+
end
|
18
|
+
|
19
|
+
# TODO: another stop for present without the contract!
|
@@ -0,0 +1,15 @@
|
|
1
|
+
module Trailblazer::Operation::Procedural
|
2
|
+
# THIS IS UNTESTED, PRIVATE API AND WILL BE REMOVED SOON.
|
3
|
+
module Contract
|
4
|
+
# Instantiate the contract, either by using the user's contract passed into #validate
|
5
|
+
# or infer the Operation contract.
|
6
|
+
def contract_for(model:self["model"], options:{}, contract_class:self["contract.default.class"])
|
7
|
+
contract!(model: model, options: options, contract_class: contract_class)
|
8
|
+
end
|
9
|
+
|
10
|
+
# Override to construct your own contract.
|
11
|
+
def contract!(model:nil, options:{}, contract_class:nil)
|
12
|
+
contract_class.new(model, options)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Trailblazer::Operation::Procedural
|
2
|
+
module Validate
|
3
|
+
def validate(params, contract:self["contract.default"], path:"contract.default") # :params
|
4
|
+
# DISCUSS: should we only have path here and then look up contract ourselves?
|
5
|
+
result = validate_contract(contract, params) # run validation. # FIXME: must be overridable.
|
6
|
+
|
7
|
+
self["result.#{path}"] = result
|
8
|
+
|
9
|
+
if valid = result.success? # FIXME: to_bool or success?
|
10
|
+
yield result if block_given?
|
11
|
+
else
|
12
|
+
# self["errors.#{path}"] = result.errors # TODO: remove me
|
13
|
+
end
|
14
|
+
|
15
|
+
valid
|
16
|
+
end
|
17
|
+
|
18
|
+
def validate_contract(contract, params)
|
19
|
+
contract.(params)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
class Trailblazer::Operation
|
2
|
+
module Policy
|
3
|
+
module Pundit
|
4
|
+
def self.import!(operation, import, policy_class, action, options={})
|
5
|
+
Policy.add!(operation, import, options) { Pundit.build(policy_class, action) }
|
6
|
+
end
|
7
|
+
|
8
|
+
def self.build(*args, &block)
|
9
|
+
Condition.new(*args, &block)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Pundit::Condition is invoked at runtime when iterating the pipe.
|
13
|
+
class Condition
|
14
|
+
def initialize(policy_class, action)
|
15
|
+
@policy_class, @action = policy_class, action
|
16
|
+
end
|
17
|
+
|
18
|
+
# Instantiate the actual policy object, and call it.
|
19
|
+
def call(skills)
|
20
|
+
policy = build_policy(skills) # this translates to Pundit interface.
|
21
|
+
result!(policy.send(@action), policy)
|
22
|
+
end
|
23
|
+
|
24
|
+
private
|
25
|
+
def build_policy(skills)
|
26
|
+
@policy_class.new(skills["current_user"], skills["model"])
|
27
|
+
end
|
28
|
+
|
29
|
+
def result!(success, policy)
|
30
|
+
data = { "policy" => policy }
|
31
|
+
data["message"] = "Breach" if !success # TODO: how to allow messages here?
|
32
|
+
|
33
|
+
Result.new(success, data)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.Pundit(*args, &block)
|
39
|
+
[ Pundit, args, block ]
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -1,98 +1,31 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
#
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
end
|
24
|
-
|
25
|
-
def representer_class
|
26
|
-
self._representer_class ||= infer_representer_class
|
27
|
-
end
|
28
|
-
|
29
|
-
def representer_class=(constant)
|
30
|
-
self._representer_class = constant
|
31
|
-
end
|
32
|
-
|
33
|
-
require "disposable/version"
|
34
|
-
def infer_representer_class
|
35
|
-
if Disposable::VERSION =~ /^0.1/
|
36
|
-
warn "[Trailblazer] Reform 2.0 won't be supported in Trailblazer 1.2. Don't be lazy and upgrade to Reform 2.1."
|
37
|
-
|
38
|
-
Disposable::Twin::Schema.from(contract_class,
|
39
|
-
include: [Representable::JSON],
|
40
|
-
options_from: :deserializer, # use :instance etc. in deserializer.
|
41
|
-
superclass: Representable::Decorator,
|
42
|
-
representer_from: lambda { |inline| inline.representer_class },
|
43
|
-
)
|
44
|
-
else
|
45
|
-
Disposable::Rescheme.from(contract_class,
|
46
|
-
include: [Representable::JSON],
|
47
|
-
options_from: :deserializer, # use :instance etc. in deserializer.
|
48
|
-
superclass: Representable::Decorator,
|
49
|
-
definitions_from: lambda { |inline| inline.definitions },
|
50
|
-
exclude_options: [:default, :populator], # TODO: test with populator: in an operation.
|
51
|
-
exclude_properties: [:persisted?]
|
52
|
-
)
|
1
|
+
class Trailblazer::Operation
|
2
|
+
module Representer
|
3
|
+
def self.infer(contract_class, format:Representable::JSON)
|
4
|
+
Disposable::Rescheme.from(contract_class,
|
5
|
+
include: [format],
|
6
|
+
options_from: :deserializer, # use :instance etc. in deserializer.
|
7
|
+
superclass: Representable::Decorator,
|
8
|
+
definitions_from: lambda { |inline| inline.definitions },
|
9
|
+
exclude_options: [:default, :populator], # TODO: test with populator: in an operation.
|
10
|
+
exclude_properties: [:persisted?]
|
11
|
+
)
|
12
|
+
end
|
13
|
+
|
14
|
+
module DSL
|
15
|
+
def representer(name=:default, constant=nil, &block)
|
16
|
+
heritage.record(:representer, name, constant, &block)
|
17
|
+
|
18
|
+
# FIXME: make this nicer. we want to extend same-named callback groups.
|
19
|
+
# TODO: allow the same with contract, or better, test it!
|
20
|
+
path, representer_class = Trailblazer::DSL::Build.new.({ prefix: :representer, class: representer_base_class, container: self }, name, constant, block)
|
21
|
+
|
22
|
+
self[path] = representer_class
|
53
23
|
end
|
54
|
-
end
|
55
|
-
end
|
56
|
-
|
57
|
-
private
|
58
|
-
module Rendering
|
59
|
-
# Override this if you need to pass options to the rendering.
|
60
|
-
#
|
61
|
-
# def to_json(*)
|
62
|
-
# super(include: @params[:include])
|
63
|
-
# end
|
64
|
-
def to_json(options={})
|
65
|
-
self.class.representer_class.new(represented).to_json(options)
|
66
|
-
end
|
67
|
-
|
68
|
-
# Override this if you want to render something else, e.g. the contract.
|
69
|
-
def represented
|
70
|
-
model
|
71
|
-
end
|
72
|
-
end
|
73
|
-
include Rendering
|
74
|
-
|
75
|
-
|
76
|
-
module Deserializer
|
77
|
-
module Hash
|
78
|
-
def validate_contract(params)
|
79
|
-
# use the inferred representer from the contract for deserialization in #validate.
|
80
|
-
contract.validate(params) do |document|
|
81
|
-
self.class.representer_class.new(contract).from_hash(document)
|
82
|
-
end
|
83
|
-
end
|
84
|
-
end
|
85
24
|
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
module JSON
|
90
|
-
def validate_contract(params)
|
91
|
-
contract.validate(params) do |document|
|
92
|
-
self.class.representer_class.new(contract).from_json(document)
|
93
|
-
end
|
25
|
+
# TODO: make engine configurable?
|
26
|
+
def representer_base_class
|
27
|
+
Class.new(Representable::Decorator) { include Representable::JSON; self }
|
94
28
|
end
|
95
29
|
end
|
96
30
|
end
|
97
|
-
include Deserializer::JSON
|
98
31
|
end
|