trailblazer 1.1.1 → 2.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (60) hide show
  1. checksums.yaml +5 -5
  2. data/.gitignore +1 -0
  3. data/.rubocop-https---raw-githubusercontent-com-trailblazer-meta-master-rubocop-yml +101 -0
  4. data/.rubocop.yml +20 -0
  5. data/.rubocop_todo.yml +556 -0
  6. data/.travis.yml +6 -7
  7. data/CHANGES.md +224 -0
  8. data/COMM-LICENSE +62 -0
  9. data/CONTRIBUTING.md +179 -0
  10. data/Gemfile +0 -10
  11. data/LICENSE +9 -0
  12. data/README.md +68 -189
  13. data/Rakefile +3 -3
  14. data/lib/trailblazer/version.rb +3 -1
  15. data/lib/trailblazer.rb +3 -6
  16. data/test/test_helper.rb +32 -3
  17. data/trailblazer.gemspec +11 -15
  18. metadata +28 -132
  19. data/LICENSE.txt +0 -22
  20. data/TODO.md +0 -11
  21. data/doc/Trb-The-Stack.png +0 -0
  22. data/doc/trb.jpg +0 -0
  23. data/gemfiles/Gemfile.rails.lock +0 -130
  24. data/gemfiles/Gemfile.reform-2.0 +0 -6
  25. data/gemfiles/Gemfile.reform-2.1 +0 -7
  26. data/lib/trailblazer/autoloading.rb +0 -15
  27. data/lib/trailblazer/endpoint.rb +0 -31
  28. data/lib/trailblazer/operation/builder.rb +0 -26
  29. data/lib/trailblazer/operation/callback.rb +0 -53
  30. data/lib/trailblazer/operation/collection.rb +0 -6
  31. data/lib/trailblazer/operation/controller.rb +0 -72
  32. data/lib/trailblazer/operation/dispatch.rb +0 -3
  33. data/lib/trailblazer/operation/model/dsl.rb +0 -29
  34. data/lib/trailblazer/operation/model/external.rb +0 -34
  35. data/lib/trailblazer/operation/model.rb +0 -50
  36. data/lib/trailblazer/operation/module.rb +0 -29
  37. data/lib/trailblazer/operation/policy/guard.rb +0 -35
  38. data/lib/trailblazer/operation/policy.rb +0 -85
  39. data/lib/trailblazer/operation/representer.rb +0 -98
  40. data/lib/trailblazer/operation/resolver.rb +0 -30
  41. data/lib/trailblazer/operation/uploaded_file.rb +0 -77
  42. data/lib/trailblazer/operation.rb +0 -175
  43. data/test/callback_test.rb +0 -104
  44. data/test/collection_test.rb +0 -57
  45. data/test/model_test.rb +0 -148
  46. data/test/module_test.rb +0 -93
  47. data/test/operation/builder_test.rb +0 -41
  48. data/test/operation/contract_test.rb +0 -30
  49. data/test/operation/controller_test.rb +0 -111
  50. data/test/operation/dsl/callback_test.rb +0 -118
  51. data/test/operation/dsl/contract_test.rb +0 -104
  52. data/test/operation/dsl/representer_test.rb +0 -142
  53. data/test/operation/external_model_test.rb +0 -71
  54. data/test/operation/guard_test.rb +0 -152
  55. data/test/operation/policy_test.rb +0 -97
  56. data/test/operation/reject_test.rb +0 -34
  57. data/test/operation/resolver_test.rb +0 -83
  58. data/test/operation_test.rb +0 -275
  59. data/test/representer_test.rb +0 -238
  60. data/test/rollback_test.rb +0 -47
@@ -1,104 +0,0 @@
1
- require "test_helper"
2
-
3
- # ::contract builds Reform::Form class
4
- class DslContractTest < MiniTest::Spec
5
- module SongProcess
6
- def process(params)
7
- validate(params, @model = OpenStruct.new)
8
- end
9
- end
10
-
11
- describe "inheritance across operations" do
12
- # inheritance
13
- class Operation < Trailblazer::Operation
14
- contract do
15
- property :title
16
- property :band
17
- end
18
-
19
- class JSON < self
20
- contract do # inherit Contract
21
- property :genre, validates: {presence: true}
22
- property :band, virtual: true
23
- end
24
- end
25
- end
26
-
27
- # inherits subclassed Contract.
28
- it { Operation.contract_class.wont_equal Operation::JSON.contract_class }
29
-
30
- it do
31
- form = Operation.contract_class.new(OpenStruct.new)
32
- form.validate({})#.must_equal true
33
- form.errors.to_s.must_equal "{}"
34
-
35
- form = Operation::JSON.contract_class.new(OpenStruct.new)
36
- form.validate({})#.must_equal true
37
- form.errors.to_s.must_equal "{:genre=>[\"can't be blank\"]}"
38
- end
39
-
40
- # allows overriding options
41
- it do
42
- form = Operation::JSON.contract_class.new(song = OpenStruct.new)
43
- form.validate({genre: "Punkrock", band: "Osker"}).must_equal true
44
- form.sync
45
-
46
- song.genre.must_equal "Punkrock"
47
- song.band.must_equal nil
48
- end
49
- end
50
-
51
- describe "Op.contract" do
52
- it { Operation.contract.must_equal Operation.contract_class }
53
- end
54
-
55
- describe "Op.contract CommentForm" do
56
- class SongForm < Reform::Form
57
- property :songTitle, validates: {presence: true}
58
- end
59
-
60
- class OpWithExternalContract < Trailblazer::Operation
61
- contract SongForm
62
- include SongProcess
63
- end
64
-
65
- it { OpWithExternalContract.("songTitle"=> "Monsterparty").contract.songTitle.must_equal "Monsterparty" }
66
- end
67
-
68
- describe "Op.contract CommentForm do .. end" do
69
- class DifferentSongForm < Reform::Form
70
- property :songTitle, validates: {presence: true}
71
- end
72
-
73
- class OpNotExtendingContract < Trailblazer::Operation
74
- contract DifferentSongForm
75
- include SongProcess
76
- end
77
-
78
- class OpExtendingContract < Trailblazer::Operation
79
- contract DifferentSongForm do
80
- property :genre
81
- end
82
- include SongProcess
83
- end
84
-
85
- # this operation copies DifferentSongForm and shouldn't have `genre`.
86
- it do
87
- contract = OpNotExtendingContract.("songTitle"=>"Monsterparty", "genre"=>"Punk").contract
88
- contract.songTitle.must_equal "Monsterparty"
89
- assert_raises(NoMethodError) { contract.genre }
90
- end
91
-
92
- # this operation copies DifferentSongForm and extends it with the property `genre`.
93
- it do
94
- contract = OpExtendingContract.("songTitle"=>"Monsterparty", "genre"=>"Punk").contract
95
- contract.songTitle.must_equal "Monsterparty"
96
- contract.genre.must_equal "Punk"
97
- end
98
-
99
- # of course, the original contract wasn't modified, either.
100
- it do
101
- assert_raises(NoMethodError) { DifferentSongForm.new(OpenStruct.new).genre }
102
- end
103
- end
104
- end
@@ -1,142 +0,0 @@
1
- require "test_helper"
2
- require "representable/json"
3
- require "trailblazer/operation/representer"
4
-
5
- class DslRepresenterTest < MiniTest::Spec
6
- module SongProcess
7
- def process(params)
8
- @model = OpenStruct.new(params)
9
- end
10
-
11
- def represented
12
- model
13
- end
14
- end
15
-
16
- describe "inheritance across operations" do
17
- class Operation < Trailblazer::Operation
18
- include Representer
19
- include SongProcess
20
-
21
- representer do
22
- property :title
23
- end
24
-
25
- class JSON < self
26
- representer do
27
- property :band
28
- end
29
- end
30
- end
31
-
32
- it { Operation.(title: "Nothing To Lose", band: "Gary Moore").to_json.must_equal %{{"title":"Nothing To Lose"}} }
33
- # only the subclass must have the `band` field, even though it's set in the original operation.
34
- it { Operation::JSON.(title: "Nothing To Lose", band: "Gary Moore").to_json.must_equal %{{"title":"Nothing To Lose","band":"Gary Moore"}} }
35
- end
36
-
37
- describe "Op.representer" do
38
- it { Operation.representer.must_equal Operation.representer_class }
39
- end
40
-
41
- describe "Op.representer CommentRepresenter" do
42
- class SongRepresenter < Representable::Decorator
43
- include Representable::JSON
44
- property :songTitle
45
- end
46
-
47
- class OpWithExternalRepresenter < Trailblazer::Operation
48
- include Representer
49
- include SongProcess
50
- representer SongRepresenter
51
- end
52
-
53
- it { OpWithExternalRepresenter.("songTitle"=>"Listen To Your Heartbeat").to_json.must_equal %{{"songTitle":"Listen To Your Heartbeat"}} }
54
- end
55
-
56
- describe "Op.representer CommentRepresenter do .. end" do
57
- class HitRepresenter < Representable::Decorator
58
- include Representable::JSON
59
- property :title
60
- end
61
-
62
- class OpNotExtendingRepresenter < Trailblazer::Operation
63
- include Representer
64
- include SongProcess
65
- representer HitRepresenter
66
- end
67
-
68
- class OpExtendingRepresenter < Trailblazer::Operation
69
- include Representer
70
- include SongProcess
71
- representer HitRepresenter do
72
- property :genre
73
- end
74
- end
75
-
76
- # this operation copies HitRepresenter and shouldn't have `genre`.
77
- it do
78
- OpNotExtendingRepresenter.("title"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"title":"Monsterparty"}}
79
- end
80
-
81
- # # this operation copies HitRepresenter and extends it with the property `genre`.
82
- it do
83
- OpExtendingRepresenter.("title"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"title":"Monsterparty","genre":"Punk"}}
84
- end
85
-
86
- # # of course, the original representer wasn't modified, either.
87
- it do
88
- HitRepresenter.new(OpenStruct.new(title: "Monsterparty", genre: "Punk")).to_json.must_equal %{{"title":"Monsterparty"}}
89
- end
90
- end
91
-
92
- describe "Op.representer (inferring)" do
93
- class OpWithContract < Trailblazer::Operation
94
- include Representer
95
- include SongProcess
96
-
97
- contract do
98
- property :songTitle
99
- end
100
- end
101
-
102
- class OpWithContract2 < Trailblazer::Operation
103
- include Representer
104
- include SongProcess
105
-
106
- contract OpWithContract.contract
107
- representer do
108
- property :genre
109
- end
110
- end
111
-
112
- it { OpWithContract.("songTitle"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"songTitle":"Monsterparty"}} }
113
- # this representer block extends the inferred from contract.
114
- it { OpWithContract2.("songTitle"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"songTitle":"Monsterparty","genre":"Punk"}} }
115
- end
116
-
117
- describe "Op.representer_class" do
118
- class PlayRepresenter < Representable::Decorator
119
- include Representable::JSON
120
- property :title
121
- end
122
-
123
- class OpSettingRepresenter < Trailblazer::Operation
124
- include Representer
125
- include SongProcess
126
- self.representer_class= PlayRepresenter
127
- end
128
-
129
- class OpExtendRepresenter < Trailblazer::Operation
130
- include Representer
131
- include SongProcess
132
- self.representer_class= PlayRepresenter
133
- representer do
134
- property :genre
135
- end
136
- end
137
-
138
- # both operations produce the same as the representer is shared, not copied.
139
- it { OpSettingRepresenter.("title"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"title":"Monsterparty","genre":"Punk"}} }
140
- it { OpExtendRepresenter.("title"=>"Monsterparty", "genre"=>"Punk").to_json.must_equal %{{"title":"Monsterparty","genre":"Punk"}} }
141
- end
142
- end
@@ -1,71 +0,0 @@
1
- require "test_helper"
2
- require "trailblazer/operation/model/external"
3
-
4
- class ExternalModelTest < MiniTest::Spec
5
- Song = Struct.new(:title, :id) do
6
- class << self
7
- attr_accessor :find_result # TODO: eventually, replace with AR test.
8
- attr_accessor :all_records
9
-
10
- def find(id)
11
- find_result.tap do |song|
12
- song.id = id
13
- end
14
- end
15
- end
16
- end # FIXME: use from CrudTest.
17
-
18
-
19
-
20
-
21
- class Bla < Trailblazer::Operation
22
- include Model::External
23
- model Song, :update
24
-
25
- def process(params)
26
- end
27
- end
28
-
29
- let (:song) { Song.new("Numbers") }
30
-
31
- before do
32
- Song.find_result = song
33
- end
34
-
35
- # ::model!
36
- it do
37
- Bla.model!(id: 1).must_equal song
38
- song.id.must_equal 1
39
- end
40
-
41
- # call style.
42
- it do
43
- Bla.(id: 2).model.must_equal song
44
- song.id.must_equal 2
45
- end
46
-
47
- # #present.
48
- it do
49
- Bla.present({}).model.must_equal song
50
- end
51
-
52
-
53
- class OpWithBuilder < Bla
54
- class A < self
55
- end
56
-
57
- builds -> (model, params) do
58
- return A if model.id == 1 and params[:user] == 2
59
- end
60
- end
61
-
62
- describe "::builds args" do
63
- it do
64
- OpWithBuilder.(id: 1, user: "different").must_be_instance_of OpWithBuilder
65
- end
66
-
67
- it do
68
- OpWithBuilder.(id: 1, user: 2).must_be_instance_of OpWithBuilder::A
69
- end
70
- end
71
- end
@@ -1,152 +0,0 @@
1
- require "test_helper"
2
- require "trailblazer/operation/policy"
3
-
4
- class OpPolicyGuardTest < MiniTest::Spec
5
- Song = Struct.new(:name)
6
-
7
- class Create < Trailblazer::Operation
8
- include Policy::Guard
9
-
10
- def model!(*)
11
- Song.new
12
- end
13
-
14
- policy do |params|
15
- model.is_a?(Song) and params[:valid]
16
- end
17
-
18
- def process(*)
19
- end
20
- end
21
-
22
- # valid.
23
- it do
24
- op = Create.(valid: true)
25
- end
26
-
27
- # invalid.
28
- it do
29
- assert_raises Trailblazer::NotAuthorizedError do
30
- op = Create.(valid: false)
31
- end
32
- end
33
-
34
-
35
- describe "inheritance" do
36
- class Update < Create
37
- policy do |params|
38
- params[:valid] == "correct"
39
- end
40
- end
41
-
42
- class Delete < Create
43
- end
44
-
45
- it do
46
- Create.(valid: true).wont_equal nil
47
- Delete.(valid: true).wont_equal nil
48
- Update.(valid: "correct").wont_equal nil
49
- end
50
- end
51
-
52
-
53
- describe "no policy defined, but included" do
54
- class Show < Trailblazer::Operation
55
- include Policy::Guard
56
-
57
- def process(*)
58
- end
59
- end
60
-
61
- it { Show.({}).wont_equal nil }
62
- end
63
-
64
-
65
- describe "#params!" do
66
- class Index < Trailblazer::Operation
67
- include Policy::Guard
68
-
69
- # make sure the guard receives the correct params.
70
- policy { |params| params[:valid] == "true" }
71
-
72
- def params!(params)
73
- { valid: params }
74
- end
75
-
76
- def process(*)
77
- end
78
- end
79
-
80
- it { Index.("true").wont_equal nil }
81
- it { assert_raises(Trailblazer::NotAuthorizedError) { Index.(false).wont_equal nil } }
82
- end
83
-
84
- describe "with Callable" do
85
- class Find < Trailblazer::Operation
86
- include Policy::Guard
87
-
88
- class Guardian
89
- include Uber::Callable
90
-
91
- def call(context, params)
92
- params == "true"
93
- end
94
- end
95
-
96
- policy Guardian.new
97
-
98
- def process(*)
99
- end
100
- end
101
-
102
- it { Find.("true").wont_equal nil }
103
- it { assert_raises(Trailblazer::NotAuthorizedError) { Find.(false).wont_equal nil } }
104
- end
105
-
106
- describe "with Proc" do
107
- class Follow < Trailblazer::Operation
108
- include Policy::Guard
109
-
110
- policy ->(params) { params == "true" } # TODO: is this really executed in op context?
111
-
112
- def process(*)
113
- end
114
- end
115
-
116
- it { Follow.("true").wont_equal nil }
117
- it { assert_raises(Trailblazer::NotAuthorizedError) { Follow.(false).wont_equal nil } }
118
- end
119
- end
120
-
121
- class OpBuilderDenyTest < MiniTest::Spec
122
- Song = Struct.new(:name)
123
-
124
- class Create < Trailblazer::Operation
125
- include Deny
126
-
127
- builds do |params|
128
- deny! unless params[:valid]
129
- end
130
-
131
- def process(params)
132
- end
133
- end
134
-
135
- class Update < Create
136
- builds -> (params) do
137
- deny! unless params[:valid]
138
- end
139
- end
140
-
141
- # valid.
142
- it do
143
- op = Create.(valid: true)
144
- end
145
-
146
- # invalid.
147
- it do
148
- assert_raises Trailblazer::NotAuthorizedError do
149
- op = Create.(valid: false)
150
- end
151
- end
152
- end
@@ -1,97 +0,0 @@
1
- require "test_helper"
2
- require "trailblazer/operation/policy"
3
-
4
-
5
- class OpPunditPolicyTest < MiniTest::Spec
6
- Song = Struct.new(:name)
7
- User = Struct.new(:name)
8
-
9
- class BlaPolicy
10
- def initialize(user, song)
11
- @user = user
12
- @song = song
13
- end
14
-
15
- def create?
16
- @user.is_a?(User) and @song.is_a?(Song)
17
- end
18
-
19
- def edit?
20
- "yepp"
21
- end
22
- end
23
-
24
- class BlaOperation < Trailblazer::Operation
25
- include Policy
26
- policy BlaPolicy, :create?
27
-
28
- def model!(*)
29
- Song.new
30
- end
31
-
32
- def process(*)
33
- end
34
- end
35
-
36
- # valid.
37
- it do
38
- op = BlaOperation.({current_user: User.new})
39
-
40
- # #policy provides the Policy instance.
41
- op.policy.edit?.must_equal "yepp"
42
- end
43
-
44
- # invalid.
45
- it do
46
- assert_raises Trailblazer::NotAuthorizedError do
47
- op = BlaOperation.({current_user: nil})
48
- end
49
- end
50
-
51
-
52
- # no policy set
53
- class NoPolicyOperation < Trailblazer::Operation
54
- include Policy
55
- # no policy.
56
-
57
- def process(*)
58
- @model = Song.new
59
- end
60
-
61
- class Delete < self
62
- end
63
-
64
-
65
- class LocalPolicy
66
- def initialize(user, song)
67
- @user = user
68
- @song = song
69
- end
70
-
71
- def update?; false end
72
- end
73
-
74
- class Update < self
75
- policy LocalPolicy, :update?
76
- end
77
- end
78
-
79
- # valid.
80
- it do
81
- op = NoPolicyOperation.({})
82
- op.model.must_be_instance_of Song
83
- end
84
-
85
- # inherited without config works.
86
- it do
87
- op = NoPolicyOperation::Delete.({})
88
- op.model.must_be_instance_of Song
89
- end
90
-
91
- # inherited can override.
92
- it do
93
- assert_raises Trailblazer::NotAuthorizedError do
94
- op = NoPolicyOperation::Update.({})
95
- end
96
- end
97
- end
@@ -1,34 +0,0 @@
1
- require "test_helper"
2
-
3
- class OperationRejectTest < MiniTest::Spec
4
- class Operation < Trailblazer::Operation
5
- def process(params)
6
- invalid! if params == false
7
- end
8
- end
9
-
10
- it do
11
- run = nil
12
- Operation.run(true) { run = true }
13
- run.must_equal true
14
- end
15
-
16
- it do
17
- run = nil
18
- Operation.run(false) { run = true }
19
- run.must_equal nil
20
- end
21
-
22
- it do
23
- run = nil
24
- op = Operation.reject(true) { run = true }
25
- run.must_equal nil
26
- op.must_be_instance_of Operation
27
- end
28
-
29
- it do
30
- run = nil
31
- Operation.reject(false) { run = true }
32
- run.must_equal true
33
- end
34
- end
@@ -1,83 +0,0 @@
1
- require "test_helper"
2
- require "trailblazer/operation/resolver"
3
-
4
- class ResolverTest < MiniTest::Spec
5
- Song = Struct.new(:title)
6
- User = Struct.new(:name)
7
-
8
- class MyKitchenRules
9
- def initialize(user, song)
10
- @user = user
11
- @song = song
12
- end
13
-
14
- def create?
15
- @user.is_a?(User) and @song.is_a?(Song)
16
- end
17
-
18
- def admin?
19
- @user && @user.name == "admin" && @song.is_a?(Song)
20
- end
21
-
22
- def true?
23
- true
24
- end
25
- end
26
-
27
- class Create < Trailblazer::Operation
28
- include Resolver
29
- model Song, :create
30
- policy MyKitchenRules, :create?
31
-
32
- builds-> (model, policy, params) do
33
- return ForGaryMoore if model.title == "Friday On My Mind"
34
- return Admin if policy.admin?
35
- return SignedIn if params[:current_user] && params[:current_user].name
36
- end
37
-
38
- def self.model!(params)
39
- Song.new(params[:title])
40
- end
41
-
42
- def process(*)
43
- end
44
-
45
- class Admin < self
46
- end
47
- class SignedIn < self
48
- end
49
- end
50
-
51
- # valid.
52
- it { Create.({current_user: User.new}).must_be_instance_of Create }
53
- it { Create.({current_user: User.new("admin")}).must_be_instance_of Create::Admin }
54
- it { Create.({current_user: User.new("kenneth")}).must_be_instance_of Create::SignedIn }
55
-
56
- # invalid.
57
- it do
58
- assert_raises Trailblazer::NotAuthorizedError do
59
- Create.({})
60
- end
61
- end
62
-
63
-
64
- describe "passes policy into operation" do
65
- class Update < Trailblazer::Operation
66
- include Resolver
67
- model Song, :create
68
- policy MyKitchenRules, :true?
69
-
70
- builds-> (model, policy, params) do
71
- policy.instance_eval { def whoami; "me!" end }
72
- nil
73
- end
74
-
75
- def process(*)
76
- end
77
- end
78
-
79
- it do
80
- Update.({}).policy.whoami.must_equal "me!"
81
- end
82
- end
83
- end