shamu 0.0.9 → 0.0.11

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/.ruby-version +1 -1
  3. data/Gemfile +5 -3
  4. data/bin/rake +17 -0
  5. data/bin/rspec +17 -0
  6. data/lib/shamu/attributes.rb +3 -1
  7. data/lib/shamu/events/active_record/migration.rb +6 -6
  8. data/lib/shamu/json_api/builder_methods/identifier.rb +18 -4
  9. data/lib/shamu/json_api/context.rb +3 -1
  10. data/lib/shamu/json_api/error.rb +7 -1
  11. data/lib/shamu/json_api/presenter.rb +23 -1
  12. data/lib/shamu/json_api/rails/controller.rb +195 -62
  13. data/lib/shamu/locale/en.yml +3 -1
  14. data/lib/shamu/rails/controller.rb +5 -2
  15. data/lib/shamu/rails/entity.rb +29 -15
  16. data/lib/shamu/rails/railtie.rb +12 -7
  17. data/lib/shamu/services/active_record.rb +2 -2
  18. data/lib/shamu/services/active_record_crud.rb +36 -38
  19. data/lib/shamu/services/error.rb +11 -1
  20. data/lib/shamu/services/lazy_transform.rb +9 -4
  21. data/lib/shamu/services/request_support.rb +5 -2
  22. data/lib/shamu/services/result.rb +40 -7
  23. data/lib/shamu/services/service.rb +17 -8
  24. data/lib/shamu/services/service_call_failed_error.rb +4 -0
  25. data/lib/shamu/version.rb +2 -2
  26. data/shamu.gemspec +4 -4
  27. data/spec/lib/shamu/json_api/builder_methods/identifier_spec.rb +45 -0
  28. data/spec/lib/shamu/json_api/rails/controller_spec.rb +141 -7
  29. data/spec/lib/shamu/json_api/rails/responder_spec.rb +9 -9
  30. data/spec/lib/shamu/rails/controller_spec.rb +4 -4
  31. data/spec/lib/shamu/rails/entity_spec.rb +34 -16
  32. data/spec/lib/shamu/rails/features_spec.rb +6 -6
  33. data/spec/lib/shamu/services/active_record_crud_spec.rb +12 -7
  34. data/spec/lib/shamu/services/lazy_transform_spec.rb +23 -14
  35. data/spec/lib/shamu/services/request_support_spec.rb +15 -1
  36. data/spec/lib/shamu/services/result_spec.rb +37 -1
  37. data/spec/lib/shamu/services/service_spec.rb +25 -14
  38. data/spec/spec_helper.rb +1 -1
  39. metadata +23 -17
@@ -16,6 +16,8 @@ module JsonApiControllerSpec
16
16
  def present
17
17
  builder.identifier :resource, resource.id
18
18
  builder.attribute name: resource.name
19
+
20
+ builder.link :self, "somewhere"
19
21
  end
20
22
  end
21
23
  end
@@ -25,12 +27,38 @@ end
25
27
  describe JsonApiControllerSpec::ResourcesController, type: :controller do
26
28
  controller JsonApiControllerSpec::ResourcesController do
27
29
  def show
28
- resource = resources.first
29
- render json: json_resource( resource )
30
+ render_resource resources.first
30
31
  end
31
32
 
32
33
  def index
33
- render json: json_collection( resources )
34
+ render_collection resources
35
+ end
36
+
37
+ def create
38
+ result = Shamu::Services::Result.new resources.first
39
+ render_result result
40
+ end
41
+
42
+ def update
43
+ result = Shamu::Services::Result.new resources.first
44
+ render_result result
45
+ end
46
+
47
+ def destroy
48
+ result = Shamu::Services::Result.new resources.first
49
+ render_result result
50
+ end
51
+
52
+ def invalid
53
+ result = Shamu::Services::Result.new
54
+ result.errors.add :base, "nope"
55
+
56
+ render_result result
57
+ end
58
+
59
+ def no_entity
60
+ result = Shamu::Services::Result.new
61
+ render_result result
34
62
  end
35
63
 
36
64
  def nope
@@ -51,7 +79,7 @@ describe JsonApiControllerSpec::ResourcesController, type: :controller do
51
79
 
52
80
  describe "#json_resource" do
53
81
  subject do
54
- get :show, id: 1, format: :json
82
+ get :show, params: { id: 1, format: :json }
55
83
  JSON.parse( response.body )
56
84
  end
57
85
 
@@ -59,19 +87,70 @@ describe JsonApiControllerSpec::ResourcesController, type: :controller do
59
87
  it { is_expected.to include "data" => hash_including( "attributes" => kind_of( Hash ) ) }
60
88
 
61
89
  it "reflects fields param to meta" do
62
- get :show, id: 1, fields: { people: "id,name" }
90
+ get :show, params: { id: 1, fields: { people: "id,name" } }
63
91
  json = JSON.parse( response.body )
64
92
  expect( json ).to include "meta" => hash_including( "fields" )
65
93
  end
66
94
 
67
95
  it "fails when 'include' paramter is given" do
68
- get :show, id: 1, include: :contact
96
+ get :show, params: { id: 1, include: :contact }
69
97
 
70
98
  expect( response.code ).to eq "400"
71
99
  expect( response.body ).to include "include"
72
100
  end
73
101
  end
74
102
 
103
+ describe "#render_resource" do
104
+ it "adds Location header" do
105
+ get :show, params: { id: 1 }
106
+ expect( response.headers ).to include 'Location'
107
+ end
108
+ end
109
+
110
+ describe "#render_result" do
111
+ it "returns status created on #create" do
112
+ post :create, params: { name: 'example' }
113
+ expect( response.status ).to eq 201
114
+ expect( response.body ).to include "data"
115
+ end
116
+
117
+ it "returns status ok on #update" do
118
+ put :update, params: { id: 1 }
119
+
120
+ expect( response.status ).to eq 200
121
+ expect( response.body ).to include "data"
122
+ end
123
+
124
+ it "returns status no_content on delete" do
125
+ delete :destroy, params: { id: 1 }
126
+
127
+ expect( response.status ).to eq 204
128
+ end
129
+
130
+ it "returns status bad_request on error" do
131
+ routes.draw do
132
+ post "invalid" => "json_api_controller_spec/resources#invalid"
133
+ end
134
+
135
+ post :invalid
136
+
137
+ expect( response.status ).to be 422
138
+ expect( response.body ).to include "errors"
139
+ end
140
+
141
+ it "returns status no_content on success without entity" do
142
+ routes.draw do
143
+ post "no_entity" => "json_api_controller_spec/resources#no_entity"
144
+ end
145
+
146
+ post :no_entity
147
+
148
+ expect( response.status ).to eq 204
149
+ expect( response.body ).to be_blank
150
+ end
151
+
152
+ end
153
+
75
154
  describe "#json_collection" do
76
155
  before( :each ) do
77
156
  allow( controller.resources ).to receive( :current_page ).and_return 1
@@ -115,4 +194,59 @@ describe JsonApiControllerSpec::ResourcesController, type: :controller do
115
194
  end
116
195
  end
117
196
 
118
- end
197
+ describe "#request_params" do
198
+ let( :body ) do
199
+ {
200
+ data: {
201
+ attributes: {
202
+ name: "Example"
203
+ },
204
+ relationships: {
205
+ book: {
206
+ data: { type: "book", id: "5", attributes: { title: "Bibliography" } }
207
+ },
208
+ stores: {
209
+ data: [
210
+ { "type": "store", id: "56", attributes: { title: "First Street" } }
211
+ ]
212
+ }
213
+ }
214
+ }
215
+ }
216
+ end
217
+
218
+ before( :each ) do
219
+ expect( request ).to receive( :body ) { StringIO.new( body.to_json ) }
220
+ end
221
+
222
+ it "maps data attributes" do
223
+ expect( controller.send( :request_params, :example ) ).to include name: "Example"
224
+ end
225
+
226
+ it "maps relationship ids to root attributes" do
227
+ expect( controller.send( :request_params, :example ) ).to include book_id: "5"
228
+ expect( controller.send( :request_params, :example ) ).to include store_ids: [ "56" ]
229
+ end
230
+
231
+ it "maps relationship data to root attributes" do
232
+ expect( controller.send( :request_params, :example ) ).to include book: { id: "5", title: "Bibliography" }
233
+ expect( controller.send( :request_params, :example ) ).to include stores: [ { id: "56", title: "First Street" } ]
234
+ end
235
+
236
+ it "maps data id if available" do
237
+ body[ :data ][ :id ] = "73"
238
+
239
+ expect( controller.send( :request_params, :example ) ).to include id: "73"
240
+ end
241
+
242
+ it "maps id request params if available" do
243
+ allow( controller.request ).to receive( :params ).and_return( { id: "90" }.with_indifferent_access )
244
+
245
+ expect( controller.send( :request_params, :example ) ).to include id: "90"
246
+ end
247
+
248
+ it "returns relationship directly if matching param key" do
249
+ expect( controller.send( :request_params, :book ) ).to include id: "5", title: "Bibliography"
250
+ end
251
+ end
252
+ end
@@ -60,7 +60,7 @@ describe JsonApiResponderSpec::ResourcesController, type: :controller do
60
60
 
61
61
  let( :resource ) { JsonApiResponderSpec::Resource.new( id: 562, name: "Example" ) }
62
62
  let( :resources ) { [ resource ] }
63
- let( :body ) { JSON.load( response.body, nil, symbolize_names: true ) }
63
+ let( :body ) { JSON.parse( response.body, symbolize_names: true ) }
64
64
 
65
65
  before( :each ) do
66
66
  allow( controller ).to receive( :_routes ).and_return @routes
@@ -69,12 +69,12 @@ describe JsonApiResponderSpec::ResourcesController, type: :controller do
69
69
 
70
70
  describe "#show" do
71
71
  it "has JSON content_type" do
72
- get :show, id: 1
72
+ get :show, params: { id: 1 }
73
73
  expect( response.content_type ).to eq Shamu::JsonApi::MIME_TYPE
74
74
  end
75
75
 
76
76
  it "renders JSON API response" do
77
- get :show, id: 1
77
+ get :show, params: { id: 1 }
78
78
  expect( body ).to include data: hash_including( id: resource.id.to_s )
79
79
  end
80
80
 
@@ -84,7 +84,7 @@ describe JsonApiResponderSpec::ResourcesController, type: :controller do
84
84
  allow( resource ).to receive( :errors ).and_return errors
85
85
  allow( resource ).to receive( :valid? ).and_return false
86
86
 
87
- get :show, id: 1
87
+ get :show, params: { id: 1 }
88
88
  expect( body ).to include :errors
89
89
  end
90
90
  end
@@ -113,22 +113,22 @@ describe JsonApiResponderSpec::ResourcesController, type: :controller do
113
113
 
114
114
  describe "#update" do
115
115
  it "has JSON content_type" do
116
- put :update, id: 1
116
+ put :update, params: { id: 1 }
117
117
  expect( response.content_type ).to eq Shamu::JsonApi::MIME_TYPE
118
118
  end
119
119
 
120
120
  it "includes location header" do
121
- post :update, id: 1
121
+ post :update, params: { id: 1 }
122
122
  expect( response.location ).to be_present
123
123
  end
124
124
 
125
125
  it "returns 200 status code" do
126
- put :update, id: 1
126
+ put :update, params: { id: 1 }
127
127
  expect( response.status ).to eq 200
128
128
  end
129
129
 
130
130
  it "includes the json entity" do
131
- put :update, id: 1
131
+ put :update, params: { id: 1 }
132
132
  expect( body ).to include data: hash_including( id: resource.id.to_s )
133
133
  end
134
134
  end
@@ -150,4 +150,4 @@ describe JsonApiResponderSpec::ResourcesController, type: :controller do
150
150
  end
151
151
  end
152
152
 
153
- end
153
+ end
@@ -18,7 +18,7 @@ describe Shamu::Rails::Controller, type: :controller do
18
18
  public :services, :secure_services, :permit?
19
19
 
20
20
  def show
21
- render text: ""
21
+ render plain: ""
22
22
  end
23
23
  end
24
24
 
@@ -47,10 +47,10 @@ describe Shamu::Rails::Controller, type: :controller do
47
47
 
48
48
  expect( controller ).to receive( :show ) do
49
49
  expect( scorpion.fetch( Shamu::Security::Principal ).user_id ).to eq 945
50
- controller.render( text: "" )
50
+ controller.render( plain: "" )
51
51
  end
52
52
 
53
- get :show, id: 5
53
+ get :show, params: { id: 5 }
54
54
  end
55
55
  end
56
56
 
@@ -71,4 +71,4 @@ describe Shamu::Rails::Controller, type: :controller do
71
71
  expect( controller ).not_to be_permitted_to :read, :something
72
72
  end
73
73
  end
74
- end
74
+ end
@@ -15,8 +15,6 @@ module LoadEntitySpec
15
15
 
16
16
  def authorize!( * )
17
17
  end
18
-
19
-
20
18
  end
21
19
 
22
20
  class ExampleEntity < Shamu::Entities::Entity
@@ -34,20 +32,24 @@ describe Shamu::Rails::Entity, type: :controller do
34
32
  hunt( :example_service, LoadEntitySpec::Service ) { scorpion.new LoadEntitySpec::Service }
35
33
 
36
34
  controller ActionController::Base do
37
- service :example_service, LoadEntitySpec::Service
35
+ service :examples_service, LoadEntitySpec::Service
38
36
  entity LoadEntitySpec::ExampleEntity
39
37
 
40
38
  def show
41
- render text: ""
39
+ render plain: ""
42
40
  end
43
41
 
44
42
  def index
45
- render text: ""
43
+ render plain: ""
46
44
  end
47
45
 
48
46
  def new
47
+ render plan: ""
48
+ end
49
+
50
+ def create
49
51
  example_request
50
- render text: ""
52
+ render plain: ""
51
53
  end
52
54
 
53
55
  end
@@ -62,6 +64,7 @@ describe Shamu::Rails::Entity, type: :controller do
62
64
 
63
65
  it "loads the entity from the service" do
64
66
  expect( example_service ).to receive( :find )
67
+ controller.params[:id] = 1
65
68
  controller.send :example
66
69
  end
67
70
 
@@ -83,7 +86,7 @@ describe Shamu::Rails::Entity, type: :controller do
83
86
 
84
87
  it "loads the entity before the request" do
85
88
  expect( controller ).to receive( :example ).and_call_original
86
- get :show, id: 1
89
+ get :show, params: { id: 1 }
87
90
  end
88
91
 
89
92
  it "invokes list for index types" do
@@ -94,7 +97,13 @@ describe Shamu::Rails::Entity, type: :controller do
94
97
  it "authorizes action for entity request" do
95
98
  expect( example_service ).to receive( :authorize! )
96
99
 
97
- get :new
100
+ post :create
101
+ end
102
+
103
+ it "doesn't load entity on create actions" do
104
+ expect( controller ).not_to receive( :example )
105
+
106
+ post :create
98
107
  end
99
108
 
100
109
  context "only some actions" do
@@ -103,21 +112,26 @@ describe Shamu::Rails::Entity, type: :controller do
103
112
  entity LoadEntitySpec::ExampleEntity, only: :show
104
113
 
105
114
  def show
106
- render text: ""
115
+ render plain: ""
107
116
  end
108
117
 
109
118
  def new
110
- render text: ""
119
+ render plain: ""
120
+ end
121
+
122
+ def create
123
+ render plain: ""
111
124
  end
112
125
  end
113
126
 
114
127
  it "loads on show" do
115
128
  expect( controller ).to receive( :example )
116
- get :show, id: 1
129
+ get :show, params: { id: 1 }
117
130
  end
118
131
 
119
132
  it "doesn't load on new" do
120
133
  expect( controller ).not_to receive( :example )
134
+ post :create
121
135
  get :new
122
136
  end
123
137
  end
@@ -128,23 +142,27 @@ describe Shamu::Rails::Entity, type: :controller do
128
142
  entity LoadEntitySpec::ExampleEntity, except: :show
129
143
 
130
144
  def show
131
- render text: ""
145
+ render plain: ""
132
146
  end
133
147
 
134
148
  def new
135
- render text: ""
149
+ render plain: ""
150
+ end
151
+
152
+ def create
153
+ render plain: ""
136
154
  end
137
155
  end
138
156
 
139
157
  it "loads on show" do
140
158
  expect( controller ).not_to receive( :example )
141
- get :show, id: 1
159
+ get :show, params: { id: 1 }
142
160
  end
143
161
 
144
162
  it "doesn't load on new" do
145
- expect( controller ).to receive( :example )
163
+ expect( controller ).not_to receive( :example )
146
164
  get :new
147
165
  end
148
166
  end
149
167
 
150
- end
168
+ end
@@ -6,7 +6,7 @@ describe Shamu::Rails::Features, type: :controller do
6
6
  public :feature_enabled?
7
7
 
8
8
  def show
9
- render text: ""
9
+ render plain: ""
10
10
  end
11
11
  end
12
12
 
@@ -22,10 +22,10 @@ describe Shamu::Rails::Features, type: :controller do
22
22
 
23
23
  expect( controller ).to receive( :show ) do
24
24
  expect( controller.feature_enabled?( "shopping/nux" ) ).to be_truthy
25
- controller.render text: ""
25
+ controller.render plain: ""
26
26
  end
27
27
 
28
- get :show, id: 1
28
+ get :show, params: { id: 1 }
29
29
  end
30
30
 
31
31
  it "allows toggles to be overridden by query param" do
@@ -36,10 +36,10 @@ describe Shamu::Rails::Features, type: :controller do
36
36
 
37
37
  expect( controller ).to receive( :show ) do
38
38
  expect( controller.feature_enabled?( "shopping/discounts" ) ).to be_truthy
39
- controller.render text: ""
39
+ controller.render plain: ""
40
40
  end
41
41
 
42
- get :show, id: 1, Shamu::Features::EnvStore::RACK_PARAMS_KEY => override
42
+ get :show, params: { id: 1, Shamu::Features::EnvStore::RACK_PARAMS_KEY => override }
43
43
  end
44
44
 
45
- end
45
+ end
@@ -65,8 +65,8 @@ describe Shamu::Services::ActiveRecordCrud do
65
65
  expect( klass.model_class ).to eq ActiveRecordSpec::Favorite
66
66
  end
67
67
 
68
- it "defines a build_entity method" do
69
- expect( klass.new.respond_to?( :build_entity, true ) ).to be_truthy
68
+ it "defines a build_entities method" do
69
+ expect( klass.new.respond_to?( :build_entities, true ) ).to be_truthy
70
70
  end
71
71
 
72
72
  Shamu::Services::ActiveRecordCrud::DSL_METHODS.each do |method|
@@ -76,7 +76,7 @@ describe Shamu::Services::ActiveRecordCrud do
76
76
  end
77
77
  end
78
78
 
79
- it "takes a block defining #build_entity" do
79
+ it "takes a block defining #build_entities" do
80
80
  expect do |b|
81
81
  yield_klass = Class.new( klass ) do
82
82
  resource( ActiveRecordCrudSpec::FavoriteEntity, ActiveRecordSpec::Favorite, &b )
@@ -223,6 +223,8 @@ describe Shamu::Services::ActiveRecordCrud do
223
223
  end
224
224
 
225
225
  it "calls #authorize!" do
226
+ entity
227
+
226
228
  expect( service ).to receive( :authorize! ).with(
227
229
  :update,
228
230
  kind_of( ActiveRecordCrudSpec::FavoriteEntity ),
@@ -434,7 +436,7 @@ describe Shamu::Services::ActiveRecordCrud do
434
436
  end
435
437
  end
436
438
 
437
- describe ".build_entity" do
439
+ describe ".build_entities" do
438
440
  let!( :entity ) { service.create( name: "Example", label: "Books" ).entity }
439
441
  let( :entity_class ) do
440
442
  Class.new( Shamu::Entities::Entity ) do
@@ -444,9 +446,12 @@ describe Shamu::Services::ActiveRecordCrud do
444
446
  let( :klass ) do
445
447
  ec = entity_class
446
448
  Class.new( super() ) do
447
- build_entity do |record, _ = nil|
448
- scorpion.fetch ec, { record: record }, {}
449
+ build_entities do |records|
450
+ records.map do |record|
451
+ scorpion.fetch ec, { record: record }, {}
452
+ end
449
453
  end
454
+ public :build_entities
450
455
  public :build_entity
451
456
  end
452
457
  end
@@ -457,4 +462,4 @@ describe Shamu::Services::ActiveRecordCrud do
457
462
  end
458
463
 
459
464
  end
460
- end
465
+ end
@@ -3,77 +3,86 @@ require "spec_helper"
3
3
  describe Shamu::Services::LazyTransform do
4
4
  let( :source ) { [ 1, 2, 3 ] }
5
5
 
6
+ def transformer( &block )
7
+ ->( records ) {
8
+ records.map do |r|
9
+ yield
10
+ r
11
+ end
12
+ }
13
+ end
14
+
6
15
  it "short-circuits count" do
7
16
  expect do |block|
8
- transformed = Shamu::Services::LazyTransform.new( source, &block )
17
+ transformed = Shamu::Services::LazyTransform.new( source, &transformer( &block ) )
9
18
  expect( transformed.count ).to eq source.count
10
19
  end.not_to yield_control
11
20
  end
12
21
 
13
22
  it "delegates when count has an arg" do
14
23
  expect do |block|
15
- transformed = Shamu::Services::LazyTransform.new( source, &block )
24
+ transformed = Shamu::Services::LazyTransform.new( source, &transformer( &block ) )
16
25
  expect( transformed.count( 1000 ) ).to eq 0
17
26
  end.to yield_control
18
27
  end
19
28
 
20
29
  it "delegates when count has a block given" do
21
30
  expect do |block|
22
- transformed = Shamu::Services::LazyTransform.new( source, &block )
31
+ transformed = Shamu::Services::LazyTransform.new( source, &transformer( &block ) )
23
32
  expect( transformed.count { true } ).to eq source.count
24
33
  end.to yield_control
25
34
  end
26
35
 
27
36
  it "short-circuits first" do
28
37
  expect do |block|
29
- transformed = Shamu::Services::LazyTransform.new( source, &block )
38
+ transformed = Shamu::Services::LazyTransform.new( source, &transformer( &block ) )
30
39
  transformed.first
31
40
  end.to yield_control.once
32
41
  end
33
42
 
34
43
  it "doesn't short-circuit first(n)" do
35
44
  expect do |block|
36
- transformed = Shamu::Services::LazyTransform.new( source, &block )
45
+ transformed = Shamu::Services::LazyTransform.new( source, &transformer( &block ) )
37
46
  transformed.first( 2 )
38
47
  end.to yield_control.exactly(3)
39
48
  end
40
49
 
41
50
  it "short-circuits empty?" do
42
51
  expect do |block|
43
- transformed = Shamu::Services::LazyTransform.new( source, &block )
52
+ transformed = Shamu::Services::LazyTransform.new( source, &transformer( &block ) )
44
53
  expect( transformed ).not_to be_empty
45
54
  end.not_to yield_control
46
55
  end
47
56
 
48
57
  it "short-circuits present?" do
49
58
  expect do |block|
50
- transformed = Shamu::Services::LazyTransform.new( source, &block )
59
+ transformed = Shamu::Services::LazyTransform.new( source, &transformer( &block ) )
51
60
  transformed.present?
52
61
  end.not_to yield_control
53
62
  end
54
63
 
55
64
  it "transforms when enumerated" do
56
65
  expect do |block|
57
- transformed = Shamu::Services::LazyTransform.new( source, &block )
66
+ transformed = Shamu::Services::LazyTransform.new( source, &transformer( &block ) )
58
67
  transformed.to_a
59
68
  end.to yield_control.exactly( 3 )
60
69
  end
61
70
 
62
71
  it "yields transformed values" do
63
- transformed = Shamu::Services::LazyTransform.new( source ) { |v| v * v }
72
+ transformed = Shamu::Services::LazyTransform.new( source ) { |vs| vs.map { |v| v * v } }
64
73
  expect( transformed.to_a ).to eq [ 1, 4, 9 ]
65
74
  end
66
75
 
67
76
  it "short-circuits drop" do
68
77
  expect do |block|
69
- transformed = Shamu::Services::LazyTransform.new( source, &block )
78
+ transformed = Shamu::Services::LazyTransform.new( source, &transformer( &block ) )
70
79
  transformed.drop( 2 ).to_a
71
80
  end.to yield_control.exactly( 1 )
72
81
  end
73
82
 
74
83
  it "uses existing transformed on drop if avaialable" do
75
84
  expect do |block|
76
- transformed = Shamu::Services::LazyTransform.new( source, &block )
85
+ transformed = Shamu::Services::LazyTransform.new( source, &transformer( &block ) )
77
86
  transformed.to_a
78
87
  transformed.drop( 2 ).to_a
79
88
  end.to yield_control.exactly( 3 )
@@ -81,16 +90,16 @@ describe Shamu::Services::LazyTransform do
81
90
 
82
91
  it "short-circuits take" do
83
92
  expect do |block|
84
- transformed = Shamu::Services::LazyTransform.new( source, &block )
93
+ transformed = Shamu::Services::LazyTransform.new( source, &transformer( &block ) )
85
94
  transformed.take( 2 ).to_a
86
95
  end.to yield_control.exactly( 2 )
87
96
  end
88
97
 
89
98
  it "users existing transformed on take if avaialable" do
90
99
  expect do |block|
91
- transformed = Shamu::Services::LazyTransform.new( source, &block )
100
+ transformed = Shamu::Services::LazyTransform.new( source, &transformer( &block ) )
92
101
  transformed.to_a
93
102
  transformed.take( 2 ).to_a
94
103
  end.to yield_control.exactly( 3 )
95
104
  end
96
- end
105
+ end
@@ -49,6 +49,15 @@ module RequestSupportSpec
49
49
  attribute :level, on: :record
50
50
  attribute :amount, on: :record
51
51
  end
52
+
53
+ class UsersService < Shamu::Services::Service
54
+ include Shamu::Services::RequestSupport
55
+ end
56
+
57
+ module UserRequest
58
+ class Change < Shamu::Services::Request
59
+ end
60
+ end
52
61
  end
53
62
 
54
63
  describe Shamu::Services::RequestSupport do
@@ -72,6 +81,11 @@ describe Shamu::Services::RequestSupport do
72
81
  end.request_class( :change )
73
82
  end.to raise_error Shamu::Services::IncompleteSetupError, /Request/
74
83
  end
84
+
85
+ it "singularizes service namespace" do
86
+ service = scorpion.new RequestSupportSpec::UsersService
87
+ expect( service.request_class( :change ) ).to be RequestSupportSpec::UserRequest::Change
88
+ end
75
89
 
76
90
  it "uses common alias fallback new -> create" do
77
91
  expect( service.request_class( :new ) ).to be RequestSupportSpec::Request::Create
@@ -150,4 +164,4 @@ describe Shamu::Services::RequestSupport do
150
164
  expect( request ).to be_a Shamu::Services::Request
151
165
  end
152
166
  end
153
- end
167
+ end