trailblazer 1.1.1 → 2.1.0

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.
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