brainstem 0.2.4 → 0.2.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +15 -0
- data/CHANGELOG.md +7 -0
- data/Gemfile.lock +41 -32
- data/README.md +136 -73
- data/brainstem.gemspec +1 -1
- data/lib/brainstem.rb +0 -2
- data/lib/brainstem/association_field.rb +6 -0
- data/lib/brainstem/controller_methods.rb +1 -1
- data/lib/brainstem/presenter.rb +12 -4
- data/lib/brainstem/presenter_collection.rb +26 -5
- data/lib/brainstem/test_helpers.rb +81 -0
- data/lib/brainstem/version.rb +1 -1
- data/spec/brainstem/controller_methods_spec.rb +21 -16
- data/spec/brainstem/presenter_collection_spec.rb +205 -172
- data/spec/brainstem/presenter_spec.rb +70 -47
- data/spec/brainstem_spec.rb +5 -5
- data/spec/spec_helpers/db.rb +2 -1
- metadata +13 -38
- data/pkg/brainstem-0.0.2.gem +0 -0
- data/pkg/brainstem-0.2.1.gem +0 -0
- data/pkg/brainstem-0.2.2.gem +0 -0
- data/pkg/brainstem-0.2.gem +0 -0
@@ -108,6 +108,9 @@ module Brainstem
|
|
108
108
|
|
109
109
|
rewrite_keys_as_objects!(struct)
|
110
110
|
|
111
|
+
# Make struct pretty-print when rendered as json, if required.
|
112
|
+
make_pretty_printable!(struct) if options[:params][:pretty] == "true"
|
113
|
+
|
111
114
|
struct
|
112
115
|
end
|
113
116
|
|
@@ -215,9 +218,9 @@ module Brainstem
|
|
215
218
|
filter_lambda = options[:presenter].filters[filter_name][1]
|
216
219
|
|
217
220
|
if filter_lambda
|
218
|
-
scope = filter_lambda.call(scope,
|
221
|
+
scope = filter_lambda.call(scope, arg)
|
219
222
|
else
|
220
|
-
scope = scope.send(filter_name,
|
223
|
+
scope = scope.send(filter_name, arg)
|
221
224
|
end
|
222
225
|
end
|
223
226
|
|
@@ -230,7 +233,7 @@ module Brainstem
|
|
230
233
|
|
231
234
|
(options[:presenter].filters || {}).each do |filter_name, filter|
|
232
235
|
requested = options[:params][filter_name]
|
233
|
-
requested = requested.present? ? requested.to_s : nil
|
236
|
+
requested = requested.is_a?(Array) ? requested : (requested.present? ? requested.to_s : nil)
|
234
237
|
requested = requested == "true" ? true : (requested == "false" ? false : requested)
|
235
238
|
|
236
239
|
filter_options = filter[0]
|
@@ -326,11 +329,11 @@ module Brainstem
|
|
326
329
|
records.tap do |models|
|
327
330
|
association_names_to_preload = includes_hash.values.map {|i| i.method_name }
|
328
331
|
if models.first
|
329
|
-
reflections = models.first.reflections
|
332
|
+
reflections = models.first.class.reflections
|
330
333
|
association_names_to_preload.reject! { |association| !reflections.has_key?(association) }
|
331
334
|
end
|
332
335
|
if association_names_to_preload.any?
|
333
|
-
|
336
|
+
preload(models, association_names_to_preload)
|
334
337
|
Brainstem.logger.info "Eager loaded #{association_names_to_preload.join(", ")}."
|
335
338
|
end
|
336
339
|
end
|
@@ -371,5 +374,23 @@ module Brainstem
|
|
371
374
|
struct[key] = struct[key].inject({}) {|memo, obj| memo[obj[:id] || obj["id"] || "unknown_id"] = obj; memo }
|
372
375
|
end
|
373
376
|
end
|
377
|
+
|
378
|
+
def preload(models, association_names)
|
379
|
+
if Gem.loaded_specs['activerecord'].version >= Gem::Version.create('4.1')
|
380
|
+
ActiveRecord::Associations::Preloader.new.preload(models, association_names)
|
381
|
+
else
|
382
|
+
ActiveRecord::Associations::Preloader.new(models, association_names).run
|
383
|
+
end
|
384
|
+
end
|
385
|
+
|
386
|
+
# Modify a hash so that it is pretty-printed when rendered as JSON. Works
|
387
|
+
# by adding a `to_json` singleton method which is called during rendering.
|
388
|
+
# Uses a copy of the hash to avoid recursion issues.
|
389
|
+
def make_pretty_printable!(struct)
|
390
|
+
struct.define_singleton_method(:to_json) do |options = nil|
|
391
|
+
copy = self.deep_dup
|
392
|
+
JSON.pretty_generate(copy)
|
393
|
+
end
|
394
|
+
end
|
374
395
|
end
|
375
396
|
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
module Brainstem
|
2
|
+
# Helpers for easing the testing of brainstem responses in controller specs
|
3
|
+
#
|
4
|
+
# To use, add the following to you spec_helper file:
|
5
|
+
#
|
6
|
+
# require 'brainstem/test_helpers'
|
7
|
+
# Rspec.configure { |config| config.include Brainstem::TestHelpers, :type => :controller }
|
8
|
+
#
|
9
|
+
module TestHelpers
|
10
|
+
# Use brainstem_data in your controller specs to easily access
|
11
|
+
# Brainstem JSON data payloads and their attributes
|
12
|
+
#
|
13
|
+
# Examples:
|
14
|
+
#
|
15
|
+
# Assume user is the model and name is an attribute
|
16
|
+
#
|
17
|
+
# Selecting an item from a collection by it's id
|
18
|
+
# expect(brainstem_data.users.by_id(235).name).to eq('name')
|
19
|
+
#
|
20
|
+
# Getting an array of all ids of in a collection without map
|
21
|
+
# expect(brainstem_data.users.ids).to include(1)
|
22
|
+
#
|
23
|
+
# Accessing the keys of a collection
|
24
|
+
# expect(brainstem_data.users.first.keys).to =~ %w(id name email address)
|
25
|
+
#
|
26
|
+
# Using standard array methods on a collection
|
27
|
+
# expect(brainstem_data.users.first.name).to eq('name')
|
28
|
+
# expect(brainstem_data.users[2].name).to eq('name')
|
29
|
+
#
|
30
|
+
def brainstem_data
|
31
|
+
BrainstemDataHelper.new(response.body)
|
32
|
+
end
|
33
|
+
|
34
|
+
class BrainstemDataHelper
|
35
|
+
def initialize(response_body)
|
36
|
+
@json = JSON.parse(response_body)
|
37
|
+
end
|
38
|
+
|
39
|
+
def method_missing(name)
|
40
|
+
data = @json[name.to_s].try(:values)
|
41
|
+
BrainstemHelperCollection.new(data) unless data.nil?
|
42
|
+
end
|
43
|
+
|
44
|
+
private
|
45
|
+
|
46
|
+
class BrainstemHelperCollection < Array
|
47
|
+
def initialize(collection)
|
48
|
+
collection.each do |item|
|
49
|
+
self << BrainstemHelperItem.new(item)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def ids
|
54
|
+
map { |item| item.id.to_i }
|
55
|
+
end
|
56
|
+
|
57
|
+
def by_id(id)
|
58
|
+
detect { |item| item.id == id.to_s }
|
59
|
+
end
|
60
|
+
|
61
|
+
def method_missing(name)
|
62
|
+
map { |item| item.send(name.to_s.singularize) }
|
63
|
+
end
|
64
|
+
end
|
65
|
+
|
66
|
+
class BrainstemHelperItem
|
67
|
+
def initialize(data)
|
68
|
+
@data = data
|
69
|
+
end
|
70
|
+
|
71
|
+
def keys
|
72
|
+
@data.keys
|
73
|
+
end
|
74
|
+
|
75
|
+
def method_missing(name)
|
76
|
+
@data[name.to_s]
|
77
|
+
end
|
78
|
+
end
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
data/lib/brainstem/version.rb
CHANGED
@@ -33,48 +33,53 @@ describe Brainstem::ControllerMethods do
|
|
33
33
|
|
34
34
|
it "works with arrays of ActiveRecord objects" do
|
35
35
|
@controller.present_object([Workspace.find(1), Workspace.find(3)])
|
36
|
-
@controller.call_results[:klass].
|
37
|
-
@controller.call_results[:options][:as].
|
38
|
-
@controller.call_results[:block_result].pluck(:id).
|
36
|
+
expect(@controller.call_results[:klass]).to eq(Workspace)
|
37
|
+
expect(@controller.call_results[:options][:as]).to eq("workspaces")
|
38
|
+
expect(@controller.call_results[:block_result].pluck(:id)).to eq([1, 3])
|
39
39
|
end
|
40
40
|
|
41
41
|
it "works with a Relation" do
|
42
42
|
@controller.present_object(Workspace.owned_by(1))
|
43
|
-
@controller.call_results[:klass].
|
44
|
-
@controller.call_results[:options][:as].
|
45
|
-
@controller.call_results[:block_result].pluck(:id).
|
43
|
+
expect(@controller.call_results[:klass]).to eq(Workspace)
|
44
|
+
expect(@controller.call_results[:options][:as]).to eq("workspaces")
|
45
|
+
expect(@controller.call_results[:block_result].pluck(:id)).to eq([1, 2, 3, 4])
|
46
46
|
end
|
47
47
|
|
48
48
|
it "works with singleton objects" do
|
49
49
|
@controller.present_object(Workspace.find(1))
|
50
|
-
@controller.call_results[:klass].
|
51
|
-
@controller.call_results[:options][:as].
|
52
|
-
@controller.call_results[:block_result].pluck(:id).
|
50
|
+
expect(@controller.call_results[:klass]).to eq(Workspace)
|
51
|
+
expect(@controller.call_results[:options][:as]).to eq("workspaces")
|
52
|
+
expect(@controller.call_results[:block_result].pluck(:id)).to eq([1])
|
53
53
|
end
|
54
54
|
|
55
55
|
it "accepts a key map" do
|
56
56
|
@controller.present_object(Workspace.find(1), :key_map => { "Workspace" => "your_workspaces" })
|
57
|
-
@controller.call_results[:klass].
|
58
|
-
@controller.call_results[:options][:as].
|
59
|
-
@controller.call_results[:block_result].pluck(:id).
|
57
|
+
expect(@controller.call_results[:klass]).to eq(Workspace)
|
58
|
+
expect(@controller.call_results[:options][:as]).to eq("your_workspaces")
|
59
|
+
expect(@controller.call_results[:block_result].pluck(:id)).to eq([1])
|
60
60
|
end
|
61
61
|
|
62
62
|
it "passes through the controller params" do
|
63
63
|
@controller.present_object(Workspace.find(1), :key_map => { "Workspace" => "your_workspaces" })
|
64
|
-
@controller.call_results[:options][:params].
|
64
|
+
expect(@controller.call_results[:options][:params]).to eq(@controller.params.merge(:only => '1'))
|
65
65
|
end
|
66
66
|
|
67
67
|
it "passes through supplied options" do
|
68
68
|
@controller.present_object(Workspace.find(1), :foo => :bar)
|
69
|
-
@controller.call_results[:options][:foo].
|
69
|
+
expect(@controller.call_results[:options][:foo]).to eq(:bar)
|
70
70
|
end
|
71
71
|
|
72
72
|
it "adds an only param if there is only one object to present" do
|
73
73
|
@controller.present_object(Workspace.find(1))
|
74
|
-
@controller.call_results[:options][:params][:only].
|
74
|
+
expect(@controller.call_results[:options][:params][:only]).to eq("1")
|
75
75
|
|
76
76
|
@controller.present_object(Workspace.all)
|
77
|
-
@controller.call_results[:options][:params][:only].
|
77
|
+
expect(@controller.call_results[:options][:params][:only]).to be_nil
|
78
|
+
end
|
79
|
+
|
80
|
+
it "passes :apply_default_filters => false to the PresenterCollection so that filters are not applied by default" do
|
81
|
+
@controller.present_object(Workspace.find(1))
|
82
|
+
expect(@controller.call_results[:options][:apply_default_filters]).to eq(false)
|
78
83
|
end
|
79
84
|
end
|
80
85
|
end
|
@@ -22,81 +22,81 @@ describe Brainstem::PresenterCollection do
|
|
22
22
|
end
|
23
23
|
|
24
24
|
it "has a global per_page default" do
|
25
|
-
@presenter_collection.presenting("workspaces") { Workspace.order('id desc') }[:workspaces].length.
|
25
|
+
expect(@presenter_collection.presenting("workspaces") { Workspace.order('id desc') }[:workspaces].length).to eq(2)
|
26
26
|
end
|
27
27
|
|
28
28
|
it "will not accept a per_page less than 1" do
|
29
|
-
@presenter_collection.presenting("workspaces", :params => { :per_page => 0 }) { Workspace.order('id desc') }[:workspaces].length.
|
30
|
-
@presenter_collection.presenting("workspaces", :per_page => 0) { Workspace.order('id desc') }[:workspaces].length.
|
29
|
+
expect(@presenter_collection.presenting("workspaces", :params => { :per_page => 0 }) { Workspace.order('id desc') }[:workspaces].length).to eq(2)
|
30
|
+
expect(@presenter_collection.presenting("workspaces", :per_page => 0) { Workspace.order('id desc') }[:workspaces].length).to eq(2)
|
31
31
|
end
|
32
32
|
|
33
33
|
it "will accept strings" do
|
34
34
|
struct = @presenter_collection.presenting("workspaces", :params => { :per_page => "1", :page => "2" }) { Workspace.order('id desc') }
|
35
|
-
struct[:results].first[:id].
|
35
|
+
expect(struct[:results].first[:id]).to eq(Workspace.order('id desc')[1].id.to_s)
|
36
36
|
end
|
37
37
|
|
38
38
|
it "has a global max_per_page default" do
|
39
|
-
@presenter_collection.presenting("workspaces", :params => { :per_page => 5 }) { Workspace.order('id desc') }[:workspaces].length.
|
39
|
+
expect(@presenter_collection.presenting("workspaces", :params => { :per_page => 5 }) { Workspace.order('id desc') }[:workspaces].length).to eq(3)
|
40
40
|
end
|
41
41
|
|
42
42
|
it "takes a configurable default page size and max page size" do
|
43
|
-
@presenter_collection.presenting("workspaces", :params => { :per_page => 5 }, :max_per_page => 4) { Workspace.order('id desc') }[:workspaces].length.
|
43
|
+
expect(@presenter_collection.presenting("workspaces", :params => { :per_page => 5 }, :max_per_page => 4) { Workspace.order('id desc') }[:workspaces].length).to eq(4)
|
44
44
|
end
|
45
45
|
|
46
46
|
describe "limits and offsets" do
|
47
47
|
context "when only per_page and page are present" do
|
48
48
|
it "honors the user's requested page size and page and returns counts" do
|
49
49
|
result = @presenter_collection.presenting("workspaces", :params => { :per_page => 1, :page => 2 }) { Workspace.order('id desc') }[:results]
|
50
|
-
result.length.
|
51
|
-
result.first[:id].
|
50
|
+
expect(result.length).to eq(1)
|
51
|
+
expect(result.first[:id]).to eq(Workspace.order('id desc')[1].id.to_s)
|
52
52
|
|
53
53
|
result = @presenter_collection.presenting("workspaces", :params => { :per_page => 2, :page => 2 }) { Workspace.order('id desc') }[:results]
|
54
|
-
result.length.
|
55
|
-
result.map { |m| m[:id] }.
|
54
|
+
expect(result.length).to eq(2)
|
55
|
+
expect(result.map { |m| m[:id] }).to eq(Workspace.order('id desc')[2..3].map(&:id).map(&:to_s))
|
56
56
|
end
|
57
57
|
|
58
58
|
it "defaults to 1 if the page number is less than 1" do
|
59
59
|
result = @presenter_collection.presenting("workspaces", :params => { :per_page => 1, :page => 0 }) { Workspace.order('id desc') }[:results]
|
60
|
-
result.length.
|
61
|
-
result.first[:id].
|
60
|
+
expect(result.length).to eq(1)
|
61
|
+
expect(result.first[:id]).to eq(Workspace.order('id desc')[0].id.to_s)
|
62
62
|
end
|
63
63
|
end
|
64
64
|
|
65
65
|
context "when only limit and offset are present" do
|
66
66
|
it "honors the user's requested limit and offset and returns counts" do
|
67
67
|
result = @presenter_collection.presenting("workspaces", :params => { :limit => 1, :offset => 2 }) { Workspace.order('id desc') }[:results]
|
68
|
-
result.length.
|
69
|
-
result.first[:id].
|
68
|
+
expect(result.length).to eq(1)
|
69
|
+
expect(result.first[:id]).to eq(Workspace.order('id desc')[2].id.to_s)
|
70
70
|
|
71
71
|
result = @presenter_collection.presenting("workspaces", :params => { :limit => 2, :offset => 2 }) { Workspace.order('id desc') }[:results]
|
72
|
-
result.length.
|
73
|
-
result.map { |m| m[:id] }.
|
72
|
+
expect(result.length).to eq(2)
|
73
|
+
expect(result.map { |m| m[:id] }).to eq(Workspace.order('id desc')[2..3].map(&:id).map(&:to_s))
|
74
74
|
end
|
75
75
|
|
76
76
|
it "defaults to offset 0 if the passed offset is less than 0 and limit to 1 if the passed limit is less than 1" do
|
77
77
|
stub.proxy(@presenter_collection).calculate_offset(anything).times(1)
|
78
78
|
stub.proxy(@presenter_collection).calculate_limit(anything).times(1)
|
79
79
|
result = @presenter_collection.presenting("workspaces", :params => { :limit => -1, :offset => -1 }) { Workspace.order('id desc') }[:results]
|
80
|
-
result.length.
|
81
|
-
result.first[:id].
|
80
|
+
expect(result.length).to eq(1)
|
81
|
+
expect(result.first[:id]).to eq(Workspace.order('id desc')[0].id.to_s)
|
82
82
|
end
|
83
83
|
end
|
84
84
|
|
85
85
|
context "when both sets of params are present" do
|
86
86
|
it "prefers limit and offset over per_page and page" do
|
87
87
|
result = @presenter_collection.presenting("workspaces", :params => { :limit => 1, :offset => 0, :per_page => 2, :page => 2 }) { Workspace.order('id desc') }[:results]
|
88
|
-
result.length.
|
89
|
-
result.first[:id].
|
88
|
+
expect(result.length).to eq(1)
|
89
|
+
expect(result.first[:id]).to eq(Workspace.order('id desc')[0].id.to_s)
|
90
90
|
end
|
91
91
|
|
92
92
|
it "uses per_page and page if limit and offset are not complete" do
|
93
93
|
result = @presenter_collection.presenting("workspaces", :params => { :limit => 5, :per_page => 1, :page => 0 }) { Workspace.order('id desc') }[:results]
|
94
|
-
result.length.
|
95
|
-
result.first[:id].
|
94
|
+
expect(result.length).to eq(1)
|
95
|
+
expect(result.first[:id]).to eq(Workspace.order('id desc')[0].id.to_s)
|
96
96
|
|
97
97
|
result = @presenter_collection.presenting("workspaces", :params => { :offset => 5, :per_page => 1, :page => 0 }) { Workspace.order('id desc') }[:results]
|
98
|
-
result.length.
|
99
|
-
result.first[:id].
|
98
|
+
expect(result.length).to eq(1)
|
99
|
+
expect(result.first[:id]).to eq(Workspace.order('id desc')[0].id.to_s)
|
100
100
|
end
|
101
101
|
end
|
102
102
|
end
|
@@ -107,34 +107,34 @@ describe Brainstem::PresenterCollection do
|
|
107
107
|
it "should raise the provided error class when the empty_error_class option is provided" do
|
108
108
|
class MyException < Exception; end
|
109
109
|
|
110
|
-
|
110
|
+
expect {
|
111
111
|
@presenter_collection.presenting("workspaces", :raise_on_empty => true, :empty_error_class => MyException) { Workspace.where(:id => nil) }
|
112
|
-
}.
|
112
|
+
}.to raise_error(MyException)
|
113
113
|
end
|
114
114
|
|
115
115
|
it "should raise ActiveRecord::RecordNotFound when the empty_error_class option is not provided" do
|
116
|
-
|
116
|
+
expect {
|
117
117
|
@presenter_collection.presenting("workspaces", :raise_on_empty => true) { Workspace.where(:id => nil) }
|
118
|
-
}.
|
118
|
+
}.to raise_error(ActiveRecord::RecordNotFound)
|
119
119
|
end
|
120
120
|
end
|
121
121
|
|
122
122
|
context "results are not empty" do
|
123
123
|
it "should not raise an exception" do
|
124
|
-
Workspace.count.
|
124
|
+
expect(Workspace.count).to be > 0
|
125
125
|
|
126
|
-
|
126
|
+
expect {
|
127
127
|
@presenter_collection.presenting("workspaces", :raise_on_empty => true) { Workspace.order('id desc') }
|
128
|
-
}.
|
128
|
+
}.not_to raise_error
|
129
129
|
end
|
130
130
|
end
|
131
131
|
end
|
132
132
|
|
133
133
|
context "raise_on_empty is false" do
|
134
134
|
it "should not raise an exception when the results are empty" do
|
135
|
-
|
135
|
+
expect {
|
136
136
|
@presenter_collection.presenting("workspaces") { Workspace.where(:id => nil) }
|
137
|
-
}.
|
137
|
+
}.not_to raise_error
|
138
138
|
end
|
139
139
|
end
|
140
140
|
end
|
@@ -147,7 +147,7 @@ describe Brainstem::PresenterCollection do
|
|
147
147
|
|
148
148
|
it "returns the unique count by model id" do
|
149
149
|
result = @presenter_collection.presenting("workspaces", :params => { :per_page => 2, :page => 1 }) { Workspace.order('id desc') }
|
150
|
-
result[:count].
|
150
|
+
expect(result[:count]).to eq(Workspace.count)
|
151
151
|
end
|
152
152
|
end
|
153
153
|
end
|
@@ -155,157 +155,157 @@ describe Brainstem::PresenterCollection do
|
|
155
155
|
describe "uses presenters" do
|
156
156
|
it "finds presenter by table name string" do
|
157
157
|
result = @presenter_collection.presenting("workspaces") { Workspace.order('id desc') }
|
158
|
-
result[:workspaces].length.
|
158
|
+
expect(result[:workspaces].length).to eq(Workspace.count)
|
159
159
|
end
|
160
160
|
|
161
161
|
it "finds presenter by model name string" do
|
162
162
|
result = @presenter_collection.presenting("Workspace") { order('id desc') }
|
163
|
-
result[:workspaces].length.
|
163
|
+
expect(result[:workspaces].length).to eq(Workspace.count)
|
164
164
|
end
|
165
165
|
|
166
166
|
it "finds presenter by model" do
|
167
167
|
result = @presenter_collection.presenting(Workspace) { order('id desc') }
|
168
|
-
result[:workspaces].length.
|
168
|
+
expect(result[:workspaces].length).to eq(Workspace.count)
|
169
169
|
end
|
170
170
|
|
171
171
|
it "infers the table name from the model" do
|
172
172
|
result = @presenter_collection.presenting("not_workspaces", :model => "Workspace", :params => { :per_page => 2, :page => 1 }) { Workspace.order('id desc') }
|
173
|
-
result[:not_workspaces].
|
174
|
-
result[:count].
|
173
|
+
expect(result[:not_workspaces]).not_to be_empty
|
174
|
+
expect(result[:count]).to eq(Workspace.count)
|
175
175
|
end
|
176
176
|
end
|
177
177
|
|
178
178
|
describe "the 'results' top level key" do
|
179
179
|
it "comes back with an explicit list of the matching results" do
|
180
180
|
structure = @presenter_collection.presenting("workspaces", :params => { :include => "tasks" }, :max_per_page => 2) { Workspace.where(:id => 1) }
|
181
|
-
structure.keys.
|
182
|
-
structure[:results].
|
183
|
-
structure[:workspaces].keys.
|
181
|
+
expect(structure.keys).to match_array([:workspaces, :tasks, :count, :results])
|
182
|
+
expect(structure[:results]).to eq(Workspace.where(:id => 1).limit(2).map {|w| { :key => "workspaces", :id => w.id.to_s } })
|
183
|
+
expect(structure[:workspaces].keys).to eq(%w[1])
|
184
184
|
end
|
185
185
|
end
|
186
186
|
|
187
187
|
describe "includes" do
|
188
188
|
it "reads allowed includes from the presenter" do
|
189
189
|
result = @presenter_collection.presenting("workspaces", :params => { :include => "drop table,tasks,users" }) { Workspace.order('id desc') }
|
190
|
-
result.keys.
|
190
|
+
expect(result.keys).to match_array([:count, :workspaces, :tasks, :results])
|
191
191
|
|
192
192
|
result = @presenter_collection.presenting("workspaces", :params => { :include => "foo,tasks,lead_user" }) { Workspace.order('id desc') }
|
193
|
-
result.keys.
|
193
|
+
expect(result.keys).to match_array([:count, :workspaces, :tasks, :users, :results])
|
194
194
|
end
|
195
195
|
|
196
196
|
it "allows the allowed includes list to have different json names and association names" do
|
197
197
|
result = @presenter_collection.presenting("tasks",
|
198
198
|
:params => { :include => "other_tasks" }) { Task.order('id desc') }
|
199
|
-
result[:tasks].
|
200
|
-
result[:other_tasks].
|
199
|
+
expect(result[:tasks]).to be_present
|
200
|
+
expect(result[:other_tasks]).to be_present
|
201
201
|
end
|
202
202
|
|
203
203
|
it "defaults to not include any allowed includes" do
|
204
204
|
tasked_workspace = Task.first
|
205
205
|
result = @presenter_collection.presenting("workspaces", :max_per_page => 2) { Workspace.where(:id => tasked_workspace.workspace_id) }
|
206
|
-
result[:workspaces].keys.
|
207
|
-
result[:tasks].
|
206
|
+
expect(result[:workspaces].keys).to eq([ tasked_workspace.workspace_id.to_s ])
|
207
|
+
expect(result[:tasks]).to be_nil
|
208
208
|
end
|
209
209
|
|
210
210
|
it "loads has_many associations and returns them when requested" do
|
211
211
|
result = @presenter_collection.presenting("workspaces", :params => { :include => "tasks" }, :max_per_page => 2) { Workspace.where(:id => 1) }
|
212
|
-
result[:tasks].keys.
|
213
|
-
result[:workspaces]["1"][:task_ids].
|
212
|
+
expect(result[:tasks].keys).to match_array(Workspace.first.tasks.map(&:id).map(&:to_s))
|
213
|
+
expect(result[:workspaces]["1"][:task_ids]).to match_array(Workspace.first.tasks.map(&:id).map(&:to_s))
|
214
214
|
end
|
215
215
|
|
216
216
|
it "returns appropriate fields" do
|
217
217
|
result = @presenter_collection.presenting("workspaces",
|
218
218
|
:params => { :include => "tasks" },
|
219
219
|
:max_per_page => 2) { Workspace.where(:id => 1) }
|
220
|
-
result[:workspaces].values.first.
|
221
|
-
result[:tasks].values.first.
|
220
|
+
expect(result[:workspaces].values.first).to have_key(:description)
|
221
|
+
expect(result[:tasks].values.first).to have_key(:name)
|
222
222
|
end
|
223
223
|
|
224
224
|
it "loads belongs_tos and returns them when requested" do
|
225
225
|
result = @presenter_collection.presenting("tasks", :params => { :include => "workspace" }, :max_per_page => 2) { Task.where(:id => 1) }
|
226
|
-
result[:workspaces].keys.
|
226
|
+
expect(result[:workspaces].keys).to eq(%w[1])
|
227
227
|
end
|
228
228
|
|
229
229
|
it "doesn't return nils when belong_tos are missing" do
|
230
230
|
t = Task.first
|
231
231
|
t.update_attribute :workspace, nil
|
232
|
-
t.reload.workspace.
|
232
|
+
expect(t.reload.workspace).to be_nil
|
233
233
|
result = @presenter_collection.presenting("tasks", :params => { :include => "workspace" }, :max_per_page => 2) { Task.where(:id => t.id) }
|
234
|
-
result[:tasks].keys.
|
235
|
-
result[:workspaces].
|
236
|
-
result.keys.
|
234
|
+
expect(result[:tasks].keys).to eq([ t.id.to_s ])
|
235
|
+
expect(result[:workspaces]).to eq({})
|
236
|
+
expect(result.keys).to match_array([:tasks, :workspaces, :count, :results])
|
237
237
|
end
|
238
238
|
|
239
239
|
it "returns sensible data when including something of the same type as the primary model" do
|
240
240
|
result = @presenter_collection.presenting("tasks", :params => { :include => "sub_tasks" }) { Task.where(:id => 2) }
|
241
241
|
sub_task_ids = Task.find(2).sub_tasks.map(&:id).map(&:to_s)
|
242
|
-
result[:tasks].keys.
|
243
|
-
result[:tasks]["2"][:sub_task_ids].
|
244
|
-
result[:tasks][sub_task_ids.first][:sub_task_ids].
|
242
|
+
expect(result[:tasks].keys).to match_array(sub_task_ids + ["2"])
|
243
|
+
expect(result[:tasks]["2"][:sub_task_ids]).to eq(sub_task_ids) # The primary should have a sub_story_ids array.
|
244
|
+
expect(result[:tasks][sub_task_ids.first][:sub_task_ids]).not_to be_present # Sub stories should not have a sub_story_ids array.
|
245
245
|
end
|
246
246
|
|
247
247
|
it "includes requested includes even when all records are filtered" do
|
248
248
|
result = @presenter_collection.presenting("workspaces", :params => { :only => "not an id", :include => "not an include,tasks" }) { Workspace.order("id desc") }
|
249
|
-
result[:workspaces].length.
|
250
|
-
result[:tasks].length.
|
249
|
+
expect(result[:workspaces].length).to eq(0)
|
250
|
+
expect(result[:tasks].length).to eq(0)
|
251
251
|
end
|
252
252
|
|
253
253
|
it "includes requested includes even when the scope has no records" do
|
254
|
-
Workspace.where(:id => 123456789).
|
254
|
+
expect(Workspace.where(:id => 123456789)).to be_empty
|
255
255
|
result = @presenter_collection.presenting("workspaces", :params => { :include => "not an include,tasks" }) { Workspace.where(:id => 123456789) }
|
256
|
-
result[:workspaces].length.
|
257
|
-
result[:tasks].length.
|
256
|
+
expect(result[:workspaces].length).to eq(0)
|
257
|
+
expect(result[:tasks].length).to eq(0)
|
258
258
|
end
|
259
259
|
|
260
260
|
it "preloads associations when they are full model-level associations" do
|
261
261
|
# Here, primary_maven is a method on Workspace, not a true association.
|
262
|
-
mock(
|
262
|
+
mock(@presenter_collection).preload(anything, [:tasks])
|
263
263
|
result = @presenter_collection.presenting("workspaces", :params => { :include => "tasks" }) { Workspace.order('id desc') }
|
264
|
-
result[:tasks].length.
|
264
|
+
expect(result[:tasks].length).to be > 0
|
265
265
|
end
|
266
266
|
|
267
267
|
it "works with model methods that load records (but without preloading)" do
|
268
268
|
result = @presenter_collection.presenting("workspaces", :params => { :include => "lead_user" }) { Workspace.order('id desc') }
|
269
|
-
result[:workspaces][Workspace.first.id.to_s].
|
270
|
-
result[:users][Workspace.first.lead_user.id.to_s].
|
269
|
+
expect(result[:workspaces][Workspace.first.id.to_s]).to be_present
|
270
|
+
expect(result[:users][Workspace.first.lead_user.id.to_s]).to be_present
|
271
271
|
end
|
272
272
|
|
273
273
|
it "can accept a lambda for the association and uses that when present" do
|
274
274
|
result = @presenter_collection.presenting("users", :params => { :include => "odd_workspaces" }) { User.where(:id => 1) }
|
275
|
-
result[:odd_workspaces][Workspace.first.id.to_s].
|
276
|
-
result[:users][Workspace.first.lead_user.id.to_s].
|
275
|
+
expect(result[:odd_workspaces][Workspace.first.id.to_s]).to be_present
|
276
|
+
expect(result[:users][Workspace.first.lead_user.id.to_s]).to be_present
|
277
277
|
end
|
278
278
|
|
279
279
|
describe "restricted associations" do
|
280
280
|
it "does apply includes that are restricted to only queries in an only query" do
|
281
281
|
t = Task.first
|
282
282
|
result = @presenter_collection.presenting("tasks", :params => { :include => "restricted", :only => t.id.to_s }, :max_per_page => 2) { Task.where(:id => t.id) }
|
283
|
-
result[:tasks][t.id.to_s].keys.
|
284
|
-
result.keys.
|
283
|
+
expect(result[:tasks][t.id.to_s].keys).to include(:restricted_id)
|
284
|
+
expect(result.keys).to include(:restricted_associations)
|
285
285
|
end
|
286
286
|
|
287
287
|
it "does not apply includes that are restricted to only queries in a non-only query" do
|
288
288
|
t = Task.first
|
289
289
|
result = @presenter_collection.presenting("tasks", :params => { :include => "restricted" }, :max_per_page => 2) { Task.where(:id => t.id) }
|
290
290
|
|
291
|
-
result[:tasks][t.id.to_s].keys.
|
292
|
-
result.keys.
|
291
|
+
expect(result[:tasks][t.id.to_s].keys).not_to include(:restricted_id)
|
292
|
+
expect(result.keys).not_to include(:restricted_associations)
|
293
293
|
end
|
294
294
|
end
|
295
295
|
|
296
296
|
describe "polymorphic associations" do
|
297
297
|
it "works with polymorphic associations" do
|
298
298
|
result = @presenter_collection.presenting("posts", :params => { :include => "subject" }) { Post.order('id desc') }
|
299
|
-
result[:posts][Post.first.id.to_s].
|
300
|
-
result[:workspaces][Workspace.first.id.to_s].
|
301
|
-
result[:tasks][Task.first.id.to_s].
|
299
|
+
expect(result[:posts][Post.first.id.to_s]).to be_present
|
300
|
+
expect(result[:workspaces][Workspace.first.id.to_s]).to be_present
|
301
|
+
expect(result[:tasks][Task.first.id.to_s]).to be_present
|
302
302
|
end
|
303
303
|
|
304
304
|
it "does not return an empty hash when none are found" do
|
305
305
|
result = @presenter_collection.presenting("posts", :params => { :include => "subject" }) { Post.where(:id => nil) }
|
306
|
-
result.
|
307
|
-
result.
|
308
|
-
result.
|
306
|
+
expect(result).to have_key(:posts)
|
307
|
+
expect(result).not_to have_key(:workspaces)
|
308
|
+
expect(result).not_to have_key(:tasks)
|
309
309
|
end
|
310
310
|
end
|
311
311
|
end
|
@@ -313,7 +313,7 @@ describe Brainstem::PresenterCollection do
|
|
313
313
|
describe "handling of only" do
|
314
314
|
it "accepts params[:only] as a list of ids to limit to" do
|
315
315
|
result = @presenter_collection.presenting("workspaces", :params => { :only => Workspace.limit(2).pluck(:id).join(",") }) { Workspace.order("id desc") }
|
316
|
-
result[:workspaces].keys.
|
316
|
+
expect(result[:workspaces].keys).to match_array(Workspace.limit(2).pluck(:id).map(&:to_s))
|
317
317
|
end
|
318
318
|
|
319
319
|
it "does not paginate only requests" do
|
@@ -323,15 +323,15 @@ describe Brainstem::PresenterCollection do
|
|
323
323
|
|
324
324
|
it "escapes ids" do
|
325
325
|
result = @presenter_collection.presenting("workspaces", :params => { :only => "#{Workspace.first.id}foo,;drop tables;,#{Workspace.first.id}" }) { Workspace.order("id desc") }
|
326
|
-
result[:workspaces].length.
|
326
|
+
expect(result[:workspaces].length).to eq(1)
|
327
327
|
end
|
328
328
|
|
329
329
|
it "only runs when it receives ids" do
|
330
330
|
result = @presenter_collection.presenting("workspaces", :params => { :only => "" }) { Workspace.order("id desc") }
|
331
|
-
result[:workspaces].length.
|
331
|
+
expect(result[:workspaces].length).to be > 1
|
332
332
|
|
333
333
|
result = @presenter_collection.presenting("workspaces", :params => { :only => "1" }) { Workspace.order("id desc") }
|
334
|
-
result[:workspaces].length.
|
334
|
+
expect(result[:workspaces].length).to be <= 1
|
335
335
|
end
|
336
336
|
end
|
337
337
|
|
@@ -343,50 +343,67 @@ describe Brainstem::PresenterCollection do
|
|
343
343
|
|
344
344
|
it "limits records to those matching given filters" do
|
345
345
|
result = @presenter_collection.presenting("workspaces", :params => { :owned_by => bob.id.to_s }) { Workspace.order("id desc") } # hit the API, filtering on owned_by:bob
|
346
|
-
result[:workspaces].
|
347
|
-
result[:workspaces].keys.all? {|id| bob_workspaces_ids.map(&:to_s).include?(id) }.
|
346
|
+
expect(result[:workspaces]).to be_present
|
347
|
+
expect(result[:workspaces].keys.all? {|id| bob_workspaces_ids.map(&:to_s).include?(id) }).to be_truthy # all of the returned workspaces should contain bob
|
348
348
|
end
|
349
349
|
|
350
350
|
it "returns all records if filters are not given" do
|
351
351
|
result = @presenter_collection.presenting("workspaces") { Workspace.order("id desc") } # hit the API again, this time not filtering on anything
|
352
|
-
result[:workspaces].keys.all? {|id| bob_workspaces_ids.map(&:to_s).include?(id) }.
|
352
|
+
expect(result[:workspaces].keys.all? {|id| bob_workspaces_ids.map(&:to_s).include?(id) }).to be_falsey # the returned workspaces no longer all contain bob
|
353
353
|
end
|
354
354
|
|
355
355
|
it "ignores unknown filters" do
|
356
356
|
result = @presenter_collection.presenting("workspaces", :params => { :wut => "is this?" }) { Workspace.order("id desc") }
|
357
|
-
result[:workspaces].keys.all? {|id| bob_workspaces_ids.map(&:to_s).include?(id) }.
|
357
|
+
expect(result[:workspaces].keys.all? {|id| bob_workspaces_ids.map(&:to_s).include?(id) }).to be_falsey
|
358
358
|
end
|
359
359
|
|
360
360
|
it "limits records to those matching all given filters" do
|
361
361
|
result = @presenter_collection.presenting("workspaces", :params => { :owned_by => bob.id.to_s, :title => "bob workspace 1" }) { Workspace.order("id desc") } # try two filters
|
362
|
-
result[:results].first[:id].
|
362
|
+
expect(result[:results].first[:id]).to eq(Workspace.where(:title => "bob workspace 1").first.id.to_s)
|
363
363
|
end
|
364
364
|
|
365
365
|
it "converts boolean parameters from strings to booleans" do
|
366
366
|
WorkspacePresenter.filter(:owned_by_bob) { |scope, boolean| boolean ? scope.where(:user_id => bob.id) : scope.where(:user_id => jane.id) }
|
367
367
|
result = @presenter_collection.presenting("workspaces", :params => { :owned_by_bob => "false" }) { Workspace.where(nil) }
|
368
|
-
result[:workspaces].values.find { |workspace| workspace[:title].include?("jane") }.
|
369
|
-
result[:workspaces].values.find { |workspace| workspace[:title].include?("bob") }.
|
368
|
+
expect(result[:workspaces].values.find { |workspace| workspace[:title].include?("jane") }).to be
|
369
|
+
expect(result[:workspaces].values.find { |workspace| workspace[:title].include?("bob") }).not_to be
|
370
370
|
end
|
371
371
|
|
372
|
-
it "ensures arguments are strings" do
|
373
|
-
|
374
|
-
|
372
|
+
it "ensures arguments are strings if they are not arrays" do
|
373
|
+
filter_was_run = false
|
374
|
+
WorkspacePresenter.filter(:owned_by_bob) do |scope, string|
|
375
|
+
filter_was_run = true
|
376
|
+
expect(string).to be_a(String)
|
377
|
+
scope
|
378
|
+
end
|
379
|
+
@presenter_collection.presenting("workspaces", :params => { :owned_by_bob => { :wut => "is this?" } }) { Workspace.where(nil) }
|
380
|
+
expect(filter_was_run).to be_truthy
|
381
|
+
end
|
382
|
+
|
383
|
+
it "preserves array arguments" do
|
384
|
+
filter_was_run = false
|
385
|
+
WorkspacePresenter.filter(:owned_by_bob) do |scope, array|
|
386
|
+
filter_was_run = true
|
387
|
+
expect(array).to be_a(Array)
|
388
|
+
scope
|
389
|
+
end
|
390
|
+
@presenter_collection.presenting("workspaces", :params => { :owned_by_bob => [1, 2] }) { Workspace.where(nil) }
|
391
|
+
expect(filter_was_run).to be_truthy
|
375
392
|
end
|
376
393
|
|
377
394
|
it "allows filters to be called with false as an argument" do
|
378
395
|
WorkspacePresenter.filter(:nothing) { |scope, bool| bool ? scope.where(:id => nil) : scope }
|
379
396
|
result = @presenter_collection.presenting("workspaces", :params => { :nothing => "true" }) { Workspace.where(nil) }
|
380
|
-
result[:workspaces].length.
|
397
|
+
expect(result[:workspaces].length).to eq(0)
|
381
398
|
result = @presenter_collection.presenting("workspaces", :params => { :nothing => "false" }) { Workspace.where(nil) }
|
382
|
-
result[:workspaces].length.
|
399
|
+
expect(result[:workspaces].length).not_to eq(0)
|
383
400
|
end
|
384
401
|
|
385
402
|
it "passes colon separated params through as a string" do
|
386
403
|
WorkspacePresenter.filter(:between) { |scope, a_and_b|
|
387
404
|
a, b = a_and_b.split(':')
|
388
|
-
a.
|
389
|
-
b.
|
405
|
+
expect(a).to eq("1")
|
406
|
+
expect(b).to eq("10")
|
390
407
|
scope
|
391
408
|
}
|
392
409
|
|
@@ -402,32 +419,32 @@ describe Brainstem::PresenterCollection do
|
|
402
419
|
|
403
420
|
it "applies the filter when it is not requested" do
|
404
421
|
result = @presenter_collection.presenting("workspaces") { Workspace.order('id desc') }
|
405
|
-
result[:workspaces].keys.
|
422
|
+
expect(result[:workspaces].keys).to match_array(bob.workspaces.map(&:id).map(&:to_s))
|
406
423
|
end
|
407
424
|
|
408
425
|
it "allows falsy defaults" do
|
409
426
|
WorkspacePresenter.filter(:include_early_workspaces, :default => false) { |scope, bool| bool ? scope : scope.where("id > 3") }
|
410
427
|
result = @presenter_collection.presenting("workspaces") { Workspace.unscoped }
|
411
|
-
result[:workspaces]["2"].
|
428
|
+
expect(result[:workspaces]["2"]).not_to be_present
|
412
429
|
result = @presenter_collection.presenting("workspaces", :params => { :include_early_workspaces => "true" }) { Workspace.unscoped }
|
413
|
-
result[:workspaces]["2"].
|
430
|
+
expect(result[:workspaces]["2"]).to be_present
|
414
431
|
end
|
415
432
|
|
416
433
|
it "allows defaults to be skipped if :apply_default_filters is false" do
|
417
434
|
WorkspacePresenter.filter(:include_early_workspaces, :default => false) { |scope, bool| bool ? scope : scope.where("id > 3") }
|
418
435
|
result = @presenter_collection.presenting("workspaces", :apply_default_filters => true) { Workspace.unscoped }
|
419
|
-
result[:workspaces]["2"].
|
436
|
+
expect(result[:workspaces]["2"]).not_to be_present
|
420
437
|
result = @presenter_collection.presenting("workspaces", :apply_default_filters => false) { Workspace.unscoped }
|
421
|
-
result[:workspaces]["2"].
|
438
|
+
expect(result[:workspaces]["2"]).to be_present
|
422
439
|
end
|
423
440
|
|
424
441
|
it "allows the default value to be overridden" do
|
425
442
|
result = @presenter_collection.presenting("workspaces", :params => { :owner => jane.id.to_s }) { Workspace.order('id desc') }
|
426
|
-
result[:workspaces].keys.
|
443
|
+
expect(result[:workspaces].keys).to match_array(jane.workspaces.map(&:id).map(&:to_s))
|
427
444
|
|
428
445
|
WorkspacePresenter.filter(:include_early_workspaces, :default => true) { |scope, bool| bool ? scope : scope.where("id > 3") }
|
429
446
|
result = @presenter_collection.presenting("workspaces", :params => { :include_early_workspaces => "false" }) { Workspace.unscoped }
|
430
|
-
result[:workspaces]["2"].
|
447
|
+
expect(result[:workspaces]["2"]).not_to be_present
|
431
448
|
end
|
432
449
|
end
|
433
450
|
|
@@ -442,27 +459,27 @@ describe Brainstem::PresenterCollection do
|
|
442
459
|
|
443
460
|
it "calls the named scope with default arguments" do
|
444
461
|
result = @presenter_collection.presenting("workspaces") { Workspace.where(nil) }
|
445
|
-
result[:workspaces].keys.
|
462
|
+
expect(result[:workspaces].keys).to eq(bob.workspaces.pluck(:id).map(&:to_s))
|
446
463
|
end
|
447
464
|
|
448
465
|
it "calls the named scope with given arguments" do
|
449
466
|
result = @presenter_collection.presenting("workspaces", :params => { :owned_by => jane.id.to_s }) { Workspace.where(nil) }
|
450
|
-
result[:workspaces].keys.
|
467
|
+
expect(result[:workspaces].keys).to eq(jane.workspaces.pluck(:id).map(&:to_s))
|
451
468
|
end
|
452
469
|
|
453
470
|
it "can use filters without lambdas in the presenter or model, but behaves strangely when false is given" do
|
454
471
|
WorkspacePresenter.filter(:numeric_description)
|
455
472
|
|
456
473
|
result = @presenter_collection.presenting("workspaces") { Workspace.where(nil) }
|
457
|
-
result[:workspaces].keys.
|
474
|
+
expect(result[:workspaces].keys).to eq(%w[1 2 3 4])
|
458
475
|
|
459
476
|
result = @presenter_collection.presenting("workspaces", :params => { :numeric_description => "true" }) { Workspace.where(nil) }
|
460
|
-
result[:workspaces].keys.
|
477
|
+
expect(result[:workspaces].keys).to eq(%w[2 4])
|
461
478
|
|
462
479
|
# This is probably not the behavior that the developer or user intends. You should always use a one-argument lambda in your
|
463
480
|
# model scope declaration!
|
464
481
|
result = @presenter_collection.presenting("workspaces", :params => { :numeric_description => "false" }) { Workspace.where(nil) }
|
465
|
-
result[:workspaces].keys.
|
482
|
+
expect(result[:workspaces].keys).to eq(%w[2 4])
|
466
483
|
end
|
467
484
|
end
|
468
485
|
end
|
@@ -478,8 +495,8 @@ describe Brainstem::PresenterCollection do
|
|
478
495
|
context "and a search request is made" do
|
479
496
|
it "calls the search method and maintains the resulting order" do
|
480
497
|
result = @presenter_collection.presenting("workspaces", :params => { :search => "blah" }) { Workspace.order("id asc") }
|
481
|
-
result[:workspaces].keys.
|
482
|
-
result[:count].
|
498
|
+
expect(result[:workspaces].keys).to eq(%w[5 3])
|
499
|
+
expect(result[:count]).to eq(2)
|
483
500
|
end
|
484
501
|
|
485
502
|
it "does not apply filters" do
|
@@ -512,22 +529,22 @@ describe Brainstem::PresenterCollection do
|
|
512
529
|
false
|
513
530
|
end
|
514
531
|
|
515
|
-
|
532
|
+
expect {
|
516
533
|
@presenter_collection.presenting("workspaces", :params => { :search => "blah" }) { Workspace.unscoped }
|
517
|
-
}.
|
534
|
+
}.to raise_error(Brainstem::SearchUnavailableError)
|
518
535
|
end
|
519
536
|
|
520
537
|
describe "passing options to the search block" do
|
521
538
|
it "passes the search method, the search string, includes, order, and paging options" do
|
522
539
|
WorkspacePresenter.filter(:owned_by) { |scope| scope }
|
523
540
|
WorkspacePresenter.search do |string, options|
|
524
|
-
string.
|
525
|
-
options[:include].
|
526
|
-
options[:owned_by].
|
527
|
-
options[:order][:sort_order].
|
528
|
-
options[:order][:direction].
|
529
|
-
options[:page].
|
530
|
-
options[:per_page].
|
541
|
+
expect(string).to eq("blah")
|
542
|
+
expect(options[:include]).to eq(["tasks", "lead_user"])
|
543
|
+
expect(options[:owned_by]).to eq(false)
|
544
|
+
expect(options[:order][:sort_order]).to eq("updated_at")
|
545
|
+
expect(options[:order][:direction]).to eq("desc")
|
546
|
+
expect(options[:page]).to eq(2)
|
547
|
+
expect(options[:per_page]).to eq(5)
|
531
548
|
[[1], 1] # returned ids, count - not testing this in this set of specs
|
532
549
|
end
|
533
550
|
|
@@ -537,7 +554,7 @@ describe Brainstem::PresenterCollection do
|
|
537
554
|
describe "includes" do
|
538
555
|
it "throws out requested inlcudes that the presenter does not have associations for" do
|
539
556
|
WorkspacePresenter.search do |string, options|
|
540
|
-
options[:include].
|
557
|
+
expect(options[:include]).to eq([])
|
541
558
|
[[1], 1]
|
542
559
|
end
|
543
560
|
|
@@ -549,7 +566,7 @@ describe Brainstem::PresenterCollection do
|
|
549
566
|
it "passes through the default filters if no filter is requested" do
|
550
567
|
WorkspacePresenter.filter(:owned_by, :default => true) { |scope| scope }
|
551
568
|
WorkspacePresenter.search do |string, options|
|
552
|
-
options[:owned_by].
|
569
|
+
expect(options[:owned_by]).to eq(true)
|
553
570
|
[[1], 1]
|
554
571
|
end
|
555
572
|
|
@@ -558,7 +575,7 @@ describe Brainstem::PresenterCollection do
|
|
558
575
|
|
559
576
|
it "throws out requested filters that the presenter does not have" do
|
560
577
|
WorkspacePresenter.search do |string, options|
|
561
|
-
options[:highest_rated].
|
578
|
+
expect(options[:highest_rated]).to be_nil
|
562
579
|
[[1], 1]
|
563
580
|
end
|
564
581
|
|
@@ -568,7 +585,7 @@ describe Brainstem::PresenterCollection do
|
|
568
585
|
it "does not pass through existing non-default filters that are not requested" do
|
569
586
|
WorkspacePresenter.filter(:owned_by) { |scope| scope }
|
570
587
|
WorkspacePresenter.search do |string, options|
|
571
|
-
options.has_key?(:owned_by).
|
588
|
+
expect(options.has_key?(:owned_by)).to eq(false)
|
572
589
|
[[1], 1]
|
573
590
|
end
|
574
591
|
|
@@ -580,8 +597,8 @@ describe Brainstem::PresenterCollection do
|
|
580
597
|
it "passes through the default sort order if no order is requested" do
|
581
598
|
WorkspacePresenter.default_sort_order("description:desc")
|
582
599
|
WorkspacePresenter.search do |string, options|
|
583
|
-
options[:order][:sort_order].
|
584
|
-
options[:order][:direction].
|
600
|
+
expect(options[:order][:sort_order]).to eq("description")
|
601
|
+
expect(options[:order][:direction]).to eq("desc")
|
585
602
|
[[1], 1]
|
586
603
|
end
|
587
604
|
|
@@ -590,8 +607,8 @@ describe Brainstem::PresenterCollection do
|
|
590
607
|
|
591
608
|
it "makes the sort order 'updated_at:desc' if the requested order doesn't match an existing sort order and there is no default" do
|
592
609
|
WorkspacePresenter.search do |string, options|
|
593
|
-
options[:order][:sort_order].
|
594
|
-
options[:order][:direction].
|
610
|
+
expect(options[:order][:sort_order]).to eq("updated_at")
|
611
|
+
expect(options[:order][:direction]).to eq("desc")
|
595
612
|
[[1], 1]
|
596
613
|
end
|
597
614
|
|
@@ -602,8 +619,8 @@ describe Brainstem::PresenterCollection do
|
|
602
619
|
describe "pagination" do
|
603
620
|
it "passes through limit and offset if they are requested" do
|
604
621
|
WorkspacePresenter.search do |string, options|
|
605
|
-
options[:limit].
|
606
|
-
options[:offset].
|
622
|
+
expect(options[:limit]).to eq(1)
|
623
|
+
expect(options[:offset]).to eq(2)
|
607
624
|
[[1], 1]
|
608
625
|
end
|
609
626
|
|
@@ -612,10 +629,10 @@ describe Brainstem::PresenterCollection do
|
|
612
629
|
|
613
630
|
it "passes through only limit and offset if all pagination options are requested" do
|
614
631
|
WorkspacePresenter.search do |string, options|
|
615
|
-
options[:limit].
|
616
|
-
options[:offset].
|
617
|
-
options[:per_page].
|
618
|
-
options[:page].
|
632
|
+
expect(options[:limit]).to eq(1)
|
633
|
+
expect(options[:offset]).to eq(2)
|
634
|
+
expect(options[:per_page]).to eq(nil)
|
635
|
+
expect(options[:page]).to eq(nil)
|
619
636
|
[[1], 1]
|
620
637
|
end
|
621
638
|
|
@@ -624,10 +641,10 @@ describe Brainstem::PresenterCollection do
|
|
624
641
|
|
625
642
|
it "passes through page and per_page when limit not present" do
|
626
643
|
WorkspacePresenter.search do |string, options|
|
627
|
-
options[:limit].
|
628
|
-
options[:offset].
|
629
|
-
options[:per_page].
|
630
|
-
options[:page].
|
644
|
+
expect(options[:limit]).to eq(nil)
|
645
|
+
expect(options[:offset]).to eq(nil)
|
646
|
+
expect(options[:per_page]).to eq(3)
|
647
|
+
expect(options[:page]).to eq(4)
|
631
648
|
[[1], 1]
|
632
649
|
end
|
633
650
|
|
@@ -636,10 +653,10 @@ describe Brainstem::PresenterCollection do
|
|
636
653
|
|
637
654
|
it "passes through page and per_page when offset not present" do
|
638
655
|
WorkspacePresenter.search do |string, options|
|
639
|
-
options[:limit].
|
640
|
-
options[:offset].
|
641
|
-
options[:per_page].
|
642
|
-
options[:page].
|
656
|
+
expect(options[:limit]).to eq(nil)
|
657
|
+
expect(options[:offset]).to eq(nil)
|
658
|
+
expect(options[:per_page]).to eq(3)
|
659
|
+
expect(options[:page]).to eq(4)
|
643
660
|
[[1], 1]
|
644
661
|
end
|
645
662
|
|
@@ -648,10 +665,10 @@ describe Brainstem::PresenterCollection do
|
|
648
665
|
|
649
666
|
it "passes through page and per_page by default" do
|
650
667
|
WorkspacePresenter.search do |string, options|
|
651
|
-
options[:limit].
|
652
|
-
options[:offset].
|
653
|
-
options[:per_page].
|
654
|
-
options[:page].
|
668
|
+
expect(options[:limit]).to eq(nil)
|
669
|
+
expect(options[:offset]).to eq(nil)
|
670
|
+
expect(options[:per_page]).to eq(20)
|
671
|
+
expect(options[:page]).to eq(1)
|
655
672
|
[[1], 1]
|
656
673
|
end
|
657
674
|
|
@@ -664,7 +681,7 @@ describe Brainstem::PresenterCollection do
|
|
664
681
|
context "and there is no search request" do
|
665
682
|
it "does not call the search method" do
|
666
683
|
result = @presenter_collection.presenting("workspaces") { Workspace.order("id asc") }
|
667
|
-
result[:workspaces].keys.
|
684
|
+
expect(result[:workspaces].keys).to eq(Workspace.pluck(:id).map(&:to_s))
|
668
685
|
end
|
669
686
|
end
|
670
687
|
end
|
@@ -673,7 +690,7 @@ describe Brainstem::PresenterCollection do
|
|
673
690
|
context "and a search request is made" do
|
674
691
|
it "returns as if there was no search" do
|
675
692
|
result = @presenter_collection.presenting("workspaces", :params => { :search => "blah" }) { Workspace.order("id asc") }
|
676
|
-
result[:workspaces].keys.
|
693
|
+
expect(result[:workspaces].keys).to eq(Workspace.pluck(:id).map(&:to_s))
|
677
694
|
end
|
678
695
|
end
|
679
696
|
end
|
@@ -683,12 +700,12 @@ describe Brainstem::PresenterCollection do
|
|
683
700
|
context "when there is no sort provided" do
|
684
701
|
it "returns an empty array when there are no objects" do
|
685
702
|
result = @presenter_collection.presenting("workspaces") { Workspace.where(:id => nil) }
|
686
|
-
result.
|
703
|
+
expect(result).to eq(:count => 0, :workspaces => {}, :results => [])
|
687
704
|
end
|
688
705
|
|
689
706
|
it "falls back to the object's sort order when nothing is provided" do
|
690
707
|
result = @presenter_collection.presenting("workspaces") { Workspace.where(:id => [1, 3]) }
|
691
|
-
result[:workspaces].keys.
|
708
|
+
expect(result[:workspaces].keys).to eq(%w[1 3])
|
692
709
|
end
|
693
710
|
end
|
694
711
|
|
@@ -696,28 +713,28 @@ describe Brainstem::PresenterCollection do
|
|
696
713
|
WorkspacePresenter.sort_order(:description, "workspaces.description")
|
697
714
|
WorkspacePresenter.default_sort_order("description:desc")
|
698
715
|
result = @presenter_collection.presenting("workspaces") { Workspace.where("id is not null") }
|
699
|
-
result[:results].map {|i| result[:workspaces][i[:id]][:description] }.
|
716
|
+
expect(result[:results].map {|i| result[:workspaces][i[:id]][:description] }).to eq(%w(c b a 3 2 1))
|
700
717
|
end
|
701
718
|
|
702
719
|
it "allows default ordering ascending" do
|
703
720
|
WorkspacePresenter.sort_order(:description, "workspaces.description")
|
704
721
|
WorkspacePresenter.default_sort_order("description:asc")
|
705
722
|
result = @presenter_collection.presenting("workspaces") { Workspace.where("id is not null") }
|
706
|
-
result[:results].map {|i| result[:workspaces][i[:id]][:description] }.
|
723
|
+
expect(result[:results].map {|i| result[:workspaces][i[:id]][:description] }).to eq(%w(1 2 3 a b c))
|
707
724
|
end
|
708
725
|
|
709
726
|
it "applies orders that match the default order" do
|
710
727
|
WorkspacePresenter.sort_order(:description, "workspaces.description")
|
711
728
|
WorkspacePresenter.default_sort_order("description:desc")
|
712
729
|
result = @presenter_collection.presenting("workspaces", :params => { :order => "description:desc"} ) { Workspace.where("id is not null") }
|
713
|
-
result[:results].map {|i| result[:workspaces][i[:id]][:description] }.
|
730
|
+
expect(result[:results].map {|i| result[:workspaces][i[:id]][:description] }).to eq(%w(c b a 3 2 1))
|
714
731
|
end
|
715
732
|
|
716
733
|
it "applies orders that conflict with the default order" do
|
717
734
|
WorkspacePresenter.sort_order(:description, "workspaces.description")
|
718
735
|
WorkspacePresenter.default_sort_order("description:desc")
|
719
736
|
result = @presenter_collection.presenting("workspaces", :params => { :order => "description:asc"} ) { Workspace.where("id is not null") }
|
720
|
-
result[:results].map {|i| result[:workspaces][i[:id]][:description] }.
|
737
|
+
expect(result[:results].map {|i| result[:workspaces][i[:id]][:description] }).to eq(%w(1 2 3 a b c))
|
721
738
|
end
|
722
739
|
|
723
740
|
it "cleans the params" do
|
@@ -725,11 +742,11 @@ describe Brainstem::PresenterCollection do
|
|
725
742
|
WorkspacePresenter.default_sort_order("description:desc")
|
726
743
|
|
727
744
|
result = @presenter_collection.presenting("workspaces", :params => { :order => "updated_at:drop table" }) { Workspace.where("id is not null") }
|
728
|
-
result.keys.
|
745
|
+
expect(result.keys).to match_array([:count, :workspaces, :results])
|
729
746
|
|
730
747
|
result = @presenter_collection.presenting("workspaces", :params => { :order => "drop table:desc" }) { Workspace.where("id is not null") }
|
731
|
-
result.keys.
|
732
|
-
result[:results].map {|i| result[:workspaces][i[:id]][:description] }.
|
748
|
+
expect(result.keys).to match_array([:count, :workspaces, :results])
|
749
|
+
expect(result[:results].map {|i| result[:workspaces][i[:id]][:description] }).to eq(%w(c b a 3 2 1))
|
733
750
|
end
|
734
751
|
|
735
752
|
it "can take a proc" do
|
@@ -738,22 +755,22 @@ describe Brainstem::PresenterCollection do
|
|
738
755
|
|
739
756
|
# Default
|
740
757
|
result = @presenter_collection.presenting("workspaces") { Workspace.where("id is not null") }
|
741
|
-
result[:results].map {|i| result[:workspaces][i[:id]][:description] }.
|
758
|
+
expect(result[:results].map {|i| result[:workspaces][i[:id]][:description] }).to eq(%w(a 1 b 2 c 3))
|
742
759
|
|
743
760
|
# Asc
|
744
761
|
result = @presenter_collection.presenting("workspaces", :params => { :order => "id:asc" }) { Workspace.where("id is not null") }
|
745
|
-
result[:results].map {|i| result[:workspaces][i[:id]][:description] }.
|
762
|
+
expect(result[:results].map {|i| result[:workspaces][i[:id]][:description] }).to eq(%w(a 1 b 2 c 3))
|
746
763
|
|
747
764
|
# Desc
|
748
765
|
result = @presenter_collection.presenting("workspaces", :params => { :order => "id:desc" }) { Workspace.where("id is not null") }
|
749
|
-
result[:results].map {|i| result[:workspaces][i[:id]][:description] }.
|
766
|
+
expect(result[:results].map {|i| result[:workspaces][i[:id]][:description] }).to eq(%w(3 c 2 b 1 a))
|
750
767
|
end
|
751
768
|
end
|
752
769
|
|
753
770
|
describe "the :as param" do
|
754
771
|
it "determines the chosen top-level key name" do
|
755
772
|
result = @presenter_collection.presenting("workspaces", :as => :my_workspaces) { Workspace.where(:id => 1) }
|
756
|
-
result.keys.
|
773
|
+
expect(result.keys).to eq([:count, :my_workspaces, :results])
|
757
774
|
end
|
758
775
|
end
|
759
776
|
|
@@ -762,16 +779,32 @@ describe Brainstem::PresenterCollection do
|
|
762
779
|
WorkspacePresenter.filter(:owned_by) { |scope, user_id| scope.owned_by(user_id.to_i) }
|
763
780
|
|
764
781
|
result = @presenter_collection.presenting("workspaces") { Workspace.where(:id => 1) }
|
765
|
-
result[:count].
|
782
|
+
expect(result[:count]).to eq(1)
|
766
783
|
|
767
784
|
result = @presenter_collection.presenting("workspaces") { Workspace.unscoped }
|
768
|
-
result[:count].
|
785
|
+
expect(result[:count]).to eq(Workspace.count)
|
769
786
|
|
770
787
|
result = @presenter_collection.presenting("workspaces", :params => { :owned_by => bob.to_param }) { Workspace.unscoped }
|
771
|
-
result[:count].
|
788
|
+
expect(result[:count]).to eq(Workspace.owned_by(bob.to_param).count)
|
772
789
|
|
773
790
|
result = @presenter_collection.presenting("workspaces", :params => { :owned_by => bob.to_param }) { Workspace.group(:id) }
|
774
|
-
result[:count].
|
791
|
+
expect(result[:count]).to eq(Workspace.owned_by(bob.to_param).count)
|
792
|
+
end
|
793
|
+
end
|
794
|
+
|
795
|
+
describe "pretty printing" do
|
796
|
+
before do
|
797
|
+
@user_result = { :count => 1, :users => { 1 => { :username => "bob", :id => "1" } }, :results => [{ :key => "users", :id => "1" }] }
|
798
|
+
end
|
799
|
+
|
800
|
+
it "should return normal json if the pretty parameter is not true" do
|
801
|
+
result = @presenter_collection.presenting("users") { User.where(:id => 1) }
|
802
|
+
expect(result.to_json).to eq(JSON.generate(@user_result))
|
803
|
+
end
|
804
|
+
|
805
|
+
it "should return pretty printed json if the pretty parameter is true" do
|
806
|
+
result = @presenter_collection.presenting("users", :params => { :pretty => "true" }) { User.where(:id => 1) }
|
807
|
+
expect(result.to_json).to eq(JSON.pretty_generate(@user_result))
|
775
808
|
end
|
776
809
|
end
|
777
810
|
end
|
@@ -788,25 +821,25 @@ describe Brainstem::PresenterCollection do
|
|
788
821
|
end
|
789
822
|
|
790
823
|
it "returns the presenter for a given class" do
|
791
|
-
Brainstem.presenter_collection("v1").for(Array).
|
824
|
+
expect(Brainstem.presenter_collection("v1").for(Array)).to be_a(V1::ArrayPresenter)
|
792
825
|
end
|
793
826
|
|
794
827
|
it "returns nil when given nil" do
|
795
|
-
Brainstem.presenter_collection("v1").for(nil).
|
828
|
+
expect(Brainstem.presenter_collection("v1").for(nil)).to be_nil
|
796
829
|
end
|
797
830
|
|
798
831
|
it "returns nil when a given class has no presenter" do
|
799
|
-
Brainstem.presenter_collection("v1").for(String).
|
832
|
+
expect(Brainstem.presenter_collection("v1").for(String)).to be_nil
|
800
833
|
end
|
801
834
|
|
802
835
|
it "uses the default namespace when the passed namespace is nil" do
|
803
|
-
Brainstem.presenter_collection.
|
836
|
+
expect(Brainstem.presenter_collection).to eq(Brainstem.presenter_collection(nil))
|
804
837
|
end
|
805
838
|
end
|
806
839
|
|
807
840
|
describe "for! method" do
|
808
841
|
it "raises if there is no presenter for the given class" do
|
809
|
-
|
842
|
+
expect{ Brainstem.presenter_collection("v1").for!(String) }.to raise_error(ArgumentError)
|
810
843
|
end
|
811
844
|
end
|
812
845
|
end
|