has_dynamic_columns 0.1.1 → 0.2.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 +4 -4
- data/README.md +153 -4
- data/bin/console +15 -0
- data/bin/setup +7 -0
- data/has_dynamic_columns.gemspec +1 -1
- data/lib/has_dynamic_columns.rb +12 -301
- data/lib/has_dynamic_columns/active_record.rb +12 -0
- data/lib/has_dynamic_columns/active_record/query_methods.rb +231 -0
- data/lib/has_dynamic_columns/active_record/relation.rb +21 -0
- data/lib/has_dynamic_columns/active_record_relation.rb +105 -0
- data/lib/has_dynamic_columns/dynamic_column.rb +1 -1
- data/lib/has_dynamic_columns/dynamic_column_datum.rb +1 -1
- data/lib/has_dynamic_columns/dynamic_column_option.rb +1 -1
- data/lib/has_dynamic_columns/dynamic_column_validation.rb +1 -1
- data/lib/has_dynamic_columns/model.rb +10 -0
- data/lib/has_dynamic_columns/model/class_methods.rb +294 -0
- data/lib/has_dynamic_columns/model/instance_methods.rb +6 -0
- data/lib/has_dynamic_columns/version.rb +1 -1
- data/spec/has_dynamic_columns_spec.rb +76 -0
- data/spec/spec_helper.rb +5 -8
- metadata +18 -13
@@ -0,0 +1,231 @@
|
|
1
|
+
module HasDynamicColumns
|
2
|
+
module ActiveRecord
|
3
|
+
module QueryMethods
|
4
|
+
def self.included(base)
|
5
|
+
|
6
|
+
base.class_eval do
|
7
|
+
alias_method_chain :where, :dynamic_columns
|
8
|
+
alias_method_chain :build_arel, :dynamic_columns
|
9
|
+
end
|
10
|
+
|
11
|
+
base.instance_eval do
|
12
|
+
def has_dynamic_column_values=a
|
13
|
+
end
|
14
|
+
def has_dynamic_column_values
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# When arel starts building - filter
|
20
|
+
def build_arel_with_dynamic_columns
|
21
|
+
#arel = Arel::SelectManager.new(table.engine, table)
|
22
|
+
|
23
|
+
# Calculate any dynamic scope that was passed
|
24
|
+
self.has_dynamic_columns_values.each { |dynamic_scope|
|
25
|
+
field_scope = dynamic_scope[:scope]
|
26
|
+
field_scope_id = (!field_scope.nil?) ? field_scope.id : nil
|
27
|
+
field_scope_type = (!field_scope.nil?) ? field_scope.class.name.constantize.to_s : nil
|
28
|
+
|
29
|
+
# TODO - make this work on compound arel queries like: table[:last_name].eq("Paterson").or(table[:first_name].eq("John"))
|
30
|
+
#collapsed = collapse_wheres(arel, dynamic_scope[:where])
|
31
|
+
|
32
|
+
dynamic_scope[:where].each_with_index { |rel, index|
|
33
|
+
case rel
|
34
|
+
when String
|
35
|
+
next
|
36
|
+
else
|
37
|
+
dynamic_type = rel.left.relation.engine.to_s
|
38
|
+
col_name = rel.left.name
|
39
|
+
value = rel.right
|
40
|
+
end
|
41
|
+
|
42
|
+
column_table = HasDynamicColumns::DynamicColumn.arel_table.alias("dynamic_where_#{index}_#{col_name}")
|
43
|
+
column_datum_table = HasDynamicColumns::DynamicColumnDatum.arel_table.alias("dynamic_where_data_#{index}_#{col_name}")
|
44
|
+
|
45
|
+
# Join on the column with the key
|
46
|
+
on_query = column_table[:key].eq(col_name)
|
47
|
+
on_query = on_query.and(
|
48
|
+
column_table[:field_scope_type].eq(field_scope_type)
|
49
|
+
) unless field_scope_type.nil?
|
50
|
+
|
51
|
+
on_query = on_query.and(
|
52
|
+
column_table[:field_scope_id].eq(field_scope_id)
|
53
|
+
) unless field_scope_id.nil?
|
54
|
+
|
55
|
+
column_table_join_on = column_table
|
56
|
+
.create_on(
|
57
|
+
on_query
|
58
|
+
)
|
59
|
+
|
60
|
+
column_table_join = table.create_join(column_table, column_table_join_on)
|
61
|
+
self.joins_values += [column_table_join]
|
62
|
+
|
63
|
+
arel_node = case rel
|
64
|
+
when Arel::Nodes::Equality
|
65
|
+
column_datum_table[:value].eq(value)
|
66
|
+
else
|
67
|
+
column_datum_table[:value].matches(value)
|
68
|
+
end
|
69
|
+
|
70
|
+
# Join on all the data with the provided key
|
71
|
+
column_table_datum_join_on = column_datum_table
|
72
|
+
.create_on(
|
73
|
+
column_datum_table[:owner_id].eq(table[:id]).and(
|
74
|
+
column_datum_table[:owner_type].eq(dynamic_type)
|
75
|
+
).and(
|
76
|
+
column_datum_table[:dynamic_column_id].eq(column_table[:id])
|
77
|
+
).and(
|
78
|
+
arel_node
|
79
|
+
)
|
80
|
+
)
|
81
|
+
|
82
|
+
column_table_datum_join = table.create_join(column_datum_table, column_table_datum_join_on)
|
83
|
+
self.joins_values += [column_table_datum_join]
|
84
|
+
}
|
85
|
+
}
|
86
|
+
|
87
|
+
build_arel_without_dynamic_columns
|
88
|
+
end
|
89
|
+
|
90
|
+
# lifted from
|
91
|
+
# http://erniemiller.org/2013/10/07/activerecord-where-not-sane-true/
|
92
|
+
module WhereChainCompatibility
|
93
|
+
#include ::ActiveRecord::QueryMethods
|
94
|
+
#define_method :build_where,
|
95
|
+
# ::ActiveRecord::QueryMethods.instance_method(:build_where)
|
96
|
+
|
97
|
+
# Extends where to chain a has_dynamic_columns method
|
98
|
+
# This builds all the joins needed to search the has_dynamic_columns_data tables
|
99
|
+
def has_dynamic_columns(opts = :chain, *rest)
|
100
|
+
# Map
|
101
|
+
dynamic_columns_value = {
|
102
|
+
:scope => nil,
|
103
|
+
:where => @scope.send(:build_where, opts, rest)
|
104
|
+
}
|
105
|
+
@scope.has_dynamic_columns_values = dynamic_columns_value
|
106
|
+
|
107
|
+
chain = ::ActiveRecord::QueryMethods::WhereChain.new(@scope)
|
108
|
+
chain.instance_eval do
|
109
|
+
# Make outer scope variable accessible
|
110
|
+
@dynamic_columns_value = dynamic_columns_value
|
111
|
+
|
112
|
+
# Extends where to chain with a has_scope method
|
113
|
+
# This scopes the where from above
|
114
|
+
def with_scope(opt)
|
115
|
+
@dynamic_columns_value[:scope] = opt
|
116
|
+
|
117
|
+
@scope
|
118
|
+
end
|
119
|
+
def without_scope
|
120
|
+
@scope
|
121
|
+
end
|
122
|
+
end
|
123
|
+
|
124
|
+
chain
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
def where_with_dynamic_columns(opts = :chain, *rest)
|
129
|
+
if opts == :chain
|
130
|
+
scope = spawn
|
131
|
+
chain = ::ActiveRecord::QueryMethods::WhereChain.new(scope)
|
132
|
+
chain.send(:extend, WhereChainCompatibility)
|
133
|
+
else
|
134
|
+
where_without_dynamic_columns(opts, rest)
|
135
|
+
end
|
136
|
+
end
|
137
|
+
=begin
|
138
|
+
def where_with_dynamic_columns(opts = :chain, *rest)
|
139
|
+
puts "--------------------------------------"
|
140
|
+
puts "Calling where_with_dynamic_columns"
|
141
|
+
puts opts.inspect
|
142
|
+
puts rest.inspect
|
143
|
+
|
144
|
+
if opts == :chain
|
145
|
+
scope = spawn
|
146
|
+
scope.extend(WhereChainCompatibility)
|
147
|
+
chain = ::ActiveRecord::QueryMethods::WhereChain.new(scope)
|
148
|
+
chain.instance_eval do
|
149
|
+
# Provide search on dynamic columns
|
150
|
+
def dynamic(opts, *rest)
|
151
|
+
options = rest.extract_options!
|
152
|
+
field_scope = opts.respond_to?(:class) && opts.class.respond_to?(:ancestors) && (opts.class.ancestors.include?(::ActiveRecord::Base)) ? opts : nil
|
153
|
+
|
154
|
+
# Field scope passed to the where clause
|
155
|
+
if !field_scope.nil?
|
156
|
+
opts = options
|
157
|
+
rest = []
|
158
|
+
end
|
159
|
+
|
160
|
+
field_scope_id = (!field_scope.nil?) ? field_scope.id : nil
|
161
|
+
field_scope_type = (!field_scope.nil?) ? field_scope.class.name.constantize.to_s : nil
|
162
|
+
|
163
|
+
# Join dynamic_column table
|
164
|
+
# This filter by scope if field_scope is passed
|
165
|
+
@scope.joins_values += @scope.send(:build_where, opts, rest).map do |rel|
|
166
|
+
dynamic_type = rel.left.relation.engine.to_s
|
167
|
+
|
168
|
+
col_name = rel.left.name
|
169
|
+
value = rel.right
|
170
|
+
|
171
|
+
table = dynamic_type.constantize.arel_table
|
172
|
+
column_table = ::HasDynamicColumns::DynamicColumn.arel_table.alias("dynamic_where_#{col_name}")
|
173
|
+
|
174
|
+
on_query = column_table[:key].eq(col_name)
|
175
|
+
if !field_scope_type.nil?
|
176
|
+
on_query = on_query.and(
|
177
|
+
column_table[:field_scope_type].eq(field_scope_type)
|
178
|
+
)
|
179
|
+
end
|
180
|
+
if !field_scope_id.nil?
|
181
|
+
on_query = on_query.and(
|
182
|
+
column_table[:field_scope_id].eq(field_scope_id)
|
183
|
+
)
|
184
|
+
end
|
185
|
+
|
186
|
+
column_table_join_on = column_table.create_on(on_query)
|
187
|
+
table.create_join(column_table, column_table_join_on)
|
188
|
+
end unless field_scope.nil?
|
189
|
+
|
190
|
+
# Join dynamic_column_data table
|
191
|
+
# This filters on data irrigardless of scope
|
192
|
+
join_value = @scope.send(:build_where, opts, rest).map do |rel|
|
193
|
+
dynamic_type = rel.left.relation.engine.to_s
|
194
|
+
|
195
|
+
col_name = rel.left.name
|
196
|
+
value = rel.right
|
197
|
+
|
198
|
+
table = dynamic_type.constantize.arel_table
|
199
|
+
column_table = ::HasDynamicColumns::DynamicColumn.arel_table.alias("dynamic_where_#{col_name}")
|
200
|
+
column_datum_table = ::HasDynamicColumns::DynamicColumnDatum.arel_table.alias("dynamic_where_data_#{col_name}")
|
201
|
+
|
202
|
+
# Join on all the data with the provided key
|
203
|
+
column_table_datum_join_on = column_datum_table
|
204
|
+
.create_on(
|
205
|
+
column_datum_table[:owner_id].eq(table[:id]).and(
|
206
|
+
column_datum_table[:owner_type].eq(dynamic_type)
|
207
|
+
).and(
|
208
|
+
column_datum_table[:dynamic_column_id].eq(column_table[:id])
|
209
|
+
).and(
|
210
|
+
column_datum_table[:value].matches("%"+value+"%")
|
211
|
+
)
|
212
|
+
)
|
213
|
+
|
214
|
+
table.create_join(column_datum_table, column_table_datum_join_on)
|
215
|
+
end
|
216
|
+
@scope.joins_values += join_value
|
217
|
+
|
218
|
+
@scope
|
219
|
+
end
|
220
|
+
end
|
221
|
+
|
222
|
+
chain
|
223
|
+
else
|
224
|
+
# Default where
|
225
|
+
where_without_dynamic_columns(opts, rest)
|
226
|
+
end
|
227
|
+
end
|
228
|
+
=end
|
229
|
+
end
|
230
|
+
end
|
231
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module HasDynamicColumns
|
2
|
+
module ActiveRecord
|
3
|
+
module Relation
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
def has_dynamic_columns_values
|
7
|
+
@values[:has_dynamic_columns_values] || []
|
8
|
+
end
|
9
|
+
def has_dynamic_columns_values=values
|
10
|
+
raise ImmutableRelation if @loaded
|
11
|
+
@values[:has_dynamic_columns_values] ||= []
|
12
|
+
@values[:has_dynamic_columns_values] << values
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
base.instance_eval do
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,105 @@
|
|
1
|
+
module HasDynamicColumns
|
2
|
+
module ActiveRecordRelation
|
3
|
+
def self.included(base)
|
4
|
+
base.class_eval do
|
5
|
+
end
|
6
|
+
|
7
|
+
base.instance_eval do
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# lifted from
|
12
|
+
# http://erniemiller.org/2013/10/07/activerecord-where-not-sane-true/
|
13
|
+
module WhereChainCompatibility
|
14
|
+
module_function
|
15
|
+
include ::ActiveRecord::QueryMethods
|
16
|
+
define_method :build_where,
|
17
|
+
::ActiveRecord::QueryMethods.instance_method(:build_where)
|
18
|
+
end
|
19
|
+
|
20
|
+
def where(opts = :chain, *rest)
|
21
|
+
if opts == :chain
|
22
|
+
scope = spawn
|
23
|
+
scope.extend(WhereChainCompatibility)
|
24
|
+
chain = ::ActiveRecord::QueryMethods::WhereChain.new(scope)
|
25
|
+
chain.instance_eval do
|
26
|
+
# Provide search on dynamic columns
|
27
|
+
def dynamic(opts, *rest)
|
28
|
+
options = rest.extract_options!
|
29
|
+
field_scope = opts.respond_to?(:class) && opts.class.respond_to?(:ancestors) && (opts.class.ancestors.include?(::ActiveRecord::Base)) ? opts : nil
|
30
|
+
|
31
|
+
# Field scope passed to the where clause
|
32
|
+
if !field_scope.nil?
|
33
|
+
opts = options
|
34
|
+
rest = []
|
35
|
+
end
|
36
|
+
|
37
|
+
field_scope_id = (!field_scope.nil?) ? field_scope.id : nil
|
38
|
+
field_scope_type = (!field_scope.nil?) ? field_scope.class.name.constantize.to_s : nil
|
39
|
+
|
40
|
+
# Join dynamic_column table
|
41
|
+
# This filter by scope if field_scope is passed
|
42
|
+
@scope.joins_values += @scope.send(:build_where, opts, rest).map do |rel|
|
43
|
+
dynamic_type = rel.left.relation.engine.to_s
|
44
|
+
|
45
|
+
col_name = rel.left.name
|
46
|
+
value = rel.right
|
47
|
+
|
48
|
+
table = dynamic_type.constantize.arel_table
|
49
|
+
column_table = ::HasDynamicColumns::DynamicColumn.arel_table.alias("dynamic_where_#{col_name}")
|
50
|
+
|
51
|
+
on_query = column_table[:key].eq(col_name)
|
52
|
+
if !field_scope_type.nil?
|
53
|
+
on_query = on_query.and(
|
54
|
+
column_table[:field_scope_type].eq(field_scope_type)
|
55
|
+
)
|
56
|
+
end
|
57
|
+
if !field_scope_id.nil?
|
58
|
+
on_query = on_query.and(
|
59
|
+
column_table[:field_scope_id].eq(field_scope_id)
|
60
|
+
)
|
61
|
+
end
|
62
|
+
|
63
|
+
column_table_join_on = column_table.create_on(on_query)
|
64
|
+
table.create_join(column_table, column_table_join_on)
|
65
|
+
end unless field_scope.nil?
|
66
|
+
|
67
|
+
# Join dynamic_column_data table
|
68
|
+
# This filters on data irrigardless of scope
|
69
|
+
join_value = @scope.send(:build_where, opts, rest).map do |rel|
|
70
|
+
dynamic_type = rel.left.relation.engine.to_s
|
71
|
+
|
72
|
+
col_name = rel.left.name
|
73
|
+
value = rel.right
|
74
|
+
|
75
|
+
table = dynamic_type.constantize.arel_table
|
76
|
+
column_table = ::HasDynamicColumns::DynamicColumn.arel_table.alias("dynamic_where_#{col_name}")
|
77
|
+
column_datum_table = ::HasDynamicColumns::DynamicColumnDatum.arel_table.alias("dynamic_where_data_#{col_name}")
|
78
|
+
|
79
|
+
# Join on all the data with the provided key
|
80
|
+
column_table_datum_join_on = column_datum_table
|
81
|
+
.create_on(
|
82
|
+
column_datum_table[:owner_id].eq(table[:id]).and(
|
83
|
+
column_datum_table[:owner_type].eq(dynamic_type)
|
84
|
+
).and(
|
85
|
+
column_datum_table[:dynamic_column_id].eq(column_table[:id])
|
86
|
+
).and(
|
87
|
+
column_datum_table[:value].matches("%"+value+"%")
|
88
|
+
)
|
89
|
+
)
|
90
|
+
|
91
|
+
table.create_join(column_datum_table, column_table_datum_join_on)
|
92
|
+
end
|
93
|
+
@scope.joins_values += join_value
|
94
|
+
|
95
|
+
@scope
|
96
|
+
end
|
97
|
+
end
|
98
|
+
|
99
|
+
chain
|
100
|
+
else
|
101
|
+
super
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module HasDynamicColumns
|
2
|
-
class DynamicColumn < ActiveRecord::Base
|
2
|
+
class DynamicColumn < ::ActiveRecord::Base
|
3
3
|
has_many :dynamic_column_options, :class_name => "HasDynamicColumns::DynamicColumnOption"
|
4
4
|
has_many :dynamic_column_validations, :class_name => "HasDynamicColumns::DynamicColumnValidation"
|
5
5
|
end
|
@@ -1,5 +1,5 @@
|
|
1
1
|
module HasDynamicColumns
|
2
|
-
class DynamicColumnDatum < ActiveRecord::Base
|
2
|
+
class DynamicColumnDatum < ::ActiveRecord::Base
|
3
3
|
belongs_to :dynamic_column, :class_name => "HasDynamicColumns::DynamicColumn"
|
4
4
|
belongs_to :dynamic_column_option, :class_name => "HasDynamicColumns::DynamicColumnOption"
|
5
5
|
belongs_to :owner, :polymorphic => true
|
@@ -0,0 +1,294 @@
|
|
1
|
+
module HasDynamicColumns
|
2
|
+
module Model
|
3
|
+
module ClassMethods
|
4
|
+
def has_dynamic_columns(*args)
|
5
|
+
options = args.extract_options!
|
6
|
+
configuration = {
|
7
|
+
:as => "dynamic_columns",
|
8
|
+
:field_scope => nil,
|
9
|
+
}
|
10
|
+
configuration.update(options) if options.is_a?(Hash)
|
11
|
+
|
12
|
+
class_eval <<-EOV
|
13
|
+
alias_method :as_json_before_#{configuration[:as]}, :as_json
|
14
|
+
|
15
|
+
# Store all our configurations for usage later
|
16
|
+
@@has_dynamic_columns_configurations ||= []
|
17
|
+
@@has_dynamic_columns_configurations << #{configuration}
|
18
|
+
|
19
|
+
include ::HasDynamicColumns::Model::InstanceMethods
|
20
|
+
|
21
|
+
has_many :activerecord_dynamic_columns,
|
22
|
+
class_name: "HasDynamicColumns::DynamicColumn",
|
23
|
+
as: :field_scope
|
24
|
+
has_many :activerecord_dynamic_column_data,
|
25
|
+
class_name: "HasDynamicColumns::DynamicColumnDatum",
|
26
|
+
as: :owner,
|
27
|
+
autosave: true
|
28
|
+
|
29
|
+
# only add to attr_accessible
|
30
|
+
# if the class has some mass_assignment_protection
|
31
|
+
if defined?(accessible_attributes) and !accessible_attributes.blank?
|
32
|
+
#attr_accessible :#{configuration[:column]}
|
33
|
+
end
|
34
|
+
|
35
|
+
validate do |field_scope|
|
36
|
+
field_scope = self.get_#{configuration[:as]}_field_scope
|
37
|
+
|
38
|
+
if field_scope
|
39
|
+
# has_many association
|
40
|
+
if field_scope.respond_to?(:select) && field_scope.respond_to?(:collect)
|
41
|
+
|
42
|
+
# belongs_to association
|
43
|
+
else
|
44
|
+
# All the fields defined on the parent model
|
45
|
+
dynamic_columns = field_scope.send("activerecord_dynamic_columns")
|
46
|
+
|
47
|
+
self.send("activerecord_dynamic_column_data").each { |dynamic_column_datum|
|
48
|
+
# Collect all validation errors
|
49
|
+
validation_errors = []
|
50
|
+
|
51
|
+
if dynamic_column_datum.dynamic_column_option_id == -1
|
52
|
+
validation_errors << "invalid_option"
|
53
|
+
end
|
54
|
+
|
55
|
+
# Find the dynamic_column defined for this datum
|
56
|
+
dynamic_column = nil
|
57
|
+
dynamic_columns.each { |i|
|
58
|
+
if i == dynamic_column_datum.dynamic_column
|
59
|
+
dynamic_column = i
|
60
|
+
break
|
61
|
+
end
|
62
|
+
}
|
63
|
+
# We have a dynamic_column - validate
|
64
|
+
if dynamic_column
|
65
|
+
dynamic_column.dynamic_column_validations.each { |validation|
|
66
|
+
if !validation.is_valid?(dynamic_column_datum.value.to_s)
|
67
|
+
validation_errors << validation.error
|
68
|
+
end
|
69
|
+
}
|
70
|
+
else
|
71
|
+
# No field found - this is probably bad - should we throw an error?
|
72
|
+
validation_errors << "not_found"
|
73
|
+
end
|
74
|
+
|
75
|
+
# If any errors exist - add them
|
76
|
+
if validation_errors.length > 0
|
77
|
+
if dynamic_column.nil?
|
78
|
+
# TODO - fix from the has_many - need to fix validations
|
79
|
+
#errors.add(:dynamic_columns, { "unknown" => validation_errors })
|
80
|
+
else
|
81
|
+
errors.add(:dynamic_columns, { dynamic_column.key.to_s => validation_errors })
|
82
|
+
end
|
83
|
+
end
|
84
|
+
}
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
|
89
|
+
public
|
90
|
+
# Order by dynamic columns
|
91
|
+
def self.dynamic_order(field_scope, key, direction = :asc)
|
92
|
+
table = self.name.constantize.arel_table
|
93
|
+
column_table = HasDynamicColumns::DynamicColumn.arel_table.alias("dynamic_order_"+key.to_s)
|
94
|
+
column_datum_table = HasDynamicColumns::DynamicColumnDatum.arel_table.alias("dynamic_order_data_"+key.to_s)
|
95
|
+
|
96
|
+
field_scope_id = (!field_scope.nil?) ? field_scope.id : nil
|
97
|
+
field_scope_type = (!field_scope.nil?) ? field_scope.class.name.constantize.to_s : nil
|
98
|
+
dynamic_type = self.name.constantize.to_s
|
99
|
+
|
100
|
+
# Join on the column with the key
|
101
|
+
on_query = column_table[:key].eq(key)
|
102
|
+
if !field_scope_type.nil?
|
103
|
+
on_query = on_query.and(
|
104
|
+
column_table[:field_scope_type].eq(field_scope_type)
|
105
|
+
)
|
106
|
+
end
|
107
|
+
if !field_scope_id.nil?
|
108
|
+
on_query = on_query.and(
|
109
|
+
column_table[:field_scope_id].eq(field_scope_id)
|
110
|
+
)
|
111
|
+
end
|
112
|
+
|
113
|
+
column_table_join_on = column_table
|
114
|
+
.create_on(
|
115
|
+
on_query
|
116
|
+
)
|
117
|
+
|
118
|
+
column_table_join = table.create_join(column_table, column_table_join_on)
|
119
|
+
query = joins(column_table_join)
|
120
|
+
|
121
|
+
# Join on all the data with the provided key
|
122
|
+
column_table_datum_join_on = column_datum_table
|
123
|
+
.create_on(
|
124
|
+
column_datum_table[:owner_id].eq(table[:id]).and(
|
125
|
+
column_datum_table[:owner_type].eq(dynamic_type)
|
126
|
+
).and(
|
127
|
+
column_datum_table[:dynamic_column_id].eq(column_table[:id])
|
128
|
+
)
|
129
|
+
)
|
130
|
+
|
131
|
+
column_table_datum_join = table.create_join(column_datum_table, column_table_datum_join_on)
|
132
|
+
query = query.joins(column_table_datum_join)
|
133
|
+
|
134
|
+
# Order
|
135
|
+
query = query.order(column_datum_table[:value].send(direction))
|
136
|
+
|
137
|
+
# Group required - we have many rows
|
138
|
+
query = query.group(table[:id])
|
139
|
+
|
140
|
+
query
|
141
|
+
end
|
142
|
+
|
143
|
+
# Find by dynamic columns
|
144
|
+
def self.dynamic_where(*args)
|
145
|
+
field_scope = args[0].is_a?(Hash) ? nil : args[0]
|
146
|
+
options = args.extract_options!
|
147
|
+
|
148
|
+
field_scope_id = (!field_scope.nil?) ? field_scope.id : nil
|
149
|
+
field_scope_type = (!field_scope.nil?) ? field_scope.class.name.constantize.to_s : nil
|
150
|
+
dynamic_type = self.name.constantize.to_s
|
151
|
+
|
152
|
+
table = self.name.constantize.arel_table
|
153
|
+
query = nil
|
154
|
+
|
155
|
+
# Need to join on each of the keys we are performing where on
|
156
|
+
options.each { |key, value|
|
157
|
+
# Don't bother with empty values
|
158
|
+
next if value.to_s.empty?
|
159
|
+
|
160
|
+
column_table = HasDynamicColumns::DynamicColumn.arel_table.alias("dynamic_where_"+key.to_s)
|
161
|
+
column_datum_table = HasDynamicColumns::DynamicColumnDatum.arel_table.alias("dynamic_where_data_"+key.to_s)
|
162
|
+
|
163
|
+
# Join on the column with the key
|
164
|
+
on_query = column_table[:key].eq(key)
|
165
|
+
if !field_scope_type.nil?
|
166
|
+
on_query = on_query.and(
|
167
|
+
column_table[:field_scope_type].eq(field_scope_type)
|
168
|
+
)
|
169
|
+
end
|
170
|
+
if !field_scope_id.nil?
|
171
|
+
on_query = on_query.and(
|
172
|
+
column_table[:field_scope_id].eq(field_scope_id)
|
173
|
+
)
|
174
|
+
end
|
175
|
+
|
176
|
+
column_table_join_on = column_table
|
177
|
+
.create_on(
|
178
|
+
on_query
|
179
|
+
)
|
180
|
+
|
181
|
+
column_table_join = table.create_join(column_table, column_table_join_on)
|
182
|
+
query = (query.nil?)? joins(column_table_join) : query.joins(column_table_join)
|
183
|
+
|
184
|
+
# Join on all the data with the provided key
|
185
|
+
column_table_datum_join_on = column_datum_table
|
186
|
+
.create_on(
|
187
|
+
column_datum_table[:owner_id].eq(table[:id]).and(
|
188
|
+
column_datum_table[:owner_type].eq(dynamic_type)
|
189
|
+
).and(
|
190
|
+
column_datum_table[:dynamic_column_id].eq(column_table[:id])
|
191
|
+
).and(
|
192
|
+
column_datum_table[:value].matches("%"+value+"%")
|
193
|
+
)
|
194
|
+
)
|
195
|
+
|
196
|
+
column_table_datum_join = table.create_join(column_datum_table, column_table_datum_join_on)
|
197
|
+
query = query.joins(column_table_datum_join)
|
198
|
+
}
|
199
|
+
# Group required - we have many rows
|
200
|
+
query = (query.nil?)? group(table[:id]) : query.group(table[:id])
|
201
|
+
|
202
|
+
query
|
203
|
+
end
|
204
|
+
|
205
|
+
def as_json(*args)
|
206
|
+
json = super(*args)
|
207
|
+
options = args.extract_options!
|
208
|
+
|
209
|
+
@@has_dynamic_columns_configurations.each { |config|
|
210
|
+
if !options[:root].nil?
|
211
|
+
json[options[:root]][config[:as].to_s] = self.send(config[:as].to_s)
|
212
|
+
else
|
213
|
+
json[config[:as].to_s] = self.send(config[:as].to_s)
|
214
|
+
end
|
215
|
+
}
|
216
|
+
|
217
|
+
json
|
218
|
+
end
|
219
|
+
|
220
|
+
# Setter for dynamic field data
|
221
|
+
def #{configuration[:as]}=data
|
222
|
+
data.each_pair { |key, value|
|
223
|
+
# We dont play well with this key
|
224
|
+
if !self.storable_#{configuration[:as].to_s.singularize}_key?(key)
|
225
|
+
raise NoMethodError
|
226
|
+
end
|
227
|
+
dynamic_column = self.#{configuration[:as].to_s.singularize}_key_to_dynamic_column(key)
|
228
|
+
|
229
|
+
# We already have this key in database
|
230
|
+
if existing = self.activerecord_dynamic_column_data.select { |i| i.dynamic_column == dynamic_column }.first
|
231
|
+
existing.value = value
|
232
|
+
else
|
233
|
+
self.activerecord_dynamic_column_data.build(:dynamic_column => dynamic_column, :value => value)
|
234
|
+
end
|
235
|
+
}
|
236
|
+
end
|
237
|
+
|
238
|
+
def #{configuration[:as]}
|
239
|
+
h = {}
|
240
|
+
self.field_scope_#{configuration[:as]}.each { |i|
|
241
|
+
h[i.key] = nil
|
242
|
+
}
|
243
|
+
|
244
|
+
self.activerecord_dynamic_column_data.each { |i|
|
245
|
+
h[i.dynamic_column.key] = i.value unless !i.dynamic_column || !h.has_key?(i.dynamic_column.key)
|
246
|
+
}
|
247
|
+
|
248
|
+
h
|
249
|
+
end
|
250
|
+
|
251
|
+
def #{configuration[:as].to_s.singularize}_keys
|
252
|
+
self.field_scope_#{configuration[:as]}.collect { |i| i.key }
|
253
|
+
end
|
254
|
+
|
255
|
+
def field_scope_#{configuration[:as]}
|
256
|
+
# has_many relationship
|
257
|
+
if self.get_#{configuration[:as]}_field_scope.respond_to?(:select) && self.get_#{configuration[:as]}_field_scope.respond_to?(:collect)
|
258
|
+
self.get_#{configuration[:as]}_field_scope.collect { |i|
|
259
|
+
i.send("activerecord_dynamic_columns")
|
260
|
+
}.flatten.select { |i|
|
261
|
+
i.dynamic_type.to_s.empty? || i.dynamic_type.to_s == self.class.to_s
|
262
|
+
}
|
263
|
+
# belongs_to relationship
|
264
|
+
else
|
265
|
+
self.get_#{configuration[:as]}_field_scope.send("activerecord_dynamic_columns").select { |i|
|
266
|
+
# Only get things with no dynamic type defined or dynamic types defined as this class
|
267
|
+
i.dynamic_type.to_s.empty? || i.dynamic_type.to_s == self.class.to_s
|
268
|
+
}
|
269
|
+
end
|
270
|
+
end
|
271
|
+
|
272
|
+
protected
|
273
|
+
def get_#{configuration[:as]}_field_scope
|
274
|
+
#{configuration[:field_scope]}
|
275
|
+
end
|
276
|
+
|
277
|
+
# Whether this is storable
|
278
|
+
def storable_#{configuration[:as].to_s.singularize}_key?(key)
|
279
|
+
self.#{configuration[:as].to_s.singularize}_keys.include?(key.to_s)
|
280
|
+
end
|
281
|
+
|
282
|
+
# Figures out which dynamic_column has which key
|
283
|
+
def #{configuration[:as].to_s.singularize}_key_to_dynamic_column(key)
|
284
|
+
found = nil
|
285
|
+
if record = self.send('field_scope_#{configuration[:as]}').select { |i| i.key == key.to_s }.first
|
286
|
+
found = record
|
287
|
+
end
|
288
|
+
found
|
289
|
+
end
|
290
|
+
EOV
|
291
|
+
end
|
292
|
+
end
|
293
|
+
end
|
294
|
+
end
|