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,162 @@
1
+ module HydraAttribute
2
+ module ActiveRecord
3
+ module Relation
4
+ module QueryMethods
5
+ extend ActiveSupport::Concern
6
+
7
+ MULTI_VALUE_METHODS = [:hydra_joins_aliases, :hydra_select_values]
8
+
9
+ included do
10
+ attr_writer *MULTI_VALUE_METHODS
11
+
12
+ MULTI_VALUE_METHODS.each do |value|
13
+ class_eval <<-EOS, __FILE__, __LINE__ + 1
14
+ def #{value}; @#{value} ||= [] end
15
+ EOS
16
+ end
17
+
18
+ alias_method_chain :where, :hydra_attribute
19
+ end
20
+
21
+ def where_with_hydra_attribute(opts, *rest)
22
+ return self if opts.blank?
23
+
24
+ if opts.is_a?(Hash)
25
+ opts.inject(self) do |relation, (name, value)|
26
+ if klass.hydra_attribute_names.include?(name.to_s)
27
+ relation, name = relation.clone, name.to_s
28
+ relation.hydra_joins_aliases << hydra_ref_alias(name, value)
29
+ relation.joins_values += build_hydra_joins_values(name, value)
30
+ relation.where_values += build_where(build_hydra_where_options(name, value))
31
+ relation
32
+ else
33
+ relation.where_without_hydra_attribute(name => value)
34
+ end
35
+ end
36
+ else
37
+ where_without_hydra_attribute(opts, *rest)
38
+ end
39
+ end
40
+
41
+ def build_arel
42
+ @order_values = build_order_values_for_arel(@order_values.uniq.reject(&:blank?))
43
+
44
+ if instance_variable_defined?(:@reorder_value) and instance_variable_get(:@reorder_value).present? # for compatibility with 3.1.x
45
+ @reorder_value = build_order_values_for_arel(@reorder_value.uniq.reject(&:blank?))
46
+ end
47
+
48
+ @hydra_select_values, @select_values = @select_values.partition { |value| klass.hydra_attribute_names.include?(value.to_s) }
49
+ @hydra_select_values.map!(&:to_s)
50
+ @select_values.map!{ |value| hydra_attr_helper.prepend_table_name(value) }
51
+
52
+ # force add ID for preloading hydra attributes
53
+ if @hydra_select_values.any? && @select_values.none? { |v| hydra_attr_helper.attr_eq_column?(v, klass.primary_key) }
54
+ @select_values << hydra_attr_helper.prepend_table_name(klass.primary_key)
55
+ @id_for_hydra_attributes = true
56
+ end
57
+
58
+ super
59
+ end
60
+
61
+ class Helper
62
+ attr_reader :relation, :klass, :connection
63
+
64
+ def initialize(relation)
65
+ @relation, @klass, @connection = relation, relation.klass, relation.connection
66
+ end
67
+
68
+ def attr_eq_column?(attr, column)
69
+ attr, column = attr.to_s, column.to_s
70
+ [
71
+ column,
72
+ "#{klass.table_name}.#{column}",
73
+ "#{klass.table_name}.#{connection.quote_column_name(column)}",
74
+ "#{klass.quoted_table_name}.#{column}",
75
+ "#{klass.quoted_table_name}.#{connection.quote_column_name(column)}"
76
+ ].include?(attr)
77
+ end
78
+
79
+ def prepend_table_name(column)
80
+ case column
81
+ when Symbol, String
82
+ if column.to_s.include?('.') or column.to_s.include?(')')
83
+ column
84
+ else
85
+ klass.quoted_table_name + '.' + connection.quote_column_name(column.to_s)
86
+ end
87
+ else
88
+ column
89
+ end
90
+ end
91
+ end
92
+
93
+ private
94
+
95
+ def hydra_attr_helper
96
+ @hydra_attr_helper ||= Helper.new(self)
97
+ end
98
+
99
+ def build_order_values_for_arel(collection)
100
+ collection.map do |attribute|
101
+ attribute = attribute.respond_to?(:to_sql) ? attribute.to_sql : attribute.to_s
102
+ if klass.hydra_attribute_names.include?(attribute)
103
+ join_alias = hydra_ref_alias(attribute, 'inner') # alias for inner join
104
+ join_alias = hydra_ref_alias(attribute, nil) unless hydra_joins_aliases.include?(join_alias) # alias for left join
105
+
106
+ @joins_values += build_hydra_joins_values(attribute, nil) unless hydra_joins_aliases.include?(join_alias)
107
+ klass.connection.quote_table_name(join_alias) + '.' + klass.connection.quote_column_name('value')
108
+ else
109
+ hydra_attr_helper.prepend_table_name(attribute)
110
+ end
111
+ end
112
+ end
113
+
114
+ def build_hydra_joins_values(name, value)
115
+ ref_alias = hydra_ref_alias(name, value)
116
+ conn = klass.connection
117
+ quoted_ref_alias = conn.quote_table_name(ref_alias)
118
+
119
+ [[
120
+ "#{hydra_join_type(value)} JOIN",
121
+ conn.quote_table_name(hydra_ref_table(name)),
122
+ 'AS',
123
+ quoted_ref_alias,
124
+ 'ON',
125
+ "#{klass.quoted_table_name}.#{klass.quoted_primary_key}",
126
+ '=',
127
+ "#{quoted_ref_alias}.#{conn.quote_column_name(:entity_id)}",
128
+ 'AND',
129
+ "#{quoted_ref_alias}.#{conn.quote_column_name(:entity_type)}",
130
+ '=',
131
+ conn.quote(klass.base_class.name),
132
+ 'AND',
133
+ "#{quoted_ref_alias}.#{conn.quote_column_name(:name)}",
134
+ '=',
135
+ conn.quote(name)
136
+ ].join(' ')]
137
+ end
138
+
139
+ def build_hydra_where_options(name, value)
140
+ {hydra_ref_alias(name, value) => {value: value}}
141
+ end
142
+
143
+ def hydra_ref_class(name)
144
+ type = klass.hydra_attributes[name]
145
+ HydraAttribute.config.associated_model_name(type).constantize
146
+ end
147
+
148
+ def hydra_ref_table(name)
149
+ hydra_ref_class(name).table_name
150
+ end
151
+
152
+ def hydra_ref_alias(name, value)
153
+ hydra_ref_table(name) + '_' + hydra_join_type(value).downcase + '_' + name
154
+ end
155
+
156
+ def hydra_join_type(value)
157
+ value.nil? ? 'LEFT' : 'INNER'
158
+ end
159
+ end
160
+ end
161
+ end
162
+ end
@@ -0,0 +1,15 @@
1
+ module HydraAttribute
2
+ module ActiveRecord
3
+ module Scoping
4
+ extend ActiveSupport::Concern
5
+
6
+ module ClassMethods
7
+ def scoped(options = nil)
8
+ relation = super(options)
9
+ relation.singleton_class.send(:include, Relation) unless relation.is_a?(Relation)
10
+ relation
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,40 @@
1
+ module HydraAttribute
2
+ class AssociationBuilder
3
+
4
+ def initialize(klass, type)
5
+ @klass, @type = klass, type
6
+ end
7
+
8
+ def build
9
+ create_associated_model
10
+ add_association_for_class
11
+ end
12
+
13
+ private
14
+
15
+ def create_associated_model
16
+ const = config.associated_const_name(@type)
17
+ unless namespace.const_defined?(const)
18
+ klass = namespace.const_set(const, Class.new(::ActiveRecord::Base))
19
+ klass.table_name = config.table_name(@type)
20
+ klass.attr_accessible :name, :value
21
+ klass.belongs_to :entity, polymorphic: true, touch: true, autosave: true
22
+ end
23
+ end
24
+
25
+ def add_association_for_class
26
+ assoc = config.association(@type)
27
+ unless @klass.reflect_on_association(assoc)
28
+ @klass.has_many assoc, as: :entity, class_name: config.associated_model_name(@type), autosave: true, dependent: :destroy
29
+ end
30
+ end
31
+
32
+ def config
33
+ HydraAttribute.config
34
+ end
35
+
36
+ def namespace
37
+ config.use_module_for_associated_models ? HydraAttribute : Object
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,57 @@
1
+ module HydraAttribute
2
+ class AttributeBuilder
3
+ NAME_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?=]?\z/
4
+ CALL_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?]?\z/
5
+
6
+ attr_reader :klass, :name, :type
7
+
8
+ def initialize(klass, name, type)
9
+ @klass, @name, @type = klass, name.to_s, type
10
+ end
11
+
12
+ def build
13
+ define_attribute_methods
14
+ save_attribute
15
+ end
16
+
17
+ private
18
+
19
+ def define_attribute_methods
20
+ m = Module.new
21
+ @klass.attribute_method_matchers.each do |matcher|
22
+ current = matcher.method_name(name)
23
+ target = matcher.method_name(:value)
24
+
25
+ if current =~ NAME_COMPILABLE_REGEXP
26
+ defn = "def #{current}(*args)"
27
+ else
28
+ defn = "define_method(:'#{current}') do |*args|"
29
+ end
30
+
31
+ if target =~ CALL_COMPILABLE_REGEXP
32
+ send = "#{target}(*args)"
33
+ else
34
+ send = "send(:'#{target}', *args)"
35
+ end
36
+
37
+ body = "hydra_attribute_model('#{name}', :#{type}).#{send}"
38
+ if current.end_with?('=')
39
+ body = "v = #{body}; @hydra_attribute_names << '#{name}' unless @hydra_attribute_names.include?('#{name}'); v"
40
+ else
41
+ body.insert(0, "missing_attribute('#{name}', caller) unless @hydra_attribute_names.include?('#{name}'); ")
42
+ end
43
+
44
+ m.class_eval <<-EOS, __FILE__, __LINE__ + 1
45
+ #{defn}
46
+ #{body}
47
+ end
48
+ EOS
49
+ end
50
+ klass.send :include, m
51
+ end
52
+
53
+ def save_attribute
54
+ klass.instance_variable_get(:@hydra_attributes)[name] = type
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,16 @@
1
+ module HydraAttribute
2
+ module AttributeProxy
3
+ def use_proxy_to_hydra_attribute(symbol)
4
+ module_eval <<-EOS, __FILE__, __LINE__ + 1
5
+ def #{symbol}(attr_name)
6
+ if self.class.hydra_attribute_names.include?(attr_name)
7
+ type = self.class.hydra_attributes[attr_name]
8
+ hydra_attribute_model(attr_name, type).#{symbol}('value')
9
+ else
10
+ super
11
+ end
12
+ end
13
+ EOS
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,25 @@
1
+ module HydraAttribute
2
+ class Builder
3
+ attr_reader :klass
4
+
5
+ def initialize(klass)
6
+ @klass = klass
7
+ @klass.class_eval do
8
+ include ActiveRecord::Scoping
9
+ include ActiveRecord::AttributeMethods
10
+ end
11
+ end
12
+
13
+ SUPPORT_TYPES.each do |type|
14
+ class_eval <<-EOS, __FILE__, __LINE__ + 1
15
+ def #{type}(*attributes)
16
+ AssociationBuilder.new(klass, :#{type}).build
17
+
18
+ attributes.each do |attribute|
19
+ AttributeBuilder.new(klass, attribute, :#{type}).build
20
+ end
21
+ end
22
+ EOS
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,47 @@
1
+ module HydraAttribute
2
+ class Configuration
3
+ def self.add_setting(name, default_value)
4
+ attr_writer name
5
+
6
+ define_method name do
7
+ instance_variable_set("@#{name}", default_value) unless instance_variable_defined?("@#{name}")
8
+ instance_variable_get("@#{name}")
9
+ end
10
+
11
+ define_method "#{name}?" do
12
+ send(name).present?
13
+ end
14
+ end
15
+
16
+ add_setting :table_prefix, 'hydra_'
17
+ add_setting :association_prefix, 'hydra_'
18
+ add_setting :use_module_for_associated_models, true
19
+
20
+ def table_name(type)
21
+ "#{table_prefix}#{type}_attributes".to_sym
22
+ end
23
+
24
+ def association(type)
25
+ "#{association_prefix}#{type}_attributes".to_sym
26
+ end
27
+
28
+ # Return string for compatibility with ActiveRecord 3.1.x
29
+ def associated_model_name(type)
30
+ klass = associated_const_name(type).to_s
31
+ klass = "HydraAttribute::#{klass}" if use_module_for_associated_models?
32
+ klass
33
+ end
34
+
35
+ def associated_const_name(type)
36
+ "#{type.to_s.titlecase}Attribute".to_sym
37
+ end
38
+
39
+ def relation_execute_method
40
+ if ::ActiveRecord::VERSION::MINOR > 1
41
+ :exec_queries
42
+ else
43
+ :to_a
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,27 @@
1
+ module HydraAttribute
2
+ class Migration
3
+ def initialize(migration)
4
+ @migration = migration
5
+ end
6
+
7
+ def migrate
8
+ SUPPORT_TYPES.each do |type|
9
+ table_name = HydraAttribute.config.table_name(type)
10
+ @migration.create_table table_name do |t|
11
+ t.integer :entity_id
12
+ t.string :entity_type
13
+ t.string :name
14
+ t.send type, :value
15
+ end
16
+
17
+ @migration.add_index table_name, [:entity_id, :entity_type, :name], unique: true, name: "index_#{table_name}_on_attribute"
18
+ end
19
+ end
20
+
21
+ def rollback
22
+ SUPPORT_TYPES.each do |type|
23
+ @migration.drop_table HydraAttribute.config.table_name(type)
24
+ end
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,9 @@
1
+ module HydraAttribute
2
+ class Railtie < Rails::Railtie
3
+ initializer 'hydra_attribute.active_record' do
4
+ ActiveSupport.on_load :active_record do
5
+ extend ::HydraAttribute::ActiveRecord
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module HydraAttribute
2
+ VERSION = '0.1.0'
3
+ end
@@ -0,0 +1,286 @@
1
+ require 'spec_helper'
2
+
3
+ describe HydraAttribute::ActiveRecord::Relation::QueryMethods do
4
+ let(:relation_class) do
5
+ mock(
6
+ :hydra_attributes => {'code' => :string},
7
+ :hydra_attribute_names => ['code'],
8
+ :hydra_attribute_types => [:string]
9
+ )
10
+ end
11
+
12
+ let(:relation) { mock(klass: relation_class, where: nil) }
13
+
14
+ before do
15
+ relation.singleton_class.send :include, ::ActiveRecord::QueryMethods
16
+ relation.singleton_class.send :include, HydraAttribute::ActiveRecord::Relation::QueryMethods
17
+ end
18
+
19
+ describe '#where' do
20
+ let(:relation) do
21
+ mock_relation = mock(klass: relation_class)
22
+ mock_relation.instance_variable_set(:@where_values, [])
23
+ mock_relation.instance_variable_set(:@joins_values, [])
24
+ mock_relation.stub(:build_hydra_joins_values) { |name, value| ["join-#{name}-#{value}"] }
25
+ mock_relation.stub(:build_hydra_where_options) { |name, value| ["where-#{name}-#{value}"] }
26
+ mock_relation.stub(:build_where) { |value, *rest| ["#{value} #{rest}"] }
27
+ mock_relation
28
+ end
29
+
30
+ before do
31
+ module HydraAttribute
32
+ class StringAttribute
33
+ def self.table_name
34
+ 'hydra_string_attributes'
35
+ end
36
+ end
37
+ end
38
+ end
39
+
40
+ after do
41
+ HydraAttribute.send :remove_const, :StringAttribute
42
+ end
43
+
44
+ describe 'first param is Hash' do
45
+ let(:params) { {'title' => 1} }
46
+
47
+ describe 'hash has not hydra attribute' do
48
+ it 'should call rails native "where" method' do
49
+ object = relation.where(params)
50
+ object.where_values.should == ['{"title"=>1} [[]]']
51
+ object.joins_values.should == []
52
+ object.hydra_joins_aliases.should == []
53
+ end
54
+ end
55
+
56
+ describe 'hash has hydra attribute' do
57
+ let(:params) { {'title' => 1, 'code' => 2, 'name' => 3} }
58
+
59
+ it 'should call both native and overwritten "where" method' do
60
+ copy_relation = relation.where(params)
61
+ copy_relation.where_values.should == ['{"title"=>1} [[]]', 'where-code-2 []', '{"name"=>3} [[]]']
62
+ copy_relation.joins_values.should == ['join-code-2']
63
+ copy_relation.hydra_joins_aliases.should == ['hydra_string_attributes_inner_code']
64
+ end
65
+ end
66
+ end
67
+
68
+ describe 'first param is not Hash' do
69
+ let(:params) { 'name = 1' }
70
+
71
+ it 'should call rails native "where" method' do
72
+ copy_relation = relation.where(params)
73
+ copy_relation.where_values.should == ['name = 1 [[]]']
74
+ copy_relation.joins_values.should == []
75
+ copy_relation.hydra_joins_aliases.should == []
76
+ end
77
+ end
78
+ end
79
+
80
+ describe '#build_arel' do
81
+ let(:arel) { mock.as_null_object }
82
+
83
+ let(:relation) do
84
+ mock_relation = mock(klass: relation_class, where: nil, build_select: nil)
85
+ mock_relation.stub_chain(:table, :from).and_return(arel)
86
+ mock_relation.instance_variable_set(:@joins_values, [])
87
+ mock_relation.instance_variable_set(:@order_values, [])
88
+ mock_relation.instance_variable_set(:@where_values, [])
89
+ mock_relation.instance_variable_set(:@having_values, [])
90
+ mock_relation.instance_variable_set(:@group_values, [])
91
+ mock_relation.instance_variable_set(:@select_values, [])
92
+ mock_relation
93
+ end
94
+
95
+ it 'should update @order_values before generate the arel object' do
96
+ relation.stub(build_order_values_for_arel: %w(build_order))
97
+ relation.build_arel.should == arel
98
+ relation.instance_variable_get(:@order_values).should == %w(build_order)
99
+ end
100
+ end
101
+
102
+ describe '#build_order_values_for_arel' do
103
+ let(:connection) do
104
+ conn = mock
105
+ conn.stub(:quote_column_name) { |column| column.to_s }
106
+ conn.stub(:quote) { |value| %Q("#{value.to_s}") }
107
+ conn.stub(:quote_table_name) { |table| table.to_s }
108
+ conn
109
+ end
110
+
111
+ let(:relation_class) do
112
+ mock(connection: connection, quoted_table_name: 'product', hydra_attribute_names: %w(code title price))
113
+ end
114
+
115
+ let(:relation) do
116
+ mock_relation = mock(klass: relation_class, connection: connection)
117
+ mock_relation.stub(where: mock_relation)
118
+ mock_relation.stub(:hydra_ref_alias) { |name, value| "#{name}_#{value}" }
119
+ mock_relation.stub(:build_hydra_joins_values) { |name, value| ["#{name}_#{value}_join"] }
120
+ mock_relation.instance_variable_set(:@joins_values, [])
121
+ mock_relation.instance_variable_set(:@hydra_joins_aliases, %w(code_inner title_))
122
+ mock_relation
123
+ end
124
+
125
+ describe 'collection has not hydra attributes' do
126
+ it 'should return the same collection' do
127
+ relation.send(:build_order_values_for_arel, %w(name zone)).should == %w(product.name product.zone)
128
+ relation.joins_values.should == []
129
+ end
130
+ end
131
+
132
+ describe 'collection has hydra attributes' do
133
+ it 'should change hydra attributes and join hydra tables' do
134
+ relation.send(:build_order_values_for_arel, %w(name code title price)).should == %w(product.name code_inner.value title_.value price_.value)
135
+ relation.joins_values.should == %w(price__join)
136
+ end
137
+ end
138
+ end
139
+
140
+ describe '#build_hydra_joins_values' do
141
+ let(:connection) do
142
+ conn = mock
143
+ conn.stub(:quote_column_name) { |column| column.to_s }
144
+ conn.stub(:quote) { |value| %Q("#{value.to_s}") }
145
+ conn.stub(:quote_table_name) { |table| table.to_s }
146
+ conn
147
+ end
148
+
149
+ let(:relation_class) do
150
+ mock(
151
+ :connection => connection,
152
+ :base_class => mock(name: 'BaseClass'),
153
+ :quoted_primary_key => 'id',
154
+ :quoted_table_name => 'hydra_string_attributes'
155
+ )
156
+ end
157
+
158
+ let(:relation) do
159
+ mock_relation = mock(klass: relation_class)
160
+ mock_relation.stub(where: mock_relation)
161
+ mock_relation.stub(:hydra_ref_alias) { |name, value| "#{name}_#{value}" }
162
+ mock_relation.stub(:hydra_ref_table) { |name| "table_#{name}" }
163
+ mock_relation
164
+ end
165
+
166
+ describe 'value is nil' do
167
+ let(:value) { nil }
168
+ let(:sql) { 'LEFT JOIN table_name AS name_ ON hydra_string_attributes.id = name_.entity_id AND name_.entity_type = "BaseClass" AND name_.name = "name"' }
169
+
170
+ it 'should return array with one SQL query element' do
171
+ relation.send(:build_hydra_joins_values, :name, value).should == [sql]
172
+ end
173
+ end
174
+
175
+ describe 'value is not nil' do
176
+ let(:value) { 'value' }
177
+ let(:sql) { 'INNER JOIN table_name AS name_value ON hydra_string_attributes.id = name_value.entity_id AND name_value.entity_type = "BaseClass" AND name_value.name = "name"' }
178
+
179
+ it 'should return array with one SQL query element' do
180
+ relation.send(:build_hydra_joins_values, :name, value).should == [sql]
181
+ end
182
+ end
183
+ end
184
+
185
+ describe '#build_hydra_where_options' do
186
+ before do
187
+ module HydraAttribute
188
+ class StringAttribute
189
+ def self.table_name
190
+ 'hydra_string_attributes'
191
+ end
192
+ end
193
+ end
194
+ end
195
+
196
+ after { HydraAttribute.send :remove_const, :StringAttribute }
197
+
198
+ it 'should create where options with table namespace' do
199
+ relation.send(:build_hydra_where_options, 'code', 'abc').should == {'hydra_string_attributes_inner_code' => { value: 'abc' }}
200
+ end
201
+ end
202
+
203
+ describe '#hydra_ref_class' do
204
+ before do
205
+ module HydraAttribute
206
+ class StringAttribute
207
+ def self.table_name
208
+ 'hydra_string_attributes'
209
+ end
210
+ end
211
+ end
212
+ end
213
+
214
+ after { HydraAttribute.send :remove_const, :StringAttribute }
215
+
216
+ it 'should return class by attribute name' do
217
+ relation.send(:hydra_ref_class, 'code').should == HydraAttribute::StringAttribute
218
+ end
219
+ end
220
+
221
+ describe '#hydra_ref_table' do
222
+ before do
223
+ module HydraAttribute
224
+ class StringAttribute
225
+ def self.table_name
226
+ 'hydra_string_attributes'
227
+ end
228
+ end
229
+ end
230
+ end
231
+
232
+ after { HydraAttribute.send :remove_const, :StringAttribute }
233
+
234
+ it 'should return table name' do
235
+ relation.send(:hydra_ref_table, 'code').should == 'hydra_string_attributes'
236
+ end
237
+ end
238
+
239
+ describe '#hydra_ref_alias' do
240
+ before do
241
+ module HydraAttribute
242
+ class StringAttribute
243
+ def self.table_name
244
+ 'hydra_string_attributes'
245
+ end
246
+ end
247
+ end
248
+ end
249
+
250
+ after { HydraAttribute.send :remove_const, :StringAttribute }
251
+
252
+ describe 'value is nil' do
253
+ let(:value) { nil }
254
+
255
+ it 'should return generated alias name' do
256
+ relation.send(:hydra_ref_alias, 'code', value).should == 'hydra_string_attributes_left_code'
257
+ end
258
+ end
259
+
260
+ describe 'value is not nil' do
261
+ let(:value) { '' }
262
+
263
+ it 'should return generated alias name' do
264
+ relation.send(:hydra_ref_alias, 'code', value).should == 'hydra_string_attributes_inner_code'
265
+ end
266
+ end
267
+ end
268
+
269
+ describe '#hydra_join_type' do
270
+ describe 'value is nil' do
271
+ let(:value) { nil }
272
+
273
+ it 'should return "LEFT"' do
274
+ relation.send(:hydra_join_type, value).should == 'LEFT'
275
+ end
276
+ end
277
+
278
+ describe 'value is not nil' do
279
+ let(:value) { '' }
280
+
281
+ it 'should return "INNER"' do
282
+ relation.send(:hydra_join_type, value).should == 'INNER'
283
+ end
284
+ end
285
+ end
286
+ end