active_any 0.0.1 → 0.0.2

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.
@@ -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: