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,88 @@
|
|
|
1
|
+
module ActionBlocks
|
|
2
|
+
# Data Engine
|
|
3
|
+
class FilterEngine
|
|
4
|
+
attr_accessor :tables, :root_klass, :root_key, :froms, :joins, :wheres,
|
|
5
|
+
:selects, :joins, :filter_reqs
|
|
6
|
+
|
|
7
|
+
def initialize(root_klass, user: nil, filter_reqs: [])
|
|
8
|
+
@root_klass = root_klass
|
|
9
|
+
@filter_reqs = filter_reqs
|
|
10
|
+
@tables = {}
|
|
11
|
+
@joins = {}
|
|
12
|
+
@wheres = []
|
|
13
|
+
@froms = []
|
|
14
|
+
end
|
|
15
|
+
|
|
16
|
+
def process
|
|
17
|
+
@root_table = @root_klass.arel_table
|
|
18
|
+
@root_key = @root_klass.to_s.underscore.to_sym
|
|
19
|
+
|
|
20
|
+
# Add base table to tables
|
|
21
|
+
@tables[@root_key.to_sym] = @root_table
|
|
22
|
+
|
|
23
|
+
[@filter_reqs].flatten.each do |matchreq|
|
|
24
|
+
node, *rest = matchreq[:path1]
|
|
25
|
+
# puts "base node: #{node} rest #{rest}"
|
|
26
|
+
base_expression = walk_filter_path(@root_klass, node, @root_key, rest)
|
|
27
|
+
|
|
28
|
+
node, *rest = matchreq[:path2]
|
|
29
|
+
# puts "related node: #{node} rest #{rest}"
|
|
30
|
+
related_expression = walk_filter_path(@root_klass, node, @root_key, rest)
|
|
31
|
+
|
|
32
|
+
if base_expression.class.ancestors.include?(Arel::Attributes::Attribute)
|
|
33
|
+
where = base_expression.send(matchreq[:predicate], related_expression)
|
|
34
|
+
else
|
|
35
|
+
where = related_expression.send(matchreq[:predicate], base_expression)
|
|
36
|
+
end
|
|
37
|
+
@wheres << where
|
|
38
|
+
|
|
39
|
+
end
|
|
40
|
+
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def walk_filter_path(klass, node, parent_key, col_path)
|
|
44
|
+
key = [parent_key, node].compact.join('_').to_sym
|
|
45
|
+
if node.class != Symbol
|
|
46
|
+
return node
|
|
47
|
+
end
|
|
48
|
+
if !col_path.empty?
|
|
49
|
+
# Create Arel Table Alias
|
|
50
|
+
relation = klass.reflections[node.to_s]
|
|
51
|
+
klass = relation.klass
|
|
52
|
+
@tables[key] = klass.arel_table.alias(key) unless @tables[key]
|
|
53
|
+
# Create Join
|
|
54
|
+
fk = relation.join_foreign_key
|
|
55
|
+
pk = relation.join_primary_key
|
|
56
|
+
join_on = @tables[key].create_on(@tables[parent_key][fk].eq(@tables[key][pk]))
|
|
57
|
+
@joins[key] = @tables[parent_key].create_join(@tables[key], join_on, Arel::Nodes::OuterJoin)
|
|
58
|
+
# Recurse
|
|
59
|
+
next_node, *rest = col_path
|
|
60
|
+
return walk_filter_path(klass, next_node, key, rest)
|
|
61
|
+
else
|
|
62
|
+
# Return expression
|
|
63
|
+
# puts "parent_key: #{node.to_sym}"
|
|
64
|
+
# puts "node: #{node.to_sym}"
|
|
65
|
+
return @tables[parent_key][node.to_sym]
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
|
|
69
|
+
def froms
|
|
70
|
+
[]
|
|
71
|
+
end
|
|
72
|
+
|
|
73
|
+
def ordered_joins
|
|
74
|
+
@joins.values
|
|
75
|
+
end
|
|
76
|
+
|
|
77
|
+
def wheres
|
|
78
|
+
@wheres.reduce(&:and)
|
|
79
|
+
end
|
|
80
|
+
|
|
81
|
+
def query
|
|
82
|
+
@root_klass
|
|
83
|
+
.from(froms)
|
|
84
|
+
.joins(ordered_joins)
|
|
85
|
+
.where(wheres)
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
module ActionBlocks
|
|
2
|
+
# Data Engine
|
|
3
|
+
class SelectionsViaWhereEngine
|
|
4
|
+
attr_accessor :tables, :root_klass, :selection_match_reqs, :selects, :joins, :selection_filter_reqs
|
|
5
|
+
|
|
6
|
+
def initialize(root_klass, user: nil, table_alias_prefix: nil, type: :many_to_many, selection_filter_reqs: [], selection_match_reqs: [], additional_where: nil)
|
|
7
|
+
@root_klass = root_klass
|
|
8
|
+
@table_alias_prefix = table_alias_prefix
|
|
9
|
+
@tables = {}
|
|
10
|
+
@joins = {}
|
|
11
|
+
@wheres = []
|
|
12
|
+
@froms = []
|
|
13
|
+
@type = type
|
|
14
|
+
|
|
15
|
+
@additional_where = additional_where
|
|
16
|
+
|
|
17
|
+
# I named them selection_match_reqs because
|
|
18
|
+
# the DataEngine may work with match_reqs in
|
|
19
|
+
# different contexts. It may use them for
|
|
20
|
+
# summary fields or it may use them for
|
|
21
|
+
# a filtering a query to the children of some
|
|
22
|
+
# base models 'children'
|
|
23
|
+
@selection_match_reqs = selection_match_reqs
|
|
24
|
+
@selection_filter_reqs = selection_filter_reqs
|
|
25
|
+
end
|
|
26
|
+
|
|
27
|
+
def process
|
|
28
|
+
@root_table = @root_klass.arel_table.alias([@table_alias_prefix, @root_klass.to_s.underscore.pluralize].compact.join('_'))
|
|
29
|
+
root_key = [@table_alias_prefix, @root_klass.to_s.underscore.pluralize].compact.join('_').to_sym
|
|
30
|
+
|
|
31
|
+
@froms << @root_table
|
|
32
|
+
# Add base table to tables
|
|
33
|
+
@tables[root_key.to_sym] = @root_table
|
|
34
|
+
|
|
35
|
+
[@selection_match_reqs, @selection_filter_reqs].flatten.compact.each do |matchreq|
|
|
36
|
+
node, *rest = matchreq[:base_path]
|
|
37
|
+
# puts "base node: #{node} rest #{rest}"
|
|
38
|
+
base_expression = walk_selection_match_path(@root_klass, node, root_key, rest)
|
|
39
|
+
|
|
40
|
+
node, *rest = matchreq[:related_path]
|
|
41
|
+
# puts "related node: #{node} rest #{rest}"
|
|
42
|
+
related_expression = walk_selection_match_path(@root_klass, node, root_key, rest)
|
|
43
|
+
|
|
44
|
+
where = if base_expression.class.ancestors.include?(Arel::Attributes::Attribute)
|
|
45
|
+
base_expression.send(matchreq[:predicate], related_expression)
|
|
46
|
+
else
|
|
47
|
+
related_expression.send(matchreq[:predicate], base_expression)
|
|
48
|
+
end
|
|
49
|
+
@wheres << where
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
def walk_selection_match_path(klass, node, parent_key, col_path)
|
|
54
|
+
# puts "klass: #{klass} node: #{node} parent_key #{parent_key.inspect} col_path #{col_path.inspect}"
|
|
55
|
+
# pp [key, rest_col_path]
|
|
56
|
+
key = if node.class == Class
|
|
57
|
+
[@table_alias_prefix, node.to_s.underscore.pluralize].compact.join('_').to_sym
|
|
58
|
+
else
|
|
59
|
+
[@table_alias_prefix, parent_key, node].compact.join('_').to_sym
|
|
60
|
+
end
|
|
61
|
+
return node if node.class != Symbol && node.class != Class
|
|
62
|
+
if !col_path.empty?
|
|
63
|
+
# Create Arel Table Alias
|
|
64
|
+
if node.class != Class
|
|
65
|
+
relation = klass.reflections[node.to_s]
|
|
66
|
+
klass = relation.klass
|
|
67
|
+
@tables[key] = klass.arel_table.alias(key) unless @tables[key]
|
|
68
|
+
# Create Join
|
|
69
|
+
fk = relation.join_foreign_key
|
|
70
|
+
pk = relation.join_primary_key
|
|
71
|
+
join_on = @tables[key].create_on(@tables[parent_key][fk].eq(@tables[key][pk]))
|
|
72
|
+
@joins[key] = @tables[parent_key].create_join(@tables[key], join_on, Arel::Nodes::OuterJoin)
|
|
73
|
+
else
|
|
74
|
+
klass = node
|
|
75
|
+
unless @tables[key]
|
|
76
|
+
@tables[key] = klass.arel_table.alias(key) unless @tables[key]
|
|
77
|
+
@froms << @tables[key]
|
|
78
|
+
end
|
|
79
|
+
end
|
|
80
|
+
# Recurse
|
|
81
|
+
next_node, *rest = col_path
|
|
82
|
+
return walk_selection_match_path(klass, next_node, key, rest)
|
|
83
|
+
else
|
|
84
|
+
# Return expression
|
|
85
|
+
# puts "parent_key: #{node.to_sym}"
|
|
86
|
+
# puts "node: #{node.to_sym}"
|
|
87
|
+
return @tables[parent_key][node.to_sym]
|
|
88
|
+
end
|
|
89
|
+
end
|
|
90
|
+
|
|
91
|
+
|
|
92
|
+
def froms
|
|
93
|
+
if @type == :many_to_many
|
|
94
|
+
[@root_table]
|
|
95
|
+
else
|
|
96
|
+
@froms
|
|
97
|
+
end
|
|
98
|
+
end
|
|
99
|
+
|
|
100
|
+
def ordered_joins
|
|
101
|
+
if @type == :many_to_many
|
|
102
|
+
[]
|
|
103
|
+
else
|
|
104
|
+
@joins.values
|
|
105
|
+
end
|
|
106
|
+
end
|
|
107
|
+
|
|
108
|
+
def wheres
|
|
109
|
+
@wheres << @additional_where if @additional_where
|
|
110
|
+
if @type == :many_to_many && (!@selection_match_reqs.empty? || !@selection_filter_reqs.empty?)
|
|
111
|
+
subquery_arel = subquery_for_many_to_many_selections.arel
|
|
112
|
+
# w = Arel::Nodes::In.new(@root_table[:id], subquery.ast)
|
|
113
|
+
@root_table[:id].in(subquery_arel)
|
|
114
|
+
else
|
|
115
|
+
@wheres.reduce(&:and)
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def subquery_for_many_to_many_selections
|
|
120
|
+
# Arel::Distinct.new(@rook_klasas[:id])
|
|
121
|
+
@root_klass
|
|
122
|
+
.from([@froms].flatten)
|
|
123
|
+
.select(@root_table[:id])
|
|
124
|
+
.joins(@joins.values)
|
|
125
|
+
.where(@wheres.reduce(&:and))
|
|
126
|
+
end
|
|
127
|
+
|
|
128
|
+
def query
|
|
129
|
+
@root_klass
|
|
130
|
+
.joins(ordered_joins)
|
|
131
|
+
.where(wheres)
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|
|
@@ -0,0 +1,72 @@
|
|
|
1
|
+
module ActionBlocks
|
|
2
|
+
# Data Engine
|
|
3
|
+
class SummaryEngine
|
|
4
|
+
attr_accessor :tables, :root_klass, :summary_reqs, :selects, :joins
|
|
5
|
+
|
|
6
|
+
def initialize(root_klass, user: nil, summary_reqs: [])
|
|
7
|
+
@root_klass = root_klass
|
|
8
|
+
@tables = {}
|
|
9
|
+
|
|
10
|
+
@user = user
|
|
11
|
+
|
|
12
|
+
@selects = []
|
|
13
|
+
|
|
14
|
+
@summary_reqs = summary_reqs
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def process
|
|
18
|
+
root_table = @root_klass.arel_table.alias(@root_klass.to_s.underscore.pluralize)
|
|
19
|
+
sub_table = @root_klass.arel_table.alias(['sub', @root_klass.to_s.underscore.pluralize].join('_'))
|
|
20
|
+
|
|
21
|
+
@summary_reqs.each do |summaryreq|
|
|
22
|
+
select_reqs = [summaryreq[:select_req]]
|
|
23
|
+
match_reqs = summaryreq[:match_reqs]
|
|
24
|
+
filter_reqs = summaryreq[:filter_reqs]
|
|
25
|
+
|
|
26
|
+
@fields_engine = ActionBlocks.config[:fields_engine].new(
|
|
27
|
+
summaryreq[:root_klass],
|
|
28
|
+
table_alias_prefix: 'sub',
|
|
29
|
+
select_reqs: select_reqs
|
|
30
|
+
)
|
|
31
|
+
|
|
32
|
+
# @selections_engine = SelectionsViaJoinsEngine.new(
|
|
33
|
+
@selections_engine = ActionBlocks.config[:selections_engine].new(
|
|
34
|
+
summaryreq[:root_klass],
|
|
35
|
+
table_alias_prefix: 'sub',
|
|
36
|
+
selection_match_reqs: match_reqs,
|
|
37
|
+
selection_filter_reqs: filter_reqs,
|
|
38
|
+
additional_where: root_table[:id].eq(sub_table[:id])
|
|
39
|
+
)
|
|
40
|
+
|
|
41
|
+
@fields_engine.process
|
|
42
|
+
@selections_engine.process
|
|
43
|
+
|
|
44
|
+
if ActionBlocks.config[:should_authorize]
|
|
45
|
+
@authorization_adapter = AuthorizationAdapter.new(engine: @fields_engine, user: @user)
|
|
46
|
+
@authorization_adapter.process
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
sub_query = summaryreq[:root_klass]
|
|
50
|
+
.from([
|
|
51
|
+
@fields_engine.froms,
|
|
52
|
+
@selections_engine.froms,
|
|
53
|
+
].flatten.uniq)
|
|
54
|
+
.select(@fields_engine.selects)
|
|
55
|
+
.joins([
|
|
56
|
+
@selections_engine.ordered_joins,
|
|
57
|
+
@fields_engine.ordered_joins
|
|
58
|
+
].flatten.compact)
|
|
59
|
+
.where([@selections_engine.wheres, @fields_engine.wheres].flatten.compact.reduce(&:and))
|
|
60
|
+
.as(summaryreq[:select_req][:field_name].to_s)
|
|
61
|
+
|
|
62
|
+
@selects << sub_query
|
|
63
|
+
end
|
|
64
|
+
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def query
|
|
68
|
+
@root_klass
|
|
69
|
+
.select(@selects)
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
module ActionBlocks
|
|
2
|
+
# Exception class to raise when there is an authorized access
|
|
3
|
+
# exception thrown. The exception has a few goodies that may
|
|
4
|
+
# be useful for capturing / recognizing security issues.
|
|
5
|
+
class AccessDenied < StandardError
|
|
6
|
+
attr_reader :user, :action, :subject
|
|
7
|
+
|
|
8
|
+
def initialize(user, action, subject = nil)
|
|
9
|
+
@user, @action, @subject = user, action, subject
|
|
10
|
+
|
|
11
|
+
super()
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def message
|
|
15
|
+
I18n.t("action_block.access_denied.message")
|
|
16
|
+
end
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
class Error < RuntimeError
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
class ErrorLoading < Error
|
|
23
|
+
# Locates the most recent file and line from the caught exception's backtrace.
|
|
24
|
+
def find_cause(folder, backtrace)
|
|
25
|
+
backtrace.grep(/\/(#{folder}\/.*\.rb):(\d+)/){ [$1, $2] }.first
|
|
26
|
+
end
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
class DatabaseHitDuringLoad < ErrorLoading
|
|
30
|
+
def initialize(exception)
|
|
31
|
+
file, line = find_cause(:app, exception.backtrace)
|
|
32
|
+
|
|
33
|
+
super "Your file, #{file} (line #{line}), caused a database error while Active Block was loading. This " +
|
|
34
|
+
"is most common when your database is missing or doesn't have the latest migrations applied. To " +
|
|
35
|
+
"prevent this error, move the code to a place where it will only be run when a page is rendered. " +
|
|
36
|
+
"One solution can be, to wrap the query in a Proc. " +
|
|
37
|
+
"Original error message: #{exception.message}"
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def self.capture
|
|
41
|
+
yield
|
|
42
|
+
rescue *database_error_classes => exception
|
|
43
|
+
raise new exception
|
|
44
|
+
end
|
|
45
|
+
|
|
46
|
+
private
|
|
47
|
+
|
|
48
|
+
def self.database_error_classes
|
|
49
|
+
@classes ||= []
|
|
50
|
+
end
|
|
51
|
+
end
|
|
52
|
+
|
|
53
|
+
class DependencyError < ErrorLoading
|
|
54
|
+
end
|
|
55
|
+
|
|
56
|
+
class NoMenuError < KeyError
|
|
57
|
+
end
|
|
58
|
+
|
|
59
|
+
class GeneratorError < Error
|
|
60
|
+
end
|
|
61
|
+
|
|
62
|
+
end
|
|
@@ -0,0 +1,134 @@
|
|
|
1
|
+
module ActionBlocks
|
|
2
|
+
module GeneratorHelper
|
|
3
|
+
protected
|
|
4
|
+
|
|
5
|
+
def variable(cname=nil)
|
|
6
|
+
cn = cname || class_name
|
|
7
|
+
variableize(cn)
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def collection_name(cname=nil)
|
|
11
|
+
cn = cname || class_name
|
|
12
|
+
collectionize(cn)
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def collectionize(s)
|
|
16
|
+
s.to_s.underscore.pluralize
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def variableize(s)
|
|
20
|
+
s.to_s.underscore.singularize
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def has_many_associations?(cname=nil)
|
|
24
|
+
cn = cname || class_name
|
|
25
|
+
cn.constantize.reflect_on_all_associations(:has_many).map(&:name).length > 0
|
|
26
|
+
end
|
|
27
|
+
|
|
28
|
+
def has_many_associations(cname=nil)
|
|
29
|
+
cn = cname || class_name
|
|
30
|
+
cn.constantize.reflect_on_all_associations(:has_many).map(&:name).select {|hm| hm.downcase != 'versions'}
|
|
31
|
+
end
|
|
32
|
+
|
|
33
|
+
def has_many_association_details(cname=nil)
|
|
34
|
+
cn = cname || class_name
|
|
35
|
+
cn.constantize.reflect_on_all_associations(:has_many).select {|hm| hm.name.downcase != 'versions'}
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
def has_one_associations(cname=nil)
|
|
39
|
+
cn = cname || class_name
|
|
40
|
+
cn.constantize.reflect_on_all_associations(:has_one).map(&:name)
|
|
41
|
+
end
|
|
42
|
+
|
|
43
|
+
def has_one_association_details(cname=nil)
|
|
44
|
+
cn = cname || class_name
|
|
45
|
+
cn.constantize.reflect_on_all_associations(:has_one)
|
|
46
|
+
end
|
|
47
|
+
|
|
48
|
+
def belongs_to_associations(cname=nil)
|
|
49
|
+
if respond_to?(:attributes) && attributes.present?
|
|
50
|
+
attributes.select{|a| a.reference?}.map(&:name)
|
|
51
|
+
else
|
|
52
|
+
cn = cname || class_name
|
|
53
|
+
begin
|
|
54
|
+
cn.constantize.reflect_on_all_associations(:belongs_to).map(&:name)
|
|
55
|
+
rescue
|
|
56
|
+
[]
|
|
57
|
+
end
|
|
58
|
+
end
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def belongs_to_association_details(cname=nil)
|
|
62
|
+
if respond_to?(:attributes) && attributes.present?
|
|
63
|
+
attributes.select{|a| a.reference?}
|
|
64
|
+
else
|
|
65
|
+
cn = cname || class_name
|
|
66
|
+
begin
|
|
67
|
+
cn.constantize.reflect_on_all_associations(:belongs_to)
|
|
68
|
+
rescue
|
|
69
|
+
[]
|
|
70
|
+
end
|
|
71
|
+
end
|
|
72
|
+
end
|
|
73
|
+
|
|
74
|
+
def content_columns(cname=nil)
|
|
75
|
+
if respond_to?(:attributes) && attributes.present?
|
|
76
|
+
cols = attributes.reject{|a| a.reference?}.map(&:name).map{|n| clean(n)}.compact
|
|
77
|
+
cols
|
|
78
|
+
else
|
|
79
|
+
cn = cname || class_name
|
|
80
|
+
begin
|
|
81
|
+
cols = cn.constantize.content_columns.map(&:name).map {|c| clean(c)}.compact
|
|
82
|
+
cols
|
|
83
|
+
rescue => ex
|
|
84
|
+
puts ex.message
|
|
85
|
+
[]
|
|
86
|
+
end
|
|
87
|
+
end
|
|
88
|
+
end
|
|
89
|
+
|
|
90
|
+
def content_column_details(cname=nil)
|
|
91
|
+
if respond_to?(:attributes) && attributes.present?
|
|
92
|
+
attributes.reject{|a| a.reference?}
|
|
93
|
+
else
|
|
94
|
+
cn = cname || class_name
|
|
95
|
+
begin
|
|
96
|
+
cn.constantize.content_columns
|
|
97
|
+
rescue
|
|
98
|
+
[]
|
|
99
|
+
end
|
|
100
|
+
end
|
|
101
|
+
end
|
|
102
|
+
|
|
103
|
+
def clean(c)
|
|
104
|
+
c = c.to_s
|
|
105
|
+
return nil if c.in?(%w(updated_at created_at deleted deleted_at)) || c =~ /_file_size|_updated_at|_content_type/
|
|
106
|
+
c.to_s.gsub('_file_name','')
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
def attachment_names(cname=nil)
|
|
110
|
+
if respond_to?(:attributes) && attributes.present?
|
|
111
|
+
attributes.select{|a| a.attachment?}.map(&:name)
|
|
112
|
+
else
|
|
113
|
+
cn = cname || class_name
|
|
114
|
+
names = cn.constantize.content_columns.select{|a| a.name.to_s =~ /_file_name\Z/}.map{|a| a.name.to_s.gsub(/_file_name\Z/, "")}
|
|
115
|
+
names
|
|
116
|
+
end
|
|
117
|
+
end
|
|
118
|
+
|
|
119
|
+
def association_class_exists_with_this_name?(association_name)
|
|
120
|
+
klass = Module.const_get(association_name.to_s.singularize.classify)
|
|
121
|
+
return klass.is_a?(Class)
|
|
122
|
+
rescue NameError
|
|
123
|
+
return false
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
def field_content_columns
|
|
127
|
+
if options.fields && options.fields.length > 0
|
|
128
|
+
return options.fields & content_columns
|
|
129
|
+
else
|
|
130
|
+
return content_columns
|
|
131
|
+
end
|
|
132
|
+
end
|
|
133
|
+
end
|
|
134
|
+
end
|