hydra_attribute 0.4.2 → 0.5.0.rc1

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.
Files changed (101) hide show
  1. data/.gitignore +2 -1
  2. data/.travis.yml +6 -5
  3. data/CHANGELOG.md +6 -0
  4. data/Gemfile +1 -1
  5. data/README.md +3 -3
  6. data/Rakefile +2 -7
  7. data/gemfiles/activerecord-3.2.gemfile +5 -0
  8. data/hydra_attribute.gemspec +6 -7
  9. data/lib/hydra_attribute.rb +17 -18
  10. data/lib/hydra_attribute/active_record.rb +34 -13
  11. data/lib/hydra_attribute/active_record/association_preloader.rb +47 -28
  12. data/lib/hydra_attribute/active_record/attribute_methods.rb +29 -140
  13. data/lib/hydra_attribute/active_record/mass_assignment_security.rb +39 -0
  14. data/lib/hydra_attribute/active_record/migration.rb +4 -4
  15. data/lib/hydra_attribute/active_record/relation.rb +6 -7
  16. data/lib/hydra_attribute/active_record/relation/query_methods.rb +28 -18
  17. data/lib/hydra_attribute/hydra_attribute.rb +12 -49
  18. data/lib/hydra_attribute/hydra_attribute_set.rb +67 -0
  19. data/lib/hydra_attribute/hydra_entity.rb +110 -0
  20. data/lib/hydra_attribute/hydra_entity_attribute_association.rb +155 -0
  21. data/lib/hydra_attribute/hydra_set.rb +24 -26
  22. data/lib/hydra_attribute/hydra_value.rb +210 -0
  23. data/lib/hydra_attribute/identity_map.rb +18 -0
  24. data/lib/hydra_attribute/middleware/identity_map.rb +15 -0
  25. data/lib/hydra_attribute/migrator.rb +24 -21
  26. data/lib/hydra_attribute/model.rb +47 -0
  27. data/lib/hydra_attribute/model/cacheable.rb +207 -0
  28. data/lib/hydra_attribute/model/dirty.rb +39 -0
  29. data/lib/hydra_attribute/model/has_many_through.rb +168 -0
  30. data/lib/hydra_attribute/model/identity_map.rb +59 -0
  31. data/lib/hydra_attribute/model/mediator.rb +89 -0
  32. data/lib/hydra_attribute/model/notifiable.rb +23 -0
  33. data/lib/hydra_attribute/model/persistence.rb +424 -0
  34. data/lib/hydra_attribute/model/validations.rb +40 -0
  35. data/lib/hydra_attribute/version.rb +1 -1
  36. data/spec/environments/mysql.rb +23 -0
  37. data/spec/environments/postgresql.rb +23 -0
  38. data/spec/environments/sqlite.rb +12 -0
  39. data/spec/fixtures/category.rb +8 -0
  40. data/spec/fixtures/product.rb +8 -0
  41. data/spec/fixtures/product_black_list.rb +13 -0
  42. data/spec/fixtures/product_white_list.rb +13 -0
  43. data/spec/hydra_attribute/active_record/attribute_methods_spec.rb +23 -28
  44. data/spec/hydra_attribute/active_record/mass_assignment_security_spec.rb +41 -0
  45. data/spec/hydra_attribute/active_record_spec.rb +577 -0
  46. data/spec/hydra_attribute/hydra_attribute_set_spec.rb +651 -0
  47. data/spec/hydra_attribute/hydra_attribute_spec.rb +208 -10
  48. data/spec/hydra_attribute/hydra_entity_attribute_association_spec.rb +216 -0
  49. data/spec/hydra_attribute/hydra_entity_spec.rb +71 -0
  50. data/spec/hydra_attribute/hydra_set_spec.rb +51 -10
  51. data/spec/hydra_attribute/hydra_value_spec.rb +286 -0
  52. data/spec/hydra_attribute/identity_map_spec.rb +47 -0
  53. data/spec/hydra_attribute/migrator_spec.rb +411 -0
  54. data/spec/hydra_attribute/model/cacheable_spec.rb +106 -0
  55. data/spec/hydra_attribute/model/has_many_through_spec.rb +132 -0
  56. data/spec/hydra_attribute/model/identity_map_spec.rb +39 -0
  57. data/spec/hydra_attribute/model/mediator_spec.rb +62 -0
  58. data/spec/hydra_attribute/model/persistence_spec.rb +550 -0
  59. data/spec/hydra_attribute/model_spec.rb +39 -0
  60. data/spec/hydra_attribute_spec.rb +36 -0
  61. data/spec/spec_helper.rb +10 -42
  62. metadata +76 -100
  63. data/Appraisals +0 -7
  64. data/cucumber.yml +0 -1
  65. data/features/entity/create.feature +0 -145
  66. data/features/entity/destroy.feature +0 -111
  67. data/features/entity/new.feature +0 -121
  68. data/features/entity/update.feature +0 -147
  69. data/features/hydra_attribute/create.feature +0 -30
  70. data/features/hydra_attribute/destroy.feature +0 -26
  71. data/features/hydra_attribute/update.feature +0 -36
  72. data/features/hydra_set/destroy.feature +0 -31
  73. data/features/migrations/create_and_drop.feature +0 -165
  74. data/features/migrations/migrate_and_rollback.feature +0 -211
  75. data/features/relation/query_methods/group.feature +0 -42
  76. data/features/relation/query_methods/order.feature +0 -67
  77. data/features/relation/query_methods/reorder.feature +0 -29
  78. data/features/relation/query_methods/reverse_order.feature +0 -29
  79. data/features/relation/query_methods/select.feature +0 -50
  80. data/features/relation/query_methods/where.feature +0 -115
  81. data/features/step_definitions/connections.rb +0 -65
  82. data/features/step_definitions/model_steps.rb +0 -136
  83. data/features/step_definitions/query_methods.rb +0 -48
  84. data/features/step_definitions/record_steps.rb +0 -93
  85. data/features/support/env.rb +0 -38
  86. data/features/support/world.rb +0 -61
  87. data/lib/hydra_attribute/active_record/association.rb +0 -113
  88. data/lib/hydra_attribute/active_record/reflection.rb +0 -16
  89. data/lib/hydra_attribute/association_builder.rb +0 -69
  90. data/lib/hydra_attribute/builder.rb +0 -37
  91. data/lib/hydra_attribute/entity_callbacks.rb +0 -26
  92. data/lib/hydra_attribute/hydra_attribute_methods.rb +0 -226
  93. data/lib/hydra_attribute/hydra_methods.rb +0 -528
  94. data/lib/hydra_attribute/hydra_set_methods.rb +0 -95
  95. data/lib/hydra_attribute/hydra_value_methods.rb +0 -21
  96. data/lib/hydra_attribute/memoizable.rb +0 -37
  97. data/spec/hydra_attribute/active_record/relation/query_methods_spec.rb +0 -31
  98. data/spec/hydra_attribute/hydra_attribute_methods_spec.rb +0 -458
  99. data/spec/hydra_attribute/hydra_methods_spec.rb +0 -456
  100. data/spec/hydra_attribute/hydra_set_methods_spec.rb +0 -203
  101. data/spec/hydra_attribute/memoizable_spec.rb +0 -95
data/.gitignore CHANGED
@@ -1,3 +1,4 @@
1
+ .DS_Store
1
2
  *.gem
2
3
  *.rbc
3
4
  .bundle
@@ -18,4 +19,4 @@ tmp
18
19
  .idea
19
20
  .rvmrc
20
21
  bin/
21
- gemfiles
22
+ gemfiles/*.lock
data/.travis.yml CHANGED
@@ -1,9 +1,10 @@
1
1
  language: ruby
2
2
  rvm:
3
3
  - 1.9.3
4
-
5
- before_install: bundle && rake appraisal:gemfiles
6
-
4
+ - 2.0.0
7
5
  gemfile:
8
- - gemfiles/3.1.gemfile
9
- - gemfiles/3.2.gemfile
6
+ - gemfiles/activerecord-3.2.gemfile
7
+ env:
8
+ - DB=sqlite
9
+ - DB=mysql
10
+ - DB=postgresql
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ **0.5.0 (...)**
2
+ * Cache all hydra attributes per request
3
+ * Add support of `decimal` backend type
4
+ * Add `id` to `hydra_attribute_sets` table
5
+ * Use new index name pattern `*_idx` instead of `*_index`
6
+
1
7
  **0.4.2 (January 20, 2013)**
2
8
  * Fixed bug in `count` method which added unnecessary columns to query [#2](https://github.com/kostyantyn/hydra_attribute/issues/2)
3
9
 
data/Gemfile CHANGED
@@ -1,3 +1,3 @@
1
- source :rubygems
1
+ source 'https://rubygems.org'
2
2
 
3
3
  gemspec
data/README.md CHANGED
@@ -1,4 +1,4 @@
1
- # hydra_attribute [![Gem Version](https://fury-badge.herokuapp.com/rb/hydra_attribute.png)](http://badge.fury.io/rb/hydra_attribute) [![Build Status](https://secure.travis-ci.org/kostyantyn/hydra_attribute.png)](http://travis-ci.org/kostyantyn/hydra_attribute)
1
+ # hydra_attribute [![Gem Version](https://fury-badge.herokuapp.com/rb/hydra_attribute.png)](http://badge.fury.io/rb/hydra_attribute) [![Build Status](https://travis-ci.org/kostyantyn/hydra_attribute.png?branch=value_model_optimization)](https://travis-ci.org/kostyantyn/hydra_attribute)
2
2
 
3
3
  [Wiki](https://github.com/kostyantyn/hydra_attribute/wiki) | [RDoc](http://rdoc.info/github/kostyantyn/hydra_attribute)
4
4
 
@@ -12,7 +12,7 @@ Until the first major version is released:
12
12
 
13
13
  ## Requirements
14
14
  * ruby >= 1.9.2
15
- * active_record ~> 3.1
15
+ * active_record ~> 3.2
16
16
 
17
17
  ## Installation
18
18
 
@@ -82,7 +82,7 @@ Creating method accepts the following options:
82
82
  * **name**. The **required** parameter. Any string is allowed.
83
83
  * **backend_type**. The **required** parameter. One of the following strings is allowed: `string`, `text`, `integer`, `float`, `boolean` and `datetime`.
84
84
  * **default_value**. The **optional** parameter. Any value is allowed. `nil` is default.
85
- * **white_list**. The **optional** parameter. Should be `true` or `flase`. `false` is default. If `white_list: true` is passed, this attribute will be added to white list and will be allowed for mass-assignment. This parameter is in black list for creation by default so if you want to pass it, you have to pass the role `as: :admin` too.
85
+ * **white_list**. The **optional** parameter. Should be `true` or `false`. `false` is default. If `white_list: true` is passed, this attribute will be added to white list and will be allowed for mass-assignment. This parameter is in black list for creation by default so if you want to pass it, you have to pass the role `as: :admin` too.
86
86
 
87
87
  ```ruby
88
88
  Product.hydra_attributes.create({name: 'title', backend_type: 'string', white_list: true}, as: :admin)
data/Rakefile CHANGED
@@ -1,12 +1,7 @@
1
1
  #!/usr/bin/env rake
2
2
  require 'bundler/gem_tasks'
3
- require 'appraisal'
4
3
  require 'rspec/core/rake_task'
5
- require 'cucumber/rake/task'
6
4
 
7
- RSpec::Core::RakeTask.new('spec')
8
- Cucumber::Rake::Task.new(:features) do |t|
9
- t.cucumber_opts = '--format pretty'
10
- end
5
+ RSpec::Core::RakeTask.new(:spec)
11
6
 
12
- task default: [:features, :spec]
7
+ task default: [:spec]
@@ -0,0 +1,5 @@
1
+ source 'https://rubygems.org'
2
+
3
+ gem 'activerecord', '~> 3.2.0'
4
+
5
+ gemspec path: '../'
@@ -8,18 +8,17 @@ Gem::Specification.new do |gem|
8
8
  gem.description = 'hydra_attribute is an implementation of EAV pattern for ActiveRecord models.'
9
9
  gem.homepage = 'https://github.com/kostyantyn/hydra_attribute'
10
10
  gem.files = `git ls-files`.split("\n")
11
- gem.test_files = `git ls-files -- Appraisals {spec,features,gemfiles}/*`.split("\n")
12
- gem.name = "hydra_attribute"
11
+ gem.test_files = `git ls-files -- {spec,features,gemfiles}/*`.split("\n")
12
+ gem.name = 'hydra_attribute'
13
13
  gem.require_paths = %w(lib)
14
14
  gem.required_ruby_version = Gem::Requirement.new('>= 1.9.2')
15
15
  gem.version = HydraAttribute::VERSION
16
16
 
17
- gem.add_dependency('activerecord', '~> 3.1')
17
+ gem.add_dependency('activerecord', '~> 3.2')
18
18
 
19
- gem.add_development_dependency('rspec')
20
- gem.add_development_dependency('cucumber')
19
+ gem.add_development_dependency('rspec', '~> 2.13')
21
20
  gem.add_development_dependency('sqlite3')
22
- gem.add_development_dependency('database_cleaner')
23
- gem.add_development_dependency('appraisal')
21
+ gem.add_development_dependency('mysql2')
22
+ gem.add_development_dependency('pg')
24
23
  gem.add_development_dependency('rake')
25
24
  end
@@ -1,5 +1,7 @@
1
+ require 'active_record'
2
+
1
3
  module HydraAttribute
2
- SUPPORTED_BACKEND_TYPES = %w(string text integer float boolean datetime).freeze
4
+ SUPPORTED_BACKEND_TYPES = %w[string text integer float decimal boolean datetime].freeze
3
5
 
4
6
  class << self
5
7
  def config
@@ -9,32 +11,29 @@ module HydraAttribute
9
11
  def setup
10
12
  yield config
11
13
  end
14
+
15
+ def identity_map
16
+ Thread.current[:hydra_attribute] ||= IdentityMap.new
17
+ end
18
+
19
+ def cache(key, value = nil, &block)
20
+ identity_map.cache(key, value, &block)
21
+ end
12
22
  end
13
23
 
14
24
  end
15
25
 
16
26
  require 'hydra_attribute/version'
17
27
  require 'hydra_attribute/configuration'
18
- require 'hydra_attribute/association_builder'
19
- require 'hydra_attribute/builder'
20
28
  require 'hydra_attribute/migrator'
21
- require 'hydra_attribute/memoizable'
29
+ require 'hydra_attribute/identity_map'
30
+ require 'hydra_attribute/model'
22
31
  require 'hydra_attribute/hydra_attribute'
23
32
  require 'hydra_attribute/hydra_set'
24
- require 'hydra_attribute/entity_callbacks'
25
- require 'hydra_attribute/hydra_set_methods'
26
- require 'hydra_attribute/hydra_value_methods'
27
- require 'hydra_attribute/hydra_attribute_methods'
28
- require 'hydra_attribute/hydra_methods'
33
+ require 'hydra_attribute/hydra_attribute_set'
34
+ require 'hydra_attribute/hydra_value'
35
+ require 'hydra_attribute/hydra_entity'
36
+ require 'hydra_attribute/hydra_entity_attribute_association'
29
37
  require 'hydra_attribute/active_record'
30
- require 'hydra_attribute/active_record/scoping'
31
- require 'hydra_attribute/active_record/reflection'
32
- require 'hydra_attribute/active_record/association'
33
- require 'hydra_attribute/active_record/association_preloader'
34
- require 'hydra_attribute/active_record/migration'
35
- require 'hydra_attribute/active_record/relation'
36
- require 'hydra_attribute/active_record/relation/calculations'
37
- require 'hydra_attribute/active_record/relation/query_methods'
38
- require 'hydra_attribute/active_record/attribute_methods'
39
38
 
40
39
  require 'hydra_attribute/railtie' if defined?(Rails)
@@ -1,29 +1,50 @@
1
+ require 'hydra_attribute/active_record/attribute_methods'
2
+ require 'hydra_attribute/active_record/migration'
3
+ require 'hydra_attribute/active_record/relation'
4
+ require 'hydra_attribute/active_record/scoping'
5
+ require 'hydra_attribute/active_record/mass_assignment_security'
6
+
1
7
  module HydraAttribute
2
8
  module ActiveRecord
3
- def self.append_features(base)
4
- unless base.ancestors.include?(::ActiveRecord::Base)
5
- raise %(Cannot include HydraAttribute::ActiveRecord module because "#{base}" is not inherited from ActiveRecord::Base)
6
- end
9
+ extend ActiveSupport::Concern
7
10
 
8
- unless base.base_class.equal?(base)
9
- raise %(HydraAttribute::ActiveRecord module should be included to base class "#{base.base_class}" instead of "#{base}")
11
+ included do
12
+ unless ancestors.include?(::ActiveRecord::Base)
13
+ raise %(Cannot include HydraAttribute::ActiveRecord module because "#{self}" is not inherited from ActiveRecord::Base)
10
14
  end
11
15
 
12
- super
13
-
14
- base.extend(ClassMethods)
15
- Builder.build(base)
16
+ unless base_class.equal?(self)
17
+ raise %(HydraAttribute::ActiveRecord module should be included to the base class "#{base_class}" instead of "#{self}")
18
+ end
16
19
  end
17
20
 
21
+ include ::HydraAttribute::HydraEntity
22
+ include ::HydraAttribute::ActiveRecord::Scoping
23
+ include ::HydraAttribute::ActiveRecord::AttributeMethods
24
+ include ::HydraAttribute::ActiveRecord::MassAssignmentSecurity
25
+
18
26
  module ClassMethods
19
- def create_reflection(macro, name, options, active_record)
20
- if name.to_s.start_with?('hydra_')
21
- reflections[name] = Reflection.new(macro, name, options, active_record)
27
+ def inspect
28
+ if hydra_attributes.any?
29
+ inspection = hydra_attributes.map do |hydra_attribute|
30
+ "#{hydra_attribute.name}: #{hydra_attribute.backend_type}"
31
+ end
32
+ super.sub(/\)$/, ", #{inspection.join(', ')})")
22
33
  else
23
34
  super
24
35
  end
25
36
  end
26
37
  end
27
38
 
39
+ def inspect
40
+ if hydra_attributes.any?
41
+ inspection = hydra_attributes.keys.map do |name|
42
+ "#{name}: #{attribute_for_inspect(name)}"
43
+ end
44
+ super.sub(/>$/, ", #{inspection.join(', ')}>")
45
+ else
46
+ super
47
+ end
48
+ end
28
49
  end
29
50
  end
@@ -6,7 +6,6 @@ module HydraAttribute
6
6
  def initialize(relation, records = [])
7
7
  @relation = relation
8
8
  @records = records
9
- prepared_records # lock attributes
10
9
  end
11
10
 
12
11
  def self.run(relation, records)
@@ -16,41 +15,69 @@ module HydraAttribute
16
15
  def run
17
16
  return if records.blank?
18
17
 
19
- prepared_records.keys.each_slice(in_clause_length || prepared_records.keys.length) do |entity_ids|
20
- grouped_attribute_ids.each do |backend_type, hydra_attribute_ids|
21
- next if hydra_attribute_ids.blank?
18
+ prepared_records.keys.each_slice(in_clause_length || records.length) do |entity_ids|
19
+ grouped_hydra_attribute_ids.each do |backend_type, hydra_attribute_ids|
20
+ next if hydra_attribute_ids.blank? # entity doesn't have any hydra attributes for the current backend type
22
21
 
22
+ # set values from database
23
+ database_entity_and_hydra_attribute_ids = {}
23
24
  hydra_attribute_ids.each_slice(in_clause_length || hydra_attribute_ids.length) do |attribute_ids|
24
- value_class(backend_type).select(%w(id entity_id hydra_attribute_id value)).where(entity_id: entity_ids, hydra_attribute_id: attribute_ids).each do |model|
25
- assoc = association_builder.association_name(backend_type)
26
- prepared_records[model.entity_id].association(assoc).target.push(model)
25
+ ::ActiveRecord::Base.connection.select_all("SELECT id, entity_id, hydra_attribute_id, value FROM hydra_#{backend_type}_#{klass.table_name} WHERE entity_id IN (#{entity_ids.join(', ')}) AND hydra_attribute_id IN (#{attribute_ids.join(', ')})").each do |attributes|
26
+ # PostgreSQL driver doesn't convert values, it returns them as strings
27
+ id = attributes['id'].to_i
28
+ entity_id = attributes['entity_id'].to_i
29
+ hydra_attribute_id = attributes['hydra_attribute_id'].to_i
30
+
31
+ assign_hydra_value_options(id: id, entity_id: entity_id, hydra_attribute_id: hydra_attribute_id, value: attributes['value'])
32
+ (database_entity_and_hydra_attribute_ids[entity_id] ||= []) << hydra_attribute_id
33
+ end
34
+ end
35
+
36
+ # set nil if attribute's value is not saved into the database
37
+ # these values are not persisted
38
+ entity_ids.each do |entity_id|
39
+ missing_hydra_attribute_ids = hydra_attribute_ids - Array(database_entity_and_hydra_attribute_ids[entity_id])
40
+ missing_hydra_attribute_ids.each do |missing_hydra_attribute_id|
41
+ assign_hydra_value_options(entity_id: entity_id, hydra_attribute_id: missing_hydra_attribute_id, value: nil)
27
42
  end
28
43
  end
29
44
  end
30
45
  end
31
46
  end
32
47
 
48
+ def assign_hydra_value_options(options = {})
49
+ entity = prepared_records[options[:entity_id]]
50
+ assoc = entity.hydra_attribute_association
51
+
52
+ if entity.hydra_set_id
53
+ assoc.hydra_value_options = options.symbolize_keys if entity.hydra_set.has_hydra_attribute_id?(options[:hydra_attribute_id])
54
+ else
55
+ assoc.hydra_value_options = options.symbolize_keys
56
+ end
57
+ end
58
+
33
59
  private
34
60
  def prepared_records
35
- limit = attribute_limit?
36
61
  @prepared_records ||= records.each_with_object({}) do |record, hash|
37
- grouped_attribute_ids.each do |backend_type, hydra_attribute_ids|
38
- association = record.association(association_builder.association_name(backend_type))
39
- limit ? association.lock!(hydra_attribute_ids) : association.loaded!
40
- end
62
+ record.hydra_attribute_association.lock_values
41
63
  hash[record.id] = record
42
64
  end
43
65
  end
44
66
 
45
- def grouped_attribute_ids
46
- @grouped_attribute_ids ||= if attribute_limit?
47
- map = klass.hydra_attribute_backend_types.each_with_object({}) { |backend_type, hash| hash[backend_type] = [] }
48
- relation.hydra_select_values.each_with_object(map) do |name, grouped_ids|
49
- hydra_attribute = klass.hydra_attribute(name)
50
- grouped_ids[hydra_attribute.backend_type] << hydra_attribute.id
67
+ def grouped_hydra_attribute_ids
68
+ @grouped_hydra_attribute_ids ||= begin
69
+ hydra_attributes = ::HydraAttribute::HydraAttribute.all_by_entity_type(klass.model_name)
70
+ if attribute_limit?
71
+ map = hydra_attributes.map(&:backend_type).each_with_object({}) { |backend_type, hash| hash[backend_type] = [] }
72
+ relation.hydra_select_values.each_with_object(map) do |name, grouped_ids|
73
+ hydra_attribute = hydra_attributes.find { |attr| attr.name == name }
74
+ grouped_ids[hydra_attribute.backend_type] << hydra_attribute.id
75
+ end
76
+ else
77
+ hydra_attributes.each_with_object({}) do |hydra_attribute, hash|
78
+ (hash[hydra_attribute.backend_type] ||= []) << hydra_attribute.id
79
+ end
51
80
  end
52
- else
53
- klass.hydra_attribute_ids_by_backend_type
54
81
  end
55
82
  end
56
83
 
@@ -58,14 +85,6 @@ module HydraAttribute
58
85
  relation.select_values.any? or relation.hydra_select_values.any?
59
86
  end
60
87
 
61
- def value_class(backend_type)
62
- instance_variable_get(:"@#{backend_type}_class") || instance_variable_set(:"@#{backend_type}_class", association_builder.class_name(klass, backend_type).constantize)
63
- end
64
-
65
- def association_builder
66
- AssociationBuilder
67
- end
68
-
69
88
  def connection
70
89
  relation.connection
71
90
  end
@@ -3,156 +3,45 @@ module HydraAttribute
3
3
  module AttributeMethods
4
4
  extend ActiveSupport::Concern
5
5
 
6
- include HydraMethods
7
-
8
- NAME_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?=]?\z/
9
- CALL_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?]?\z/
10
-
11
- included do
12
- @hydra_attribute_methods_mutex = Mutex.new
6
+ # Returns type casted attributes
7
+ #
8
+ # @return [Hash]
9
+ def attributes
10
+ super.merge(hydra_attributes)
13
11
  end
14
12
 
15
- module ClassMethods
16
- def hydra_attribute_methods_generated?
17
- base_class.instance_variable_get(:@hydra_attribute_methods_generated) || base_class.instance_variable_set(:@hydra_attribute_methods_generated, false)
18
- end
19
-
20
- def generated_hydra_attribute_methods
21
- return base_class.instance_variable_get(:@generated_hydra_attribute_methods) if base_class.instance_variable_defined?(:@generated_hydra_attribute_methods)
22
-
23
- mod = Module.new
24
- base_class.send(:include, mod)
25
- base_class.instance_variable_set(:@generated_hydra_attribute_methods, mod)
26
- end
27
-
28
- def define_hydra_attribute_methods
29
- base_class.instance_variable_get(:@hydra_attribute_methods_mutex).synchronize do
30
- return if hydra_attribute_methods_generated?
31
- hydra_attributes.each { |hydra_attribute| define_hydra_attribute_method(hydra_attribute) }
32
- base_class.instance_variable_set(:@hydra_attribute_methods_generated, true)
33
- end
34
- end
35
-
36
- def define_hydra_attribute_method(hydra_attribute)
37
- attribute_method_matchers.each do |matcher|
38
- current = matcher.method_name(hydra_attribute.name)
39
- target = matcher.method_name(:value)
40
-
41
- if current =~ NAME_COMPILABLE_REGEXP
42
- defn = "def #{current}(*args)"
43
- else
44
- defn = "define_method(:'#{current}') do |*args|"
45
- end
46
-
47
- if target =~ CALL_COMPILABLE_REGEXP
48
- send = "#{target}(*args)"
49
- else
50
- send = "send(:'#{target}', *args)"
51
- end
52
-
53
- generated_hydra_attribute_methods.module_eval <<-EOS, __FILE__, __LINE__ + 1
54
- #{defn}
55
- if hydra_set_id? and self.class.hydra_set_attribute_ids(hydra_set_id).exclude?(#{hydra_attribute.id})
56
- raise MissingAttributeInHydraSetError, %(Hydra attribute "#{hydra_attribute.name}" does not exist in hydra set "\#{self.class.hydra_set(hydra_set_id).name}")
57
- end
58
-
59
- if value_model = hydra_value_model(#{hydra_attribute.id})
60
- value_model.#{send}
61
- else
62
- missing_attribute('#{hydra_attribute.name}', caller)
63
- end
64
- end
65
- EOS
66
- end
67
- end
68
-
69
- def reset_hydra_attribute_methods!
70
- generated_hydra_attribute_methods.module_eval do
71
- instance_methods.each { |m| undef_method(m) }
72
- end
73
- base_class.instance_variable_set(:@hydra_attribute_methods_generated, false)
74
-
75
- clear_hydra_method_cache!
76
- end
77
-
78
- def undefine_attribute_methods
79
- reset_hydra_attribute_methods!
80
- super
81
- end
82
-
83
- def inspect
84
- attr_list = columns.map { |c| "#{c.name}: #{c.type}" }
85
- attr_list += hydra_attributes.map { |a| "#{a.name}: #{a.backend_type}" }
86
- "#{name}(#{attr_list.join(', ')})"
87
- end
13
+ # Returns attributes before type casting
14
+ #
15
+ # @return [Hash]
16
+ def attributes_before_type_cast
17
+ super.merge(hydra_attributes_before_type_cast)
88
18
  end
89
19
 
90
- def respond_to?(name, include_private = false)
91
- self.class.define_hydra_attribute_methods unless self.class.hydra_attribute_methods_generated?
92
-
93
- # @COMPATIBILITY with 3.1.x active_model doesn't have "attribute_method_matcher" method
94
- if ::ActiveRecord::VERSION::STRING.start_with?('3.1.')
95
- matchers = attribute_method_matchers.partition { |m| m.prefix.empty? && m.suffix.empty? }.reverse.flatten(1)
96
- matcher = matchers.detect { |method| method.match(name) }
97
- attr_name = matcher.match(name).attr_name
98
- else
99
- attr_name = self.class.send(:attribute_method_matcher, name).attr_name
100
- end
101
-
102
- if self.class.hydra_attribute_names.include?(attr_name)
103
- self.class.hydra_set_attribute_names(hydra_set_id).include?(attr_name)
20
+ # Read type cast attribute value by its name
21
+ #
22
+ # @param [String,Symbol] name
23
+ # @return [Hash]
24
+ def read_attribute(name)
25
+ name = name.to_s
26
+ if hydra_attributes.has_key?(name)
27
+ hydra_attributes[name]
104
28
  else
105
29
  super
106
30
  end
107
31
  end
108
32
 
109
- def attributes
110
- hydra_value_models.each_with_object(super) do |model, attributes|
111
- attributes[model.hydra_attribute_name] = model.read_attribute('value')
112
- end
113
- end
114
-
115
- def attributes_before_type_cast
116
- hydra_value_models.each_with_object(super) do |model, attributes|
117
- attributes[model.hydra_attribute_name] = model.read_attribute_before_type_cast('value')
118
- end
119
- end
120
-
121
- %w(read_attribute read_attribute_before_type_cast).each do |method|
122
- class_eval <<-EOS, __FILE__, __LINE__ + 1
123
- def #{method}(attr_name)
124
- identifier = attr_name.to_s
125
- if self.class.hydra_set_attribute_names(hydra_set_id).include?(identifier)
126
- if value_model = hydra_value_model(identifier)
127
- value_model.#{method}('value')
128
- else
129
- missing_attribute(identifier, caller)
130
- end
131
- else
132
- super
133
- end
134
- end
135
- EOS
136
- end
137
-
138
- def inspect
139
- attrs = hydra_value_models.map { |model| "#{model.hydra_attribute_name}: #{model.attribute_for_inspect('value')}" }
140
- super.gsub(/>$/, ", #{attrs.join(', ')}>")
33
+ # Assigns attributes to the model
34
+ #
35
+ # @param [Hash] new_attributes
36
+ # @param [Hash] options
37
+ # @return [NilClass]
38
+ def assign_attributes(new_attributes, options = {})
39
+ if new_attributes[:hydra_set_id]
40
+ # set :hydra_set_id attribute as a last attribute to avoid HydraAttribute::HydraSet::MissingAttributeInHydraSetError error
41
+ new_attributes[:hydra_set_id] = new_attributes.delete(:hydra_set_id)
42
+ end
43
+ super
141
44
  end
142
-
143
- private
144
- def method_missing(method, *args, &block)
145
- if self.class.hydra_attribute_methods_generated?
146
- super
147
- else
148
- self.class.define_hydra_attribute_methods
149
- if respond_to_without_attributes?(method)
150
- send(method, *args, &block)
151
- else
152
- super
153
- end
154
- end
155
- end
156
45
  end
157
46
  end
158
47
  end