hydra_attribute 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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