hydra_attribute 0.3.2 → 0.4.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 (65) hide show
  1. data/CHANGELOG.md +1 -1
  2. data/README.md +7 -0
  3. data/features/entity/create.feature +128 -0
  4. data/features/entity/destroy.feature +111 -0
  5. data/features/entity/new.feature +121 -0
  6. data/features/entity/update.feature +147 -0
  7. data/features/{attributes → hydra_attribute}/create.feature +7 -7
  8. data/features/{attributes → hydra_attribute}/destroy.feature +2 -4
  9. data/features/{attributes → hydra_attribute}/update.feature +10 -10
  10. data/features/hydra_set/destroy.feature +31 -0
  11. data/features/migrations/create_and_drop.feature +165 -0
  12. data/features/migrations/migrate_and_rollback.feature +211 -0
  13. data/features/relation/query_methods/group.feature +42 -0
  14. data/features/relation/query_methods/order.feature +67 -0
  15. data/features/relation/query_methods/reorder.feature +29 -0
  16. data/features/relation/query_methods/reverse_order.feature +29 -0
  17. data/features/relation/query_methods/select.feature +50 -0
  18. data/features/relation/query_methods/where.feature +70 -0
  19. data/features/step_definitions/connections.rb +65 -0
  20. data/features/step_definitions/model_steps.rb +79 -6
  21. data/features/step_definitions/query_methods.rb +3 -3
  22. data/features/step_definitions/record_steps.rb +3 -4
  23. data/features/support/env.rb +17 -3
  24. data/features/support/world.rb +15 -10
  25. data/gemfiles/3.1.gemfile.lock +15 -15
  26. data/gemfiles/3.2.gemfile.lock +15 -15
  27. data/lib/hydra_attribute/active_record/association.rb +74 -38
  28. data/lib/hydra_attribute/active_record/association_preloader.rb +49 -49
  29. data/lib/hydra_attribute/active_record/attribute_methods.rb +37 -85
  30. data/lib/hydra_attribute/active_record/migration.rb +2 -2
  31. data/lib/hydra_attribute/active_record/reflection.rb +1 -1
  32. data/lib/hydra_attribute/active_record/relation/query_methods.rb +8 -7
  33. data/lib/hydra_attribute/active_record/relation.rb +1 -0
  34. data/lib/hydra_attribute/active_record.rb +20 -12
  35. data/lib/hydra_attribute/association_builder.rb +1 -2
  36. data/lib/hydra_attribute/builder.rb +7 -8
  37. data/lib/hydra_attribute/entity_callbacks.rb +12 -32
  38. data/lib/hydra_attribute/hydra_attribute.rb +25 -16
  39. data/lib/hydra_attribute/hydra_attribute_methods.rb +85 -0
  40. data/lib/hydra_attribute/hydra_methods.rb +123 -0
  41. data/lib/hydra_attribute/hydra_set.rb +36 -0
  42. data/lib/hydra_attribute/hydra_set_methods.rb +39 -0
  43. data/lib/hydra_attribute/hydra_value_methods.rb +14 -0
  44. data/lib/hydra_attribute/memoize.rb +37 -0
  45. data/lib/hydra_attribute/migrator.rb +100 -51
  46. data/lib/hydra_attribute/railtie.rb +1 -3
  47. data/lib/hydra_attribute/version.rb +1 -1
  48. data/lib/hydra_attribute.rb +7 -1
  49. data/spec/hydra_attribute_methods_spec.rb +458 -0
  50. data/spec/hydra_attribute_spec.rb +19 -0
  51. data/spec/hydra_methods_spec.rb +457 -0
  52. data/spec/hydra_set_methods_spec.rb +203 -0
  53. data/spec/hydra_set_spec.rb +19 -0
  54. data/spec/memoize_spec.rb +95 -0
  55. data/spec/spec_helper.rb +42 -2
  56. metadata +71 -43
  57. data/features/create.feature +0 -47
  58. data/features/define.feature +0 -38
  59. data/features/destroy.feature +0 -102
  60. data/features/query_methods/group.feature +0 -42
  61. data/features/query_methods/order.feature +0 -99
  62. data/features/query_methods/select.feature +0 -50
  63. data/features/query_methods/where.feature +0 -70
  64. data/features/support/schema.rb +0 -79
  65. data/features/update.feature +0 -114
@@ -0,0 +1,70 @@
1
+ Feature: hydra attribute where conditions
2
+ When filter by hydra attribute and this value is not nil
3
+ Then records with this attribute should be selected
4
+
5
+ When filter by hydra attribute and this value is nil
6
+ Then records with nil and blank value should be selected
7
+
8
+ Background: create hydra attributes
9
+ Given create hydra attributes for "Product" with role "admin" as "hashes":
10
+ | name | backend_type | white_list |
11
+ | code | string | [bool:t] |
12
+ | summary | string | [bool:t] |
13
+ | title | string | [bool:t] |
14
+ | price | float | [bool:t] |
15
+ | active | boolean | [bool:t] |
16
+ | state | integer | [bool:t] |
17
+
18
+ Scenario: filter by one hydra attribute
19
+ Given create "Product" model with attributes as "hashes":
20
+ | code | price |
21
+ | 1 | 2.75 |
22
+ | 2 | 2.75 |
23
+ | 3 | 2.76 |
24
+ | 4 | |
25
+ When filter "Product" by:
26
+ | field | value |
27
+ | price | 2.75 |
28
+ Then total records should be "2"
29
+ And records should have the following attributes:
30
+ | field | value |
31
+ | code | 1 |
32
+ | code | 2 |
33
+
34
+ Scenario: filter by one hydra attribute with nil value
35
+ Given create "Product" model with attributes as "hashes":
36
+ | code | price |
37
+ | 1 | |
38
+ | 2 | 0 |
39
+ | 3 | |
40
+ When filter "Product" by:
41
+ | field | value |
42
+ | price | [nil:] |
43
+ Then total records should be "2"
44
+ And records should have the following attributes:
45
+ | field | value |
46
+ | code | 1 |
47
+ | code | 3 |
48
+
49
+ Scenario: filter by several fields including both the hydra and general attributes
50
+ Given create "Product" model with attributes as "hashes":
51
+ | name | code | title | price | active | state | summary |
52
+ | toy | 1 | story | 2.40 | 1 | | |
53
+ | toy | 2 | story | 2.45 | 1 | | |
54
+ | toy | 3 | story | 2.45 | 1 | | |
55
+ | toy | 4 | | 2.45 | 0 | | |
56
+ | | 5 | | 2.45 | 1 | | |
57
+ | toy | 6 | | 2.46 | 1 | | |
58
+ When filter "Product" by:
59
+ | field | value |
60
+ | name | toy |
61
+ | title | story |
62
+ | summary | [nil:] |
63
+ | price | [float:2.45] |
64
+ | active | [bool:t] |
65
+ | state | [nil:] |
66
+ Then total records should be "2"
67
+ And records should have the following attributes:
68
+ | field | value |
69
+ | code | 2 |
70
+ | code | 3 |
@@ -0,0 +1,65 @@
1
+ Given /^create connection$/ do
2
+ spec = ActiveRecord::Base::ConnectionSpecification.new({database: ':memory:', adapter: 'sqlite3'}, 'sqlite3_connection')
3
+ @connection = ActiveRecord::ConnectionAdapters::ConnectionPool.new(spec).connection
4
+ end
5
+
6
+ Given /^(create|drop|migrate|rollback)(?:\sto|\sfrom)? hydra entity "([^"]+)"$/ do |method, table|
7
+ HydraAttribute::Migrator.send(method, @connection, table)
8
+ end
9
+
10
+ Given /^create table "([^"]+)"$/ do |table|
11
+ @connection.create_table(table)
12
+ end
13
+
14
+ Then /^should have the following (\d+) tables?:$/ do |count, table|
15
+ @connection.should have(count.to_i).tables
16
+
17
+ table.rows.flatten.each do |name|
18
+ @connection.tables.should include(name)
19
+ end
20
+ end
21
+
22
+ Then /^should not have any tables$/ do
23
+ @connection.should have(0).tables
24
+ end
25
+
26
+ Then /^table "([^"]+)" should have the following (columns|indexes):$/ do |table_name, method, table|
27
+ connection_params = @connection.send(method, table_name).sort_by(&:name)
28
+ checked_params = table.hashes.map { |hash| type_cast_hash(hash) }.sort_by { |param| param['name'] }
29
+
30
+ unless connection_params.length == checked_params.length
31
+ raise %(Table "#{table_name}" has "#{connection_params.length}" #{method} but in our test "#{checked_params.length}". Diff: is "#{connection_params.map(&:name)}" but should be "#{checked_params.map { |h| h['name'] }}")
32
+ end
33
+
34
+ connection_params.zip(checked_params).each do |(real_params, params)|
35
+ params.keys.each do |name|
36
+ params[name].should == real_params.send(name)
37
+ end
38
+ end
39
+ end
40
+
41
+ Then /^table "([^"]+)" should have (\d+) records?$/ do |table_name, count|
42
+ result = ActiveRecord::Base.connection.select_one("SELECT COUNT(*) AS count FROM #{table_name}")
43
+ result['count'].should == count.to_i
44
+ end
45
+
46
+ Then /^table "([^"]+)" should have (\d+) records?:$/ do |table_name, count, table|
47
+ step %(table "#{table_name}" should have #{count} records)
48
+
49
+ table.hashes.each do |hash|
50
+ table = Arel::Table.new(table_name, ActiveRecord::Base)
51
+ select = Arel.sql('COUNT(*)').as('count')
52
+ where = hash.map { |name, value| table[name].eq(type_cast_value(value)) }.inject(:and)
53
+
54
+ result = ActiveRecord::Base.connection.select_one(table.project(select).where(where))
55
+ unless result['count']
56
+ raise %(Query "#{table.project(select).where(where).to_sql}" return nil)
57
+ end
58
+
59
+ unless result['count'] == 1
60
+ raise %(Query "#{table.project(select).where(where).to_sql}" return "#{result['count']}")
61
+ end
62
+ result['count'].should be(1)
63
+ end
64
+ end
65
+
@@ -6,6 +6,34 @@ Given /^create "([^"]+)" model$/ do |klass|
6
6
  klass.constantize.create!
7
7
  end
8
8
 
9
+ Given /^build "([^"]+)" model$/ do |klass|
10
+ @model = klass.constantize.new
11
+ end
12
+
13
+ Given /^build "([^"]+)" model:$/ do |klass, table|
14
+ @model = klass.constantize.new(type_cast_hash(table.rows_hash))
15
+ end
16
+
17
+ When /^set "([^"]+)" to "([^"]+)"$/ do |attribute, value|
18
+ @model.send("#{attribute}=", type_cast_value(value))
19
+ end
20
+
21
+ When /^(save|destroy) model$/ do |method|
22
+ @model.send(method)
23
+ end
24
+
25
+ When /^find "([^"]+)" model by attribute "([^"]+)" and value "([^"]+)"$/ do |class_name, attribute, value|
26
+ @model = class_name.constantize.send("find_by_#{attribute}", type_cast_value(value))
27
+ end
28
+
29
+ When /^find (first|last) "([^"]+)" model$/ do |method, class_name|
30
+ @model = class_name.constantize.send(method)
31
+ end
32
+
33
+ When /^reload model$/ do
34
+ @model.reload
35
+ end
36
+
9
37
  Given /^create "([^"]+)" model with attributes as "([^"]+):"$/ do |klass, format, table|
10
38
  Array.wrap(table.send(format)).each do |hash|
11
39
  klass.constantize.create!(type_cast_hash(hash))
@@ -18,6 +46,26 @@ Given /^create hydra attributes for "([^"]+)" with role "([^"]+)" as "([^"]+)":$
18
46
  end
19
47
  end
20
48
 
49
+ Given /^create hydra sets for "([^"]+)" as "([^"]+)":$/ do |klass, format, table|
50
+ Array.wrap(table.send(format)).each do |hash|
51
+ step %(create hydra set "#{hash['name']}" for "#{klass}")
52
+ end
53
+ end
54
+
55
+ Given /^create hydra set "([^"]+)" for "([^"]+)"$/ do |hydra_set_name, klass|
56
+ klass.constantize.hydra_sets.create!(name: type_cast_value(hydra_set_name))
57
+ end
58
+
59
+ Given /^add "([^"]+)" hydra attributes to hydra set:$/ do |klass, table|
60
+ klass = klass.constantize
61
+
62
+ Array.wrap(table.hashes).each do |hash|
63
+ Array.wrap(type_cast_value(hash['hydra set name'])).each do |set|
64
+ klass.hydra_sets.find_by_name(set).hydra_attributes << klass.hydra_attributes.find_by_name(type_cast_value(hash['hydra attribute name']))
65
+ end
66
+ end
67
+ end
68
+
21
69
  Given /^(load and )?(save|create|update(?: all| attributes)?|destroy(?: all)?|delete(?: all)?)(?: for)? "([^"]+)" models? with attributes as "([^"]+)":$/ do |load, action, klass, format, table|
22
70
  action = action.gsub(' ', '_')
23
71
  klass = klass.constantize
@@ -30,20 +78,45 @@ Given /^(load and )?(save|create|update(?: all| attributes)?|destroy(?: all)?|de
30
78
  end
31
79
  end
32
80
 
33
- Then /^model "([^"]+)" (should(?:\snot)?) respond to "([^"]+)"$/ do |klass, method, attributes|
34
- model = klass.constantize.new
81
+ Then /^model (should(?:\snot)?) respond to "([^"]+)"$/ do |method, attributes|
82
+ method = method.gsub(/\s+/, '_')
83
+ attributes.split.each do |attribute|
84
+ @model.send(method, respond_to(attribute))
85
+ end
86
+ end
87
+
88
+ Then /^model attributes (should(?:\snot)?) include "([^"]+)"$/ do |method, attributes|
35
89
  method = method.gsub(/\s+/, '_')
36
90
  attributes.split.each do |attribute|
37
- model.send(method, respond_to(attribute))
91
+ @model.attributes.keys.send(method, include(attribute))
38
92
  end
39
93
  end
40
94
 
41
- Then /^(last|first) created "([^"]+)" (should|should not) have the following attributes:$/ do |method, klass, match, table|
42
- table.rows_hash.each_with_object(klass.constantize.send(method)) do |(attribute, value), model|
43
- model.send(attribute).send(match) == type_cast_value(value)
95
+ Then /^model attributes (should(?:\snot)?) match "([^"]+)"$/ do |match, attribute|
96
+ @model.attributes.keys.send(match) =~ Array(type_cast_value(attribute))
97
+ end
98
+
99
+ Then /^model "([^"]+)" (should(?:\snot)?) respond to "([^"]+)"$/ do |klass, method, attributes|
100
+ @model = klass.constantize.new
101
+ step %(model #{method} respond to "#{attributes}")
102
+ end
103
+
104
+ Then /^error "([^"]+)" (should(?:\snot)?) be risen when methods? "([^"]+)" (?:is|are) called$/ do |error_class, expect, methods|
105
+ Array.wrap(methods.split).each do |method|
106
+ lambda { @model.send(method) }.send(expect.gsub(/\s+/, '_'), raise_error(error_class.constantize))
44
107
  end
45
108
  end
46
109
 
110
+ Then /^(last|first) created "([^"]+)" (should|should not) have the following attributes:$/ do |position, klass, match, table|
111
+ table.rows_hash.each do |attribute, value|
112
+ step %(#{position} created "#{klass}" #{match} have attribute "#{attribute}" with value "#{value}")
113
+ end
114
+ end
115
+
116
+ Then /^(last|first) created "([^"]+)" (should|should not) have attribute "([^"]+)" with value "([^"]+)"$/ do |position, klass, match, attribute, value|
117
+ klass.constantize.send(position).send(attribute).send(match) == type_cast_value(value)
118
+ end
119
+
47
120
  Then /^class "([^"]+)" (should(?:\snot)?) have "([^"]+)" in white list$/ do |klass, accept, attribute|
48
121
  method = accept.sub(' ', '_')
49
122
  klass.constantize.accessible_attributes.send(method, include(attribute))
@@ -5,7 +5,7 @@ end
5
5
 
6
6
  When /^filter "([^"]+)" records by "([^"]+)"$/ do |klass, attribute|
7
7
  @records = Object.const_get(klass)
8
- step %Q(filter records by "#{attribute}")
8
+ step %(filter records by "#{attribute}")
9
9
  end
10
10
 
11
11
  When /^filter records by "([^"]+)"$/ do |attribute|
@@ -15,7 +15,7 @@ end
15
15
 
16
16
  When /^group "([^"]+)" by "([^"]+)"$/ do |klass, attributes|
17
17
  @records = Object.const_get(klass)
18
- step %Q(group by "#{attributes}")
18
+ step %(group by "#{attributes}")
19
19
  end
20
20
 
21
21
  When /^group by "([^"]+)"$/ do |attributes|
@@ -24,7 +24,7 @@ end
24
24
 
25
25
  When /^(order|reorder) "([^"]+)" records by "([^"]+)"$/ do |sort_method, klass, attributes|
26
26
  @records = Object.const_get(klass)
27
- step %Q(#{sort_method} records by "#{attributes}")
27
+ step %(#{sort_method} records by "#{attributes}")
28
28
  end
29
29
 
30
30
  When /^(order|reorder) records by "([^"]+)"$/ do |sort_method, attributes|
@@ -58,11 +58,10 @@ Then /^records should have only the following "([^"]+)" names$/ do |attributes|
58
58
  end
59
59
  end
60
60
 
61
- Then /^records should raise "([^"]+)" when call the following "([^"]+)"$/ do |error, methods|
62
- error_class = error.constantize
61
+ Then /^records should raise "([^"]+)" when call the following "([^"]+)"$/ do |error_class, methods|
63
62
  @records.each do |record|
64
63
  methods.split(/\s+/).each do |method|
65
- lambda { record.send(method) }.should raise_error(error_class)
64
+ lambda { record.send(method) }.should raise_error(error_class.constantize)
66
65
  end
67
66
  end
68
67
  end
@@ -73,7 +72,7 @@ end
73
72
 
74
73
  Then /^total "([^"]+)" records should be "([^"]+)"$/ do |klass, count|
75
74
  @records = klass.constantize.scoped
76
- step %Q(total records should be "#{count}")
75
+ step %(total records should be "#{count}")
77
76
  end
78
77
 
79
78
  Then /^records "(should|should_not)" have loaded associations:$/ do |should, table|
@@ -1,19 +1,33 @@
1
1
  require 'active_record'
2
2
  require 'hydra_attribute'
3
3
  require 'database_cleaner'
4
- require 'database_cleaner/cucumber'
5
4
 
6
5
  ActiveSupport.on_load(:active_record) do
7
6
  self.default_timezone = :utc
8
- unless ActiveRecord::VERSION::STRING.start_with?('3.1.')
7
+ unless ActiveRecord::VERSION::STRING.start_with?('3.1.') # @COMPATIBILITY with 3.1.x. active_record 3.1 doesn't have "mass_assignment_sanitizer" method
9
8
  self.mass_assignment_sanitizer = :strict
10
9
  end
11
- extend HydraAttribute::ActiveRecord
10
+
11
+ ActiveRecord::Migration.send(:include, HydraAttribute::ActiveRecord::Migration)
12
12
  end
13
13
 
14
14
  ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:')
15
15
  DatabaseCleaner.strategy = :truncation
16
16
 
17
+ class Migration < ActiveRecord::Migration
18
+ def up
19
+ create_hydra_entity :products do |t|
20
+ t.string :name
21
+ t.timestamps
22
+ end
23
+ end
24
+
25
+ def down
26
+ end
27
+ end
28
+
29
+ Migration.new.up
30
+
17
31
  Before do
18
32
  redefine_hydra_entity('Product')
19
33
  DatabaseCleaner.start
@@ -3,15 +3,19 @@ module HydraAttribute
3
3
  module World
4
4
  def type_cast_value(schema)
5
5
  return schema unless schema.is_a?(String)
6
- type, value = schema.gsub(/\[|\]/, '').split(':', 2)
6
+ type, value = schema.gsub(/(^\[)|(\]$)/, '').split(':', 2)
7
7
  return schema if schema == type && value.nil?
8
8
 
9
9
  case type
10
- when 'integer' then value.to_i
11
- when 'float' then value.to_f
12
- when 'boolean' then value == 'true' ? true : false
13
- when 'nil' then nil
14
- when 'datetime' then ActiveSupport::TimeZone.new('UTC').parse(value)
10
+ when 'int' then type_cast_value(value).to_i
11
+ when 'float' then type_cast_value(value).to_f
12
+ when 'bool' then ::ActiveRecord::ConnectionAdapters::Column::TRUE_VALUES.include?(type_cast_value(value))
13
+ when 'nil' then nil
14
+ when 'date' then ActiveSupport::TimeZone.new('UTC').parse(type_cast_value(value))
15
+ when 'sym' then type_cast_value(value).to_sym
16
+ when 'array' then value.split(',').map { |v| type_cast_value(v) }
17
+ when 'eval' then eval(type_cast_value(value))
18
+ when 'str' then type_cast_value(value).to_s
15
19
  else value
16
20
  end
17
21
  end
@@ -22,7 +26,8 @@ module HydraAttribute
22
26
  end
23
27
 
24
28
  def type_cast_attributes(attributes)
25
- attributes.split(/(?<=\])\s+/).flatten.each_with_object({}) do |attribute, hash|
29
+ # 'a=[b c] c=a c=[v abc]' => ["a=[b c]", "c=a", "c=[v abc]"]
30
+ attributes.gsub(/\s+(\w+=)/, '<###>\1').split('<###>').each_with_object({}) do |attribute, hash|
26
31
  name, value = type_cast_attribute(attribute)
27
32
  hash[name] = value
28
33
  end
@@ -39,15 +44,15 @@ module HydraAttribute
39
44
 
40
45
  Object.send(:remove_const, klass.to_sym) if Object.const_defined?(klass.to_sym)
41
46
 
42
- ::HydraAttribute::SUPPORT_TYPES.each do |type|
47
+ ::HydraAttribute::SUPPORTED_BACKEND_TYPES.each do |type|
43
48
  class_name = "Hydra#{type.capitalize}#{klass}".to_sym
44
49
  ::HydraAttribute.send(:remove_const, class_name) if ::HydraAttribute.const_defined?(class_name)
45
50
  end
46
51
 
47
52
  Object.const_set(klass.to_sym, Class.new(::ActiveRecord::Base))
48
53
  klass.to_s.constantize.send(:accessible_attributes_configs).values.each(&:clear)
49
- klass.to_s.constantize.attr_accessible :name
50
- klass.to_s.constantize.use_hydra_attributes
54
+ klass.to_s.constantize.attr_accessible :name, :hydra_set_id
55
+ klass.to_s.constantize.send(:include, ::HydraAttribute::ActiveRecord)
51
56
  end
52
57
  end
53
58
  end
@@ -1,22 +1,22 @@
1
1
  PATH
2
2
  remote: /Users/kostyantyn/Sites/github/gems/hydra_attribute
3
3
  specs:
4
- hydra_attribute (0.3.2.beta)
4
+ hydra_attribute (0.4.0.beta)
5
5
  activerecord (>= 3.1.0)
6
6
 
7
7
  GEM
8
8
  remote: http://rubygems.org/
9
9
  specs:
10
- activemodel (3.1.6)
11
- activesupport (= 3.1.6)
10
+ activemodel (3.1.7)
11
+ activesupport (= 3.1.7)
12
12
  builder (~> 3.0.0)
13
13
  i18n (~> 0.6)
14
- activerecord (3.1.6)
15
- activemodel (= 3.1.6)
16
- activesupport (= 3.1.6)
14
+ activerecord (3.1.7)
15
+ activemodel (= 3.1.7)
16
+ activesupport (= 3.1.7)
17
17
  arel (~> 2.2.3)
18
18
  tzinfo (~> 0.3.29)
19
- activesupport (3.1.6)
19
+ activesupport (3.1.7)
20
20
  multi_json (>= 1.0, < 1.3)
21
21
  appraisal (0.4.1)
22
22
  bundler
@@ -33,17 +33,17 @@ GEM
33
33
  gherkin (2.11.1)
34
34
  json (>= 1.4.6)
35
35
  i18n (0.6.0)
36
- json (1.7.3)
36
+ json (1.7.4)
37
37
  multi_json (1.2.0)
38
38
  rake (0.9.2.2)
39
- rspec (2.10.0)
40
- rspec-core (~> 2.10.0)
41
- rspec-expectations (~> 2.10.0)
42
- rspec-mocks (~> 2.10.0)
43
- rspec-core (2.10.1)
44
- rspec-expectations (2.10.0)
39
+ rspec (2.11.0)
40
+ rspec-core (~> 2.11.0)
41
+ rspec-expectations (~> 2.11.0)
42
+ rspec-mocks (~> 2.11.0)
43
+ rspec-core (2.11.1)
44
+ rspec-expectations (2.11.2)
45
45
  diff-lcs (~> 1.1.3)
46
- rspec-mocks (2.10.1)
46
+ rspec-mocks (2.11.1)
47
47
  sqlite3 (1.3.6)
48
48
  tzinfo (0.3.33)
49
49
 
@@ -1,21 +1,21 @@
1
1
  PATH
2
2
  remote: /Users/kostyantyn/Sites/github/gems/hydra_attribute
3
3
  specs:
4
- hydra_attribute (0.3.2.beta)
4
+ hydra_attribute (0.4.0.beta)
5
5
  activerecord (>= 3.1.0)
6
6
 
7
7
  GEM
8
8
  remote: http://rubygems.org/
9
9
  specs:
10
- activemodel (3.2.6)
11
- activesupport (= 3.2.6)
10
+ activemodel (3.2.7)
11
+ activesupport (= 3.2.7)
12
12
  builder (~> 3.0.0)
13
- activerecord (3.2.6)
14
- activemodel (= 3.2.6)
15
- activesupport (= 3.2.6)
13
+ activerecord (3.2.7)
14
+ activemodel (= 3.2.7)
15
+ activesupport (= 3.2.7)
16
16
  arel (~> 3.0.2)
17
17
  tzinfo (~> 0.3.29)
18
- activesupport (3.2.6)
18
+ activesupport (3.2.7)
19
19
  i18n (~> 0.6)
20
20
  multi_json (~> 1.0)
21
21
  appraisal (0.4.1)
@@ -33,17 +33,17 @@ GEM
33
33
  gherkin (2.11.1)
34
34
  json (>= 1.4.6)
35
35
  i18n (0.6.0)
36
- json (1.7.3)
36
+ json (1.7.4)
37
37
  multi_json (1.3.6)
38
38
  rake (0.9.2.2)
39
- rspec (2.10.0)
40
- rspec-core (~> 2.10.0)
41
- rspec-expectations (~> 2.10.0)
42
- rspec-mocks (~> 2.10.0)
43
- rspec-core (2.10.1)
44
- rspec-expectations (2.10.0)
39
+ rspec (2.11.0)
40
+ rspec-core (~> 2.11.0)
41
+ rspec-expectations (~> 2.11.0)
42
+ rspec-mocks (~> 2.11.0)
43
+ rspec-core (2.11.1)
44
+ rspec-expectations (2.11.2)
45
45
  diff-lcs (~> 1.1.3)
46
- rspec-mocks (2.10.1)
46
+ rspec-mocks (2.11.1)
47
47
  sqlite3 (1.3.6)
48
48
  tzinfo (0.3.33)
49
49
 
@@ -2,76 +2,112 @@ module HydraAttribute
2
2
  module ActiveRecord
3
3
  class Association < ::ActiveRecord::Associations::HasManyAssociation
4
4
 
5
- def find_model(attributes = {})
5
+ def find_model(hydra_attribute_id)
6
6
  load_target unless loaded?
7
- target.detect do |model|
8
- model.hydra_attribute_id == attributes[:hydra_attribute_id]
7
+
8
+ hydra_set_target.detect do |model|
9
+ model.hydra_attribute_id == hydra_attribute_id
9
10
  end
10
11
  end
11
12
 
12
- def find_model_or_build(attributes = {})
13
- find_model(attributes) || build(attributes)
13
+ def find_model_or_build(options = {})
14
+ find_model(options[:hydra_attribute_id]) || build(options)
14
15
  end
15
16
 
16
17
  def build(attributes = {}, options = {}, &block)
17
- return if locked_for_build? and white_list_for_build.exclude?(attributes[:hydra_attribute_id])
18
+ return if hydra_attribute_ids.exclude?(attributes[:hydra_attribute_id])
19
+ return if target.any? { |model| model.hydra_attribute_id == attributes[:hydra_attribute_id] }
20
+
18
21
  super
19
22
  end
20
23
 
21
24
  def all_models
22
25
  unless @full_loaded
23
- (all_attribute_ids - target.map(&:hydra_attribute_id)).each do |hydra_attribute_id|
26
+ (hydra_attribute_ids - target.map(&:hydra_attribute_id)).each do |hydra_attribute_id|
24
27
  build(hydra_attribute_id: hydra_attribute_id)
25
28
  end
26
29
  @full_loaded = true
27
30
  end
28
- target
31
+ hydra_set_target
29
32
  end
30
33
 
31
- def locked_for_build
32
- @locked_for_build ||= false
34
+ def save
35
+ changed = false
36
+ all_models.each do |model|
37
+ model.entity_id = owner.id
38
+ model.save
39
+ changed = true unless model.previous_changes.blank?
40
+ end
41
+ changed
33
42
  end
34
- alias_method :locked_for_build?, :locked_for_build
35
43
 
36
- def lock_for_build!(white_list_for_build = [])
37
- @locked_for_build = true
38
- @white_list_for_build = Array(white_list_for_build)
44
+ def lock!(white_list = [])
45
+ @white_list = Array(white_list)
39
46
  loaded!
40
47
  end
41
48
 
42
- def white_list_for_build
43
- @white_list_for_build ||= []
49
+ def hydra_attributes
50
+ if @white_list
51
+ @hydra_attributes ||= owner.class.hydra_set_attributes_for_backend_type(owner.hydra_set_id, reflection.backend_type)
52
+ else
53
+ owner.class.hydra_set_attributes_for_backend_type(owner.hydra_set_id, reflection.backend_type)
54
+ end
44
55
  end
45
56
 
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
57
+ def hydra_attribute_ids
58
+ if @white_list
59
+ @hydra_attribute_ids ||= owner.class.hydra_set_attribute_ids_for_backend_type(owner.hydra_set_id, reflection.backend_type) & @white_list
60
+ else
61
+ owner.class.hydra_set_attribute_ids_for_backend_type(owner.hydra_set_id, reflection.backend_type)
62
+ end
50
63
  end
51
64
 
52
- private
65
+ def hydra_attribute_names
66
+ if @white_list
67
+ hydra_attributes.map(&:name)
68
+ else
69
+ owner.class.hydra_set_attribute_names_for_backend_type(owner.hydra_set_id, reflection.backend_type)
70
+ end
71
+ end
53
72
 
54
- # Optimized method
55
- # Remove unnecessary callbacks
56
- def add_to_target(record)
57
- @target << record
58
- record
73
+ def hydra_set_target
74
+ @hydra_set_target ||= target.select do |model|
75
+ hydra_attribute_ids.include?(model.hydra_attribute_id)
76
+ end
59
77
  end
60
78
 
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
79
+ def clear_cache!
80
+ @hydra_attributes = nil
81
+ @hydra_attribute_ids = nil
82
+ @full_loaded = nil
83
+ @hydra_set_target = nil
84
+ end
85
+
86
+ private
68
87
 
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]
88
+ # Optimized method
89
+ # Remove unnecessary callbacks
90
+ def add_to_target(record)
91
+ @target << record
92
+ record
93
+ end
94
+
95
+ # Optimized method
96
+ # Attributes are written via low level function without additional checks
97
+ def build_record(options, _)
98
+ reflection.klass.new do |record|
99
+ unless options.has_key?(:value)
100
+ options[:value] = owner.class.hydra_attribute(options[:hydra_attribute_id]).default_value
101
+ end
102
+
103
+ record.send :write_attribute, 'id', options[:id]
104
+ record.send :write_attribute, 'entity_id', owner.id
105
+ record.send :write_attribute, 'hydra_attribute_id', options[:hydra_attribute_id]
106
+ record.send :write_attribute, 'value', options[:value]
107
+
108
+ hydra_set_target << record unless hydra_set_target.include?(record)
109
+ end
73
110
  end
74
- end
75
111
  end
76
112
  end
77
113
  end