hydra_attribute 0.4.2 → 0.5.0.rc1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +2 -1
- data/.travis.yml +6 -5
- data/CHANGELOG.md +6 -0
- data/Gemfile +1 -1
- data/README.md +3 -3
- data/Rakefile +2 -7
- data/gemfiles/activerecord-3.2.gemfile +5 -0
- data/hydra_attribute.gemspec +6 -7
- data/lib/hydra_attribute.rb +17 -18
- data/lib/hydra_attribute/active_record.rb +34 -13
- data/lib/hydra_attribute/active_record/association_preloader.rb +47 -28
- data/lib/hydra_attribute/active_record/attribute_methods.rb +29 -140
- data/lib/hydra_attribute/active_record/mass_assignment_security.rb +39 -0
- data/lib/hydra_attribute/active_record/migration.rb +4 -4
- data/lib/hydra_attribute/active_record/relation.rb +6 -7
- data/lib/hydra_attribute/active_record/relation/query_methods.rb +28 -18
- data/lib/hydra_attribute/hydra_attribute.rb +12 -49
- data/lib/hydra_attribute/hydra_attribute_set.rb +67 -0
- data/lib/hydra_attribute/hydra_entity.rb +110 -0
- data/lib/hydra_attribute/hydra_entity_attribute_association.rb +155 -0
- data/lib/hydra_attribute/hydra_set.rb +24 -26
- data/lib/hydra_attribute/hydra_value.rb +210 -0
- data/lib/hydra_attribute/identity_map.rb +18 -0
- data/lib/hydra_attribute/middleware/identity_map.rb +15 -0
- data/lib/hydra_attribute/migrator.rb +24 -21
- data/lib/hydra_attribute/model.rb +47 -0
- data/lib/hydra_attribute/model/cacheable.rb +207 -0
- data/lib/hydra_attribute/model/dirty.rb +39 -0
- data/lib/hydra_attribute/model/has_many_through.rb +168 -0
- data/lib/hydra_attribute/model/identity_map.rb +59 -0
- data/lib/hydra_attribute/model/mediator.rb +89 -0
- data/lib/hydra_attribute/model/notifiable.rb +23 -0
- data/lib/hydra_attribute/model/persistence.rb +424 -0
- data/lib/hydra_attribute/model/validations.rb +40 -0
- data/lib/hydra_attribute/version.rb +1 -1
- data/spec/environments/mysql.rb +23 -0
- data/spec/environments/postgresql.rb +23 -0
- data/spec/environments/sqlite.rb +12 -0
- data/spec/fixtures/category.rb +8 -0
- data/spec/fixtures/product.rb +8 -0
- data/spec/fixtures/product_black_list.rb +13 -0
- data/spec/fixtures/product_white_list.rb +13 -0
- data/spec/hydra_attribute/active_record/attribute_methods_spec.rb +23 -28
- data/spec/hydra_attribute/active_record/mass_assignment_security_spec.rb +41 -0
- data/spec/hydra_attribute/active_record_spec.rb +577 -0
- data/spec/hydra_attribute/hydra_attribute_set_spec.rb +651 -0
- data/spec/hydra_attribute/hydra_attribute_spec.rb +208 -10
- data/spec/hydra_attribute/hydra_entity_attribute_association_spec.rb +216 -0
- data/spec/hydra_attribute/hydra_entity_spec.rb +71 -0
- data/spec/hydra_attribute/hydra_set_spec.rb +51 -10
- data/spec/hydra_attribute/hydra_value_spec.rb +286 -0
- data/spec/hydra_attribute/identity_map_spec.rb +47 -0
- data/spec/hydra_attribute/migrator_spec.rb +411 -0
- data/spec/hydra_attribute/model/cacheable_spec.rb +106 -0
- data/spec/hydra_attribute/model/has_many_through_spec.rb +132 -0
- data/spec/hydra_attribute/model/identity_map_spec.rb +39 -0
- data/spec/hydra_attribute/model/mediator_spec.rb +62 -0
- data/spec/hydra_attribute/model/persistence_spec.rb +550 -0
- data/spec/hydra_attribute/model_spec.rb +39 -0
- data/spec/hydra_attribute_spec.rb +36 -0
- data/spec/spec_helper.rb +10 -42
- metadata +76 -100
- data/Appraisals +0 -7
- data/cucumber.yml +0 -1
- data/features/entity/create.feature +0 -145
- data/features/entity/destroy.feature +0 -111
- data/features/entity/new.feature +0 -121
- data/features/entity/update.feature +0 -147
- data/features/hydra_attribute/create.feature +0 -30
- data/features/hydra_attribute/destroy.feature +0 -26
- data/features/hydra_attribute/update.feature +0 -36
- data/features/hydra_set/destroy.feature +0 -31
- data/features/migrations/create_and_drop.feature +0 -165
- data/features/migrations/migrate_and_rollback.feature +0 -211
- data/features/relation/query_methods/group.feature +0 -42
- data/features/relation/query_methods/order.feature +0 -67
- data/features/relation/query_methods/reorder.feature +0 -29
- data/features/relation/query_methods/reverse_order.feature +0 -29
- data/features/relation/query_methods/select.feature +0 -50
- data/features/relation/query_methods/where.feature +0 -115
- data/features/step_definitions/connections.rb +0 -65
- data/features/step_definitions/model_steps.rb +0 -136
- data/features/step_definitions/query_methods.rb +0 -48
- data/features/step_definitions/record_steps.rb +0 -93
- data/features/support/env.rb +0 -38
- data/features/support/world.rb +0 -61
- data/lib/hydra_attribute/active_record/association.rb +0 -113
- data/lib/hydra_attribute/active_record/reflection.rb +0 -16
- data/lib/hydra_attribute/association_builder.rb +0 -69
- data/lib/hydra_attribute/builder.rb +0 -37
- data/lib/hydra_attribute/entity_callbacks.rb +0 -26
- data/lib/hydra_attribute/hydra_attribute_methods.rb +0 -226
- data/lib/hydra_attribute/hydra_methods.rb +0 -528
- data/lib/hydra_attribute/hydra_set_methods.rb +0 -95
- data/lib/hydra_attribute/hydra_value_methods.rb +0 -21
- data/lib/hydra_attribute/memoizable.rb +0 -37
- data/spec/hydra_attribute/active_record/relation/query_methods_spec.rb +0 -31
- data/spec/hydra_attribute/hydra_attribute_methods_spec.rb +0 -458
- data/spec/hydra_attribute/hydra_methods_spec.rb +0 -456
- data/spec/hydra_attribute/hydra_set_methods_spec.rb +0 -203
- data/spec/hydra_attribute/memoizable_spec.rb +0 -95
@@ -0,0 +1,39 @@
|
|
1
|
+
module HydraAttribute
|
2
|
+
module ActiveRecord
|
3
|
+
module MassAssignmentSecurity
|
4
|
+
|
5
|
+
class PermissionSet
|
6
|
+
def initialize(entity, authorizer)
|
7
|
+
@entity = entity
|
8
|
+
@authorizer = authorizer
|
9
|
+
end
|
10
|
+
|
11
|
+
def deny?(attribute_name)
|
12
|
+
hydra_attribute = hydra_attribute_by_name(attribute_name)
|
13
|
+
hydra_attribute ? !hydra_attribute.white_list : @authorizer.deny?(attribute_name)
|
14
|
+
end
|
15
|
+
|
16
|
+
private
|
17
|
+
def respond_to_missing?(method, include_private)
|
18
|
+
@authorizer.respond_to?(method, include_private)
|
19
|
+
end
|
20
|
+
|
21
|
+
def method_missing(method, *args, &block)
|
22
|
+
@authorizer.send(method, *args, &block)
|
23
|
+
end
|
24
|
+
|
25
|
+
# TODO should be optimized. List of allowed attributes should be cached
|
26
|
+
def hydra_attribute_by_name(attribute_name)
|
27
|
+
::HydraAttribute::HydraAttribute.all_by_entity_type(@entity.class.name).find do |attribute|
|
28
|
+
attribute.name == attribute_name
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
protected
|
34
|
+
def mass_assignment_authorizer(role)
|
35
|
+
PermissionSet.new(self, super(role))
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -2,19 +2,19 @@ module HydraAttribute
|
|
2
2
|
module ActiveRecord
|
3
3
|
module Migration
|
4
4
|
def create_hydra_entity(name, options = {}, &block)
|
5
|
-
Migrator.create(self, name, options, &block)
|
5
|
+
::HydraAttribute::Migrator.create(self, name, options, &block)
|
6
6
|
end
|
7
7
|
|
8
8
|
def drop_hydra_entity(name)
|
9
|
-
Migrator.drop(self, name)
|
9
|
+
::HydraAttribute::Migrator.drop(self, name)
|
10
10
|
end
|
11
11
|
|
12
12
|
def migrate_to_hydra_entity(name, options = {}, &block)
|
13
|
-
Migrator.migrate(self, name, options, &block)
|
13
|
+
::HydraAttribute::Migrator.migrate(self, name, options, &block)
|
14
14
|
end
|
15
15
|
|
16
16
|
def rollback_from_hydra_entity(name)
|
17
|
-
Migrator.rollback(self, name)
|
17
|
+
::HydraAttribute::Migrator.rollback(self, name)
|
18
18
|
end
|
19
19
|
end
|
20
20
|
end
|
@@ -1,3 +1,7 @@
|
|
1
|
+
require 'hydra_attribute/active_record/relation/calculations'
|
2
|
+
require 'hydra_attribute/active_record/relation/query_methods'
|
3
|
+
require 'hydra_attribute/active_record/association_preloader'
|
4
|
+
|
1
5
|
module HydraAttribute
|
2
6
|
module ActiveRecord
|
3
7
|
module Relation
|
@@ -6,16 +10,11 @@ module HydraAttribute
|
|
6
10
|
included do
|
7
11
|
include Calculation
|
8
12
|
include QueryMethods
|
9
|
-
|
10
|
-
# @COMPATIBILITY with 3.1.x active_record 3.1 doesn't have "exec_queries" method
|
11
|
-
target = ::ActiveRecord::VERSION::STRING.starts_with?('3.1.') ? :to_a : :exec_queries
|
12
|
-
alias_method :__old_exec_queries__, target
|
13
|
-
alias_method target, :__exec_queries__
|
14
13
|
end
|
15
14
|
|
16
|
-
def
|
15
|
+
def exec_queries
|
17
16
|
return @records if loaded?
|
18
|
-
records =
|
17
|
+
records = super
|
19
18
|
return records if records.empty?
|
20
19
|
|
21
20
|
AssociationPreloader.run(self, records)
|
@@ -23,7 +23,7 @@ module HydraAttribute
|
|
23
23
|
|
24
24
|
if opts.is_a?(Hash)
|
25
25
|
opts.inject(self) do |relation, (name, value)|
|
26
|
-
if klass.
|
26
|
+
if ::HydraAttribute::HydraAttribute.names_by_entity_type(klass.model_name).include?(name.to_s)
|
27
27
|
relation, name = relation.clone, name.to_s
|
28
28
|
relation.hydra_attributes << name
|
29
29
|
relation.hydra_joins_aliases << hydra_helper.ref_alias(name, value)
|
@@ -40,16 +40,12 @@ module HydraAttribute
|
|
40
40
|
end
|
41
41
|
|
42
42
|
def build_arel
|
43
|
+
# remove duplicate columns and add table prefix to all of them
|
43
44
|
@group_values = hydra_helper.quote_columns(@group_values.uniq.reject(&:blank?))
|
44
45
|
@order_values = hydra_helper.quote_columns(@order_values.uniq.reject(&:blank?))
|
45
46
|
|
46
|
-
# @COMPATIBILITY with 3.1.x active_record 3.1 uses the separate @reorder_value instance
|
47
|
-
if instance_variable_defined?(:@reorder_value) and instance_variable_get(:@reorder_value).present?
|
48
|
-
@reorder_value = hydra_helper.quote_columns(@reorder_value.uniq.reject(&:blank?))
|
49
|
-
end
|
50
|
-
|
51
47
|
# detect hydra attributes from select list
|
52
|
-
@hydra_select_values, @select_values = @select_values.partition { |value| klass.
|
48
|
+
@hydra_select_values, @select_values = @select_values.partition { |value| ::HydraAttribute::HydraAttribute.names_by_entity_type(klass.model_name).include?(value.to_s) }
|
53
49
|
@hydra_select_values.map!(&:to_s)
|
54
50
|
@select_values.map!{ |value| hydra_helper.prepend_table_name(value) }
|
55
51
|
|
@@ -61,8 +57,16 @@ module HydraAttribute
|
|
61
57
|
@select_values << hydra_helper.prepend_table_name('hydra_set_id')
|
62
58
|
end
|
63
59
|
|
64
|
-
|
65
|
-
|
60
|
+
# Add filter by hydra sets which have all these attributes
|
61
|
+
if hydra_attributes.any? or @hydra_select_values.any?
|
62
|
+
hydra_attribute_ids = (hydra_attributes | hydra_select_values).map { |name| hydra_helper.hydra_attribute_id(name) }
|
63
|
+
|
64
|
+
hydra_sets = ::HydraAttribute::HydraSet.all_by_entity_type(klass.model_name).select do |hydra_set|
|
65
|
+
hydra_attribute_ids.all? do |hydra_attribute_id|
|
66
|
+
hydra_set.has_hydra_attribute_id?(hydra_attribute_id)
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
66
70
|
@where_values << table[:hydra_set_id].in(hydra_sets.map(&:id)).or(table[:hydra_set_id].eq(nil))
|
67
71
|
end
|
68
72
|
|
@@ -125,13 +129,9 @@ module HydraAttribute
|
|
125
129
|
{ref_alias(name, value) => {value: value}}
|
126
130
|
end
|
127
131
|
|
128
|
-
def ref_class(name)
|
129
|
-
type = klass.hydra_attribute(name).backend_type
|
130
|
-
AssociationBuilder.class_name(klass, type).constantize
|
131
|
-
end
|
132
|
-
|
133
132
|
def ref_table(name)
|
134
|
-
|
133
|
+
hydra_attribute = hydra_attribute_by_name(name)
|
134
|
+
"hydra_#{hydra_attribute.backend_type}_#{klass.table_name}"
|
135
135
|
end
|
136
136
|
|
137
137
|
def ref_alias(name, value)
|
@@ -139,17 +139,27 @@ module HydraAttribute
|
|
139
139
|
end
|
140
140
|
|
141
141
|
def join_type(value)
|
142
|
-
value.nil? ?
|
142
|
+
if value.nil? or (value.is_a?(Array) and value.include?(nil))
|
143
|
+
'LEFT'
|
144
|
+
else
|
145
|
+
'INNER'
|
146
|
+
end
|
143
147
|
end
|
144
148
|
|
145
149
|
def hydra_attribute_id(name)
|
146
|
-
|
150
|
+
hydra_attribute_by_name(name).id
|
151
|
+
end
|
152
|
+
|
153
|
+
def hydra_attribute_by_name(name)
|
154
|
+
::HydraAttribute::HydraAttribute.all_by_entity_type(klass.model_name).find do |hydra_attribute|
|
155
|
+
hydra_attribute.name == name.to_s
|
156
|
+
end
|
147
157
|
end
|
148
158
|
|
149
159
|
def quote_columns(columns)
|
150
160
|
columns.map do |column|
|
151
161
|
column = column.respond_to?(:to_sql) ? column.to_sql : column.to_s
|
152
|
-
if klass.
|
162
|
+
if ::HydraAttribute::HydraAttribute.names_by_entity_type(klass.model_name).include?(column)
|
153
163
|
join_alias = ref_alias(column, 'inner') # alias for inner join
|
154
164
|
join_alias = ref_alias(column, nil) unless relation.hydra_joins_aliases.include?(join_alias) # alias for left join
|
155
165
|
|
@@ -1,58 +1,21 @@
|
|
1
|
-
require 'active_support/core_ext/object/with_options'
|
2
|
-
|
3
1
|
module HydraAttribute
|
4
|
-
class HydraAttribute
|
5
|
-
self.table_name = 'hydra_attributes'
|
6
|
-
|
7
|
-
with_options as: [:default, :admin] do |klass|
|
8
|
-
klass.attr_accessible :name, :backend_type, :default_value
|
9
|
-
end
|
10
|
-
attr_accessible :white_list, as: :admin
|
11
|
-
|
12
|
-
has_and_belongs_to_many :hydra_sets, join_table: 'hydra_attribute_sets', class_name: 'HydraAttribute::HydraSet', conditions: proc { {hydra_sets: {entity_type: entity_type}} }
|
13
|
-
|
14
|
-
with_options presence: true do |klass|
|
15
|
-
klass.validates :entity_type, inclusion: { in: lambda { |attr| [(attr.entity_type.constantize.name rescue nil)] } }
|
16
|
-
klass.validates :name, uniqueness: { scope: :entity_type }
|
17
|
-
klass.validates :backend_type, inclusion: SUPPORTED_BACKEND_TYPES
|
18
|
-
end
|
19
|
-
|
20
|
-
before_destroy :delete_dependent_values
|
21
|
-
after_commit :clear_entity_cache
|
22
|
-
after_commit :update_mass_assignment_security
|
23
|
-
|
24
|
-
# @COMPATIBILITY with 3.1.x association module is directly added to the class instead of including module
|
25
|
-
def hydra_sets_with_clearing_cache=(value)
|
26
|
-
self.hydra_sets_without_clearing_cache = value
|
27
|
-
entity_type.constantize.clear_hydra_method_cache!
|
28
|
-
value
|
29
|
-
end
|
30
|
-
alias_method_chain :hydra_sets=, :clearing_cache
|
2
|
+
class HydraAttribute
|
31
3
|
|
32
|
-
|
33
|
-
|
34
|
-
remove_from_white_list
|
35
|
-
else
|
36
|
-
add_to_white_list
|
37
|
-
end
|
4
|
+
# This error is raised when created +HydraAttribute::HydraAttribute+ models don't have this ID
|
5
|
+
class UnknownHydraAttributeIdError < ArgumentError
|
38
6
|
end
|
39
7
|
|
40
|
-
|
41
|
-
def delete_dependent_values
|
42
|
-
value_class = AssociationBuilder.class_name(entity_type.constantize, backend_type).constantize
|
43
|
-
value_class.delete_all(hydra_attribute_id: id)
|
44
|
-
end
|
8
|
+
include ::HydraAttribute::Model
|
45
9
|
|
46
|
-
|
47
|
-
|
48
|
-
|
10
|
+
validates :entity_type, presence: true
|
11
|
+
validates :name, presence: true, unique: { scope: :entity_type }
|
12
|
+
validates :backend_type, presence: true, inclusion: { in: ::HydraAttribute::SUPPORTED_BACKEND_TYPES }
|
49
13
|
|
50
|
-
|
51
|
-
|
52
|
-
|
14
|
+
define_cached_singleton_method :all_by_entity_type, cache_key: :entity_type, cache_value: :self, cache_key_cast: :to_s
|
15
|
+
define_cached_singleton_method :ids_by_entity_type, cache_key: :entity_type, cache_value: :id, cache_key_cast: :to_s
|
16
|
+
define_cached_singleton_method :names_by_entity_type, cache_key: :entity_type, cache_value: :name, cache_key_cast: :to_s
|
17
|
+
define_cached_singleton_method :backend_types_by_entity_type, cache_key: :entity_type, cache_value: :backend_type, cache_key_cast: :to_s
|
53
18
|
|
54
|
-
|
55
|
-
entity_type.constantize.accessible_attributes.delete(name)
|
56
|
-
end
|
19
|
+
has_many :hydra_sets, through: :hydra_attribute_set, copy_attribute: :entity_type
|
57
20
|
end
|
58
21
|
end
|
@@ -0,0 +1,67 @@
|
|
1
|
+
module HydraAttribute
|
2
|
+
class HydraAttributeSet
|
3
|
+
include ::HydraAttribute::Model
|
4
|
+
|
5
|
+
define_cached_singleton_method :all_by_hydra_attribute_id, cache_key: :hydra_attribute_id, cache_value: :self, cache_key_cast: :to_i
|
6
|
+
define_cached_singleton_method :all_by_hydra_set_id, cache_key: :hydra_set_id, cache_value: :self, cache_key_cast: :to_i
|
7
|
+
define_cached_singleton_method :hydra_attributes_by_hydra_set_id, cache_key: :hydra_set_id, cache_value: :hydra_attribute, cache_key_cast: :to_i
|
8
|
+
define_cached_singleton_method :hydra_sets_by_hydra_attribute_id, cache_key: :hydra_attribute_id, cache_value: :hydra_set, cache_key_cast: :to_i
|
9
|
+
define_cached_singleton_method :hydra_attribute_ids_by_hydra_set_id, cache_key: :hydra_set_id, cache_value: :hydra_attribute_id, cache_key_cast: :to_i
|
10
|
+
define_cached_singleton_method :hydra_set_ids_by_hydra_attribute_id, cache_key: :hydra_attribute_id, cache_value: :hydra_set_id, cache_key_cast: :to_i
|
11
|
+
|
12
|
+
register_nested_cache :hydra_attribute_ids_by_hydra_set_id_as_hash
|
13
|
+
|
14
|
+
observe 'HydraAttribute::HydraAttribute', after_destroy: :hydra_attribute_destroyed
|
15
|
+
observe 'HydraAttribute::HydraSet', after_destroy: :hydra_set_destroyed
|
16
|
+
|
17
|
+
validates :hydra_set_id, presence: true
|
18
|
+
validates :hydra_attribute_id, presence: true, unique: { scope: :hydra_set_id }
|
19
|
+
|
20
|
+
class << self
|
21
|
+
def has_hydra_attribute_id_in_hydra_set_id?(hydra_attribute_id, hydra_set_id)
|
22
|
+
hydra_attribute_ids = get_from_nested_cache_or_load_all_models(:hydra_attribute_ids_by_hydra_set_id_as_hash, hydra_set_id.to_i)
|
23
|
+
hydra_attribute_ids and hydra_attribute_ids.has_key?(hydra_attribute_id.to_i)
|
24
|
+
end
|
25
|
+
|
26
|
+
# Remove hydra attribute from the cache
|
27
|
+
def hydra_attribute_destroyed(hydra_attribute) #:nodoc:
|
28
|
+
all_by_hydra_attribute_id(hydra_attribute.id).each(&:destroy)
|
29
|
+
end
|
30
|
+
|
31
|
+
# Remove hydra set from the cache
|
32
|
+
def hydra_set_destroyed(hydra_set) #:nodoc:
|
33
|
+
all_by_hydra_set_id(hydra_set.id).each(&:destroy)
|
34
|
+
end
|
35
|
+
|
36
|
+
private
|
37
|
+
def add_to_hydra_attribute_ids_by_hydra_set_id_as_hash_cache(hydra_attribute_set)
|
38
|
+
add_value_to_nested_hash_cache(:hydra_attribute_ids_by_hydra_set_id_as_hash, key: hydra_attribute_set.hydra_set_id, value: hydra_attribute_set.hydra_attribute_id)
|
39
|
+
end
|
40
|
+
|
41
|
+
def update_hydra_attribute_ids_by_hydra_set_id_as_hash_cache(hydra_attribute_set)
|
42
|
+
delete_value_from_nested_hash_cache(:hydra_attribute_ids_by_hydra_set_id_as_hash, key: hydra_attribute_set.hydra_set_id_was, value: hydra_attribute_set.hydra_attribute_id_was)
|
43
|
+
add_to_hydra_attribute_ids_by_hydra_set_id_as_hash_cache(hydra_attribute_set)
|
44
|
+
end
|
45
|
+
|
46
|
+
def delete_from_hydra_attribute_ids_by_hydra_set_id_as_hash_cache(hydra_attribute_set)
|
47
|
+
delete_value_from_nested_hash_cache(:hydra_attribute_ids_by_hydra_set_id_as_hash, key: hydra_attribute_set.hydra_set_id, value: hydra_attribute_set.hydra_attribute_id)
|
48
|
+
end
|
49
|
+
end
|
50
|
+
|
51
|
+
# Returns hydra attribute for this relation
|
52
|
+
#
|
53
|
+
# @return [HydraAttribute::HydraAttribute, NilClass]
|
54
|
+
def hydra_attribute
|
55
|
+
::HydraAttribute::HydraAttribute.find(hydra_attribute_id) if hydra_attribute_id
|
56
|
+
end
|
57
|
+
alias_method :hydra_attribute_was, :hydra_attribute # used for cache sweepers
|
58
|
+
|
59
|
+
# Returns hydra set for this relation
|
60
|
+
#
|
61
|
+
# @return [HydraAttribute::HydraSet, NilClass]
|
62
|
+
def hydra_set
|
63
|
+
::HydraAttribute::HydraSet.find(hydra_set_id) if hydra_set_id
|
64
|
+
end
|
65
|
+
alias_method :hydra_set_was, :hydra_set # used for cache sweepers
|
66
|
+
end
|
67
|
+
end
|
@@ -0,0 +1,110 @@
|
|
1
|
+
module HydraAttribute
|
2
|
+
module HydraEntity
|
3
|
+
class RelationDecorator
|
4
|
+
def initialize(entity_type, model_class)
|
5
|
+
@entity_type = entity_type
|
6
|
+
@model_class = model_class
|
7
|
+
end
|
8
|
+
|
9
|
+
def create(attributes = {})
|
10
|
+
@model_class.create(attributes.merge(entity_type: @entity_type))
|
11
|
+
end
|
12
|
+
|
13
|
+
def build(attributes = {})
|
14
|
+
@model_class.new(attributes.merge(entity_type: @entity_type))
|
15
|
+
end
|
16
|
+
|
17
|
+
private
|
18
|
+
def respond_to_missing?(symbol, include_private)
|
19
|
+
_models.respond_to?(symbol, include_private)
|
20
|
+
end
|
21
|
+
|
22
|
+
def method_missing(method, *args, &block)
|
23
|
+
_models.send(method, *args, &block)
|
24
|
+
end
|
25
|
+
|
26
|
+
def _models
|
27
|
+
@model_class.all_by_entity_type(@entity_type)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
extend ActiveSupport::Concern
|
32
|
+
|
33
|
+
included do
|
34
|
+
after_save :save_hydra_attributes
|
35
|
+
after_destroy :destroy_hydra_attributes
|
36
|
+
end
|
37
|
+
|
38
|
+
module ClassMethods
|
39
|
+
# Returns collection of hydra attributes for current entity
|
40
|
+
#
|
41
|
+
# @return [HydraAttribute::HydraEntity::RelationDecorator]
|
42
|
+
def hydra_attributes
|
43
|
+
@hydra_attributes ||= RelationDecorator.new(model_name, ::HydraAttribute::HydraAttribute)
|
44
|
+
end
|
45
|
+
|
46
|
+
# Returns collection of hydra sets for current entity
|
47
|
+
#
|
48
|
+
# @return [HydraAttribute::HydraEntity::RelationDecorator]
|
49
|
+
def hydra_sets
|
50
|
+
@hydra_sets ||= RelationDecorator.new(model_name, ::HydraAttribute::HydraSet)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
|
54
|
+
# Returns association between hydra attributes and their values
|
55
|
+
#
|
56
|
+
# @return [HydraAttribute::HydraEntityAttributeAssociation]
|
57
|
+
def hydra_attribute_association
|
58
|
+
@hydra_attribute_association ||= HydraEntityAttributeAssociation.new(self)
|
59
|
+
end
|
60
|
+
|
61
|
+
# Sets association object which connects hydra attributes with their values
|
62
|
+
#
|
63
|
+
# @param [HydraAttribute::HydraEntityAttributeAssociation]
|
64
|
+
def hydra_attribute_association=(association)
|
65
|
+
@hydra_attribute_association = association
|
66
|
+
end
|
67
|
+
|
68
|
+
# Return +HydraSet+ object if it exists
|
69
|
+
#
|
70
|
+
# @return [HydraAttribute::HydraSet]
|
71
|
+
def hydra_set
|
72
|
+
HydraSet.find(hydra_set_id) if hydra_set_id
|
73
|
+
end
|
74
|
+
|
75
|
+
# Returns hydra attribute names with theirs values
|
76
|
+
#
|
77
|
+
# @return [Hash]
|
78
|
+
def hydra_attributes
|
79
|
+
hydra_attribute_association.hydra_attributes
|
80
|
+
end
|
81
|
+
|
82
|
+
# Returns hydra attribute names with theirs values before type casting
|
83
|
+
#
|
84
|
+
# @return [Hash]
|
85
|
+
def hydra_attributes_before_type_cast
|
86
|
+
hydra_attribute_association.hydra_attributes_before_type_cast
|
87
|
+
end
|
88
|
+
|
89
|
+
def respond_to?(method, include_private = false)
|
90
|
+
hydra_attribute_association.has_proxy_method?(method) || super
|
91
|
+
end
|
92
|
+
|
93
|
+
private
|
94
|
+
def save_hydra_attributes
|
95
|
+
hydra_attribute_association.save
|
96
|
+
end
|
97
|
+
|
98
|
+
def destroy_hydra_attributes
|
99
|
+
hydra_attribute_association.destroy
|
100
|
+
end
|
101
|
+
|
102
|
+
def method_missing(method, *args, &block)
|
103
|
+
hydra_attribute_association.delegate(method, *args, &block)
|
104
|
+
rescue HydraSet::MissingAttributeInHydraSetError, HydraEntityAttributeAssociation::AttributeWasNotSelectedError
|
105
|
+
raise
|
106
|
+
rescue
|
107
|
+
super
|
108
|
+
end
|
109
|
+
end
|
110
|
+
end
|
@@ -0,0 +1,155 @@
|
|
1
|
+
module HydraAttribute
|
2
|
+
class HydraEntityAttributeAssociation
|
3
|
+
class WrongProxyMethodError < ArgumentError
|
4
|
+
def initialize(method)
|
5
|
+
super("Unknown :#{method} method")
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
class AttributeWasNotSelectedError < ArgumentError
|
10
|
+
def initialize(attribute_id)
|
11
|
+
super("Attribute ID #{attribute_id} was not selected from DB")
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
include ::HydraAttribute::Model::Mediator
|
16
|
+
include ::HydraAttribute::Model::IdentityMap
|
17
|
+
|
18
|
+
observe 'HydraAttribute::HydraAttribute', after_create: :hydra_attribute_created, after_update: :hydra_attribute_updated, after_destroy: :hydra_attribute_destroyed
|
19
|
+
|
20
|
+
attr_reader :entity
|
21
|
+
|
22
|
+
class << self
|
23
|
+
# Generate hydra attribute methods
|
24
|
+
def generate_methods
|
25
|
+
::HydraAttribute::HydraAttribute.all.each do |hydra_attribute|
|
26
|
+
add_to_cache(hydra_attribute.entity_type, hydra_attribute.name, hydra_attribute.id)
|
27
|
+
end
|
28
|
+
identity_map[:___methods_generated___] = true
|
29
|
+
end
|
30
|
+
|
31
|
+
# Checks if methods were generated
|
32
|
+
def methods_generated?
|
33
|
+
identity_map[:___methods_generated___] ||= false
|
34
|
+
end
|
35
|
+
|
36
|
+
# Callback
|
37
|
+
def hydra_attribute_created(hydra_attribute) # :nodoc:
|
38
|
+
return unless methods_generated?
|
39
|
+
add_to_cache(hydra_attribute.entity_type, hydra_attribute.name, hydra_attribute.id)
|
40
|
+
end
|
41
|
+
|
42
|
+
# Callback
|
43
|
+
def hydra_attribute_updated(hydra_attribute) # :nodoc:
|
44
|
+
delete_from_cache(hydra_attribute.entity_type_was, hydra_attribute.name_was)
|
45
|
+
add_to_cache(hydra_attribute.entity_type, hydra_attribute.name, hydra_attribute.id)
|
46
|
+
end
|
47
|
+
|
48
|
+
# Callback
|
49
|
+
def hydra_attribute_destroyed(hydra_attribute) # :nodoc:
|
50
|
+
delete_from_cache(hydra_attribute.entity_type, hydra_attribute.name)
|
51
|
+
end
|
52
|
+
|
53
|
+
private
|
54
|
+
def add_to_cache(entity_type, name, id)
|
55
|
+
identity_map[entity_type] ||= {names: {}, names_as_hash: {}, ids_as_hash: {}}
|
56
|
+
::ActiveRecord::Base.attribute_method_matchers.each do |matcher|
|
57
|
+
current = matcher.method_name(name).to_sym
|
58
|
+
proxy = matcher.method_name(:value)
|
59
|
+
|
60
|
+
identity_map[entity_type][:names][name] ||= []
|
61
|
+
identity_map[entity_type][:names][name] << current
|
62
|
+
identity_map[entity_type][:names_as_hash][current] = proxy
|
63
|
+
identity_map[entity_type][:ids_as_hash][current] = id
|
64
|
+
end
|
65
|
+
end
|
66
|
+
|
67
|
+
def delete_from_cache(entity_type, name)
|
68
|
+
current_methods = identity_map[entity_type] && identity_map[entity_type][:names][name]
|
69
|
+
return unless current_methods
|
70
|
+
current_methods.each do |current_method|
|
71
|
+
identity_map[entity_type][:names_as_hash].delete(current_method)
|
72
|
+
identity_map[entity_type][:ids_as_hash].delete(current_method)
|
73
|
+
end
|
74
|
+
identity_map[entity_type][:names].delete(name)
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
def initialize(entity)
|
79
|
+
@entity = entity
|
80
|
+
end
|
81
|
+
|
82
|
+
def save
|
83
|
+
touch = false
|
84
|
+
accessible_hydra_values do |hydra_value|
|
85
|
+
touch = true if hydra_value.save
|
86
|
+
end
|
87
|
+
entity.touch if touch
|
88
|
+
end
|
89
|
+
|
90
|
+
def destroy
|
91
|
+
HydraValue.delete_entity_values(entity)
|
92
|
+
end
|
93
|
+
|
94
|
+
def hydra_values
|
95
|
+
@hydra_values ||= ::HydraAttribute::HydraAttribute.ids_by_entity_type(entity.class.model_name).inject({}) do |hydra_values, hydra_attribute_id|
|
96
|
+
hydra_values[hydra_attribute_id] = HydraValue.new(entity, hydra_attribute_id: hydra_attribute_id)
|
97
|
+
hydra_values
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
def hydra_attributes
|
102
|
+
to_enum(:accessible_hydra_values).each_with_object({}) do |hydra_value, hydra_attributes|
|
103
|
+
hydra_attributes[hydra_value.hydra_attribute.name] = hydra_value.value
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
def hydra_attributes_before_type_cast
|
108
|
+
to_enum(:accessible_hydra_values).each_with_object({}) do |hydra_value, hydra_attributes|
|
109
|
+
hydra_attributes[hydra_value.hydra_attribute.name] = hydra_value.value_before_type_cast
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
def lock_values
|
114
|
+
@hydra_values ||= {}
|
115
|
+
end
|
116
|
+
|
117
|
+
def hydra_value_options=(options = {})
|
118
|
+
hydra_values[options[:hydra_attribute_id]] = HydraValue.new(entity, options)
|
119
|
+
end
|
120
|
+
|
121
|
+
def has_proxy_method?(method)
|
122
|
+
self.class.generate_methods unless self.class.methods_generated?
|
123
|
+
identity_map = self.class.identity_map[entity.class.model_name]
|
124
|
+
return false unless identity_map
|
125
|
+
!!identity_map[:names_as_hash][method.to_sym]
|
126
|
+
end
|
127
|
+
|
128
|
+
def delegate(method, *args, &block)
|
129
|
+
self.class.generate_methods unless self.class.methods_generated?
|
130
|
+
identity_map = self.class.identity_map[entity.class.model_name] or raise WrongProxyMethodError, method
|
131
|
+
attribute_id = identity_map[:ids_as_hash][method] or raise WrongProxyMethodError, method
|
132
|
+
attribute_method = identity_map[:names_as_hash][method] or raise WrongProxyMethodError, method
|
133
|
+
|
134
|
+
if has_attribute_id?(attribute_id)
|
135
|
+
hydra_value = hydra_values[attribute_id] or raise AttributeWasNotSelectedError, attribute_id
|
136
|
+
hydra_value.send(attribute_method, *args, &block)
|
137
|
+
else
|
138
|
+
raise HydraSet::MissingAttributeInHydraSetError, "Attribute ID #{attribute_id} is missed in Set ID #{entity.hydra_set.id}"
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
142
|
+
private
|
143
|
+
def has_attribute_id?(hydra_attribute_id)
|
144
|
+
!entity.hydra_set || entity.hydra_set.has_hydra_attribute_id?(hydra_attribute_id)
|
145
|
+
end
|
146
|
+
|
147
|
+
def accessible_hydra_values
|
148
|
+
hydra_values.each do |hydra_attribute_id, hydra_value|
|
149
|
+
if has_attribute_id?(hydra_attribute_id)
|
150
|
+
yield hydra_value
|
151
|
+
end
|
152
|
+
end
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|