hydra_attribute 0.2.0 → 0.3.0.beta1
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.
- 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
|