hydra_attribute 0.1.0

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 (57) hide show
  1. data/.gitignore +20 -0
  2. data/.rspec +2 -0
  3. data/Appraisals +7 -0
  4. data/CHANGELOG.md +6 -0
  5. data/Gemfile +3 -0
  6. data/LICENSE +22 -0
  7. data/README.md +122 -0
  8. data/Rakefile +12 -0
  9. data/features/attribute_methods.feature +151 -0
  10. data/features/define_attributes.feature +61 -0
  11. data/features/load_associations.feature +45 -0
  12. data/features/order_conditions.feature +93 -0
  13. data/features/select_attributes.feature +56 -0
  14. data/features/step_definitions/class_steps.rb +32 -0
  15. data/features/step_definitions/model_steps.rb +31 -0
  16. data/features/step_definitions/record_steps.rb +103 -0
  17. data/features/support/env.rb +24 -0
  18. data/features/support/schema.rb +51 -0
  19. data/features/support/world.rb +31 -0
  20. data/features/typecast_attributes.feature +29 -0
  21. data/features/where_conditions.feature +77 -0
  22. data/gemfiles/3.1.gemfile +7 -0
  23. data/gemfiles/3.1.gemfile.lock +60 -0
  24. data/gemfiles/3.2.gemfile +7 -0
  25. data/gemfiles/3.2.gemfile.lock +60 -0
  26. data/hydra_attribute.gemspec +24 -0
  27. data/lib/generators/hydra_attribute/install/USAGE +8 -0
  28. data/lib/generators/hydra_attribute/install/install_generator.rb +11 -0
  29. data/lib/generators/hydra_attribute/install/templates/hydra_attribute.txt +11 -0
  30. data/lib/hydra_attribute.rb +31 -0
  31. data/lib/hydra_attribute/active_record.rb +7 -0
  32. data/lib/hydra_attribute/active_record/attribute_methods.rb +72 -0
  33. data/lib/hydra_attribute/active_record/attribute_methods/before_type_cast.rb +16 -0
  34. data/lib/hydra_attribute/active_record/attribute_methods/read.rb +13 -0
  35. data/lib/hydra_attribute/active_record/relation.rb +44 -0
  36. data/lib/hydra_attribute/active_record/relation/query_methods.rb +162 -0
  37. data/lib/hydra_attribute/active_record/scoping.rb +15 -0
  38. data/lib/hydra_attribute/association_builder.rb +40 -0
  39. data/lib/hydra_attribute/attribute_builder.rb +57 -0
  40. data/lib/hydra_attribute/attribute_proxy.rb +16 -0
  41. data/lib/hydra_attribute/builder.rb +25 -0
  42. data/lib/hydra_attribute/configuration.rb +47 -0
  43. data/lib/hydra_attribute/migration.rb +27 -0
  44. data/lib/hydra_attribute/railtie.rb +9 -0
  45. data/lib/hydra_attribute/version.rb +3 -0
  46. data/spec/hydra_attribute/active_record/relation/query_methods_spec.rb +286 -0
  47. data/spec/hydra_attribute/active_record/relation_spec.rb +93 -0
  48. data/spec/hydra_attribute/active_record/scoping_spec.rb +19 -0
  49. data/spec/hydra_attribute/active_record_spec.rb +20 -0
  50. data/spec/hydra_attribute/association_builder_spec.rb +95 -0
  51. data/spec/hydra_attribute/attribute_builder_spec.rb +70 -0
  52. data/spec/hydra_attribute/attribute_helpers_spec.rb +70 -0
  53. data/spec/hydra_attribute/builder_spec.rb +39 -0
  54. data/spec/hydra_attribute/configuration_spec.rb +96 -0
  55. data/spec/hydra_attribute_spec.rb +20 -0
  56. data/spec/spec_helper.rb +17 -0
  57. metadata +196 -0
@@ -0,0 +1,77 @@
1
+ Feature: hydra attribute where conditions
2
+ When filter by hydra attribute and this value is not nil
3
+ Then the correct hydra attribute table should be joined and filter by this value should be added
4
+
5
+ When filter by hydra attribute and this value is nil
6
+ Then records with nil value should be selected or records which don't have this hydra attribute
7
+
8
+ Background: create models and describe hydra attributes
9
+ Given removed constants if they exist:
10
+ | name |
11
+ | GroupProduct |
12
+ | SimpleProduct |
13
+ | Product |
14
+ And create model class "Product"
15
+ And create model class "SimpleProduct" as "Product" with hydra attributes:
16
+ | type | name |
17
+ | string | code |
18
+ | string | summary |
19
+ | string | title |
20
+ | float | price |
21
+ | boolean | active |
22
+ | integer | state |
23
+
24
+ Scenario: add filter by one hydra attribute
25
+ Given create models:
26
+ | model | attributes |
27
+ | SimpleProduct | code=[string:1] price=[float:2.75] |
28
+ | SimpleProduct | code=[string:2] price=[float:2.75] |
29
+ | SimpleProduct | code=[string:3] price=[float:2.76] |
30
+ | SimpleProduct | code=[string:4] price=[nil:] |
31
+ When filter "SimpleProduct" by:
32
+ | field | value |
33
+ | price | [string:2.75] |
34
+ Then total records should be "2"
35
+ And records should have the following attributes:
36
+ | field | value |
37
+ | code | [string:1] |
38
+ | code | [string:2] |
39
+
40
+ Scenario: add nil filter by one hydra attribute
41
+ Given create models:
42
+ | model | attributes |
43
+ | SimpleProduct | code=[string:1] price=[nil:] |
44
+ | SimpleProduct | code=[string:2] price=[float:0] |
45
+ | SimpleProduct | code=[string:3] |
46
+ When filter "SimpleProduct" by:
47
+ | field | value |
48
+ | price | [nil:] |
49
+ Then total records should be "2"
50
+ And records should have the following attributes:
51
+ | field | value |
52
+ | code | [string:1] |
53
+ | code | [string:3] |
54
+
55
+ Scenario: add filter by several fields including both the hydra and general attributes
56
+ Given create models:
57
+ | model | attributes |
58
+ | SimpleProduct | name=[string:toy] code=[string:1] title=[string:story] price=[float:2.45] active=[boolean:true] info=[string:] |
59
+ | SimpleProduct | name=[string:toy] code=[string:2] title=[string:story] price=[float:2.45] active=[boolean:true] info=[string:a] summary=[nil:] |
60
+ | SimpleProduct | name=[string:toy] code=[string:3] title=[string:story] price=[float:2.45] active=[boolean:true] state=[nil:] info=[string:a] summary=[nil:] |
61
+ | SimpleProduct | name=[string:toy] code=[string:4] price=[float:2.45] active=[boolean:false] state=[nil:] info=[string:a] summary=[nil:] |
62
+ | SimpleProduct | code=[string:5] price=[float:2.45] active=[boolean:true] state=[nil:] info=[string:a] summary=[nil:] |
63
+ | SimpleProduct | name=[string:toy] code=[string:6] price=[float:2.46] active=[boolean:true] state=[nil:] info=[string:a] summary=[nil:] |
64
+ When filter "SimpleProduct" by:
65
+ | field | value |
66
+ | name | [string:toy] |
67
+ | title | [string:story] |
68
+ | summary | [nil:] |
69
+ | price | [string:2.45] |
70
+ | active | [boolean:true] |
71
+ | info | [string:a] |
72
+ | state | [nil:] |
73
+ Then total records should be "2"
74
+ And records should have the following attributes:
75
+ | field | value |
76
+ | code | [string:2] |
77
+ | code | [string:3] |
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source :rubygems
4
+
5
+ gem "activerecord", "~> 3.1.0"
6
+
7
+ gemspec :path=>"../"
@@ -0,0 +1,60 @@
1
+ PATH
2
+ remote: /Users/kostyantyn/Sites/github/gems/hydra_attribute
3
+ specs:
4
+ hydra_attribute (0.1.0.beta2)
5
+ activerecord (>= 3.1.0)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ activemodel (3.1.5)
11
+ activesupport (= 3.1.5)
12
+ builder (~> 3.0.0)
13
+ i18n (~> 0.6)
14
+ activerecord (3.1.5)
15
+ activemodel (= 3.1.5)
16
+ activesupport (= 3.1.5)
17
+ arel (~> 2.2.3)
18
+ tzinfo (~> 0.3.29)
19
+ activesupport (3.1.5)
20
+ multi_json (>= 1.0, < 1.3)
21
+ appraisal (0.4.1)
22
+ bundler
23
+ rake
24
+ arel (2.2.3)
25
+ builder (3.0.0)
26
+ cucumber (1.2.0)
27
+ builder (>= 2.1.2)
28
+ diff-lcs (>= 1.1.3)
29
+ gherkin (~> 2.10.0)
30
+ json (>= 1.4.6)
31
+ database_cleaner (0.8.0)
32
+ diff-lcs (1.1.3)
33
+ gherkin (2.10.0)
34
+ json (>= 1.4.6)
35
+ i18n (0.6.0)
36
+ json (1.7.3)
37
+ multi_json (1.2.0)
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)
45
+ diff-lcs (~> 1.1.3)
46
+ rspec-mocks (2.10.1)
47
+ sqlite3 (1.3.6)
48
+ tzinfo (0.3.33)
49
+
50
+ PLATFORMS
51
+ ruby
52
+
53
+ DEPENDENCIES
54
+ activerecord (~> 3.1.0)
55
+ appraisal
56
+ cucumber
57
+ database_cleaner
58
+ hydra_attribute!
59
+ rspec
60
+ sqlite3
@@ -0,0 +1,7 @@
1
+ # This file was generated by Appraisal
2
+
3
+ source :rubygems
4
+
5
+ gem "activerecord", "~> 3.2.0"
6
+
7
+ gemspec :path=>"../"
@@ -0,0 +1,60 @@
1
+ PATH
2
+ remote: /Users/kostyantyn/Sites/github/gems/hydra_attribute
3
+ specs:
4
+ hydra_attribute (0.1.0.beta2)
5
+ activerecord (>= 3.1.0)
6
+
7
+ GEM
8
+ remote: http://rubygems.org/
9
+ specs:
10
+ activemodel (3.2.5)
11
+ activesupport (= 3.2.5)
12
+ builder (~> 3.0.0)
13
+ activerecord (3.2.5)
14
+ activemodel (= 3.2.5)
15
+ activesupport (= 3.2.5)
16
+ arel (~> 3.0.2)
17
+ tzinfo (~> 0.3.29)
18
+ activesupport (3.2.5)
19
+ i18n (~> 0.6)
20
+ multi_json (~> 1.0)
21
+ appraisal (0.4.1)
22
+ bundler
23
+ rake
24
+ arel (3.0.2)
25
+ builder (3.0.0)
26
+ cucumber (1.2.0)
27
+ builder (>= 2.1.2)
28
+ diff-lcs (>= 1.1.3)
29
+ gherkin (~> 2.10.0)
30
+ json (>= 1.4.6)
31
+ database_cleaner (0.8.0)
32
+ diff-lcs (1.1.3)
33
+ gherkin (2.10.0)
34
+ json (>= 1.4.6)
35
+ i18n (0.6.0)
36
+ json (1.7.3)
37
+ multi_json (1.3.6)
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)
45
+ diff-lcs (~> 1.1.3)
46
+ rspec-mocks (2.10.1)
47
+ sqlite3 (1.3.6)
48
+ tzinfo (0.3.33)
49
+
50
+ PLATFORMS
51
+ ruby
52
+
53
+ DEPENDENCIES
54
+ activerecord (~> 3.2.0)
55
+ appraisal
56
+ cucumber
57
+ database_cleaner
58
+ hydra_attribute!
59
+ rspec
60
+ sqlite3
@@ -0,0 +1,24 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require File.expand_path('../lib/hydra_attribute/version', __FILE__)
3
+
4
+ Gem::Specification.new do |gem|
5
+ gem.author = 'Kostyantyn Stepanyuk'
6
+ gem.email = 'kostya.stepanyuk@gmail.com'
7
+ gem.summary = 'hydra_attribute allows to use EAV database structure for ActiveRecord models.'
8
+ gem.description = 'hydra_attribute allows to use EAV database structure for ActiveRecord models.'
9
+ gem.homepage = ""
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"
13
+ gem.require_paths = %w(lib)
14
+ gem.required_ruby_version = Gem::Requirement.new('>= 1.9.2')
15
+ gem.version = HydraAttribute::VERSION
16
+
17
+ gem.add_dependency('activerecord', '>= 3.1.0')
18
+
19
+ gem.add_development_dependency('rspec')
20
+ gem.add_development_dependency('cucumber')
21
+ gem.add_development_dependency('sqlite3')
22
+ gem.add_development_dependency('database_cleaner')
23
+ gem.add_development_dependency('appraisal')
24
+ end
@@ -0,0 +1,8 @@
1
+ Description:
2
+ This generator generate the default initializer file.
3
+
4
+ Usage:
5
+ rails generate hydra_attribute:install
6
+
7
+ This will create:
8
+ config/initializers/hydra_attribute.rb
@@ -0,0 +1,11 @@
1
+ module HydraAttribute
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::Base
4
+ source_root File.expand_path("../templates", __FILE__)
5
+
6
+ def copy_initializer_file
7
+ copy_file 'hydra_attribute.txt', 'config/initializers/hydra_attribute.rb'
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,11 @@
1
+ # Change default hydra_attribute configurations
2
+ HydraAttribute.setup do |config|
3
+ # Add prefix for all attribute tables
4
+ # config.table_prefix = 'hydra_'
5
+
6
+ # Add prefix for has_many associations
7
+ # config.association_prefix = 'hydra_'
8
+
9
+ # Wrap all associated models in HydraAttribute module
10
+ # config.use_module_for_associated_models = true
11
+ end
@@ -0,0 +1,31 @@
1
+ module HydraAttribute
2
+ SUPPORT_TYPES = [:string, :text, :integer, :float, :boolean, :datetime]
3
+
4
+ class << self
5
+ def config
6
+ @config ||= Configuration.new
7
+ end
8
+
9
+ def setup
10
+ yield config
11
+ end
12
+ end
13
+
14
+ end
15
+
16
+ require 'hydra_attribute/version'
17
+ require 'hydra_attribute/configuration'
18
+ require 'hydra_attribute/association_builder'
19
+ require 'hydra_attribute/attribute_builder'
20
+ require 'hydra_attribute/builder'
21
+ require 'hydra_attribute/migration'
22
+ require 'hydra_attribute/attribute_proxy'
23
+ require 'hydra_attribute/active_record'
24
+ require 'hydra_attribute/active_record/scoping'
25
+ require 'hydra_attribute/active_record/relation'
26
+ require 'hydra_attribute/active_record/relation/query_methods'
27
+ require 'hydra_attribute/active_record/attribute_methods'
28
+ require 'hydra_attribute/active_record/attribute_methods/read'
29
+ require 'hydra_attribute/active_record/attribute_methods/before_type_cast'
30
+
31
+ require 'hydra_attribute/railtie' if defined?(Rails)
@@ -0,0 +1,7 @@
1
+ module HydraAttribute
2
+ module ActiveRecord
3
+ def define_hydra_attributes
4
+ yield Builder.new(self)
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,72 @@
1
+ module HydraAttribute
2
+ module ActiveRecord
3
+ module AttributeMethods
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ @hydra_attributes = {}
8
+
9
+ include Read
10
+ include BeforeTypeCast
11
+ end
12
+
13
+ module ClassMethods
14
+ def inherited(base)
15
+ base.instance_variable_set(:@hydra_attributes, hydra_attributes)
16
+ super
17
+ end
18
+
19
+ def hydra_attributes
20
+ @hydra_attributes.dup
21
+ end
22
+
23
+ def hydra_attribute_names
24
+ hydra_attributes.keys
25
+ end
26
+
27
+ def hydra_attribute_types
28
+ hydra_attributes.values.uniq
29
+ end
30
+ end
31
+
32
+ def initialize(attributes = nil, options = {})
33
+ @hydra_attribute_names = self.class.hydra_attribute_names
34
+ super
35
+ end
36
+
37
+ def init_with(coder)
38
+ @hydra_attribute_names = self.class.hydra_attribute_names
39
+ super
40
+ end
41
+
42
+ def initialize_dup(other)
43
+ if other.instance_variable_defined?(:@hydra_attribute_names)
44
+ @hydra_attribute_names = other.instance_variable_get(:@hydra_attribute_names)
45
+ else
46
+ @hydra_attribute_names = self.class.hydra_attribute_names
47
+ end
48
+ super
49
+ end
50
+
51
+ def hydra_attribute_model(name, type)
52
+ collection = send(HydraAttribute.config.association(type))
53
+ collection.detect { |model| model.name == name } || collection.build(name: name)
54
+ end
55
+
56
+ def attributes
57
+ super.merge(hydra_attributes)
58
+ end
59
+
60
+ %w(attributes attributes_before_type_cast).each do |attr_method|
61
+ module_eval <<-EOS, __FILE__, __LINE__ + 1
62
+ def hydra_#{attr_method}
63
+ @hydra_attribute_names.each_with_object({}) do |name, attributes|
64
+ type = self.class.hydra_attributes[name]
65
+ attributes[name] = hydra_attribute_model(name, type).#{attr_method}['value']
66
+ end
67
+ end
68
+ EOS
69
+ end
70
+ end
71
+ end
72
+ end
@@ -0,0 +1,16 @@
1
+ module HydraAttribute
2
+ module ActiveRecord
3
+ module AttributeMethods
4
+ module BeforeTypeCast
5
+ extend ActiveSupport::Concern
6
+ extend AttributeProxy
7
+
8
+ use_proxy_to_hydra_attribute :read_attribute_before_type_cast
9
+
10
+ def attributes_before_type_cast
11
+ super.merge(hydra_attributes_before_type_cast)
12
+ end
13
+ end
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,13 @@
1
+ module HydraAttribute
2
+ module ActiveRecord
3
+ module AttributeMethods
4
+ module Read
5
+ extend ActiveSupport::Concern
6
+ extend AttributeProxy
7
+
8
+ use_proxy_to_hydra_attribute :read_attribute
9
+
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,44 @@
1
+ module HydraAttribute
2
+ module ActiveRecord
3
+ module Relation
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ include QueryMethods
8
+ end
9
+
10
+ define_method HydraAttribute.config.relation_execute_method do
11
+ return @records if loaded?
12
+ records = super()
13
+ return records if records.empty?
14
+
15
+ limit_values = select_values.any? || hydra_select_values.any?
16
+
17
+ if records.many?
18
+ if limit_values
19
+ hydra_attribute_types = hydra_select_values.map { |value| records.first.class.hydra_attributes[value] }.uniq
20
+ else
21
+ hydra_attribute_types = records.first.class.hydra_attribute_types
22
+ end
23
+
24
+ hydra_attribute_types.each do |type|
25
+ association = HydraAttribute.config.association(type)
26
+ unless records.first.association(association).loaded?
27
+ ::ActiveRecord::Associations::Preloader.new(records, association).run
28
+ end
29
+ end
30
+ end
31
+
32
+ if limit_values
33
+ records.each do |record| # force limit getter methods for hydra attributes
34
+ record.instance_variable_set(:@hydra_attribute_names, hydra_select_values)
35
+ record.instance_variable_get(:@attributes).delete('id') if @id_for_hydra_attributes
36
+ end
37
+ end
38
+
39
+ records
40
+ end
41
+
42
+ end
43
+ end
44
+ end