brainstem 0.2.6.1 → 1.0.0.pre.1
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +5 -13
- data/CHANGELOG.md +16 -2
- data/Gemfile.lock +51 -36
- data/README.md +531 -110
- data/brainstem.gemspec +6 -2
- data/lib/brainstem.rb +25 -9
- data/lib/brainstem/concerns/controller_param_management.rb +22 -0
- data/lib/brainstem/concerns/error_presentation.rb +58 -0
- data/lib/brainstem/concerns/inheritable_configuration.rb +29 -0
- data/lib/brainstem/concerns/lookup.rb +30 -0
- data/lib/brainstem/concerns/presenter_dsl.rb +111 -0
- data/lib/brainstem/controller_methods.rb +17 -8
- data/lib/brainstem/dsl/association.rb +55 -0
- data/lib/brainstem/dsl/associations_block.rb +12 -0
- data/lib/brainstem/dsl/base_block.rb +31 -0
- data/lib/brainstem/dsl/conditional.rb +25 -0
- data/lib/brainstem/dsl/conditionals_block.rb +15 -0
- data/lib/brainstem/dsl/configuration.rb +112 -0
- data/lib/brainstem/dsl/field.rb +68 -0
- data/lib/brainstem/dsl/fields_block.rb +25 -0
- data/lib/brainstem/preloader.rb +98 -0
- data/lib/brainstem/presenter.rb +325 -134
- data/lib/brainstem/presenter_collection.rb +82 -286
- data/lib/brainstem/presenter_validator.rb +96 -0
- data/lib/brainstem/query_strategies/README.md +107 -0
- data/lib/brainstem/query_strategies/base_strategy.rb +62 -0
- data/lib/brainstem/query_strategies/filter_and_search.rb +50 -0
- data/lib/brainstem/query_strategies/filter_or_search.rb +103 -0
- data/lib/brainstem/test_helpers.rb +5 -1
- data/lib/brainstem/version.rb +1 -1
- data/spec/brainstem/concerns/controller_param_management_spec.rb +42 -0
- data/spec/brainstem/concerns/error_presentation_spec.rb +113 -0
- data/spec/brainstem/concerns/inheritable_configuration_spec.rb +210 -0
- data/spec/brainstem/concerns/presenter_dsl_spec.rb +412 -0
- data/spec/brainstem/controller_methods_spec.rb +15 -27
- data/spec/brainstem/dsl/association_spec.rb +123 -0
- data/spec/brainstem/dsl/conditional_spec.rb +93 -0
- data/spec/brainstem/dsl/configuration_spec.rb +1 -0
- data/spec/brainstem/dsl/field_spec.rb +212 -0
- data/spec/brainstem/preloader_spec.rb +137 -0
- data/spec/brainstem/presenter_collection_spec.rb +565 -244
- data/spec/brainstem/presenter_spec.rb +726 -167
- data/spec/brainstem/presenter_validator_spec.rb +209 -0
- data/spec/brainstem/query_strategies/filter_and_search_spec.rb +46 -0
- data/spec/brainstem/query_strategies/filter_or_search_spec.rb +45 -0
- data/spec/spec_helper.rb +11 -3
- data/spec/spec_helpers/db.rb +32 -65
- data/spec/spec_helpers/presenters.rb +124 -29
- data/spec/spec_helpers/rr.rb +11 -0
- data/spec/spec_helpers/schema.rb +115 -0
- metadata +126 -30
- data/lib/brainstem/association_field.rb +0 -53
- data/lib/brainstem/engine.rb +0 -4
- data/pkg/brainstem-0.2.5.gem +0 -0
- data/pkg/brainstem-0.2.6.gem +0 -0
- data/spec/spec_helpers/cleanup.rb +0 -23
@@ -0,0 +1,209 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
require 'brainstem/presenter_validator'
|
3
|
+
|
4
|
+
describe Brainstem::PresenterValidator do
|
5
|
+
let(:presenter_class) do
|
6
|
+
Class.new(Brainstem::Presenter) do
|
7
|
+
presents Workspace
|
8
|
+
|
9
|
+
preload :lead_user
|
10
|
+
|
11
|
+
conditionals do
|
12
|
+
model :title_is_hello, lambda { |model| model.title == 'hello' }, 'visible when the title is hello'
|
13
|
+
request :user_is_bob, lambda { current_user == 'bob' }, 'visible only to bob'
|
14
|
+
end
|
15
|
+
|
16
|
+
fields do
|
17
|
+
field :title, :string
|
18
|
+
field :description, :string
|
19
|
+
field :updated_at, :datetime
|
20
|
+
field :secret, :string, 'a secret, via secret_info',
|
21
|
+
via: :secret_info,
|
22
|
+
if: [:user_is_bob, :title_is_hello]
|
23
|
+
|
24
|
+
with_options if: :user_is_bob do
|
25
|
+
field :bob_title, :string, 'another name for the title, only for Bob',
|
26
|
+
via: :title
|
27
|
+
end
|
28
|
+
end
|
29
|
+
|
30
|
+
associations do
|
31
|
+
association :tasks, Task, 'The Tasks in this Workspace',
|
32
|
+
restrict_to_only: true
|
33
|
+
association :lead_user, User, 'The user who runs this Workspace'
|
34
|
+
association :subtasks, Task, 'Only Tasks in this Workspace that are subtasks',
|
35
|
+
dynamic: lambda { |workspace| workspace.tasks.where('parent_id IS NOT NULL') }
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
let(:validator) { Brainstem::PresenterValidator.new(presenter_class) }
|
41
|
+
|
42
|
+
it 'should be valid' do
|
43
|
+
expect(presenter_class.configuration[:preloads]).not_to be_empty
|
44
|
+
expect(presenter_class.configuration[:fields]).not_to be_empty
|
45
|
+
expect(presenter_class.configuration[:associations]).not_to be_empty
|
46
|
+
expect(validator).to be_valid
|
47
|
+
end
|
48
|
+
|
49
|
+
describe 'validating preload' do
|
50
|
+
it 'adds an error if the requested preload does not exist on the presented class' do
|
51
|
+
presenter_class.preload :foo
|
52
|
+
expect(validator).not_to be_valid
|
53
|
+
expect(validator.errors[:preload]).to eq ["not all presented classes respond to 'foo'"]
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'adds an error if the requested preload does not exist on only one of the presented classes' do
|
57
|
+
presenter_class.presents User
|
58
|
+
expect(validator).not_to be_valid
|
59
|
+
expect(validator.errors[:preload]).to eq ["not all presented classes respond to 'lead_user'"]
|
60
|
+
end
|
61
|
+
|
62
|
+
it 'supports nested preloads' do
|
63
|
+
presenter_class.preload foo: :bar
|
64
|
+
expect(validator).not_to be_valid
|
65
|
+
expect(validator.errors[:preload]).to eq ["not all presented classes respond to 'foo'"]
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
describe 'validating associations' do
|
70
|
+
it 'adds an error for any field that is not on all presented classes' do
|
71
|
+
presenter_class.associations do
|
72
|
+
association :foo, Workspace
|
73
|
+
association :bar, Workspace
|
74
|
+
end
|
75
|
+
|
76
|
+
expect(validator).not_to be_valid
|
77
|
+
expect(validator.errors[:associations]).to eq ["'foo' is not valid because not all presented classes respond to 'foo'",
|
78
|
+
"'bar' is not valid because not all presented classes respond to 'bar'"]
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'adds an error for any association that does not have a known presenter' do
|
82
|
+
class NewConcept; end
|
83
|
+
|
84
|
+
presenter_class.associations do
|
85
|
+
association :tasks, NewConcept
|
86
|
+
end
|
87
|
+
|
88
|
+
expect(validator).not_to be_valid
|
89
|
+
expect(validator.errors[:associations]).to eq ["'tasks' is not valid because no presenter could be found for the NewConcept class"]
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
describe 'validating fields' do
|
94
|
+
it 'adds an error for any field that is not on all presented classes' do
|
95
|
+
presenter_class.fields do
|
96
|
+
field :foo, :string
|
97
|
+
field :bar, :integer
|
98
|
+
end
|
99
|
+
|
100
|
+
expect(validator).not_to be_valid
|
101
|
+
expect(validator.errors[:fields]).to eq ["'foo' is not valid because not all presented classes respond to 'foo'",
|
102
|
+
"'bar' is not valid because not all presented classes respond to 'bar'"]
|
103
|
+
end
|
104
|
+
|
105
|
+
it 'errors when one of the presented classes is missing a field' do
|
106
|
+
presenter_class.presents User
|
107
|
+
expect(validator).not_to be_valid
|
108
|
+
expect(validator.errors[:fields]).to be_present
|
109
|
+
end
|
110
|
+
|
111
|
+
it 'supports :via' do
|
112
|
+
presenter_class.fields do
|
113
|
+
field :foo, :string, via: :title
|
114
|
+
end
|
115
|
+
|
116
|
+
expect(validator).to be_valid
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'supports dynamic fields' do
|
120
|
+
presenter_class.fields do
|
121
|
+
field :foo, :string, dynamic: lambda { 2 }
|
122
|
+
end
|
123
|
+
|
124
|
+
expect(validator).to be_valid
|
125
|
+
end
|
126
|
+
|
127
|
+
it 'supports nested fields' do
|
128
|
+
presenter_class.fields do
|
129
|
+
fields :permissions do
|
130
|
+
field :title, :string
|
131
|
+
end
|
132
|
+
end
|
133
|
+
|
134
|
+
expect(validator).to be_valid
|
135
|
+
|
136
|
+
presenter_class.fields do
|
137
|
+
fields :permissions do
|
138
|
+
field :missing, :string
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
expect(validator).not_to be_valid
|
143
|
+
expect(validator.errors[:fields]).to eq ["'missing' is not valid because not all presented classes respond to 'missing'"]
|
144
|
+
end
|
145
|
+
|
146
|
+
it "checks that any 'if' option has a matching conditional(s)" do
|
147
|
+
expect(presenter_class.configuration[:conditionals][:title_is_hello]).to be_present
|
148
|
+
|
149
|
+
presenter_class.fields do
|
150
|
+
field :title, :string, if: :title_is_hello
|
151
|
+
end
|
152
|
+
|
153
|
+
expect(validator).to be_valid
|
154
|
+
|
155
|
+
presenter_class.fields do
|
156
|
+
field :title, :string, if: [:title_is_hello, :wat]
|
157
|
+
end
|
158
|
+
|
159
|
+
expect(validator).not_to be_valid
|
160
|
+
expect(validator.errors[:fields]).to eq ["'title' is not valid because one or more of the specified conditionals does not exist"]
|
161
|
+
end
|
162
|
+
|
163
|
+
it "checks conditionals on nested fields too" do
|
164
|
+
presenter_class.fields do
|
165
|
+
fields :permissions do
|
166
|
+
field :title, :string, if: [:title_is_hello, :wat]
|
167
|
+
end
|
168
|
+
end
|
169
|
+
|
170
|
+
expect(validator).not_to be_valid
|
171
|
+
expect(validator.errors[:fields]).to eq ["'title' is not valid because one or more of the specified conditionals does not exist"]
|
172
|
+
end
|
173
|
+
end
|
174
|
+
|
175
|
+
describe 'validating sort_orders' do
|
176
|
+
it 'checks that a default is selected if any sort_orders are defined' do
|
177
|
+
presenter_class.sort_order :alphabetical, "workspaces.title"
|
178
|
+
expect(validator).not_to be_valid
|
179
|
+
expect(validator.errors[:default_sort_order]).to eq ["A default_sort_order is highly recommended if any sort_orders are declared"]
|
180
|
+
|
181
|
+
presenter_class.default_sort_order "alphabetical:desc"
|
182
|
+
expect(validator).to be_valid
|
183
|
+
end
|
184
|
+
|
185
|
+
it 'checks that a default matches an existing sort_order' do
|
186
|
+
presenter_class.default_sort_order "alphabetical:desc"
|
187
|
+
|
188
|
+
expect(validator).not_to be_valid
|
189
|
+
expect(validator.errors[:default_sort_order]).to eq ["The declared default_sort_order ('alphabetical') does not match an existing sort_order"]
|
190
|
+
|
191
|
+
presenter_class.sort_order :alphabetical, "workspaces.title"
|
192
|
+
expect(validator).to be_valid
|
193
|
+
end
|
194
|
+
end
|
195
|
+
|
196
|
+
describe 'validating presence of brainstem_key' do
|
197
|
+
it 'requires a brainstem_key to be defined when more than one object is being presented' do
|
198
|
+
presenter_class.presents Task
|
199
|
+
expect(validator).not_to be_valid
|
200
|
+
expect(validator.errors[:brainstem_key]).to eq ["a brainstem_key must be provided when multiple classes are presented."]
|
201
|
+
end
|
202
|
+
end
|
203
|
+
|
204
|
+
specify 'all spec presenters should be valid' do
|
205
|
+
Brainstem.presenter_collection.presenters.each do |name, klass|
|
206
|
+
expect(Brainstem::PresenterValidator.new(klass)).to be_valid
|
207
|
+
end
|
208
|
+
end
|
209
|
+
end
|
@@ -0,0 +1,46 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Brainstem::QueryStrategies::FilterAndSearch do
|
4
|
+
let(:bob) { User.find_by_username('bob') }
|
5
|
+
|
6
|
+
let(:params) { {
|
7
|
+
"owned_by" => bob.id.to_s,
|
8
|
+
per_page: 7,
|
9
|
+
page: 1,
|
10
|
+
search: 'toot, otto, toot',
|
11
|
+
} }
|
12
|
+
|
13
|
+
let(:options) { {
|
14
|
+
primary_presenter: CheesePresenter.new,
|
15
|
+
table_name: 'cheeses',
|
16
|
+
default_per_page: 20,
|
17
|
+
default_max_per_page: 200,
|
18
|
+
default_max_filter_and_search_page: 500,
|
19
|
+
params: params
|
20
|
+
} }
|
21
|
+
|
22
|
+
describe '#execute' do
|
23
|
+
before do
|
24
|
+
CheesePresenter.search do |string, options|
|
25
|
+
[[2,3,4,5,6,8,9,10,11,12], 11]
|
26
|
+
end
|
27
|
+
|
28
|
+
CheesePresenter.filter(:owned_by) { |scope, user_id| scope.owned_by(user_id.to_i) }
|
29
|
+
CheesePresenter.sort_order(:id) { |scope, direction| scope.order("cheeses.id #{direction}") }
|
30
|
+
end
|
31
|
+
|
32
|
+
it 'takes the intersection of the search and filter results' do
|
33
|
+
results, count = described_class.new(options).execute(Cheese.unscoped)
|
34
|
+
expect(count).to eq(8)
|
35
|
+
expect(results.map(&:id)).to eq([2,3,4,5,8,10,11])
|
36
|
+
end
|
37
|
+
|
38
|
+
it "applies ordering to the scope" do
|
39
|
+
options[:params]["order"] = 'id:desc'
|
40
|
+
proxy.instance_of(Brainstem::Presenter).apply_ordering_to_scope(anything, anything).times(1)
|
41
|
+
results, count = described_class.new(options).execute(Cheese.unscoped)
|
42
|
+
expect(count).to eq(8)
|
43
|
+
expect(results.map(&:id)).to eq([12,11,10,8,5,4,3])
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
@@ -0,0 +1,45 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
# The functionality of this is mainly tested in more integration-y tests in presenter_collection_spec.rb
|
4
|
+
|
5
|
+
describe Brainstem::QueryStrategies::FilterOrSearch do
|
6
|
+
describe '#execute' do
|
7
|
+
context 'we are searching' do
|
8
|
+
let(:subject) { described_class.new(@options) }
|
9
|
+
|
10
|
+
before do
|
11
|
+
@options = { primary_presenter: WorkspacePresenter.new,
|
12
|
+
table_name: 'workspaces',
|
13
|
+
default_per_page: 20,
|
14
|
+
default_max_per_page: 200,
|
15
|
+
params: { search: "cheese" } }
|
16
|
+
|
17
|
+
@results = [Workspace.first, Workspace.second]
|
18
|
+
|
19
|
+
WorkspacePresenter.search do |string|
|
20
|
+
[[1,2], 2]
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
it 'returns the primary models and count' do
|
25
|
+
expect(subject.execute(Workspace.unscoped)).to eq([@results, 2])
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
context 'we are not searching' do
|
30
|
+
let(:subject) { described_class.new(@options) }
|
31
|
+
|
32
|
+
before do
|
33
|
+
@options = { primary_presenter: WorkspacePresenter.new,
|
34
|
+
table_name: 'workspaces',
|
35
|
+
default_per_page: 20,
|
36
|
+
default_max_per_page: 200,
|
37
|
+
params: { } }
|
38
|
+
end
|
39
|
+
|
40
|
+
it 'returns the primary models and count' do
|
41
|
+
expect(subject.execute(Workspace.unscoped)).to eq([Workspace.unscoped.to_a, Workspace.count])
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
data/spec/spec_helper.rb
CHANGED
@@ -3,20 +3,28 @@ require 'logger'
|
|
3
3
|
require 'rr'
|
4
4
|
require 'rspec'
|
5
5
|
require 'sqlite3'
|
6
|
+
require 'database_cleaner'
|
7
|
+
require 'pry'
|
8
|
+
require 'pry-nav'
|
6
9
|
|
7
10
|
require 'brainstem'
|
11
|
+
require_relative 'spec_helpers/schema'
|
8
12
|
require_relative 'spec_helpers/db'
|
9
|
-
require_relative 'spec_helpers/
|
13
|
+
require_relative 'spec_helpers/rr'
|
14
|
+
|
15
|
+
DatabaseCleaner.strategy = :transaction
|
10
16
|
|
11
17
|
RSpec.configure do |config|
|
12
18
|
config.mock_with :rr
|
13
19
|
|
14
20
|
config.before(:each) do
|
15
21
|
Brainstem.logger = Logger.new(StringIO.new)
|
22
|
+
Brainstem.reset!
|
23
|
+
load File.join(File.dirname(__FILE__), 'spec_helpers', 'presenters.rb')
|
24
|
+
DatabaseCleaner.start
|
16
25
|
end
|
17
26
|
|
18
27
|
config.after(:each) do
|
19
|
-
|
20
|
-
Brainstem.default_namespace = nil
|
28
|
+
DatabaseCleaner.clean
|
21
29
|
end
|
22
30
|
end
|
data/spec/spec_helpers/db.rb
CHANGED
@@ -1,65 +1,3 @@
|
|
1
|
-
ActiveRecord::Base.establish_connection(:adapter => 'sqlite3', :database => ':memory:')
|
2
|
-
ActiveRecord::Schema.define do
|
3
|
-
self.verbose = false
|
4
|
-
|
5
|
-
create_table :users, :force => true do |t|
|
6
|
-
t.string :username
|
7
|
-
t.timestamps null: true
|
8
|
-
end
|
9
|
-
|
10
|
-
create_table :workspaces, :force => true do |t|
|
11
|
-
t.string :title
|
12
|
-
t.string :description
|
13
|
-
t.belongs_to :user
|
14
|
-
t.timestamps null: true
|
15
|
-
end
|
16
|
-
|
17
|
-
create_table :tasks, :force => true do |t|
|
18
|
-
t.string :name
|
19
|
-
t.integer :parent_id
|
20
|
-
t.belongs_to :workspace
|
21
|
-
t.timestamps null: true
|
22
|
-
end
|
23
|
-
|
24
|
-
create_table :posts, :force => true do |t|
|
25
|
-
t.string :body
|
26
|
-
t.integer :subject_id
|
27
|
-
t.string :subject_type
|
28
|
-
t.timestamps null: true
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
class User < ActiveRecord::Base
|
33
|
-
has_many :workspaces
|
34
|
-
end
|
35
|
-
|
36
|
-
class Task < ActiveRecord::Base
|
37
|
-
belongs_to :workspace
|
38
|
-
has_many :sub_tasks, :foreign_key => :parent_id, :class_name => "Task"
|
39
|
-
has_many :posts
|
40
|
-
|
41
|
-
def tags
|
42
|
-
%w[some tags]
|
43
|
-
end
|
44
|
-
end
|
45
|
-
|
46
|
-
class Workspace < ActiveRecord::Base
|
47
|
-
belongs_to :user
|
48
|
-
has_many :tasks
|
49
|
-
has_many :posts
|
50
|
-
|
51
|
-
scope :owned_by, -> id { where(:user_id => id) }
|
52
|
-
scope :numeric_description, -> description { where(:description => ["1", "2", "3"]) }
|
53
|
-
|
54
|
-
def lead_user
|
55
|
-
user
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
class Post < ActiveRecord::Base
|
60
|
-
belongs_to :subject, :polymorphic => true
|
61
|
-
end
|
62
|
-
|
63
1
|
User.create!(:id => 1, :username => "bob")
|
64
2
|
User.create!(:id => 2, :username => "jane")
|
65
3
|
|
@@ -70,11 +8,40 @@ Workspace.create!(:id => 4, :user_id => 1, :title => "bob workspace 4", :descrip
|
|
70
8
|
Workspace.create!(:id => 5, :user_id => 2, :title => "jane workspace 1", :description => "c")
|
71
9
|
Workspace.create!(:id => 6, :user_id => 2, :title => "jane workspace 2", :description => "3")
|
72
10
|
|
11
|
+
|
12
|
+
Cheese.create!(id: 1, user_id: 1, flavor: 'colby jack' )
|
13
|
+
Cheese.create!(id: 2, user_id: 1, flavor: 'swiss' )
|
14
|
+
Cheese.create!(id: 3, user_id: 1, flavor: 'cheese curds' )
|
15
|
+
Cheese.create!(id: 4, user_id: 1, flavor: 'bleu' )
|
16
|
+
Cheese.create!(id: 5, user_id: 1, flavor: 'cheddar' )
|
17
|
+
Cheese.create!(id: 6, user_id: 2, flavor: 'gorgonzola' )
|
18
|
+
Cheese.create!(id: 7, user_id: 1, flavor: 'search text' )
|
19
|
+
Cheese.create!(id: 8, user_id: 1, flavor: 'search text' )
|
20
|
+
Cheese.create!(id: 9, user_id: 2, flavor: 'provologne')
|
21
|
+
Cheese.create!(id: 10, user_id: 1, flavor: 'old amsterdam' )
|
22
|
+
Cheese.create!(id: 11, user_id: 1, flavor: 'brie' )
|
23
|
+
Cheese.create!(id: 12, user_id: 1, flavor: 'wensleydale' )
|
24
|
+
# Group.create!(:id => 7, :user_id => 2, :title => "a group", :description => "this is a group")
|
25
|
+
|
73
26
|
Task.create!(:id => 1, :workspace_id => 1, :name => "Buy milk")
|
74
27
|
Task.create!(:id => 2, :workspace_id => 1, :name => "Buy bananas")
|
75
28
|
Task.create!(:id => 3, :workspace_id => 1, :parent_id => 2, :name => "Green preferred")
|
76
29
|
Task.create!(:id => 4, :workspace_id => 1, :parent_id => 2, :name => "One bunch")
|
30
|
+
Task.create!(:id => 5, :workspace_id => 6, :name => "In another Workspace")
|
31
|
+
|
32
|
+
Post.create!(:id => 1, :user_id => 1, :subject => Workspace.first, :body => "first post!")
|
33
|
+
Post.create!(:id => 2, :user_id => 1, :subject => Task.first, :body => "this is important. get on it!")
|
34
|
+
Post.create!(:id => 3, :user_id => 2, :body => "Post without subject")
|
35
|
+
|
36
|
+
Attachments::PostAttachment.create!(id: 1, subject: Post.first, filename: 'I am an attachment on a post')
|
37
|
+
Attachments::TaskAttachment.create!(id: 2, subject: Task.first, filename: 'I am an attachment on a task')
|
38
|
+
|
39
|
+
# TODO:
|
40
|
+
# user can group all of an STI structure into the brainstem_keys, of their choice
|
41
|
+
# user can put STI classes into seperate brainstem_keys
|
42
|
+
# inheriting presenters and showing that they work
|
43
|
+
# Spec presents attachments, including their subjects, shows that right presenters are used.
|
44
|
+
# Show that the base class is always used as the brainstem_key for attachments as polymorphic association targets
|
77
45
|
|
78
|
-
|
79
|
-
|
80
|
-
Post.create!(:id => 3, :body => "Post without subject")
|
46
|
+
# Use Group / Workspace as the polymorphic target of an association where we do not
|
47
|
+
# want the baseclass to be used as the brainstem_key (like line_items0)
|
@@ -1,43 +1,138 @@
|
|
1
1
|
class WorkspacePresenter < Brainstem::Presenter
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
2
|
+
presents Workspace
|
3
|
+
|
4
|
+
helper do
|
5
|
+
def current_user
|
6
|
+
'jane'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
preload :lead_user
|
11
|
+
|
12
|
+
conditionals do
|
13
|
+
model :title_is_hello, lambda { |model| model.title == 'hello' }, 'visible when the title is hello'
|
14
|
+
request :user_is_bob, lambda { current_user == 'bob' }, 'visible only to bob'
|
15
|
+
end
|
16
|
+
|
17
|
+
fields do
|
18
|
+
field :title, :string
|
19
|
+
field :description, :string
|
20
|
+
field :updated_at, :datetime
|
21
|
+
field :dynamic_title, :string, dynamic: lambda { |model| "title: #{model.title}" }
|
22
|
+
field :lookup_title, :string, lookup: lambda { |models| Hash[models.map { |model| [model.id, "lookup_title: #{model.title}"] }] }
|
23
|
+
field :lookup_fetch_title, :string,
|
24
|
+
lookup: lambda { |models| Hash[models.map { |model| [model.id, model.title] }] },
|
25
|
+
lookup_fetch: lambda { |lookup, model| "lookup_fetch_title: #{lookup[model.id]}" }
|
26
|
+
field :expensive_title, :string, via: :title, optional: true
|
27
|
+
field :expensive_title2, :string, via: :title, optional: true
|
28
|
+
field :expensive_title3, :string, via: :title, optional: true
|
29
|
+
field :conditional_expensive_title, :string, via: :title, optional: true, if: :title_is_hello
|
30
|
+
|
31
|
+
fields :permissions do
|
32
|
+
field :access_level, :integer, dynamic: lambda { 2 }
|
33
|
+
end
|
34
|
+
|
35
|
+
field :hello_title, :string, 'the title, when hello',
|
36
|
+
dynamic: lambda { 'title is hello' },
|
37
|
+
if: :title_is_hello
|
38
|
+
|
39
|
+
field :secret, :string, 'a secret, via secret_info',
|
40
|
+
via: :secret_info,
|
41
|
+
if: [:user_is_bob, :title_is_hello]
|
42
|
+
|
43
|
+
with_options if: :user_is_bob do
|
44
|
+
field :bob_title, :string, 'another name for the title, only for Bob',
|
45
|
+
via: :title
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
associations do
|
50
|
+
association :tasks, Task, 'The Tasks in this Workspace'
|
51
|
+
association :lead_user, User, 'The user who runs this Workspace'
|
52
|
+
# association :subtasks, Task, 'Only Tasks in this Workspace that are subtasks',
|
53
|
+
# dynamic: lambda { |workspace| workspace.tasks.where('parent_id IS NOT NULL') },
|
54
|
+
# brainstem_key: 'sub_tasks'
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
class CheesePresenter < Brainstem::Presenter
|
59
|
+
presents Cheese
|
60
|
+
|
61
|
+
fields do
|
62
|
+
field :flavor, :string
|
63
|
+
end
|
64
|
+
|
65
|
+
associations do
|
66
|
+
association :user, User, 'The owner of the cheese'
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
class GroupPresenter < Brainstem::Presenter
|
71
|
+
presents Group
|
72
|
+
|
73
|
+
fields do
|
74
|
+
field :title, :string
|
75
|
+
end
|
76
|
+
|
77
|
+
associations do
|
78
|
+
association :tasks, Task, 'The Tasks in this Group'
|
10
79
|
end
|
11
80
|
end
|
12
81
|
|
13
82
|
class TaskPresenter < Brainstem::Presenter
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
83
|
+
presents Task
|
84
|
+
|
85
|
+
fields do
|
86
|
+
field :name, :string
|
87
|
+
end
|
88
|
+
|
89
|
+
associations do
|
90
|
+
association :sub_tasks, Task
|
91
|
+
association :other_tasks, Task, 'another copy of the sub_tasks association',
|
92
|
+
via: :sub_tasks
|
93
|
+
association :workspace, Workspace
|
94
|
+
association :restricted, Task, 'only available on only / show requests',
|
95
|
+
dynamic: lambda { |task| Task.last },
|
96
|
+
restrict_to_only: true
|
22
97
|
end
|
23
98
|
end
|
24
99
|
|
25
100
|
class UserPresenter < Brainstem::Presenter
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
101
|
+
presents User
|
102
|
+
|
103
|
+
fields do
|
104
|
+
field :username, :string
|
105
|
+
end
|
106
|
+
|
107
|
+
associations do
|
108
|
+
association :odd_workspaces, Workspace, 'only the odd numbered workspaces',
|
109
|
+
dynamic: lambda { |user| user.workspaces.select { |workspace| workspace.id % 2 == 1 } }
|
33
110
|
end
|
34
111
|
end
|
35
|
-
|
112
|
+
|
36
113
|
class PostPresenter < Brainstem::Presenter
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
114
|
+
presents Post
|
115
|
+
|
116
|
+
fields do
|
117
|
+
field :body, :string
|
118
|
+
end
|
119
|
+
|
120
|
+
associations do
|
121
|
+
association :subject, :polymorphic
|
122
|
+
association :attachments, Attachments::PostAttachment
|
42
123
|
end
|
43
124
|
end
|
125
|
+
|
126
|
+
class AttachmentPresenter < Brainstem::Presenter
|
127
|
+
presents Attachments::TaskAttachment, Attachments::PostAttachment
|
128
|
+
|
129
|
+
brainstem_key :attachments
|
130
|
+
|
131
|
+
fields do
|
132
|
+
field :filename, :string
|
133
|
+
end
|
134
|
+
|
135
|
+
associations do
|
136
|
+
association :subject, :polymorphic
|
137
|
+
end
|
138
|
+
end
|