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,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