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.
- data/.gitignore +20 -0
- data/.rspec +2 -0
- data/Appraisals +7 -0
- data/CHANGELOG.md +6 -0
- data/Gemfile +3 -0
- data/LICENSE +22 -0
- data/README.md +122 -0
- data/Rakefile +12 -0
- data/features/attribute_methods.feature +151 -0
- data/features/define_attributes.feature +61 -0
- data/features/load_associations.feature +45 -0
- data/features/order_conditions.feature +93 -0
- data/features/select_attributes.feature +56 -0
- data/features/step_definitions/class_steps.rb +32 -0
- data/features/step_definitions/model_steps.rb +31 -0
- data/features/step_definitions/record_steps.rb +103 -0
- data/features/support/env.rb +24 -0
- data/features/support/schema.rb +51 -0
- data/features/support/world.rb +31 -0
- data/features/typecast_attributes.feature +29 -0
- data/features/where_conditions.feature +77 -0
- data/gemfiles/3.1.gemfile +7 -0
- data/gemfiles/3.1.gemfile.lock +60 -0
- data/gemfiles/3.2.gemfile +7 -0
- data/gemfiles/3.2.gemfile.lock +60 -0
- data/hydra_attribute.gemspec +24 -0
- data/lib/generators/hydra_attribute/install/USAGE +8 -0
- data/lib/generators/hydra_attribute/install/install_generator.rb +11 -0
- data/lib/generators/hydra_attribute/install/templates/hydra_attribute.txt +11 -0
- data/lib/hydra_attribute.rb +31 -0
- data/lib/hydra_attribute/active_record.rb +7 -0
- data/lib/hydra_attribute/active_record/attribute_methods.rb +72 -0
- data/lib/hydra_attribute/active_record/attribute_methods/before_type_cast.rb +16 -0
- data/lib/hydra_attribute/active_record/attribute_methods/read.rb +13 -0
- data/lib/hydra_attribute/active_record/relation.rb +44 -0
- data/lib/hydra_attribute/active_record/relation/query_methods.rb +162 -0
- data/lib/hydra_attribute/active_record/scoping.rb +15 -0
- data/lib/hydra_attribute/association_builder.rb +40 -0
- data/lib/hydra_attribute/attribute_builder.rb +57 -0
- data/lib/hydra_attribute/attribute_proxy.rb +16 -0
- data/lib/hydra_attribute/builder.rb +25 -0
- data/lib/hydra_attribute/configuration.rb +47 -0
- data/lib/hydra_attribute/migration.rb +27 -0
- data/lib/hydra_attribute/railtie.rb +9 -0
- data/lib/hydra_attribute/version.rb +3 -0
- data/spec/hydra_attribute/active_record/relation/query_methods_spec.rb +286 -0
- data/spec/hydra_attribute/active_record/relation_spec.rb +93 -0
- data/spec/hydra_attribute/active_record/scoping_spec.rb +19 -0
- data/spec/hydra_attribute/active_record_spec.rb +20 -0
- data/spec/hydra_attribute/association_builder_spec.rb +95 -0
- data/spec/hydra_attribute/attribute_builder_spec.rb +70 -0
- data/spec/hydra_attribute/attribute_helpers_spec.rb +70 -0
- data/spec/hydra_attribute/builder_spec.rb +39 -0
- data/spec/hydra_attribute/configuration_spec.rb +96 -0
- data/spec/hydra_attribute_spec.rb +20 -0
- data/spec/spec_helper.rb +17 -0
- 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,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,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,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,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,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
|