action_blocks 0.1.0
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- checksums.yaml +7 -0
- data/LICENSE +21 -0
- data/README.md +121 -0
- data/Rakefile +33 -0
- data/app/assets/config/action_blocks.js +2 -0
- data/app/assets/javascripts/action_blocks/application.js +15 -0
- data/app/assets/stylesheets/action_blocks/application.css +15 -0
- data/app/controllers/action_blocks/attachments_controller.rb +22 -0
- data/app/controllers/action_blocks/barchart_blocks_controller.rb +14 -0
- data/app/controllers/action_blocks/base_controller.rb +22 -0
- data/app/controllers/action_blocks/blocks_controller.rb +16 -0
- data/app/controllers/action_blocks/command_blocks_controller.rb +6 -0
- data/app/controllers/action_blocks/form_blocks_controller.rb +13 -0
- data/app/controllers/action_blocks/model_blocks_controller.rb +13 -0
- data/app/controllers/action_blocks/table_blocks_controller.rb +13 -0
- data/app/controllers/action_blocks/workspace_blocks_controller.rb +6 -0
- data/app/helpers/action_blocks/application_helper.rb +4 -0
- data/app/jobs/action_blocks/application_job.rb +4 -0
- data/app/mailers/action_blocks/application_mailer.rb +6 -0
- data/app/models/action_blocks/application_record.rb +5 -0
- data/app/views/layouts/action_blocks/application.html.erb +16 -0
- data/config/initializers/action_blocks.rb +9 -0
- data/config/routes.rb +10 -0
- data/lib/action_block_loader.rb +120 -0
- data/lib/action_blocks.rb +76 -0
- data/lib/action_blocks/builders/authorization_builder.rb +21 -0
- data/lib/action_blocks/builders/barchart_builder.rb +48 -0
- data/lib/action_blocks/builders/base_builder.rb +221 -0
- data/lib/action_blocks/builders/block_type.rb +11 -0
- data/lib/action_blocks/builders/command_builder.rb +6 -0
- data/lib/action_blocks/builders/form_builder.rb +117 -0
- data/lib/action_blocks/builders/layout_builder.rb +15 -0
- data/lib/action_blocks/builders/model_builder.rb +566 -0
- data/lib/action_blocks/builders/summary_field_aggregation_functions.rb +41 -0
- data/lib/action_blocks/builders/table_builder.rb +259 -0
- data/lib/action_blocks/builders/workspace_builder.rb +282 -0
- data/lib/action_blocks/data_engine/authorization_adapter.rb +127 -0
- data/lib/action_blocks/data_engine/data_engine.rb +116 -0
- data/lib/action_blocks/data_engine/database_functions.rb +39 -0
- data/lib/action_blocks/data_engine/fields_engine.rb +103 -0
- data/lib/action_blocks/data_engine/filter_adapter.rb +105 -0
- data/lib/action_blocks/data_engine/filter_engine.rb +88 -0
- data/lib/action_blocks/data_engine/selections_via_where_engine.rb +134 -0
- data/lib/action_blocks/data_engine/summary_engine.rb +72 -0
- data/lib/action_blocks/engine.rb +5 -0
- data/lib/action_blocks/error.rb +62 -0
- data/lib/action_blocks/generator_helper.rb +134 -0
- data/lib/action_blocks/generators/action_blocks/model_block/USAGE +8 -0
- data/lib/action_blocks/generators/action_blocks/model_block/model_block_generator.rb +17 -0
- data/lib/action_blocks/generators/action_blocks/model_block/templates/model_block.rb +13 -0
- data/lib/action_blocks/generators/action_blocks/type/USAGE +8 -0
- data/lib/action_blocks/generators/action_blocks/type/templates/controller.rb +3 -0
- data/lib/action_blocks/generators/action_blocks/type/templates/dsl.rb +38 -0
- data/lib/action_blocks/generators/action_blocks/type/templates/type.css +3 -0
- data/lib/action_blocks/generators/action_blocks/type/templates/type.js +22 -0
- data/lib/action_blocks/generators/action_blocks/type/type_generator.rb +33 -0
- data/lib/action_blocks/store.rb +151 -0
- data/lib/action_blocks/version.rb +3 -0
- data/lib/generators/active_blocks/model_block/USAGE +8 -0
- data/lib/generators/active_blocks/model_block/model_block_generator.rb +17 -0
- data/lib/generators/active_blocks/model_block/templates/model_block.rb +13 -0
- data/lib/generators/active_blocks/type/USAGE +8 -0
- data/lib/generators/active_blocks/type/templates/controller.rb +3 -0
- data/lib/generators/active_blocks/type/templates/dsl.rb +38 -0
- data/lib/generators/active_blocks/type/templates/type.css +3 -0
- data/lib/generators/active_blocks/type/templates/type.js +22 -0
- data/lib/generators/active_blocks/type/type_generator.rb +33 -0
- data/lib/tasks/active_blocks_tasks.rake +4 -0
- metadata +128 -0
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
module ActionBlocks
|
|
2
|
+
module SummaryFieldAggregationFunctions
|
|
3
|
+
def count
|
|
4
|
+
# mk = @parent_reference.related_model_key.clone
|
|
5
|
+
# mk["model-"]=''
|
|
6
|
+
# field_key = "field-#{mk}-#{id}"
|
|
7
|
+
# field_builder = ActionBlocks.find(field_key)
|
|
8
|
+
|
|
9
|
+
return {
|
|
10
|
+
path: [:id],
|
|
11
|
+
function: ->(*args) { count(*args) }\
|
|
12
|
+
}
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def concat(field, delimiter)
|
|
16
|
+
mk = @parent_reference.related_model_key.clone
|
|
17
|
+
mk["model-"]=''
|
|
18
|
+
field_key = "field-#{mk}-#{field}"
|
|
19
|
+
puts field_key
|
|
20
|
+
field_builder = ActionBlocks.find(field_key)
|
|
21
|
+
|
|
22
|
+
return {
|
|
23
|
+
path: field_builder.select_requirements[:path],
|
|
24
|
+
function: ->(*args) { string_agg(delimiter, *args) }
|
|
25
|
+
}
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def every(field, predicate, value)
|
|
29
|
+
mk = @parent_reference.related_model_key.clone
|
|
30
|
+
mk["model-"]=''
|
|
31
|
+
field_key = "field-#{mk}-#{field}"
|
|
32
|
+
puts field_key
|
|
33
|
+
field_builder = ActionBlocks.find(field_key)
|
|
34
|
+
|
|
35
|
+
return {
|
|
36
|
+
path: field_builder.select_requirements[:path],
|
|
37
|
+
function: ->(*args) { every(predicate, value, *args) }
|
|
38
|
+
}
|
|
39
|
+
end
|
|
40
|
+
end
|
|
41
|
+
end
|
|
@@ -0,0 +1,259 @@
|
|
|
1
|
+
module ActionBlocks
|
|
2
|
+
class TableBuilder < ActionBlocks::BlockType
|
|
3
|
+
attr_accessor :workspace, :subspace, :dashboard
|
|
4
|
+
block_type :table
|
|
5
|
+
sets :title
|
|
6
|
+
sets :scope
|
|
7
|
+
sets :view
|
|
8
|
+
sets :columns
|
|
9
|
+
references :model
|
|
10
|
+
references :selection_model
|
|
11
|
+
references :selection, handler: -> (builder, model, selection) {
|
|
12
|
+
builder.selection_model_key = "model-#{model}"
|
|
13
|
+
builder.selection_key = "selection-#{model}-#{selection}"
|
|
14
|
+
}
|
|
15
|
+
|
|
16
|
+
builds_many :table_columns, :col, 'ActionBlocks::TableColumnBuilder'
|
|
17
|
+
|
|
18
|
+
validates :columns, presence: true
|
|
19
|
+
validates :model_key, presence: true
|
|
20
|
+
|
|
21
|
+
validate :scope, :validate_scope
|
|
22
|
+
def validate_scope
|
|
23
|
+
return unless scope
|
|
24
|
+
errors.add(:scope, 'requires Proc -> () {}') if scope.class != Proc
|
|
25
|
+
if scope && scope.class == Proc
|
|
26
|
+
valid_parameters = [%i[keyreq current_user]]
|
|
27
|
+
valid_parameters << [:keyreq, dashboard.model.id.to_sym] if dashboard.model
|
|
28
|
+
valid_parameters << [:keyreq, subspace.model.id.to_sym] if subspace.model
|
|
29
|
+
invalid_parameters = scope.parameters - valid_parameters
|
|
30
|
+
errors.add(:scope, "has invalid parameter: #{invalid_parameters} Allowed: #{valid_parameters}") unless invalid_parameters.empty?
|
|
31
|
+
end
|
|
32
|
+
end
|
|
33
|
+
|
|
34
|
+
validate :scope, :validate_selection_model
|
|
35
|
+
def validate_selection_model
|
|
36
|
+
if dashboard.model != selection_model && subspace.model != selection_model
|
|
37
|
+
errors.add(:selection_model, "selection model not in subspace or dashboard")
|
|
38
|
+
end
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def before_build(parent, *args)
|
|
42
|
+
@dashboard = parent
|
|
43
|
+
@subspace = @dashboard.subspace
|
|
44
|
+
@workspace = @subspace.workspace
|
|
45
|
+
@title = id.to_s.titleize
|
|
46
|
+
@id = args[0]
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
# Create a field reference for each column
|
|
50
|
+
def after_build(_parent, *_args)
|
|
51
|
+
(@columns || []).each do |c|
|
|
52
|
+
@dsl_delegate.col(c)
|
|
53
|
+
end
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
def key
|
|
57
|
+
"table-#{workspace.id}_#{subspace.id}_#{dashboard.id}_#{@id}"
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def allowed_columns(_user)
|
|
61
|
+
model_associations = model.active_model.reflect_on_all_associations(:belongs_to)
|
|
62
|
+
model_association_columns = model_associations.map { |ma| ma.name.to_s + '_id' }
|
|
63
|
+
columns = [:id] + @columns + model_association_columns
|
|
64
|
+
columns.uniq
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def select_fields()
|
|
68
|
+
[
|
|
69
|
+
data_select_fields,
|
|
70
|
+
view_link_select_fields
|
|
71
|
+
].flatten
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def selection_match_requirements(user)
|
|
75
|
+
if selection
|
|
76
|
+
selection.match_reqs(user)
|
|
77
|
+
else
|
|
78
|
+
[]
|
|
79
|
+
end
|
|
80
|
+
end
|
|
81
|
+
|
|
82
|
+
def filter_requirements(user:, record:)
|
|
83
|
+
filter_reqs = []
|
|
84
|
+
if selection && record
|
|
85
|
+
filter_reqs << selection.record_filter_reqs(user: user, record: record)
|
|
86
|
+
end
|
|
87
|
+
return filter_reqs.flatten
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
# Reason to move logic to model
|
|
91
|
+
# would be to centralize user level
|
|
92
|
+
# access to columns
|
|
93
|
+
|
|
94
|
+
def data_select_fields
|
|
95
|
+
@table_columns.map(&:field)
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
# Technical debt deep discovery
|
|
99
|
+
# e.g. Subspace model being case
|
|
100
|
+
# and this model being change order
|
|
101
|
+
|
|
102
|
+
def view_link_select_fields
|
|
103
|
+
# Find dashboards in modeled subspace
|
|
104
|
+
@workspace.subspaces.each do |ss|
|
|
105
|
+
next unless ss.model && ss.model.id != model.id
|
|
106
|
+
ss.dashboards.each do |d|
|
|
107
|
+
next unless d.model && d.model.id == model.id
|
|
108
|
+
ref_f = model.fields.find do |f|
|
|
109
|
+
f.field_type == 'reference' &&
|
|
110
|
+
f.id == model.id &&
|
|
111
|
+
f.model.id == ss.model.id
|
|
112
|
+
end || model.fields.find do |f|
|
|
113
|
+
f.field_type == 'reference' &&
|
|
114
|
+
f.model.id == ss.model.id
|
|
115
|
+
end
|
|
116
|
+
return [
|
|
117
|
+
model.fields_hash[:id],
|
|
118
|
+
model.fields_hash[ref_f.relation.join_foreign_key.to_sym]
|
|
119
|
+
].flatten
|
|
120
|
+
end
|
|
121
|
+
end
|
|
122
|
+
[
|
|
123
|
+
model.fields_hash[:id]
|
|
124
|
+
]
|
|
125
|
+
end
|
|
126
|
+
|
|
127
|
+
def hashify(_user)
|
|
128
|
+
{
|
|
129
|
+
title: @title,
|
|
130
|
+
key: key,
|
|
131
|
+
type: type,
|
|
132
|
+
column_keys: allowed_columns(nil),
|
|
133
|
+
view: @view,
|
|
134
|
+
model_key: model.key,
|
|
135
|
+
table_columns: @table_columns.map(&:hashify)
|
|
136
|
+
}
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Given params subspace_model_id and/or workspace_model_id
|
|
140
|
+
# Find records and create named arguments
|
|
141
|
+
# If subspace_model_id was 4 and this table was in a subspace belonging to Work Order
|
|
142
|
+
# arguments would be { work_order: WorkOrder.find(4) }
|
|
143
|
+
def scope_args(params:, user:)
|
|
144
|
+
if scope
|
|
145
|
+
subspace_variable_name = subspace.model.try(:id).try(:to_sym) # returns a label such as :work_order
|
|
146
|
+
dashboard_variable_name = dashboard.model.try(:id).try(:to_sym) # returns a label such as :work_order
|
|
147
|
+
args = {}
|
|
148
|
+
if subspace_variable_name && scope.parameters.include?([:keyreq, subspace_variable_name])
|
|
149
|
+
subspace_record = subspace.model.active_model.find(params[:subspace_model_id])
|
|
150
|
+
# Todo: check user has read access to subspace record
|
|
151
|
+
args[subspace_variable_name] = subspace_record
|
|
152
|
+
end
|
|
153
|
+
if dashboard_variable_name && scope.parameters.include?([:keyreq, dashboard_variable_name])
|
|
154
|
+
dashboard_record = dashboard.model.active_model.find(params[:dashboard_model_id])
|
|
155
|
+
# Todo: check user has read access to dashboard_record
|
|
156
|
+
args[dashboard_variable_name] = dashboard_record
|
|
157
|
+
end
|
|
158
|
+
if scope.parameters.include?([:keyreq, :current_user])
|
|
159
|
+
args[:current_user] = user
|
|
160
|
+
end
|
|
161
|
+
args
|
|
162
|
+
else
|
|
163
|
+
{}
|
|
164
|
+
end
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
# Given params subspace_model_id and/or workspace_model_id
|
|
168
|
+
# Get the parent selection record
|
|
169
|
+
def selection_record(params:, user: nil)
|
|
170
|
+
if selection
|
|
171
|
+
subspace_model_name = subspace.model.try(:id).try(:to_sym) # returns a label such as :work_order
|
|
172
|
+
dashboard_model_name = dashboard.model.try(:id).try(:to_sym) # returns a label such as :work_order
|
|
173
|
+
selection_model_name = selection.base_model.try(:id).try(:to_sym)
|
|
174
|
+
|
|
175
|
+
if !subspace_model_name.blank? && selection_model_name == subspace_model_name
|
|
176
|
+
record = subspace.model.active_model.find(params[:subspace_model_id])
|
|
177
|
+
end
|
|
178
|
+
if !dashboard_model_name.blank? && selection_model_name == dashboard_model_name
|
|
179
|
+
record = dashboard.model.active_model.find(params[:dashboard_model_id])
|
|
180
|
+
end
|
|
181
|
+
if dashboard_model_name == subspace_model_name
|
|
182
|
+
raise "Ambiguous model nesting. ActionBlock validation should have prevented this."
|
|
183
|
+
end
|
|
184
|
+
if dashboard_model_name != selection_model_name && subspace_model_name != selection_model_name
|
|
185
|
+
raise "Invalid selection model. ActionBlock validation should have prevented this."
|
|
186
|
+
end
|
|
187
|
+
record
|
|
188
|
+
else
|
|
189
|
+
nil
|
|
190
|
+
end
|
|
191
|
+
end
|
|
192
|
+
|
|
193
|
+
def to_json(params:, user:)
|
|
194
|
+
if scope
|
|
195
|
+
scope_to_json(params: params, user: user)
|
|
196
|
+
else
|
|
197
|
+
pp({
|
|
198
|
+
params: params,
|
|
199
|
+
user: user
|
|
200
|
+
})
|
|
201
|
+
builder_to_json(params: params, user: user)
|
|
202
|
+
end
|
|
203
|
+
end
|
|
204
|
+
|
|
205
|
+
# Legacy Support
|
|
206
|
+
def scope_to_json(params:, user:)
|
|
207
|
+
if scope.parameters.length > 0
|
|
208
|
+
s = scope.call(scope_args(params: params, user: user))
|
|
209
|
+
else
|
|
210
|
+
s = scope.call()
|
|
211
|
+
end
|
|
212
|
+
return s.to_json
|
|
213
|
+
end
|
|
214
|
+
|
|
215
|
+
def builder_engine(params:, user:)
|
|
216
|
+
klass = model.active_model
|
|
217
|
+
selection_match_reqs = selection_match_requirements(user)
|
|
218
|
+
record = selection_record(params: params, user: user)
|
|
219
|
+
filter_reqs = filter_requirements(user: user, record: record)
|
|
220
|
+
|
|
221
|
+
# pp({
|
|
222
|
+
# record: record,
|
|
223
|
+
# select_fields: select_fields,
|
|
224
|
+
# selection_match_reqs: selection_match_reqs,
|
|
225
|
+
# filter_reqs: filter_reqs
|
|
226
|
+
# })
|
|
227
|
+
|
|
228
|
+
data_engine = ActionBlocks::DataEngine.new(klass,
|
|
229
|
+
select_fields: select_fields,
|
|
230
|
+
selection_match_reqs: selection_match_reqs,
|
|
231
|
+
selection_filter_reqs: filter_reqs
|
|
232
|
+
)
|
|
233
|
+
data_engine
|
|
234
|
+
end
|
|
235
|
+
|
|
236
|
+
def builder_to_json(params:, user:)
|
|
237
|
+
engine = builder_engine(params: params, user: user)
|
|
238
|
+
return engine.to_json
|
|
239
|
+
end
|
|
240
|
+
|
|
241
|
+
|
|
242
|
+
end
|
|
243
|
+
|
|
244
|
+
class TableColumnBuilder < ActionBlocks::BaseBuilder
|
|
245
|
+
references :field
|
|
246
|
+
def before_build(parent, *args)
|
|
247
|
+
@id = args[0]
|
|
248
|
+
pm = parent.model_key.dup
|
|
249
|
+
pm['model-'] = ''
|
|
250
|
+
@field_key = "field-#{pm}-#{@id}"
|
|
251
|
+
end
|
|
252
|
+
|
|
253
|
+
def hashify
|
|
254
|
+
{
|
|
255
|
+
field_key: @field_key
|
|
256
|
+
}
|
|
257
|
+
end
|
|
258
|
+
end
|
|
259
|
+
end
|
|
@@ -0,0 +1,282 @@
|
|
|
1
|
+
module ActionBlocks
|
|
2
|
+
|
|
3
|
+
class WorkspaceBuilder < ActionBlocks::BlockType
|
|
4
|
+
block_type :workspace
|
|
5
|
+
sets :title
|
|
6
|
+
builds_many :subspaces, :subspace, 'ActionBlocks::SubspaceBuilder'
|
|
7
|
+
# builds_many :record_paths, :record_path, 'ActionBlocks::RecordPathBuilder'
|
|
8
|
+
|
|
9
|
+
validate :one_route_per_model
|
|
10
|
+
validate :one_non_model_subspace_per_category
|
|
11
|
+
|
|
12
|
+
def one_non_model_subspace_per_category
|
|
13
|
+
subspace_categories.each do |sc|
|
|
14
|
+
if @subspaces.
|
|
15
|
+
select {|ss| ss.category == sc && ss.model_key.nil? }.count > 1
|
|
16
|
+
errors.add(:subspaces, "Subspace category #{sc.to_s} has more than one non-modeled subspace")
|
|
17
|
+
end
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def one_route_per_model
|
|
22
|
+
models = subspaces.map {|ss| ss.model_key}.compact
|
|
23
|
+
subspaces.each do |ss|
|
|
24
|
+
models += ss.dashboards.map {|ss| ss.model_key}.compact
|
|
25
|
+
end
|
|
26
|
+
models.uniq.each do |model|
|
|
27
|
+
if models.select {|m| model == m}.count > 1
|
|
28
|
+
errors.add(:subspaces, "More than one subspace or dashboard uses model #{model}")
|
|
29
|
+
end
|
|
30
|
+
end
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def before_build(parent, *args)
|
|
34
|
+
@title = args[0].to_s.titleize
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
def subspace_categories
|
|
38
|
+
@subspaces.map(&:category).uniq
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
def model_paths
|
|
42
|
+
subspace_paths.merge(dashboard_paths)
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
def subspace_paths
|
|
46
|
+
paths = {}
|
|
47
|
+
@subspaces.each do |ss|
|
|
48
|
+
if ss.model_key
|
|
49
|
+
paths[ss.model_key] = {
|
|
50
|
+
path_type: :subspace,
|
|
51
|
+
subspace_category: ss.category,
|
|
52
|
+
subspace_model: ss.model_key,
|
|
53
|
+
subspace_key: ss.key
|
|
54
|
+
}
|
|
55
|
+
end
|
|
56
|
+
end
|
|
57
|
+
return paths
|
|
58
|
+
end
|
|
59
|
+
|
|
60
|
+
def dashboard_paths
|
|
61
|
+
paths = {}
|
|
62
|
+
@subspaces.each do |ss|
|
|
63
|
+
if ss.model_key
|
|
64
|
+
ss.dashboards.each do |ds|
|
|
65
|
+
paths[ds.model_key] = {
|
|
66
|
+
path_type: :dashboard,
|
|
67
|
+
subspace_category: ss.category,
|
|
68
|
+
subspace_model: ss.model_key,
|
|
69
|
+
subspace_key: ss.key,
|
|
70
|
+
dashboard_category: ds.category,
|
|
71
|
+
dashboard_model: ds.model_key,
|
|
72
|
+
dashboard_key: ds.key
|
|
73
|
+
}
|
|
74
|
+
end
|
|
75
|
+
end
|
|
76
|
+
end
|
|
77
|
+
return paths
|
|
78
|
+
end
|
|
79
|
+
|
|
80
|
+
def hashify_subspace_categories(user)
|
|
81
|
+
isFirst = true
|
|
82
|
+
results = []
|
|
83
|
+
subspace_categories.each do |category|
|
|
84
|
+
results << {
|
|
85
|
+
first: isFirst,
|
|
86
|
+
type: 'subspace_category',
|
|
87
|
+
category: category,
|
|
88
|
+
title: category.to_s.titleize,
|
|
89
|
+
subspaces: @subspaces.
|
|
90
|
+
select {|ss| ss.category == category}.
|
|
91
|
+
map { |ss| ss.hashify(user) },
|
|
92
|
+
}
|
|
93
|
+
isFirst = false
|
|
94
|
+
end
|
|
95
|
+
return results
|
|
96
|
+
end
|
|
97
|
+
|
|
98
|
+
def hashify(user)
|
|
99
|
+
{
|
|
100
|
+
# key: key,
|
|
101
|
+
title: @title,
|
|
102
|
+
id: @id,
|
|
103
|
+
subspaces: @subspaces.map { |ss| ss.hashify(user) },
|
|
104
|
+
subspace_categories: hashify_subspace_categories(user),
|
|
105
|
+
model_paths: model_paths
|
|
106
|
+
# record_paths: @record_paths.map { |rp| rp.hashify(user) }
|
|
107
|
+
}
|
|
108
|
+
end
|
|
109
|
+
end
|
|
110
|
+
|
|
111
|
+
# class RecordPathBuilder < ActionBlocks::BaseBuilder
|
|
112
|
+
# sets :model # currently used by table to find this
|
|
113
|
+
#
|
|
114
|
+
# sets :subspace # What path/url to share to control active navigation
|
|
115
|
+
# sets :recordspace # What recordspace to render
|
|
116
|
+
# sets :dashboard # What path/url of the recordspace dashboard to control active navigation
|
|
117
|
+
# sets :recordboard # What recordboard to render
|
|
118
|
+
#
|
|
119
|
+
# def before_build(parent, *args)
|
|
120
|
+
# # When Creating Links for Records of type @model
|
|
121
|
+
# @model = args[0]
|
|
122
|
+
# # Create Links with this information
|
|
123
|
+
# @subspace = args[1]
|
|
124
|
+
# @recordspace = args[2]
|
|
125
|
+
# @dashboard = args[3]
|
|
126
|
+
# @recordboard = args[4]
|
|
127
|
+
# end
|
|
128
|
+
#
|
|
129
|
+
# def hashify(user)
|
|
130
|
+
# {
|
|
131
|
+
# model: @model,
|
|
132
|
+
# subspace: @subspace,
|
|
133
|
+
# dashboard: @dashboard,
|
|
134
|
+
# recordspace: @recordspace,
|
|
135
|
+
# recordboard: @recordboard
|
|
136
|
+
# }
|
|
137
|
+
# end
|
|
138
|
+
# end
|
|
139
|
+
|
|
140
|
+
class SubspaceBuilder < ActionBlocks::BaseBuilder
|
|
141
|
+
attr_accessor :workspace
|
|
142
|
+
sets :title
|
|
143
|
+
sets :category
|
|
144
|
+
references :model
|
|
145
|
+
builds_many :dashboards, :dashboard, 'ActionBlocks::DashboardBuilder'
|
|
146
|
+
sets_many :recordspace_keys, :mounts
|
|
147
|
+
|
|
148
|
+
validates :category, presence: true
|
|
149
|
+
|
|
150
|
+
def key
|
|
151
|
+
"subspace-#{@id}".to_sym
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
def before_build(parent, *args)
|
|
155
|
+
@workspace = parent
|
|
156
|
+
@title = args[0].to_s.titleize
|
|
157
|
+
@category = args[0]
|
|
158
|
+
if args[1]
|
|
159
|
+
@model_key = "model-#{args[1]}"
|
|
160
|
+
end
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
def dashboard_categories
|
|
164
|
+
@dashboards.map(&:category).uniq
|
|
165
|
+
end
|
|
166
|
+
|
|
167
|
+
def hashify_dashboard_categories(user)
|
|
168
|
+
isFirst = true
|
|
169
|
+
results = []
|
|
170
|
+
dashboard_categories.each do |category|
|
|
171
|
+
results << {
|
|
172
|
+
first: isFirst,
|
|
173
|
+
type: 'dashboard_categories',
|
|
174
|
+
category: category,
|
|
175
|
+
title: category.to_s.titleize,
|
|
176
|
+
dashboards: @dashboards.
|
|
177
|
+
select {|d| d.category == category}.
|
|
178
|
+
map { |d| d.hashify(user) },
|
|
179
|
+
}
|
|
180
|
+
isFirst = false
|
|
181
|
+
end
|
|
182
|
+
return results
|
|
183
|
+
end
|
|
184
|
+
|
|
185
|
+
|
|
186
|
+
def hashify(user)
|
|
187
|
+
{
|
|
188
|
+
key: key,
|
|
189
|
+
title: @title,
|
|
190
|
+
category: @category,
|
|
191
|
+
model: @model_key,
|
|
192
|
+
dashboards: @dashboards.map{ |db| db.hashify(user) },
|
|
193
|
+
dashboard_categories: hashify_dashboard_categories(user),
|
|
194
|
+
recordspace_keys: @recordspace_keys
|
|
195
|
+
}
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
class DashboardBuilder < ActionBlocks::BaseBuilder
|
|
200
|
+
attr_accessor :workspace, :subspace
|
|
201
|
+
sets :title
|
|
202
|
+
sets :category
|
|
203
|
+
references :model
|
|
204
|
+
builds_many :dashlets, :table, 'ActionBlocks::TableBuilder'
|
|
205
|
+
builds_many :dashlets, :barchart, 'ActionBlocks::BarchartBuilder'
|
|
206
|
+
builds_many :dashlets, :mount_form, 'ActionBlocks::MountedFormBuilder'
|
|
207
|
+
|
|
208
|
+
def before_build(parent, *args)
|
|
209
|
+
@subspace = parent
|
|
210
|
+
@workspace = @subspace.workspace
|
|
211
|
+
@category = args[0]
|
|
212
|
+
@title = @category.to_s.titleize
|
|
213
|
+
if args[1]
|
|
214
|
+
@model_key = "model-#{args[1]}"
|
|
215
|
+
end
|
|
216
|
+
end
|
|
217
|
+
|
|
218
|
+
validates :category, presence: true
|
|
219
|
+
validate :has_unique_dashlets
|
|
220
|
+
def has_unique_dashlets
|
|
221
|
+
@dashlets.group_by(&:key).each do |k, dashlets|
|
|
222
|
+
if(dashlets.length > 1)
|
|
223
|
+
d = dashlets.first
|
|
224
|
+
errors.add(:dashlets, "Duplicate #{d.type.inspect} added to dashboard #{workspace.id}/#{subspace.id}/#{id} with id #{d.id.inspect}")
|
|
225
|
+
end
|
|
226
|
+
end
|
|
227
|
+
end
|
|
228
|
+
|
|
229
|
+
def hashify(user)
|
|
230
|
+
{
|
|
231
|
+
key: key,
|
|
232
|
+
title: @title,
|
|
233
|
+
model: @model_key,
|
|
234
|
+
category: @category,
|
|
235
|
+
dashlet_keys: @dashlets.map(&:key)
|
|
236
|
+
}
|
|
237
|
+
end
|
|
238
|
+
end
|
|
239
|
+
|
|
240
|
+
class MountedFormBuilder < ActionBlocks::BlockType
|
|
241
|
+
block_type :mounted_form
|
|
242
|
+
references :form
|
|
243
|
+
|
|
244
|
+
def before_build(parent, *args)
|
|
245
|
+
@id = args[0]
|
|
246
|
+
@dashboard = parent
|
|
247
|
+
@subspace = @dashboard.subspace
|
|
248
|
+
@form_key = "form-#{@id}"
|
|
249
|
+
end
|
|
250
|
+
|
|
251
|
+
# validate :validate_mounted_to
|
|
252
|
+
# def validate_mounted_to
|
|
253
|
+
# return unless [:dashboard, :subspace].include?(@mounted_to)
|
|
254
|
+
# errors.add(:mounted_to, "#{@parent.key} #{type} must be mounted to :dashboard or :subspace but #{@mounted_to.inspect} was specified.")
|
|
255
|
+
# end
|
|
256
|
+
#
|
|
257
|
+
# validate :validate_mount_point_matches_model
|
|
258
|
+
# def validate_mount_point_matches_model
|
|
259
|
+
# # TODO
|
|
260
|
+
# end
|
|
261
|
+
|
|
262
|
+
def after_load
|
|
263
|
+
# After load is called after all blocks are in the store
|
|
264
|
+
if form.model.id == @subspace.model.try(:id)
|
|
265
|
+
@mounted_to = :subspace
|
|
266
|
+
end
|
|
267
|
+
if form.model.id == @dashboard.model.try(:id)
|
|
268
|
+
@mounted_to = :dashboard
|
|
269
|
+
end
|
|
270
|
+
end
|
|
271
|
+
|
|
272
|
+
def hashify(user)
|
|
273
|
+
{
|
|
274
|
+
type: :mounted_form,
|
|
275
|
+
form_key: @form_key,
|
|
276
|
+
mounted_to: @mounted_to
|
|
277
|
+
}
|
|
278
|
+
end
|
|
279
|
+
end
|
|
280
|
+
|
|
281
|
+
|
|
282
|
+
end
|