trailblazer 2.1.0.beta4 → 2.1.0.beta5
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/.rubocop_todo.yml +194 -502
- data/CHANGES.md +4 -0
- data/CONTRIBUTING.md +170 -0
- data/Gemfile +4 -1
- data/README.md +183 -40
- data/Rakefile +6 -2
- data/lib/trailblazer/version.rb +1 -1
- data/lib/trailblazer.rb +3 -12
- data/test/{operation/dsl → dsl}/contract_test.rb +2 -2
- data/trailblazer.gemspec +3 -3
- metadata +17 -63
- data/lib/trailblazer/operation/contract.rb +0 -82
- data/lib/trailblazer/operation/guard.rb +0 -18
- data/lib/trailblazer/operation/model.rb +0 -65
- data/lib/trailblazer/operation/nested.rb +0 -91
- data/lib/trailblazer/operation/persist.rb +0 -14
- data/lib/trailblazer/operation/policy.rb +0 -44
- data/lib/trailblazer/operation/pundit.rb +0 -38
- data/lib/trailblazer/operation/representer.rb +0 -36
- data/lib/trailblazer/operation/rescue.rb +0 -24
- data/lib/trailblazer/operation/validate.rb +0 -74
- data/lib/trailblazer/operation/wrap.rb +0 -64
- data/test/docs/contract_test.rb +0 -545
- data/test/docs/dry_test.rb +0 -31
- data/test/docs/guard_test.rb +0 -162
- data/test/docs/macro_test.rb +0 -36
- data/test/docs/model_test.rb +0 -75
- data/test/docs/nested_test.rb +0 -300
- data/test/docs/policy_test.rb +0 -2
- data/test/docs/pundit_test.rb +0 -133
- data/test/docs/representer_test.rb +0 -268
- data/test/docs/rescue_test.rb +0 -154
- data/test/docs/wrap_test.rb +0 -219
- data/test/nested_test.rb +0 -293
- data/test/operation/contract_test.rb +0 -290
- data/test/operation/dsl/representer_test.rb +0 -169
- data/test/operation/model_test.rb +0 -54
- data/test/operation/persist_test.rb +0 -51
- data/test/operation/pundit_test.rb +0 -106
- data/test/operation/representer_test.rb +0 -254
@@ -1,169 +0,0 @@
|
|
1
|
-
# require "test_helper"
|
2
|
-
# require "representable/json"
|
3
|
-
# require "trailblazer/operation/representer"
|
4
|
-
# require "trailblazer/operation/contract"
|
5
|
-
|
6
|
-
# class DslRepresenterTest < MiniTest::Spec
|
7
|
-
# module SongProcess
|
8
|
-
# def process(params)
|
9
|
-
# self["model"] = OpenStruct.new(params)
|
10
|
-
# end
|
11
|
-
|
12
|
-
# def represented
|
13
|
-
# self["model"]
|
14
|
-
# end
|
15
|
-
# end
|
16
|
-
|
17
|
-
# describe "inheritance across operations" do
|
18
|
-
# class Operation < Trailblazer::Operation
|
19
|
-
# include Representer
|
20
|
-
# include SongProcess
|
21
|
-
|
22
|
-
# representer do
|
23
|
-
# property :title
|
24
|
-
# end
|
25
|
-
|
26
|
-
# class JSON < self
|
27
|
-
# representer do
|
28
|
-
# property :band
|
29
|
-
# end
|
30
|
-
# end
|
31
|
-
# end
|
32
|
-
|
33
|
-
# it { Operation.(title: "Nothing To Lose", band: "Gary Moore").to_json.must_equal %{{"title":"Nothing To Lose"}} }
|
34
|
-
# # only the subclass must have the `band` field, even though it's set in the original operation.
|
35
|
-
# it { Operation::JSON.(title: "Nothing To Lose", band: "Gary Moore").to_json.must_equal %{{"title":"Nothing To Lose","band":"Gary Moore"}} }
|
36
|
-
# end
|
37
|
-
|
38
|
-
# describe "Op.representer CommentRepresenter" do
|
39
|
-
# class SongRepresenter < Representable::Decorator
|
40
|
-
# include Representable::JSON
|
41
|
-
# property :songTitle
|
42
|
-
# end
|
43
|
-
|
44
|
-
# class OpWithExternalRepresenter < Trailblazer::Operation
|
45
|
-
# include Representer
|
46
|
-
# include SongProcess
|
47
|
-
# representer SongRepresenter
|
48
|
-
# end
|
49
|
-
|
50
|
-
# it { OpWithExternalRepresenter.("songTitle"=>"Listen To Your Heartbeat").to_json.must_equal %{{"songTitle":"Listen To Your Heartbeat"}} }
|
51
|
-
# end
|
52
|
-
|
53
|
-
# # name for representer
|
54
|
-
# describe "1) Op.representer :parse, Representer" do
|
55
|
-
# class Op1 < Trailblazer::Operation
|
56
|
-
# include Representer
|
57
|
-
# representer :parse, String
|
58
|
-
# end
|
59
|
-
|
60
|
-
# it { Op1["representer.parse.class"].superclass.must_equal String }
|
61
|
-
# it { Op1.({})["representer.parse.class"].superclass.must_equal String }
|
62
|
-
# end
|
63
|
-
|
64
|
-
# # name for default representer
|
65
|
-
# describe "2) Op.representer Representer" do
|
66
|
-
# class Op2 < Trailblazer::Operation
|
67
|
-
# include Representer
|
68
|
-
# representer String
|
69
|
-
# def call(*); self; end
|
70
|
-
# end
|
71
|
-
|
72
|
-
# it { Op2["representer.default.class"].superclass.must_equal String }
|
73
|
-
# it { Op2.({})["representer.default.class"].superclass.must_equal String }
|
74
|
-
# it { Op2.({}, "representer.default.class" => Integer)["representer.default.class"].must_equal Integer }
|
75
|
-
# end
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
# describe "Op.representer CommentRepresenter do .. end" do
|
80
|
-
# class HitRepresenter < Representable::Decorator
|
81
|
-
# include Representable::JSON
|
82
|
-
# property :title
|
83
|
-
# end
|
84
|
-
|
85
|
-
# class OpNotExtendingRepresenter < Trailblazer::Operation
|
86
|
-
# include Representer
|
87
|
-
# include SongProcess
|
88
|
-
# representer HitRepresenter
|
89
|
-
# end
|
90
|
-
|
91
|
-
# class OpExtendingRepresenter < Trailblazer::Operation
|
92
|
-
# include Representer
|
93
|
-
# include SongProcess
|
94
|
-
# representer HitRepresenter do
|
95
|
-
# property :genre
|
96
|
-
# end
|
97
|
-
# end
|
98
|
-
|
99
|
-
# # this operation copies HitRepresenter and shouldn't have `genre`.
|
100
|
-
# it do
|
101
|
-
# OpNotExtendingRepresenter.("title"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"title":"Monsterparty"}}
|
102
|
-
# end
|
103
|
-
|
104
|
-
# # # this operation copies HitRepresenter and extends it with the property `genre`.
|
105
|
-
# it do
|
106
|
-
# OpExtendingRepresenter.("title"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"title":"Monsterparty","genre":"Punk"}}
|
107
|
-
# end
|
108
|
-
|
109
|
-
# # # of course, the original representer wasn't modified, either.
|
110
|
-
# it do
|
111
|
-
# HitRepresenter.new(OpenStruct.new(title: "Monsterparty", genre: "Punk")).to_json.must_equal %{{"title":"Monsterparty"}}
|
112
|
-
# end
|
113
|
-
# end
|
114
|
-
|
115
|
-
# describe "Op.representer (inferring)" do
|
116
|
-
# class ContractOperation < Trailblazer::Operation
|
117
|
-
# include Representer
|
118
|
-
# include Representer::InferFromContract
|
119
|
-
# include SongProcess
|
120
|
-
# include Contract::Explicit
|
121
|
-
|
122
|
-
# contract do
|
123
|
-
# property :songTitle
|
124
|
-
# end
|
125
|
-
# end
|
126
|
-
|
127
|
-
# class ContractOperation2 < Trailblazer::Operation
|
128
|
-
# include Representer
|
129
|
-
# include SongProcess
|
130
|
-
# include Contract::Explicit
|
131
|
-
# include Representer::InferFromContract
|
132
|
-
# contract ContractOperation["contract.default.class"]
|
133
|
-
|
134
|
-
# representer do
|
135
|
-
# property :genre
|
136
|
-
# end
|
137
|
-
# end
|
138
|
-
|
139
|
-
# it { ContractOperation.("songTitle"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"songTitle":"Monsterparty"}} }
|
140
|
-
# # this representer block extends the inferred from contract.
|
141
|
-
# it { ContractOperation2.("songTitle"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"songTitle":"Monsterparty","genre":"Punk"}} }
|
142
|
-
# end
|
143
|
-
|
144
|
-
# describe "Op.representer_class" do
|
145
|
-
# class PlayRepresenter < Representable::Decorator
|
146
|
-
# include Representable::JSON
|
147
|
-
# property :title
|
148
|
-
# end
|
149
|
-
|
150
|
-
# class OpSettingRepresenter < Trailblazer::Operation
|
151
|
-
# include Representer
|
152
|
-
# include SongProcess
|
153
|
-
# self["representer.default.class"] = PlayRepresenter
|
154
|
-
# end
|
155
|
-
|
156
|
-
# class OpExtendRepresenter < Trailblazer::Operation
|
157
|
-
# include Representer
|
158
|
-
# include SongProcess
|
159
|
-
# self["representer.default.class"] = PlayRepresenter
|
160
|
-
# representer do
|
161
|
-
# property :genre
|
162
|
-
# end
|
163
|
-
# end
|
164
|
-
|
165
|
-
# # both operations produce the same as the representer is shared, not copied.
|
166
|
-
# it { skip; OpSettingRepresenter.("title"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"title":"Monsterparty","genre":"Punk"}} }
|
167
|
-
# it { OpExtendRepresenter.("title"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"title":"Monsterparty","genre":"Punk"}} }
|
168
|
-
# end
|
169
|
-
# end
|
@@ -1,54 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
class ModelTest < Minitest::Spec
|
4
|
-
Song = Struct.new(:id) do
|
5
|
-
def self.find(id); new(id) end
|
6
|
-
def self.find_by(id:nil); id.nil? ? nil : new(id) end
|
7
|
-
end
|
8
|
-
|
9
|
-
#---
|
10
|
-
# use Model semantics, no customizations.
|
11
|
-
class Create < Trailblazer::Operation
|
12
|
-
step Model( Song, :new )
|
13
|
-
end
|
14
|
-
|
15
|
-
# :new new.
|
16
|
-
it { Create.(params: {})[:model].inspect.must_equal %{#<struct ModelTest::Song id=nil>} }
|
17
|
-
|
18
|
-
class Update < Create
|
19
|
-
step Model( Song, :find ), override: true
|
20
|
-
end
|
21
|
-
|
22
|
-
#---
|
23
|
-
#- inheritance
|
24
|
-
|
25
|
-
# :find it
|
26
|
-
it { Update.(params: { id: 1 })[:model].inspect.must_equal %{#<struct ModelTest::Song id=1>} }
|
27
|
-
|
28
|
-
# inherited inspect is ok
|
29
|
-
it { Trailblazer::Operation::Inspect.(Update).must_equal %{[>model.build]} }
|
30
|
-
|
31
|
-
#---
|
32
|
-
# :find_by, exceptionless.
|
33
|
-
class Find < Trailblazer::Operation
|
34
|
-
step Model Song, :find_by
|
35
|
-
step :process
|
36
|
-
|
37
|
-
def process(options, **); options["x"] = true end
|
38
|
-
end
|
39
|
-
|
40
|
-
# can't find model.
|
41
|
-
#- result object, model
|
42
|
-
it do
|
43
|
-
Find.(params: {id: nil})["result.model"].failure?.must_equal true
|
44
|
-
Find.(params: {id: nil})["x"].must_be_nil
|
45
|
-
Find.(params: {id: nil}).failure?.must_equal true
|
46
|
-
end
|
47
|
-
|
48
|
-
#- result object, model
|
49
|
-
it do
|
50
|
-
Find.(params: {id: 9})["result.model"].success?.must_equal true
|
51
|
-
Find.(params: {id: 9})["x"].must_equal true
|
52
|
-
Find.(params: {id: 9})[:model].inspect.must_equal %{#<struct ModelTest::Song id=9>}
|
53
|
-
end
|
54
|
-
end
|
@@ -1,51 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
|
3
|
-
class PersistTest < Minitest::Spec
|
4
|
-
Song = Struct.new(:title, :saved) do
|
5
|
-
def save; title=="Fail!" ? false : self.saved = true; end
|
6
|
-
end
|
7
|
-
|
8
|
-
class Create < Trailblazer::Operation
|
9
|
-
class Form < Reform::Form
|
10
|
-
property :title
|
11
|
-
end
|
12
|
-
|
13
|
-
class Fail1
|
14
|
-
def self.call(options, **); options["1. fail"] = "Validate" end
|
15
|
-
end
|
16
|
-
|
17
|
-
class Fail2
|
18
|
-
def self.call(options, **); options["2. fail"] = "Persist" end
|
19
|
-
end
|
20
|
-
|
21
|
-
step Model( Song, :new )
|
22
|
-
step Contract::Build( constant: Form )
|
23
|
-
step Contract::Validate()
|
24
|
-
fail Fail1
|
25
|
-
step Contract::Persist()
|
26
|
-
fail Fail2
|
27
|
-
end
|
28
|
-
|
29
|
-
it { Create.(params: {title: "In Recital"})[:model].title.must_equal "In Recital" }
|
30
|
-
it { Create.(params: {title: "In Recital"})[:model].saved.must_equal true }
|
31
|
-
# failure
|
32
|
-
it do
|
33
|
-
result = Create.(params: {title: "Fail!"})
|
34
|
-
result[:model].saved.must_be_nil
|
35
|
-
result[:model].title.must_equal "Fail!"
|
36
|
-
result["2. fail"].must_equal "Persist"
|
37
|
-
result.success?.must_equal false
|
38
|
-
end
|
39
|
-
|
40
|
-
#---
|
41
|
-
#- inheritance
|
42
|
-
class Update < Create
|
43
|
-
end
|
44
|
-
|
45
|
-
it { Operation::Inspect.( Update ).must_equal %{[>model.build,>contract.build,>contract.default.validate,<<PersistTest::Create::Fail1,>persist.save,<<PersistTest::Create::Fail2]} }
|
46
|
-
|
47
|
-
#---
|
48
|
-
it do
|
49
|
-
skip "show how save! could be applied and how we could rescue and deviate to left track"
|
50
|
-
end
|
51
|
-
end
|
@@ -1,106 +0,0 @@
|
|
1
|
-
require "test_helper"
|
2
|
-
require "trailblazer/operation/policy"
|
3
|
-
|
4
|
-
class PolicyTest < Minitest::Spec
|
5
|
-
Song = Struct.new(:id) do
|
6
|
-
def self.find(id); new(id) end
|
7
|
-
end
|
8
|
-
|
9
|
-
class Auth
|
10
|
-
def initialize(user, model); @user, @model = user, model end
|
11
|
-
def only_user?; @user == Module && @model.nil? end
|
12
|
-
def user_object?; @user == Object end
|
13
|
-
def user_and_model?; @user == Module && @model.class == Song end
|
14
|
-
def inspect; "<Auth: user:#{@user.inspect}, model:#{@model.inspect}>" end
|
15
|
-
end
|
16
|
-
|
17
|
-
#---
|
18
|
-
# Instance-level: Only policy, no model
|
19
|
-
class Create < Trailblazer::Operation
|
20
|
-
step Policy::Pundit( Auth, :only_user? )
|
21
|
-
step :process
|
22
|
-
|
23
|
-
def process(options, **)
|
24
|
-
options["process"] = true
|
25
|
-
end
|
26
|
-
end
|
27
|
-
|
28
|
-
# successful.
|
29
|
-
it do
|
30
|
-
result = Create.(params: {}, current_user: Module)
|
31
|
-
result["process"].must_equal true
|
32
|
-
#- result object, policy
|
33
|
-
result["result.policy.default"].success?.must_equal true
|
34
|
-
result["result.policy.default"]["message"].must_be_nil
|
35
|
-
# result[:valid].must_be_nil
|
36
|
-
result["policy.default"].inspect.must_equal %{<Auth: user:Module, model:nil>}
|
37
|
-
end
|
38
|
-
# breach.
|
39
|
-
it do
|
40
|
-
result = Create.(params: {}, current_user: nil)
|
41
|
-
result["process"].must_be_nil
|
42
|
-
#- result object, policy
|
43
|
-
result["result.policy.default"].success?.must_equal false
|
44
|
-
result["result.policy.default"]["message"].must_equal "Breach"
|
45
|
-
end
|
46
|
-
# inject different policy.Condition it { Create.(params: {}, current_user: Object, "policy.default.eval" => Trailblazer::Operation::Policy::Pundit::Condition.new(Auth, :user_object?))["process"].must_equal true }
|
47
|
-
it { Create.(params: {}, current_user: Module, "policy.default.eval" => Trailblazer::Operation::Policy::Pundit::Condition.new(Auth, :user_object?))["process"].must_be_nil }
|
48
|
-
|
49
|
-
|
50
|
-
#---
|
51
|
-
# inheritance, adding Model
|
52
|
-
class Show < Create
|
53
|
-
step Model( Song, :new ), before: "policy.default.eval"
|
54
|
-
end
|
55
|
-
|
56
|
-
it { Trailblazer::Operation::Inspect.(Show).must_equal %{[>model.build,>policy.default.eval,>process]} }
|
57
|
-
|
58
|
-
# invalid because user AND model.
|
59
|
-
it do
|
60
|
-
result = Show.(params: {}, current_user: Module)
|
61
|
-
result["process"].must_be_nil
|
62
|
-
result[:model].inspect.must_equal %{#<struct PolicyTest::Song id=nil>}
|
63
|
-
# result["policy"].inspect.must_equal %{#<struct PolicyTest::Song id=nil>}
|
64
|
-
end
|
65
|
-
|
66
|
-
# valid because new policy.
|
67
|
-
it do
|
68
|
-
# puts Show["pipetree"].inspect
|
69
|
-
result = Show.(params: {}, current_user: Module, "policy.default.eval" => Trailblazer::Operation::Policy::Pundit::Condition.new(Auth, :user_and_model?))
|
70
|
-
result["process"].must_equal true
|
71
|
-
result[:model].inspect.must_equal %{#<struct PolicyTest::Song id=nil>}
|
72
|
-
result["policy.default"].inspect.must_equal %{<Auth: user:Module, model:#<struct PolicyTest::Song id=nil>>}
|
73
|
-
end
|
74
|
-
|
75
|
-
##--
|
76
|
-
# TOOOODOOO: Policy and Model before Build ("External" or almost Resolver)
|
77
|
-
class Edit < Trailblazer::Operation
|
78
|
-
step Model Song, :find
|
79
|
-
step Policy::Pundit( Auth, :user_and_model? )
|
80
|
-
step :process
|
81
|
-
|
82
|
-
def process(options, **)
|
83
|
-
options["process"] = true
|
84
|
-
end
|
85
|
-
end
|
86
|
-
|
87
|
-
# successful.
|
88
|
-
it do
|
89
|
-
result = Edit.(params: { id: 1 }, current_user: Module)
|
90
|
-
result["process"].must_equal true
|
91
|
-
result[:model].inspect.must_equal %{#<struct PolicyTest::Song id=1>}
|
92
|
-
result["result.policy.default"].success?.must_equal true
|
93
|
-
result["result.policy.default"]["message"].must_be_nil
|
94
|
-
# result[:valid].must_be_nil
|
95
|
-
result["policy.default"].inspect.must_equal %{<Auth: user:Module, model:#<struct PolicyTest::Song id=1>>}
|
96
|
-
end
|
97
|
-
|
98
|
-
# breach.
|
99
|
-
it do
|
100
|
-
result = Edit.(params: { id: 4 }, current_user: nil)
|
101
|
-
result[:model].inspect.must_equal %{#<struct PolicyTest::Song id=4>}
|
102
|
-
result["process"].must_be_nil
|
103
|
-
result["result.policy.default"].success?.must_equal false
|
104
|
-
result["result.policy.default"]["message"].must_equal "Breach"
|
105
|
-
end
|
106
|
-
end
|
@@ -1,254 +0,0 @@
|
|
1
|
-
# require "test_helper"
|
2
|
-
# require "representable/json"
|
3
|
-
|
4
|
-
# class RepresenterTest < MiniTest::Spec
|
5
|
-
# Album = Struct.new(:title, :artist)
|
6
|
-
# Artist = Struct.new(:name)
|
7
|
-
|
8
|
-
# class Create < Trailblazer::Operation
|
9
|
-
# include Contract
|
10
|
-
# include Representer
|
11
|
-
# include Representer::InferFromContract
|
12
|
-
# attr_reader :model # FIXME: all we want is #model.
|
13
|
-
|
14
|
-
# contract do
|
15
|
-
# property :title
|
16
|
-
# validates :title, presence: true
|
17
|
-
# property :artist, populate_if_empty: Artist do
|
18
|
-
# property :name
|
19
|
-
# validates :name, presence: true
|
20
|
-
# end
|
21
|
-
# end
|
22
|
-
|
23
|
-
# def call(params)
|
24
|
-
# self["model"] = Album.new # NO artist!!!
|
25
|
-
# validate(params[:album], model: self["model"])
|
26
|
-
# self
|
27
|
-
# end
|
28
|
-
# end
|
29
|
-
|
30
|
-
|
31
|
-
# # Infers representer from contract, no customization.
|
32
|
-
# class Show < Create
|
33
|
-
# def call(params)
|
34
|
-
# self["model"] = Album.new("After The War", Artist.new("Gary Moore"))
|
35
|
-
# self
|
36
|
-
# end
|
37
|
-
# end
|
38
|
-
|
39
|
-
|
40
|
-
# # Infers representer, adds hypermedia.
|
41
|
-
# require "roar/json/hal"
|
42
|
-
# class HypermediaCreate < Create
|
43
|
-
# representer do
|
44
|
-
# include Roar::JSON::HAL
|
45
|
-
|
46
|
-
# link(:self) { "//album/#{represented.title}" }
|
47
|
-
# end
|
48
|
-
# end
|
49
|
-
|
50
|
-
# class HypermediaShow < HypermediaCreate
|
51
|
-
# def call(params)
|
52
|
-
# self["model"] = Album.new("After The War", Artist.new("Gary Moore"))
|
53
|
-
# self
|
54
|
-
# end
|
55
|
-
# end
|
56
|
-
|
57
|
-
|
58
|
-
# # rendering
|
59
|
-
# # generic contract -> representer
|
60
|
-
# it { Show.().to_json.must_equal %{{"title":"After The War","artist":{"name":"Gary Moore"}}} }
|
61
|
-
|
62
|
-
# # contract -> representer with hypermedia
|
63
|
-
# it do
|
64
|
-
# HypermediaShow.().to_json.must_equal %{{"title":"After The War","artist":{"name":"Gary Moore"},"_links":{"self":{"href":"//album/After The War"}}}}
|
65
|
-
# end
|
66
|
-
|
67
|
-
|
68
|
-
# # parsing
|
69
|
-
# it do
|
70
|
-
# op = Create.(album: %{{"title":"Run For Cover","artist":{"name":"Gary Moore"}}})
|
71
|
-
# op.contract.title.must_equal "Run For Cover"
|
72
|
-
# op.contract.artist.name.must_equal "Gary Moore"
|
73
|
-
# end
|
74
|
-
|
75
|
-
# it do
|
76
|
-
# op = HypermediaCreate.(album: %{{"title":"After The War","artist":{"name":"Gary Moore"},"_links":{"self":{"href":"//album/After The War"}}}})
|
77
|
-
# op.contract.title.must_equal "After The War"
|
78
|
-
# op.contract.artist.name.must_equal "Gary Moore"
|
79
|
-
# end
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
# # explicit representer set with ::representer_class=.
|
86
|
-
# require "roar/decorator"
|
87
|
-
# class JsonApiCreate < Trailblazer::Operation
|
88
|
-
# include Contract
|
89
|
-
# include Representer
|
90
|
-
# attr_reader :model
|
91
|
-
|
92
|
-
# contract do # we still need contract as the representer writes to the contract twin.
|
93
|
-
# property :title
|
94
|
-
# end
|
95
|
-
|
96
|
-
# class AlbumRepresenter < Roar::Decorator
|
97
|
-
# include Roar::JSON
|
98
|
-
# property :title
|
99
|
-
# end
|
100
|
-
|
101
|
-
# # FIXME: this won't inherit, of course.
|
102
|
-
# # self["representer.class"] = AlbumRepresenter
|
103
|
-
# representer AlbumRepresenter
|
104
|
-
|
105
|
-
# def call(params)
|
106
|
-
# self["model"] = Album.new # NO artist!!!
|
107
|
-
# validate(params[:album], model: self["model"])
|
108
|
-
# self
|
109
|
-
# end
|
110
|
-
# end
|
111
|
-
|
112
|
-
# class JsonApiShow < JsonApiCreate
|
113
|
-
# def call(params)
|
114
|
-
# self["model"] = Album.new("After The War", Artist.new("Gary Moore"))
|
115
|
-
# self
|
116
|
-
# end
|
117
|
-
# end
|
118
|
-
|
119
|
-
# # render.
|
120
|
-
# it do
|
121
|
-
# JsonApiShow.().to_json.must_equal %{{"title":"After The War"}}
|
122
|
-
# end
|
123
|
-
|
124
|
-
# # parse.
|
125
|
-
# it do
|
126
|
-
# JsonApiCreate.(album: %{{"title":"Run For Cover"}}).contract.title.must_equal "Run For Cover"
|
127
|
-
# end
|
128
|
-
# end
|
129
|
-
|
130
|
-
# class InternalRepresenterAPITest < MiniTest::Spec
|
131
|
-
# Song = Struct.new(:id)
|
132
|
-
|
133
|
-
# describe "#represented" do
|
134
|
-
# class Show < Trailblazer::Operation
|
135
|
-
# include Contract
|
136
|
-
# include Representer, Model
|
137
|
-
# model Song, :create
|
138
|
-
|
139
|
-
# representer do
|
140
|
-
# property :class
|
141
|
-
# end
|
142
|
-
|
143
|
-
# def call(*)
|
144
|
-
# self
|
145
|
-
# end
|
146
|
-
|
147
|
-
# def model # FIXME.
|
148
|
-
# self["model"]
|
149
|
-
# end
|
150
|
-
# end
|
151
|
-
|
152
|
-
# it "uses #model as represented, per default" do
|
153
|
-
# Show.({}).to_json.must_equal '{"class":"InternalRepresenterAPITest::Song"}'
|
154
|
-
# end
|
155
|
-
|
156
|
-
# class ShowContract < Show
|
157
|
-
# def represented
|
158
|
-
# "Object"
|
159
|
-
# end
|
160
|
-
# end
|
161
|
-
|
162
|
-
# it "can be overriden to use the contract" do
|
163
|
-
# ShowContract.({}).to_json.must_equal %{{"class":"String"}}
|
164
|
-
# end
|
165
|
-
# end
|
166
|
-
|
167
|
-
# describe "#to_json" do
|
168
|
-
# class OptionsShow < Trailblazer::Operation
|
169
|
-
# include Representer
|
170
|
-
|
171
|
-
# representer do
|
172
|
-
# property :class
|
173
|
-
# property :id
|
174
|
-
# end
|
175
|
-
|
176
|
-
# def to_json(*)
|
177
|
-
# super(self["params"])
|
178
|
-
# end
|
179
|
-
|
180
|
-
# include Model::Builder
|
181
|
-
# def model!(*)
|
182
|
-
# Song.new(1)
|
183
|
-
# end
|
184
|
-
# end
|
185
|
-
|
186
|
-
# it "allows to pass options to #to_json" do
|
187
|
-
# OptionsShow.(include: [:id]).to_json.must_equal %{{"id":1}}
|
188
|
-
# end
|
189
|
-
# end
|
190
|
-
# end
|
191
|
-
|
192
|
-
# class DifferentParseAndRenderingRepresenterTest < MiniTest::Spec
|
193
|
-
# Album = Struct.new(:title)
|
194
|
-
|
195
|
-
# # rendering
|
196
|
-
# class Create < Trailblazer::Operation
|
197
|
-
# include Contract
|
198
|
-
# extend Representer::DSL
|
199
|
-
# include Representer::Rendering # no Deserializer::Hash here or anything.
|
200
|
-
|
201
|
-
# contract do
|
202
|
-
# property :title
|
203
|
-
# end
|
204
|
-
|
205
|
-
# representer do
|
206
|
-
# property :title, as: :Title
|
207
|
-
# end
|
208
|
-
|
209
|
-
# def call(params)
|
210
|
-
# self["model"] = Album.new
|
211
|
-
# validate(params) do
|
212
|
-
# contract.sync
|
213
|
-
# end
|
214
|
-
# self
|
215
|
-
# end
|
216
|
-
# end
|
217
|
-
|
218
|
-
# it do
|
219
|
-
# Create.(title: "The Kids").to_json.must_equal %{{"Title":"The Kids"}}
|
220
|
-
# end
|
221
|
-
|
222
|
-
# # parsing
|
223
|
-
# class Update < Trailblazer::Operation
|
224
|
-
# include Contract
|
225
|
-
# extend Representer::DSL
|
226
|
-
# include Representer::Deserializer::Hash # no Rendering.
|
227
|
-
|
228
|
-
# representer do
|
229
|
-
# property :title, as: :Title
|
230
|
-
# end
|
231
|
-
|
232
|
-
# contract do
|
233
|
-
# property :title
|
234
|
-
# end
|
235
|
-
|
236
|
-
# def call(params)
|
237
|
-
# self["model"] = Album.new
|
238
|
-
|
239
|
-
# validate(params) do
|
240
|
-
# contract.sync
|
241
|
-
# end
|
242
|
-
|
243
|
-
# self
|
244
|
-
# end
|
245
|
-
|
246
|
-
# def to_json(*)
|
247
|
-
# %{{"title": "#{self["model"].title}"}}
|
248
|
-
# end
|
249
|
-
# end
|
250
|
-
|
251
|
-
# it do
|
252
|
-
# Update.("Title" => "The Kids").to_json.must_equal %{{"title": "The Kids"}}
|
253
|
-
# end
|
254
|
-
# end
|