active_any 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
@@ -54,8 +54,73 @@ module ActiveAny
54
54
  @primary_key ||= options[:primary_key] || primary_key_for_record_class
55
55
  end
56
56
 
57
+ def scope_for(klass)
58
+ scope ? klass.unscoped.instance_exec(nil, &scope) : klass.unscoped
59
+ end
60
+
61
+ def check_preloadable!
62
+ return unless scope
63
+
64
+ if scope.arity.positive?
65
+ raise ArgumentError, <<-MSG.squish
66
+ The association scope '#{name}' is instance dependent (the scope
67
+ block takes an argument). Preloading instance dependent scopes is
68
+ not supported.
69
+ MSG
70
+ end
71
+ end
72
+ alias check_eager_loadable! check_preloadable!
73
+
74
+ def inverse_of
75
+ return unless inverse_name
76
+
77
+ @inverse_of ||= klass._reflect_on_association inverse_name
78
+ end
79
+
57
80
  private
58
81
 
82
+ def inverse_name
83
+ options.fetch(:inverse_of) do
84
+ @automatic_inverse_of ||= automatic_inverse_of
85
+ end
86
+ end
87
+
88
+ def automatic_inverse_of
89
+ if can_find_inverse_of_automatically?(self)
90
+ inverse_name = ActiveSupport::Inflector.underscore(options[:as] || active_record.name.demodulize).to_sym
91
+
92
+ begin
93
+ reflection = klass._reflect_on_association(inverse_name)
94
+ rescue NameError
95
+ # Give up: we couldn't compute the klass type so we won't be able
96
+ # to find any associations either.
97
+ reflection = false
98
+ end
99
+
100
+ if valid_inverse_reflection?(reflection)
101
+ return inverse_name
102
+ end
103
+ end
104
+
105
+ false
106
+ end
107
+
108
+ VALID_AUTOMATIC_INVERSE_MACROS = %i[has_many has_one belongs_to].freeze
109
+ INVALID_AUTOMATIC_INVERSE_OPTIONS = %i[foreign_key].freeze
110
+
111
+ def can_find_inverse_of_automatically?(reflection)
112
+ reflection.options[:inverse_of] != false &&
113
+ VALID_AUTOMATIC_INVERSE_MACROS.include?(reflection.macro) &&
114
+ INVALID_AUTOMATIC_INVERSE_OPTIONS.none? { |opt| reflection.options[opt] } &&
115
+ !reflection.scope
116
+ end
117
+
118
+ def valid_inverse_reflection?(reflection)
119
+ reflection &&
120
+ klass.name == reflection.record_class.name &&
121
+ can_find_inverse_of_automatically?(reflection)
122
+ end
123
+
59
124
  def join_pk
60
125
  raise NotImplementedError
61
126
  end
@@ -10,7 +10,9 @@ module ActiveAny
10
10
 
11
11
  included do
12
12
  class_attribute :reflections, instance_writer: false
13
+ class_attribute :_reflections, instance_writer: false
13
14
  self.reflections = {}
15
+ self._reflections = {}
14
16
  end
15
17
 
16
18
  class UnknownPrimaryKey < StandardError
@@ -45,5 +47,9 @@ module ActiveAny
45
47
  def self.add_reflection(klass, name, reflection)
46
48
  klass.reflections = klass.reflections.merge(name.to_s => reflection)
47
49
  end
50
+
51
+ def self._reflect_on_association(association)
52
+ _reflections[association.to_s]
53
+ end
48
54
  end
49
55
  end
@@ -0,0 +1,34 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveAny
4
+ class Relation
5
+ module FinderMethods
6
+ def find_by(condition)
7
+ where(condition).take
8
+ end
9
+
10
+ def first(limit = nil)
11
+ if loaded
12
+ limit ? records.first(limit) : records.first
13
+ else
14
+ limit ? spawn.records.first(limit) : spawn.records.first
15
+ end
16
+ end
17
+
18
+ def last(limit = nil)
19
+ return find_last(limit) if loaded? || limit_value
20
+
21
+ result = limit(limit)
22
+ result.order!(klass.primary_key) if order_clause.empty? && klass.primary_key
23
+ result = result.reverse_order!
24
+ limit ? result.reverse : result.first
25
+ end
26
+
27
+ private
28
+
29
+ def find_last(limit)
30
+ limit ? records.last(limit) : records.last
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,145 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveAny
4
+ class Relation
5
+ MULTI_VALUE_METHODS = %i[group includes join].freeze
6
+ SINGLE_VALUE_METHODS = %i[limit].freeze
7
+ CLAUSE_METHODS = %i[where order].freeze
8
+ VALUE_METHODS = (MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS).freeze
9
+
10
+ module QueryMethods # rubocop:disable Metrics/ModuleLength
11
+ extend ActiveSupport::Concern
12
+
13
+ Relation::VALUE_METHODS.each do |name|
14
+ method_name = \
15
+ case name
16
+ when *Relation::MULTI_VALUE_METHODS then "#{name}_values"
17
+ when *Relation::SINGLE_VALUE_METHODS then "#{name}_value"
18
+ when *Relation::CLAUSE_METHODS then "#{name}_clause"
19
+ end
20
+ class_eval <<-CODE, __FILE__, __LINE__ + 1
21
+ def #{method_name} # def includes_values
22
+ get_value(#{name.inspect}) # get_value(:includes)
23
+ end # end
24
+
25
+ def #{method_name}=(value) # def includes_values=(value)
26
+ set_value(#{name.inspect}, value) # set_value(:includes, value)
27
+ end # end
28
+ CODE
29
+ end
30
+
31
+ def get_value(name)
32
+ @values[name] || default_value_for(name)
33
+ end
34
+
35
+ def set_value(name, value)
36
+ assert_mutability!
37
+ @values[name] = value
38
+ end
39
+
40
+ def values
41
+ @values.dup
42
+ end
43
+
44
+ def limit(value)
45
+ spawn.limit!(value)
46
+ end
47
+
48
+ def where(condition)
49
+ spawn.where!(condition)
50
+ end
51
+
52
+ def take(limit = nil)
53
+ limit ? find_take_with_limit(limit) : find_take
54
+ end
55
+
56
+ def reverse_order
57
+ spawn.reverse_order!
58
+ end
59
+
60
+ def reverse_order!
61
+ self.order_clause = order_clause.reverse!
62
+ self
63
+ end
64
+
65
+ def group(*args)
66
+ spawn.group!(*args)
67
+ end
68
+
69
+ def order(*args)
70
+ spawn.order!(*args)
71
+ end
72
+
73
+ def limit!(value)
74
+ self.limit_value = value
75
+ self
76
+ end
77
+
78
+ def where!(condition)
79
+ self.where_clause += WhereClause.new(condition)
80
+ self
81
+ end
82
+
83
+ def group!(*args)
84
+ args.flatten!
85
+
86
+ self.group_values += args
87
+ self
88
+ end
89
+
90
+ def order!(*args)
91
+ args.flatten!
92
+
93
+ self.order_clause += OrderClause.new(args)
94
+ self
95
+ end
96
+
97
+ def includes(*args)
98
+ spawn.includes!(*args)
99
+ end
100
+
101
+ def includes!(*args)
102
+ args.reject!(&:blank?)
103
+ args.flatten!
104
+
105
+ self.includes_values |= args
106
+ self
107
+ end
108
+
109
+ private
110
+
111
+ def find_take
112
+ if loaded
113
+ records.first
114
+ else
115
+ limit(1).records.first
116
+ end
117
+ end
118
+
119
+ def find_take_with_limit(limit)
120
+ if loaded
121
+ records.take(limit)
122
+ else
123
+ limit(limit).to_a
124
+ end
125
+ end
126
+
127
+ FROZEN_EMPTY_ARRAY = [].freeze
128
+
129
+ def default_value_for(name)
130
+ case name
131
+ when :where then WhereClause.empty
132
+ when :order then OrderClause.empty
133
+ when *Relation::MULTI_VALUE_METHODS then FROZEN_EMPTY_ARRAY
134
+ when *Relation::SINGLE_VALUE_METHODS then nil
135
+ else
136
+ raise ArgumentError, "unknown relation value #{name.inspect}"
137
+ end
138
+ end
139
+
140
+ def assert_mutability!
141
+ raise ImmutableRelation if @loaded
142
+ end
143
+ end
144
+ end
145
+ end
@@ -1,42 +1,23 @@
1
1
  # frozen_string_literal: true
2
2
 
3
+ require 'active_any/relation/finder_methods'
4
+ require 'active_any/relation/query_methods'
3
5
  require 'active_any/relation/merger'
4
6
  require 'active_any/relation/where_clause'
5
7
  require 'active_any/relation/order_clause'
6
8
 
7
9
  module ActiveAny
8
- class Relation # rubocop:disable Metrics/ClassLength
10
+ class Relation
9
11
  attr_reader :loaded, :klass
10
12
 
11
13
  delegate :each, to: :records
12
14
 
13
15
  include Enumerable
16
+ include FinderMethods
17
+ include QueryMethods
14
18
 
15
19
  class ImmutableRelation < StandardError; end
16
20
 
17
- MULTI_VALUE_METHODS = %i[group].freeze
18
- SINGLE_VALUE_METHODS = %i[limit].freeze
19
- CLAUSE_METHODS = %i[where order].freeze
20
- VALUE_METHODS = (MULTI_VALUE_METHODS + SINGLE_VALUE_METHODS + CLAUSE_METHODS).freeze
21
-
22
- Relation::VALUE_METHODS.each do |name|
23
- method_name = \
24
- case name
25
- when *Relation::MULTI_VALUE_METHODS then "#{name}_values"
26
- when *Relation::SINGLE_VALUE_METHODS then "#{name}_value"
27
- when *Relation::CLAUSE_METHODS then "#{name}_clause"
28
- end
29
- class_eval <<-CODE, __FILE__, __LINE__ + 1
30
- def #{method_name} # def includes_values
31
- get_value(#{name.inspect}) # get_value(:includes)
32
- end # end
33
-
34
- def #{method_name}=(value) # def includes_values=(value)
35
- set_value(#{name.inspect}, value) # set_value(:includes, value)
36
- end # end
37
- CODE
38
- end
39
-
40
21
  def self.create(klass, *args)
41
22
  new(klass, *args)
42
23
  end
@@ -48,73 +29,10 @@ module ActiveAny
48
29
  @values = {}
49
30
  end
50
31
 
51
- def get_value(name)
52
- @values[name] || default_value_for(name)
53
- end
54
-
55
- def set_value(name, value)
56
- assert_mutability!
57
- @values[name] = value
58
- end
59
-
60
32
  def to_a
61
33
  records.dup
62
34
  end
63
35
 
64
- def limit(value)
65
- spawn.limit!(value)
66
- end
67
-
68
- def where(condition)
69
- spawn.where!(condition)
70
- end
71
-
72
- def find_by(condition)
73
- where(condition).take
74
- end
75
-
76
- def take(limit = nil)
77
- limit ? find_take_with_limit(limit) : find_take
78
- end
79
-
80
- def first(limit = nil)
81
- if loaded
82
- limit ? records.first(limit) : records.first
83
- else
84
- limit ? spawn.records.first(limit) : spawn.records.first
85
- end
86
- end
87
-
88
- def last(limit = nil)
89
- return find_last(limit) if loaded? || limit_value
90
-
91
- result = limit(limit)
92
- result.order!(klass.primary_key) if order_clause.empty? && klass.primary_key
93
- result = result.reverse_order!
94
- limit ? result.reverse : result.first
95
- end
96
-
97
- def reverse_order
98
- spawn.reverse_order!
99
- end
100
-
101
- def reverse_order!
102
- self.order_clause = order_clause.reverse!
103
- self
104
- end
105
-
106
- def find_last(limit)
107
- limit ? records.last(limit) : records.last
108
- end
109
-
110
- def group(*args)
111
- spawn.group!(*args)
112
- end
113
-
114
- def order(*args)
115
- spawn.order!(*args)
116
- end
117
-
118
36
  def merge(other)
119
37
  if other.is_a?(Array)
120
38
  records & other
@@ -137,32 +55,8 @@ module ActiveAny
137
55
  end
138
56
  end
139
57
 
140
- def limit!(value)
141
- self.limit_value = value
142
- self
143
- end
144
-
145
- def where!(condition)
146
- self.where_clause += WhereClause.new(condition)
147
- self
148
- end
149
-
150
- def group!(*args)
151
- args.flatten!
152
-
153
- self.group_values += args
154
- self
155
- end
156
-
157
- def order!(*args)
158
- args.flatten!
159
-
160
- self.order_clause += OrderClause.new(args)
161
- self
162
- end
163
-
164
58
  def records
165
- load_unless_loaded
59
+ load
166
60
  @records
167
61
  end
168
62
 
@@ -172,10 +66,6 @@ module ActiveAny
172
66
  super
173
67
  end
174
68
 
175
- def values
176
- @values.dup
177
- end
178
-
179
69
  def reset
180
70
  @loaded = nil
181
71
  @records = [].freeze
@@ -186,58 +76,52 @@ module ActiveAny
186
76
  @loaded
187
77
  end
188
78
 
189
- private
190
-
191
- def load_unless_loaded
79
+ def load
192
80
  exec_query unless loaded
193
81
  self
194
82
  end
195
83
 
196
- def spawn
197
- clone
84
+ def eager_loading?
85
+ false
198
86
  end
199
87
 
200
- def find_take_with_limit(limit)
201
- if loaded
202
- records.take(limit)
203
- else
204
- limit(limit).to_a
205
- end
206
- end
88
+ private
207
89
 
208
- def find_take
209
- if loaded
210
- records.first
211
- else
212
- limit(1).records.first
213
- end
90
+ def spawn
91
+ clone
214
92
  end
215
93
 
216
- def exec_query
217
- @records = klass.find_by_query(
94
+ def clauses
95
+ {
218
96
  where_clause: where_clause,
219
97
  limit_value: limit_value,
220
98
  group_values: group_values,
221
99
  order_clause: order_clause
222
- )
223
- @loaded = true
100
+ }
224
101
  end
225
102
 
226
- def assert_mutability!
227
- raise ImmutableRelation if @loaded
103
+ def exec_query
104
+ ActiveSupport::Notifications.instrument('exec_query.active_any', clauses: clauses, class_name: klass.name) do
105
+ @records = klass.find_by_query(clauses)
106
+ preload_records(@records)
107
+ @loaded = true
108
+ end
228
109
  end
229
110
 
230
- FROZEN_EMPTY_ARRAY = [].freeze
231
-
232
- def default_value_for(name)
233
- case name
234
- when :where then WhereClause.empty
235
- when :order then OrderClause.empty
236
- when *Relation::MULTI_VALUE_METHODS then FROZEN_EMPTY_ARRAY
237
- when *Relation::SINGLE_VALUE_METHODS then nil
238
- else
239
- raise ArgumentError, "unknown relation value #{name.inspect}"
111
+ def preload_records(records)
112
+ # TODO: implement preload
113
+ # preload = preload_values
114
+ preload = []
115
+ preload += includes_values unless eager_loading?
116
+ preloader = nil
117
+ preload.each do |associations|
118
+ preloader ||= build_preloader
119
+ preloader.preload records, associations
240
120
  end
241
121
  end
122
+
123
+ def build_preloader
124
+ ActiveAny::Associations::Preloader.new
125
+ end
242
126
  end
243
127
  end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ module ActiveAny
4
+ class Subscriber < ActiveSupport::LogSubscriber
5
+ attach_to :active_any
6
+
7
+ def exec_query(event)
8
+ class_name = event.payload[:class_name]
9
+ clauses = event.payload[:clauses]
10
+
11
+ debug "#{class_name} exec_query(clauses: #{clauses})"
12
+ end
13
+
14
+ private
15
+
16
+ def logger
17
+ super || ActiveSupport::LogSubscriber.logger = ActiveAny.config.logger
18
+ end
19
+ end
20
+ end
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module ActiveAny
4
- VERSION = '0.0.1'
4
+ VERSION = '0.0.2'
5
5
  end
data/lib/active_any.rb CHANGED
@@ -4,6 +4,8 @@ require 'active_support'
4
4
  require 'active_support/core_ext'
5
5
 
6
6
  require 'active_any/version'
7
+ require 'active_any/attribute'
8
+ require 'active_any/configuration'
7
9
  require 'active_any/relation'
8
10
  require 'active_any/reflection'
9
11
  require 'active_any/associations'
@@ -11,52 +13,18 @@ require 'active_any/association_relation'
11
13
  require 'active_any/attribute_assignment'
12
14
  require 'active_any/finders'
13
15
  require 'active_any/core'
16
+ require 'active_any/base'
17
+ require 'active_any/subscriber'
14
18
  require 'active_any/adapter'
15
19
  require 'active_any/adapters/abstract_adapter'
16
20
  require 'active_any/adapters/object_adapter'
17
21
 
18
22
  module ActiveAny
19
- class Base
20
- include Core
21
- include Associations
22
- include AttributeAssignment
23
- include Finders
24
- include Reflection
23
+ def self.configure
24
+ yield config
25
25
  end
26
26
 
27
- class Object < Base
28
- def self.inherited(child)
29
- child.abstract_class = false
30
- end
31
-
32
- class << self
33
- attr_accessor :data
34
-
35
- def adapter
36
- @adapter ||= ObjectAdapter.new(self)
37
- end
38
- end
39
- end
40
-
41
- class Hash < Base
42
- def self.inherited(child)
43
- child.abstract_class = false
44
- end
45
-
46
- class << self
47
- attr_reader :data
48
-
49
- def data=(data)
50
- data.map(&:keys).flatten.each do |method|
51
- attr_accessor method
52
- end
53
-
54
- @data = data.map { |d| new(d) }
55
- end
56
-
57
- def adapter
58
- @adapter ||= ObjectAdapter.new(self)
59
- end
60
- end
27
+ def self.config
28
+ @config ||= Configuration.new
61
29
  end
62
30
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: active_any
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  platform: ruby
6
6
  authors:
7
7
  - yuemori
8
8
  autorequire:
9
9
  bindir: exe
10
10
  cert_chain: []
11
- date: 2017-07-24 00:00:00.000000000 Z
11
+ date: 2017-07-25 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: activesupport
@@ -212,7 +212,14 @@ files:
212
212
  - lib/active_any/associations/builder/has_many.rb
213
213
  - lib/active_any/associations/collection_proxy.rb
214
214
  - lib/active_any/associations/has_many_association.rb
215
+ - lib/active_any/associations/preloader.rb
216
+ - lib/active_any/associations/preloader/association.rb
217
+ - lib/active_any/associations/preloader/belongs_to.rb
218
+ - lib/active_any/associations/preloader/has_many.rb
219
+ - lib/active_any/attribute.rb
215
220
  - lib/active_any/attribute_assignment.rb
221
+ - lib/active_any/base.rb
222
+ - lib/active_any/configuration.rb
216
223
  - lib/active_any/core.rb
217
224
  - lib/active_any/finders.rb
218
225
  - lib/active_any/reflection.rb
@@ -220,9 +227,12 @@ files:
220
227
  - lib/active_any/reflection/belongs_to_reflection.rb
221
228
  - lib/active_any/reflection/has_many_reflection.rb
222
229
  - lib/active_any/relation.rb
230
+ - lib/active_any/relation/finder_methods.rb
223
231
  - lib/active_any/relation/merger.rb
224
232
  - lib/active_any/relation/order_clause.rb
233
+ - lib/active_any/relation/query_methods.rb
225
234
  - lib/active_any/relation/where_clause.rb
235
+ - lib/active_any/subscriber.rb
226
236
  - lib/active_any/version.rb
227
237
  homepage: https://github.com/yuemori/active_any
228
238
  licenses: