hydra_attribute 0.2.0 → 0.3.0.beta1
Sign up to get free protection for your applications and to get access to all the features.
- data/README.md +74 -68
- data/cucumber.yml +1 -0
- data/features/attributes/create.feature +22 -0
- data/features/attributes/destroy.feature +18 -0
- data/features/attributes/update.feature +19 -0
- data/features/create.feature +47 -0
- data/features/define.feature +33 -0
- data/features/query_methods/group.feature +13 -14
- data/features/query_methods/order.feature +63 -52
- data/features/query_methods/select.feature +36 -37
- data/features/query_methods/where.feature +36 -38
- data/features/step_definitions/model_steps.rb +23 -19
- data/features/step_definitions/query_methods.rb +6 -2
- data/features/step_definitions/record_steps.rb +28 -10
- data/features/support/env.rb +12 -6
- data/features/support/schema.rb +62 -35
- data/features/support/world.rb +14 -5
- data/features/update.feature +114 -0
- data/gemfiles/3.1.gemfile.lock +10 -10
- data/gemfiles/3.2.gemfile.lock +10 -10
- data/lib/hydra_attribute/active_record/association.rb +77 -0
- data/lib/hydra_attribute/active_record/association_preloader.rb +82 -0
- data/lib/hydra_attribute/active_record/attribute_methods.rb +145 -37
- data/lib/hydra_attribute/active_record/migration.rb +21 -0
- data/lib/hydra_attribute/active_record/reflection.rb +16 -0
- data/lib/hydra_attribute/active_record/relation/query_methods.rb +73 -71
- data/lib/hydra_attribute/active_record/relation.rb +1 -24
- data/lib/hydra_attribute/active_record.rb +16 -2
- data/lib/hydra_attribute/association_builder.rb +44 -20
- data/lib/hydra_attribute/builder.rb +15 -13
- data/lib/hydra_attribute/configuration.rb +9 -30
- data/lib/hydra_attribute/entity_callbacks.rb +46 -0
- data/lib/hydra_attribute/hydra_attribute.rb +27 -0
- data/lib/hydra_attribute/migrator.rb +106 -0
- data/lib/hydra_attribute/railtie.rb +2 -0
- data/lib/hydra_attribute/version.rb +2 -2
- data/lib/hydra_attribute.rb +8 -6
- data/lib/rails/generators/hydra_attribute/install/templates/hydra_attribute.txt +4 -0
- data/spec/spec_helper.rb +1 -2
- metadata +42 -60
- data/features/attribute_methods.feature +0 -146
- data/features/define_attributes.feature +0 -56
- data/features/load_associations.feature +0 -40
- data/features/step_definitions/class_steps.rb +0 -32
- data/features/typecast_attributes.feature +0 -24
- data/lib/generators/hydra_attribute/install/templates/hydra_attribute.txt +0 -11
- data/lib/hydra_attribute/active_record/attribute_methods/before_type_cast.rb +0 -16
- data/lib/hydra_attribute/active_record/attribute_methods/read.rb +0 -13
- data/lib/hydra_attribute/attribute_builder.rb +0 -57
- data/lib/hydra_attribute/attribute_proxy.rb +0 -16
- data/lib/hydra_attribute/migration.rb +0 -27
- data/spec/hydra_attribute/active_record/relation/query_methods_spec.rb +0 -334
- data/spec/hydra_attribute/active_record/relation_spec.rb +0 -67
- data/spec/hydra_attribute/active_record/scoping_spec.rb +0 -23
- data/spec/hydra_attribute/active_record_spec.rb +0 -18
- data/spec/hydra_attribute/association_builder_spec.rb +0 -95
- data/spec/hydra_attribute/attribute_builder_spec.rb +0 -70
- data/spec/hydra_attribute/attribute_helpers_spec.rb +0 -70
- data/spec/hydra_attribute/builder_spec.rb +0 -39
- data/spec/hydra_attribute/configuration_spec.rb +0 -65
- data/spec/hydra_attribute_spec.rb +0 -20
- /data/lib/{generators → rails/generators}/hydra_attribute/install/USAGE +0 -0
- /data/lib/{generators → rails/generators}/hydra_attribute/install/install_generator.rb +0 -0
@@ -0,0 +1,77 @@
|
|
1
|
+
module HydraAttribute
|
2
|
+
module ActiveRecord
|
3
|
+
class Association < ::ActiveRecord::Associations::HasManyAssociation
|
4
|
+
|
5
|
+
def find_model(attributes = {})
|
6
|
+
load_target unless loaded?
|
7
|
+
target.detect do |model|
|
8
|
+
model.hydra_attribute_id == attributes[:hydra_attribute_id]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def find_model_or_build(attributes = {})
|
13
|
+
find_model(attributes) || build(attributes)
|
14
|
+
end
|
15
|
+
|
16
|
+
def build(attributes = {}, options = {}, &block)
|
17
|
+
return if locked_for_build? and white_list_for_build.exclude?(attributes[:hydra_attribute_id])
|
18
|
+
super
|
19
|
+
end
|
20
|
+
|
21
|
+
def all_models
|
22
|
+
unless @full_loaded
|
23
|
+
(all_attribute_ids - target.map(&:hydra_attribute_id)).each do |hydra_attribute_id|
|
24
|
+
build(hydra_attribute_id: hydra_attribute_id)
|
25
|
+
end
|
26
|
+
@full_loaded = true
|
27
|
+
end
|
28
|
+
target
|
29
|
+
end
|
30
|
+
|
31
|
+
def locked_for_build
|
32
|
+
@locked_for_build ||= false
|
33
|
+
end
|
34
|
+
alias_method :locked_for_build?, :locked_for_build
|
35
|
+
|
36
|
+
def lock_for_build!(white_list_for_build = [])
|
37
|
+
@locked_for_build = true
|
38
|
+
@white_list_for_build = Array(white_list_for_build)
|
39
|
+
loaded!
|
40
|
+
end
|
41
|
+
|
42
|
+
def white_list_for_build
|
43
|
+
@white_list_for_build ||= []
|
44
|
+
end
|
45
|
+
|
46
|
+
def all_attribute_ids
|
47
|
+
hydra_attribute_ids = owner.class.grouped_hydra_attribute_ids(reflection.backend_type)
|
48
|
+
hydra_attribute_ids &= white_list_for_build if locked_for_build?
|
49
|
+
hydra_attribute_ids
|
50
|
+
end
|
51
|
+
|
52
|
+
private
|
53
|
+
|
54
|
+
# Optimized method
|
55
|
+
# Remove unnecessary callbacks
|
56
|
+
def add_to_target(record)
|
57
|
+
@target << record
|
58
|
+
record
|
59
|
+
end
|
60
|
+
|
61
|
+
# Optimized method
|
62
|
+
# Attributes are written via low level function without additional checks
|
63
|
+
def build_record(attributes, _)
|
64
|
+
reflection.klass.new do |record|
|
65
|
+
unless attributes.has_key?(:value)
|
66
|
+
attributes[:value] = owner.class.hydra_attribute(attributes[:hydra_attribute_id]).default_value
|
67
|
+
end
|
68
|
+
|
69
|
+
record.send :write_attribute, 'id', attributes[:id]
|
70
|
+
record.send :write_attribute, 'entity_id', owner.id
|
71
|
+
record.send :write_attribute, 'hydra_attribute_id', attributes[:hydra_attribute_id]
|
72
|
+
record.send :write_attribute, 'value', attributes[:value]
|
73
|
+
end
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
end
|
@@ -0,0 +1,82 @@
|
|
1
|
+
module HydraAttribute
|
2
|
+
module ActiveRecord
|
3
|
+
class AssociationPreloader
|
4
|
+
attr_reader :relation, :records, :hashed_records
|
5
|
+
|
6
|
+
def initialize(relation, records = [])
|
7
|
+
@relation = relation
|
8
|
+
@records = records
|
9
|
+
@hashed_records = records.each_with_object({}) { |record, hash| hash[record.id] = record }
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.run(relation, records)
|
13
|
+
new(relation, records).run
|
14
|
+
end
|
15
|
+
|
16
|
+
def run
|
17
|
+
grouped_values do |association, values, white_list_attribute_ids|
|
18
|
+
association.target.concat(values)
|
19
|
+
association.lock_for_build!(white_list_attribute_ids)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
private
|
24
|
+
|
25
|
+
def grouped_values
|
26
|
+
grouped_attribute_ids do |type, attribute_ids|
|
27
|
+
association_name = AssociationBuilder.association_name(type)
|
28
|
+
load_values(type, attribute_ids) do |record, values|
|
29
|
+
yield(record.association(association_name), values, attribute_ids)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def load_values(type, attribute_ids)
|
35
|
+
return records.each { |record| yield(record, []) } if attribute_ids.empty?
|
36
|
+
|
37
|
+
attribute_ids.each_slice(in_clause_length || attribute_ids.length) do |sliced_attribute_ids|
|
38
|
+
records.each_slice(in_clause_length || records.length) do |sliced_records|
|
39
|
+
values = AssociationBuilder.class_name(klass, type).constantize.select([:id, :entity_id, :hydra_attribute_id, :value]).where(entity_id: sliced_records, hydra_attribute_id: sliced_attribute_ids)
|
40
|
+
group_records_with_values(sliced_records, values).each do |record_id, grouped_values|
|
41
|
+
yield(hashed_records[record_id], grouped_values)
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
def group_records_with_values(records, values)
|
48
|
+
hash = records.each_with_object({}) { |record, hash| hash[record.id] = [] }
|
49
|
+
values.each { |value| hash[value.entity_id] << value }
|
50
|
+
hash
|
51
|
+
end
|
52
|
+
|
53
|
+
def grouped_attribute_ids
|
54
|
+
map = klass.hydra_attribute_backend_types.each_with_object({}) { |type, object| object[type] = [] }
|
55
|
+
mapping = hydra_attributes.each_with_object(map) do |hydra_attribute, mapping|
|
56
|
+
mapping[hydra_attribute.backend_type] << hydra_attribute.id
|
57
|
+
end
|
58
|
+
mapping.each { |type, ids| yield(type, ids) }
|
59
|
+
end
|
60
|
+
|
61
|
+
def hydra_attributes
|
62
|
+
if attribute_limit?
|
63
|
+
relation.hydra_select_values.map { |name| klass.hydra_attribute(name) }
|
64
|
+
else
|
65
|
+
klass.hydra_attributes
|
66
|
+
end
|
67
|
+
end
|
68
|
+
|
69
|
+
def attribute_limit?
|
70
|
+
relation.select_values.any? or relation.hydra_select_values.any?
|
71
|
+
end
|
72
|
+
|
73
|
+
def klass
|
74
|
+
relation.klass
|
75
|
+
end
|
76
|
+
|
77
|
+
def in_clause_length
|
78
|
+
klass.connection.in_clause_length
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
@@ -3,69 +3,177 @@ module HydraAttribute
|
|
3
3
|
module AttributeMethods
|
4
4
|
extend ActiveSupport::Concern
|
5
5
|
|
6
|
-
|
7
|
-
|
6
|
+
NAME_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?=]?\z/
|
7
|
+
CALL_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?]?\z/
|
8
8
|
|
9
|
-
|
10
|
-
|
9
|
+
included do
|
10
|
+
@hydra_attribute_methods_mutex = Mutex.new
|
11
11
|
end
|
12
12
|
|
13
13
|
module ClassMethods
|
14
|
-
def
|
15
|
-
|
16
|
-
|
14
|
+
def hydra_attribute_methods_generated?
|
15
|
+
@hydra_attribute_methods_generated ||= false
|
16
|
+
end
|
17
|
+
|
18
|
+
def generated_hydra_attribute_methods
|
19
|
+
@generated_hydra_attribute_methods ||= begin
|
20
|
+
mod = Module.new
|
21
|
+
include mod
|
22
|
+
mod
|
23
|
+
end
|
17
24
|
end
|
18
25
|
|
19
26
|
def hydra_attributes
|
20
|
-
@hydra_attributes.
|
27
|
+
@hydra_attributes ||= HydraAttribute.where(entity_type: base_class.model_name)
|
28
|
+
end
|
29
|
+
|
30
|
+
def grouped_hydra_attributes
|
31
|
+
@grouped_hydra_attributes ||= hydra_attributes.group_by(&:backend_type)
|
32
|
+
end
|
33
|
+
|
34
|
+
%w(id name backend_type).each do |prefix|
|
35
|
+
class_eval <<-EOS, __FILE__, __LINE__ + 1
|
36
|
+
def hydra_attribute_#{prefix}s
|
37
|
+
@hydra_attribute_#{prefix}s ||= hydra_attributes.map(&:#{prefix}).uniq
|
38
|
+
end
|
39
|
+
|
40
|
+
def grouped_hydra_attribute_#{prefix}s(type)
|
41
|
+
instance = "@grouped_hydra_attribute_\#{type}_#{prefix}s"
|
42
|
+
instance_variable_get(instance) || instance_variable_set(instance, grouped_hydra_attributes[type].map(&:#{prefix}).uniq)
|
43
|
+
end
|
44
|
+
EOS
|
21
45
|
end
|
22
46
|
|
23
|
-
def
|
24
|
-
|
47
|
+
def hydra_attribute(identifier)
|
48
|
+
@hydra_attribute_cache ||= {}
|
49
|
+
@hydra_attribute_cache[identifier] ||= hydra_attributes.find do |hydra_attribute|
|
50
|
+
hydra_attribute.id == identifier || hydra_attribute.name == identifier
|
51
|
+
end
|
25
52
|
end
|
26
53
|
|
27
|
-
def
|
28
|
-
|
54
|
+
def define_hydra_attribute_methods
|
55
|
+
@hydra_attribute_methods_mutex.synchronize do
|
56
|
+
return if hydra_attribute_methods_generated?
|
57
|
+
hydra_attributes.each { |hydra_attribute| define_hydra_attribute_method(hydra_attribute) }
|
58
|
+
@hydra_attribute_methods_generated = true
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def define_hydra_attribute_method(hydra_attribute)
|
63
|
+
attribute_method_matchers.each do |matcher|
|
64
|
+
current = matcher.method_name(hydra_attribute.name)
|
65
|
+
target = matcher.method_name(:value)
|
66
|
+
|
67
|
+
if current =~ NAME_COMPILABLE_REGEXP
|
68
|
+
defn = "def #{current}(*args)"
|
69
|
+
else
|
70
|
+
defn = "define_method(:'#{current}') do |*args|"
|
71
|
+
end
|
72
|
+
|
73
|
+
if target =~ CALL_COMPILABLE_REGEXP
|
74
|
+
send = "#{target}(*args)"
|
75
|
+
else
|
76
|
+
send = "send(:'#{target}', *args)"
|
77
|
+
end
|
78
|
+
|
79
|
+
generated_hydra_attribute_methods.module_eval <<-EOS, __FILE__, __LINE__ + 1
|
80
|
+
#{defn}
|
81
|
+
if value_model = hydra_value_model(#{hydra_attribute.id})
|
82
|
+
value_model.#{send}
|
83
|
+
else
|
84
|
+
missing_attribute('#{hydra_attribute.name}', caller)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
EOS
|
88
|
+
end
|
89
|
+
end
|
90
|
+
|
91
|
+
def reset_hydra_attribute_methods
|
92
|
+
generated_hydra_attribute_methods.module_eval do
|
93
|
+
instance_methods.each { |m| undef_method(m) }
|
94
|
+
end
|
95
|
+
|
96
|
+
@hydra_attributes = nil
|
97
|
+
@hydra_attribute_methods_generated = false
|
98
|
+
@hydra_attribute_cache = {}
|
99
|
+
@grouped_hydra_attributes = nil
|
100
|
+
|
101
|
+
%w(id name backend_type).each do |prefix|
|
102
|
+
instance_variable_set("@hydra_attribute_#{prefix}s", nil)
|
103
|
+
|
104
|
+
SUPPORT_TYPES.each do |type|
|
105
|
+
instance_variable_set("@grouped_hydra_attribute_#{type}_#{prefix}s", nil)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
end
|
109
|
+
|
110
|
+
def undefine_attribute_methods
|
111
|
+
reset_hydra_attribute_methods
|
112
|
+
super
|
29
113
|
end
|
30
114
|
end
|
31
115
|
|
32
|
-
def
|
33
|
-
|
116
|
+
def respond_to?(name, include_private = false)
|
117
|
+
self.class.define_hydra_attribute_methods unless self.class.hydra_attribute_methods_generated?
|
34
118
|
super
|
35
119
|
end
|
36
120
|
|
37
|
-
|
38
|
-
|
39
|
-
|
121
|
+
%w(attributes attributes_before_type_cast).each do |method|
|
122
|
+
class_eval <<-EOS, __FILE__, __LINE__ + 1
|
123
|
+
def #{method}
|
124
|
+
self.class.hydra_attribute_backend_types.each_with_object(super) do |type, attributes|
|
125
|
+
hydra_value_association(type).all_models.each do |model|
|
126
|
+
hydra_attribute = self.class.hydra_attribute(model.hydra_attribute_id)
|
127
|
+
attributes[hydra_attribute.name] = model.#{method}['value']
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
131
|
+
EOS
|
40
132
|
end
|
41
133
|
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
134
|
+
%w(read_attribute read_attribute_before_type_cast).each do |method|
|
135
|
+
class_eval <<-EOS, __FILE__, __LINE__ + 1
|
136
|
+
def #{method}(attr_name)
|
137
|
+
identifier = attr_name.to_s
|
138
|
+
if self.class.hydra_attribute_names.include?(identifier)
|
139
|
+
if value_model = hydra_value_model(identifier)
|
140
|
+
value_model.#{method}('value')
|
141
|
+
else
|
142
|
+
missing_attribute(identifier, caller)
|
143
|
+
end
|
144
|
+
else
|
145
|
+
super
|
146
|
+
end
|
147
|
+
end
|
148
|
+
EOS
|
49
149
|
end
|
50
150
|
|
51
|
-
|
52
|
-
|
53
|
-
|
151
|
+
private
|
152
|
+
|
153
|
+
def hydra_value_model(identifier)
|
154
|
+
@hydra_value_model_cache ||= {}
|
155
|
+
@hydra_value_model_cache[identifier] ||= begin
|
156
|
+
hydra_attribute = self.class.hydra_attribute(identifier)
|
157
|
+
association = hydra_value_association(hydra_attribute.backend_type)
|
158
|
+
association.find_model_or_build(hydra_attribute_id: hydra_attribute.id)
|
159
|
+
end
|
54
160
|
end
|
55
161
|
|
56
|
-
def
|
57
|
-
|
162
|
+
def hydra_value_association(backend_type)
|
163
|
+
association(::HydraAttribute::AssociationBuilder.association_name(backend_type))
|
58
164
|
end
|
59
165
|
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
166
|
+
def method_missing(method, *args, &block)
|
167
|
+
if self.class.hydra_attribute_methods_generated?
|
168
|
+
super
|
169
|
+
else
|
170
|
+
self.class.define_hydra_attribute_methods
|
171
|
+
if respond_to_without_attributes?(method)
|
172
|
+
send(method, *args, &block)
|
173
|
+
else
|
174
|
+
super
|
67
175
|
end
|
68
|
-
|
176
|
+
end
|
69
177
|
end
|
70
178
|
end
|
71
179
|
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module HydraAttribute
|
2
|
+
module ActiveRecord
|
3
|
+
module Migration
|
4
|
+
def create_hydra_entity(name, options = {}, &block)
|
5
|
+
Migrator.create(self, name, options, &block)
|
6
|
+
end
|
7
|
+
|
8
|
+
def drop_hydra_entity(name)
|
9
|
+
Migrator.drop(self, name)
|
10
|
+
end
|
11
|
+
|
12
|
+
def migrate_to_hydra_entity(name)
|
13
|
+
Migrator.migrate(self, name)
|
14
|
+
end
|
15
|
+
|
16
|
+
def rollback_from_hydra_entity(name)
|
17
|
+
Migrator.rollback(self, name)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,16 @@
|
|
1
|
+
module HydraAttribute
|
2
|
+
module ActiveRecord
|
3
|
+
class Reflection < ::ActiveRecord::Reflection::AssociationReflection
|
4
|
+
|
5
|
+
# Return custom association class
|
6
|
+
# which is optimized to load hydra attributes
|
7
|
+
def association_class
|
8
|
+
Association
|
9
|
+
end
|
10
|
+
|
11
|
+
def backend_type
|
12
|
+
@backend_type ||= SUPPORT_TYPES.find { |type| type == klass.model_name.demodulize.underscore.split('_')[1] }
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
@@ -25,9 +25,9 @@ module HydraAttribute
|
|
25
25
|
opts.inject(self) do |relation, (name, value)|
|
26
26
|
if klass.hydra_attribute_names.include?(name.to_s)
|
27
27
|
relation, name = relation.clone, name.to_s
|
28
|
-
relation.hydra_joins_aliases <<
|
29
|
-
relation.joins_values +=
|
30
|
-
relation.where_values += build_where(
|
28
|
+
relation.hydra_joins_aliases << hydra_helper.ref_alias(name, value)
|
29
|
+
relation.joins_values += hydra_helper.build_joins(name, value)
|
30
|
+
relation.where_values += build_where(hydra_helper.where_options(name, value))
|
31
31
|
relation
|
32
32
|
else
|
33
33
|
relation.where_without_hydra_attribute(name => value)
|
@@ -39,21 +39,21 @@ module HydraAttribute
|
|
39
39
|
end
|
40
40
|
|
41
41
|
def build_arel
|
42
|
-
@group_values =
|
43
|
-
@order_values =
|
42
|
+
@group_values = hydra_helper.quote_columns(@group_values.uniq.reject(&:blank?))
|
43
|
+
@order_values = hydra_helper.quote_columns(@order_values.uniq.reject(&:blank?))
|
44
44
|
|
45
45
|
if instance_variable_defined?(:@reorder_value) and instance_variable_get(:@reorder_value).present? # for compatibility with 3.1.x
|
46
|
-
@reorder_value =
|
46
|
+
@reorder_value = hydra_helper.quote_columns(@reorder_value.uniq.reject(&:blank?))
|
47
47
|
end
|
48
48
|
|
49
|
+
# detect hydra attributes from select list
|
49
50
|
@hydra_select_values, @select_values = @select_values.partition { |value| klass.hydra_attribute_names.include?(value.to_s) }
|
50
51
|
@hydra_select_values.map!(&:to_s)
|
51
|
-
@select_values.map!{ |value|
|
52
|
+
@select_values.map!{ |value| hydra_helper.prepend_table_name(value) }
|
52
53
|
|
53
54
|
# force add ID for preloading hydra attributes
|
54
|
-
if @hydra_select_values.any? && @select_values.none? { |v|
|
55
|
-
@select_values <<
|
56
|
-
@id_for_hydra_attributes = true
|
55
|
+
if @hydra_select_values.any? && @select_values.none? { |v| hydra_helper.attr_eq_column?(v, klass.primary_key) }
|
56
|
+
@select_values << hydra_helper.prepend_table_name(klass.primary_key)
|
57
57
|
end
|
58
58
|
|
59
59
|
super
|
@@ -77,83 +77,85 @@ module HydraAttribute
|
|
77
77
|
].include?(attr)
|
78
78
|
end
|
79
79
|
|
80
|
-
def prepend_table_name(column)
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
80
|
+
def prepend_table_name(column, table = klass.table_name)
|
81
|
+
case column
|
82
|
+
when String, Symbol
|
83
|
+
copy = column.to_s.strip
|
84
|
+
if copy =~ /^\w+$/
|
85
|
+
connection.quote_table_name(table) + '.' + connection.quote_column_name(copy)
|
86
|
+
else
|
87
|
+
column
|
88
|
+
end
|
86
89
|
else
|
87
90
|
column
|
88
91
|
end
|
89
92
|
end
|
90
|
-
end
|
91
93
|
|
92
|
-
|
94
|
+
def build_joins(name, value)
|
95
|
+
conn = klass.connection
|
96
|
+
quoted_alias = conn.quote_table_name(ref_alias(name, value))
|
97
|
+
|
98
|
+
[[
|
99
|
+
"#{join_type(value)} JOIN",
|
100
|
+
conn.quote_table_name(ref_table(name)),
|
101
|
+
'AS',
|
102
|
+
quoted_alias,
|
103
|
+
'ON',
|
104
|
+
"#{klass.quoted_table_name}.#{klass.quoted_primary_key}",
|
105
|
+
'=',
|
106
|
+
"#{quoted_alias}.#{conn.quote_column_name(:entity_id)}",
|
107
|
+
'AND',
|
108
|
+
"#{quoted_alias}.#{conn.quote_column_name(:hydra_attribute_id)}",
|
109
|
+
'=',
|
110
|
+
hydra_attribute_id(name)
|
111
|
+
].join(' ')]
|
112
|
+
end
|
93
113
|
|
94
|
-
|
95
|
-
|
96
|
-
|
114
|
+
def where_options(name, value)
|
115
|
+
{ref_alias(name, value) => {value: value}}
|
116
|
+
end
|
97
117
|
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
join_alias = hydra_ref_alias(attribute, 'inner') # alias for inner join
|
103
|
-
join_alias = hydra_ref_alias(attribute, nil) unless hydra_joins_aliases.include?(join_alias) # alias for left join
|
118
|
+
def ref_class(name)
|
119
|
+
type = klass.hydra_attribute(name).backend_type
|
120
|
+
AssociationBuilder.class_name(klass, type).constantize
|
121
|
+
end
|
104
122
|
|
105
|
-
|
106
|
-
|
107
|
-
else
|
108
|
-
hydra_attr_helper.prepend_table_name(attribute)
|
109
|
-
end
|
123
|
+
def ref_table(name)
|
124
|
+
ref_class(name).table_name
|
110
125
|
end
|
111
|
-
end
|
112
126
|
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
quoted_ref_alias = conn.quote_table_name(ref_alias)
|
117
|
-
|
118
|
-
[[
|
119
|
-
"#{hydra_join_type(value)} JOIN",
|
120
|
-
conn.quote_table_name(hydra_ref_table(name)),
|
121
|
-
'AS',
|
122
|
-
quoted_ref_alias,
|
123
|
-
'ON',
|
124
|
-
"#{klass.quoted_table_name}.#{klass.quoted_primary_key}",
|
125
|
-
'=',
|
126
|
-
"#{quoted_ref_alias}.#{conn.quote_column_name(:entity_id)}",
|
127
|
-
'AND',
|
128
|
-
"#{quoted_ref_alias}.#{conn.quote_column_name(:entity_type)}",
|
129
|
-
'=',
|
130
|
-
conn.quote(klass.base_class.name),
|
131
|
-
'AND',
|
132
|
-
"#{quoted_ref_alias}.#{conn.quote_column_name(:name)}",
|
133
|
-
'=',
|
134
|
-
conn.quote(name)
|
135
|
-
].join(' ')]
|
136
|
-
end
|
127
|
+
def ref_alias(name, value)
|
128
|
+
ref_table(name) + '_' + join_type(value).downcase + '_' + name
|
129
|
+
end
|
137
130
|
|
138
|
-
|
139
|
-
|
140
|
-
|
131
|
+
def join_type(value)
|
132
|
+
value.nil? ? 'LEFT' : 'INNER'
|
133
|
+
end
|
141
134
|
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
end
|
135
|
+
def hydra_attribute_id(name)
|
136
|
+
klass.hydra_attribute(name).id
|
137
|
+
end
|
146
138
|
|
147
|
-
|
148
|
-
|
149
|
-
|
139
|
+
def quote_columns(columns)
|
140
|
+
columns.map do |column|
|
141
|
+
column = column.respond_to?(:to_sql) ? column.to_sql : column.to_s
|
142
|
+
if klass.hydra_attribute_names.include?(column)
|
143
|
+
join_alias = ref_alias(column, 'inner') # alias for inner join
|
144
|
+
join_alias = ref_alias(column, nil) unless relation.hydra_joins_aliases.include?(join_alias) # alias for left join
|
150
145
|
|
151
|
-
|
152
|
-
|
146
|
+
relation.joins_values += build_joins(column, nil) unless relation.hydra_joins_aliases.include?(join_alias)
|
147
|
+
prepend_table_name('value', join_alias)
|
148
|
+
else
|
149
|
+
prepend_table_name(column)
|
150
|
+
end
|
151
|
+
end
|
152
|
+
end
|
153
153
|
end
|
154
154
|
|
155
|
-
|
156
|
-
|
155
|
+
private
|
156
|
+
|
157
|
+
def hydra_helper
|
158
|
+
@hydra_helper ||= Helper.new(self)
|
157
159
|
end
|
158
160
|
end
|
159
161
|
end
|
@@ -16,30 +16,7 @@ module HydraAttribute
|
|
16
16
|
records = __old_exec_queries__
|
17
17
|
return records if records.empty?
|
18
18
|
|
19
|
-
|
20
|
-
|
21
|
-
if records.many?
|
22
|
-
if limit_values
|
23
|
-
hydra_attribute_types = hydra_select_values.map { |value| records.first.class.hydra_attributes[value] }.uniq
|
24
|
-
else
|
25
|
-
hydra_attribute_types = records.first.class.hydra_attribute_types
|
26
|
-
end
|
27
|
-
|
28
|
-
hydra_attribute_types.each do |type|
|
29
|
-
association = HydraAttribute.config.association(type)
|
30
|
-
unless records.first.association(association).loaded?
|
31
|
-
::ActiveRecord::Associations::Preloader.new(records, association).run
|
32
|
-
end
|
33
|
-
end
|
34
|
-
end
|
35
|
-
|
36
|
-
if limit_values
|
37
|
-
records.each do |record| # force limit getter methods for hydra attributes
|
38
|
-
record.instance_variable_set(:@hydra_attribute_names, hydra_select_values)
|
39
|
-
record.instance_variable_get(:@attributes).delete('id') if @id_for_hydra_attributes
|
40
|
-
end
|
41
|
-
end
|
42
|
-
|
19
|
+
AssociationPreloader.run(self, records)
|
43
20
|
records
|
44
21
|
end
|
45
22
|
|
@@ -1,7 +1,21 @@
|
|
1
1
|
module HydraAttribute
|
2
|
+
|
3
|
+
# ActiveRecord::Base extends this module.
|
2
4
|
module ActiveRecord
|
3
|
-
|
4
|
-
|
5
|
+
|
6
|
+
# Add EAV behavior to this model.
|
7
|
+
# Generate attribute and value associations.
|
8
|
+
def use_hydra_attributes
|
9
|
+
Builder.build(self)
|
10
|
+
end
|
11
|
+
|
12
|
+
# Create reflection for hydra association
|
13
|
+
def create_reflection(macro, name, options, active_record)
|
14
|
+
if name.to_s.start_with?('hydra_')
|
15
|
+
reflections[name] = Reflection.new(macro, name, options, active_record)
|
16
|
+
else
|
17
|
+
super
|
18
|
+
end
|
5
19
|
end
|
6
20
|
end
|
7
21
|
end
|