active_record-framing 0.1.0.pre.9 → 0.1.0.pre.10
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.
- checksums.yaml +4 -4
- data/lib/active_record/framing/compat.rb +14 -0
- data/lib/active_record/framing/compat/active_record_4_2.rb +17 -11
- data/lib/active_record/framing/compat/active_record_5_0.rb +17 -10
- data/lib/active_record/framing/compat/active_record_5_1.rb +16 -10
- data/lib/active_record/framing/compat/active_record_6_0.rb +23 -0
- data/lib/active_record/framing/compat/arel.rb +24 -0
- data/lib/active_record/framing/core_extension.rb +19 -0
- data/lib/active_record/framing/default.rb +36 -5
- data/lib/active_record/framing/join_dependency.rb +26 -0
- data/lib/active_record/framing/model_proxy.rb +53 -0
- data/lib/active_record/framing/named.rb +44 -44
- data/lib/active_record/framing/query_methods.rb +4 -21
- data/lib/active_record/framing/railtie.rb +6 -0
- data/lib/active_record/framing/reflection.rb +36 -0
- data/lib/active_record/framing/relation.rb +50 -10
- data/lib/active_record/framing/version.rb +1 -1
- metadata +30 -4
- data/lib/active_record/framing.rb +0 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 3470c754f26d19c299b29aed737299fc49be9e8b32ae738b241bf4f857f15aae
|
4
|
+
data.tar.gz: 347bc600be62caca146be5062c6244d3d91c6425d12576b6dab0908d7f5e6716
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 59105768fd8a95a7dc826ca0a8708f05fac62e060a4556703ea6721b4ed8b4dbde6273faafa5e808757499c35f8dcd2825a745e29a768612c408a3bc3760374c
|
7
|
+
data.tar.gz: db85b4f5bc35430dfa888402ad5e3a3973efa8f36a2182f3b1781a5f8efa772febb2ea10a26c820c97be477ad4318532f74ede6459c76ad50f2656543b93df55
|
@@ -0,0 +1,14 @@
|
|
1
|
+
require 'active_record/framing/compat/arel'
|
2
|
+
|
3
|
+
case ::ActiveRecord.version
|
4
|
+
when Gem::Requirement.new('~> 4.2') # 4.2.x
|
5
|
+
require 'active_record/framing/compat/active_record_4_2'
|
6
|
+
when Gem::Requirement.new('~> 5.0.0') # 5.0.x
|
7
|
+
require 'active_record/framing/compat/active_record_5_0'
|
8
|
+
when Gem::Requirement.new('~> 5.0') # 5.1+
|
9
|
+
require 'active_record/framing/compat/active_record_5_1'
|
10
|
+
when Gem::Requirement.new('~> 6.0.0') # 6.0.x
|
11
|
+
require 'active_record/framing/compat/active_record_6_0'
|
12
|
+
else
|
13
|
+
raise NotImplementedError, "ActiveRecord::Framing does not support Rails #{::ActiveRecord.version}" unless ENV['EDGE_TESTING']
|
14
|
+
end
|
@@ -1,13 +1,19 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
1
|
+
module ActiveRecord
|
2
|
+
module Framing
|
3
|
+
module QueryMethods
|
4
|
+
{
|
5
|
+
frames: {},
|
6
|
+
reframe: {}
|
7
|
+
}.each do |value_name, default_value|
|
8
|
+
define_method("#{value_name}_values") do
|
9
|
+
@values[value_name] || default_value
|
10
|
+
end
|
11
|
+
define_method("#{value_name}_values=") do |values|
|
12
|
+
raise ImmutableRelation if @loaded
|
13
|
+
check_cached_relation
|
14
|
+
@values[value_name] = values
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
12
18
|
end
|
13
19
|
end
|
@@ -1,12 +1,19 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
1
|
+
module ActiveRecord
|
2
|
+
module Framing
|
3
|
+
module QueryMethods
|
4
|
+
{
|
5
|
+
frames: ::ActiveRecord::QueryMethods::FROZEN_EMPTY_HASH,
|
6
|
+
reframe: ::ActiveRecord::QueryMethods::FROZEN_EMPTY_HASH
|
7
|
+
}.each do |value_name, default_value|
|
8
|
+
define_method("#{value_name}_values") do
|
9
|
+
@values[value_name] || default_value
|
10
|
+
end
|
11
|
+
define_method("#{value_name}_values=") do |values|
|
12
|
+
assert_mutability!
|
13
|
+
@values[value_name] = values
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
11
17
|
end
|
12
18
|
end
|
19
|
+
|
@@ -1,12 +1,18 @@
|
|
1
|
-
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
1
|
+
module ActiveRecord
|
2
|
+
module Framing
|
3
|
+
module QueryMethods
|
4
|
+
{
|
5
|
+
frames: ::ActiveRecord::QueryMethods::FROZEN_EMPTY_HASH,
|
6
|
+
reframe: ::ActiveRecord::QueryMethods::FROZEN_EMPTY_HASH
|
7
|
+
}.each do |value_name, default_value|
|
8
|
+
define_method("#{value_name}_values") do
|
9
|
+
get_value(value_name)
|
10
|
+
end
|
11
|
+
define_method("#{value_name}_values=") do |value|
|
12
|
+
set_value(value_name, value)
|
13
|
+
end
|
14
|
+
::ActiveRecord::QueryMethods::DEFAULT_VALUES[value_name] = default_value
|
15
|
+
end
|
16
|
+
end
|
7
17
|
end
|
8
|
-
define_method("#{value_name}_values=") do |value|
|
9
|
-
set_value(value_name, value)
|
10
|
-
end
|
11
|
-
::ActiveRecord::QueryMethods::DEFAULT_VALUES[value_name] = default_value
|
12
18
|
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Framing
|
3
|
+
module QueryMethods
|
4
|
+
{
|
5
|
+
frames: ::ActiveRecord::QueryMethods::FROZEN_EMPTY_HASH,
|
6
|
+
reframe: ::ActiveRecord::QueryMethods::FROZEN_EMPTY_HASH
|
7
|
+
}.each do |value_name, default_value|
|
8
|
+
::ActiveRecord::QueryMethods::DEFAULT_VALUES[value_name] = default_value
|
9
|
+
class_eval <<-CODE, __FILE__, __LINE__ + 1
|
10
|
+
def #{value_name}_values # def frames_values
|
11
|
+
default = ::ActiveRecord::QueryMethods::DEFAULT_VALUES[:#{value_name}] # default = DEFAULT_VALUES[:frames]
|
12
|
+
@values.fetch(:#{value_name}, default) # @values.fetch(:frames, default)
|
13
|
+
end # end
|
14
|
+
|
15
|
+
def #{value_name}_values=(value) # def frames_values=(value)
|
16
|
+
assert_mutability! # assert_mutability!
|
17
|
+
@values[:#{value_name}] = value # @values[:frames] = value
|
18
|
+
end # end
|
19
|
+
CODE
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
case Gem::Version.new(Arel::VERSION)
|
2
|
+
when Gem::Requirement.new('>= 7.0')
|
3
|
+
Arel::Table.class_eval do
|
4
|
+
def engine
|
5
|
+
type_caster.send(:types)
|
6
|
+
end
|
7
|
+
|
8
|
+
def engine=(value)
|
9
|
+
@type_caster = value.type_caster
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
Arel::Nodes::TableAlias.class_eval do
|
14
|
+
def engine
|
15
|
+
left.send(:type_caster)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
else
|
19
|
+
Arel::Table.class_eval do
|
20
|
+
def engine=(value)
|
21
|
+
@engine = value
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -10,6 +10,8 @@ module ActiveRecord
|
|
10
10
|
def self.prepended(subclass)
|
11
11
|
subclass.singleton_class.class_eval do
|
12
12
|
alias_method :unframed_all, :all
|
13
|
+
# NOTE: not for 4.2
|
14
|
+
alias_method :unframed_scope_for_association, :scope_for_association if subclass.respond_to?(:scope_for_association)
|
13
15
|
|
14
16
|
# Subclasses need to be able to call its parent's
|
15
17
|
protected :relation
|
@@ -32,6 +34,13 @@ module ActiveRecord
|
|
32
34
|
@disabled == true
|
33
35
|
end
|
34
36
|
|
37
|
+
class Foo
|
38
|
+
|
39
|
+
def self.whatever
|
40
|
+
@whatever
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
35
44
|
module ClassMethods # :nodoc:
|
36
45
|
def current_frame
|
37
46
|
FrameRegistry.value_for(:current_frame, self)
|
@@ -40,6 +49,16 @@ module ActiveRecord
|
|
40
49
|
def current_frame=(frame)
|
41
50
|
FrameRegistry.set_value_for(:current_frame, self, frame)
|
42
51
|
end
|
52
|
+
|
53
|
+
def const_missing(const_name)
|
54
|
+
case const_name
|
55
|
+
when *frames.keys
|
56
|
+
columns
|
57
|
+
frames[const_name].call
|
58
|
+
else
|
59
|
+
super
|
60
|
+
end
|
61
|
+
end
|
43
62
|
end
|
44
63
|
|
45
64
|
# This class stores the +:current_frame+ and +:ignore_default_frame+ values
|
@@ -49,7 +49,7 @@ module ActiveRecord
|
|
49
49
|
# The ignore_default_frame flag is used to prevent an infinite recursion
|
50
50
|
# situation where a default frame references a frame which has a default
|
51
51
|
# frame which references a frame...
|
52
|
-
def
|
52
|
+
def evaluate_default_frame
|
53
53
|
return if ignore_default_frame?
|
54
54
|
self.ignore_default_frame = true
|
55
55
|
yield
|
@@ -57,6 +57,13 @@ module ActiveRecord
|
|
57
57
|
self.ignore_default_frame = false
|
58
58
|
end
|
59
59
|
|
60
|
+
def default_arel_table
|
61
|
+
@default_arel_table ||= arel_table.dup.tap do |at|
|
62
|
+
at.name = sovereign_table_name
|
63
|
+
end
|
64
|
+
# @default_arel_table ||= arel_table.alias(sovereign_table_name)
|
65
|
+
end
|
66
|
+
|
60
67
|
protected
|
61
68
|
|
62
69
|
def build_frame(frames, arel_table, base_rel = nil, &block)
|
@@ -68,6 +75,7 @@ module ActiveRecord
|
|
68
75
|
end
|
69
76
|
|
70
77
|
relation.frame!(Arel::Nodes::As.new(arel_table, cte_relation.arel)).tap do |rel|
|
78
|
+
rel.table = arel_table
|
71
79
|
extension = Module.new(&block) if block_given?
|
72
80
|
rel.extending!(extension) if extension
|
73
81
|
end
|
@@ -120,7 +128,7 @@ module ActiveRecord
|
|
120
128
|
# # Should return a frame, you can call 'super' here etc.
|
121
129
|
# end
|
122
130
|
# end
|
123
|
-
def default_frame(
|
131
|
+
def default_frame(frame_name = nil, frame: nil) # :doc:
|
124
132
|
frame = Proc.new if block_given?
|
125
133
|
|
126
134
|
if frame.is_a?(Relation) || !frame.respond_to?(:call)
|
@@ -131,9 +139,31 @@ module ActiveRecord
|
|
131
139
|
"self.default_frame.)"
|
132
140
|
end
|
133
141
|
|
142
|
+
# TODO: Include default_scopes?
|
143
|
+
# frame(:unframed)
|
144
|
+
|
145
|
+
# default_arel_table.tap do |at|
|
146
|
+
# at.name = frame_name
|
147
|
+
# end if frame_name
|
148
|
+
|
134
149
|
self.default_frames += [frame]
|
135
150
|
end
|
136
151
|
|
152
|
+
def sovereign_table_name
|
153
|
+
# TODO: Use compute_table_name to disambiguate between users and admins
|
154
|
+
if superclass < ::ActiveRecord::Base && table_name == superclass.table_name
|
155
|
+
begin
|
156
|
+
orig, superclass.abstract_class = superclass.abstract_class, true
|
157
|
+
# return compute_table_name
|
158
|
+
return table_name
|
159
|
+
ensure
|
160
|
+
superclass.abstract_class = orig
|
161
|
+
end
|
162
|
+
end
|
163
|
+
# compute_table_name
|
164
|
+
table_name
|
165
|
+
end
|
166
|
+
|
137
167
|
def build_default_frame(base_rel = nil)
|
138
168
|
return if abstract_class?
|
139
169
|
|
@@ -143,14 +173,15 @@ module ActiveRecord
|
|
143
173
|
|
144
174
|
if default_frame_override
|
145
175
|
# The user has defined their own default frame method, so call that
|
146
|
-
|
176
|
+
evaluate_default_frame do
|
147
177
|
if (frame = default_frame)
|
148
178
|
(base_rel ||= relation).frame!(frame)
|
149
179
|
end
|
150
180
|
end
|
151
181
|
elsif default_frames.any?
|
152
|
-
|
153
|
-
cte_table = Arel::Table.new(table_name)
|
182
|
+
evaluate_default_frame do
|
183
|
+
# cte_table = Arel::Table.new(table_name)
|
184
|
+
cte_table = default_arel_table
|
154
185
|
build_frame(default_frames, cte_table, base_rel || relation)
|
155
186
|
end
|
156
187
|
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Framing
|
3
|
+
module JoinDependency
|
4
|
+
def table_aliases_for(parent, node)
|
5
|
+
super.tap do |list|
|
6
|
+
list.zip(node.reflection.chain).each do |aliaz, reflection|
|
7
|
+
engine = @join_root.base_klass.reframe_values.fetch(reflection.name) { reflection }.klass
|
8
|
+
fix_table_engine(aliaz, engine)
|
9
|
+
end
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def fix_table_engine(table, engine)
|
14
|
+
case table
|
15
|
+
when ::Arel::Nodes::TableAlias
|
16
|
+
table.left.engine = engine # if table.left.engine == ::ActiveRecord::Base
|
17
|
+
table.left.name = engine.table_name
|
18
|
+
else
|
19
|
+
table.engine = engine # if table.engine == ::ActiveRecord::Base
|
20
|
+
table.name = engine.table_name
|
21
|
+
end
|
22
|
+
# binding.pry if table.name == "users"
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,53 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Framing
|
3
|
+
class ModelProxy < SimpleDelegator
|
4
|
+
|
5
|
+
def initialize(klass, table)
|
6
|
+
@table = table
|
7
|
+
super(klass)
|
8
|
+
end
|
9
|
+
|
10
|
+
def unscoped
|
11
|
+
__getobj__.unscoped.tap do |rel|
|
12
|
+
rel.instance_variable_set(:@table, @table)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
# NOTE: Arel 8.x / Rails 5.1 compatibility
|
17
|
+
# Method exists on AR::Model, but default value for table
|
18
|
+
# is scoped incorrectly without this proxy override
|
19
|
+
def arel_attribute(column_name, table = arel_table)
|
20
|
+
super
|
21
|
+
end
|
22
|
+
|
23
|
+
def table=(value)
|
24
|
+
@table = value
|
25
|
+
end
|
26
|
+
|
27
|
+
def table_name
|
28
|
+
@table.name
|
29
|
+
end
|
30
|
+
|
31
|
+
def reframe_values
|
32
|
+
@reframe_values ||= {}
|
33
|
+
end
|
34
|
+
|
35
|
+
def reframe_values=(value)
|
36
|
+
@reframe_values = value
|
37
|
+
end
|
38
|
+
|
39
|
+
def send(*args)
|
40
|
+
__getobj__.send(*args)
|
41
|
+
end
|
42
|
+
|
43
|
+
def arel_table
|
44
|
+
@table
|
45
|
+
end
|
46
|
+
|
47
|
+
# Might not need this
|
48
|
+
def is_a?(obj)
|
49
|
+
__getobj__.is_a?(obj)
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
@@ -18,23 +18,34 @@ module ActiveRecord
|
|
18
18
|
# You can define a frame that applies to all finders using
|
19
19
|
# {default_frame}[rdoc-ref:Framing::Default::ClassMethods#default_frame].
|
20
20
|
def all
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
21
|
+
unframed_all.merge(current_frame || default_framed)
|
22
|
+
end
|
23
|
+
|
24
|
+
# def all
|
25
|
+
# current_frame = self.current_frame
|
26
|
+
|
27
|
+
# if current_frame
|
28
|
+
# if self == current_frame.klass
|
29
|
+
# current_frame.clone
|
30
|
+
# else
|
31
|
+
# unframed_all.merge!(current_frame)
|
32
|
+
# end
|
33
|
+
# else
|
34
|
+
# default_framed
|
35
|
+
# end
|
36
|
+
# end
|
37
|
+
|
38
|
+
def scope_for_association(scope = relation) # :nodoc:
|
39
|
+
unframed_scope_for_association(scope).merge(current_frame || default_framed)
|
31
40
|
end
|
32
41
|
|
33
|
-
|
34
|
-
|
35
|
-
!ignore_default_frame? && !ActiveRecord::Framing.disabled? && build_default_frame(frame) || frame
|
42
|
+
def default_framed(my_frame = relation)
|
43
|
+
build_default_frame(my_frame) || my_frame
|
36
44
|
end
|
37
45
|
|
46
|
+
def frames
|
47
|
+
@frames ||= {}
|
48
|
+
end
|
38
49
|
# Adds a class method for retrieving and querying objects.
|
39
50
|
# The method is intended to return an ActiveRecord::Relation
|
40
51
|
# object, which is composable with other frames.
|
@@ -136,50 +147,39 @@ module ActiveRecord
|
|
136
147
|
#
|
137
148
|
# Article.published.featured.latest_article
|
138
149
|
# Article.featured.titles
|
139
|
-
def frame(frame_name, body, &block)
|
140
|
-
|
141
|
-
|
150
|
+
def frame(frame_name, body = nil, &block)
|
151
|
+
constant = frame_name.to_s.classify.to_sym
|
152
|
+
|
153
|
+
if !body.respond_to?(:call)
|
154
|
+
# raise ArgumentError, "The frame body needs to be callable."
|
155
|
+
warn "You've assigned a frame (#{frame_name}) with no callable block." unless
|
156
|
+
AttributeMethods::BLACKLISTED_CLASS_CONSTS.include?(frame_name)
|
157
|
+
elsif dangerous_class_const?(constant)
|
158
|
+
raise ArgumentError, "You tried to define a frame named \"#{constant}\" " \
|
159
|
+
"on the model \"#{self.constant}\", but Active Record already defined " \
|
160
|
+
"a class method with the same name."
|
142
161
|
end
|
143
162
|
|
144
|
-
constant = frame_name.to_s.classify.to_sym
|
145
163
|
valid_frame_name?(constant)
|
146
164
|
|
147
165
|
arel_tn = "#{frame_name}/#{self.table_name}"
|
148
166
|
|
149
|
-
|
150
|
-
|
151
|
-
klass.table_name = superclass.table_name
|
152
|
-
|
153
|
-
def klass.discriminate_class_for_record(record)
|
154
|
-
superclass.send(:discriminate_class_for_record, record)
|
155
|
-
end
|
156
|
-
|
157
|
-
klass.default_frames = [body]
|
158
|
-
|
159
|
-
@current_frame_extension = block
|
160
|
-
|
161
|
-
def klass.current_frame
|
162
|
-
build_frame(default_frames, arel_table, superclass.relation, &@current_frame_extension)
|
163
|
-
end
|
164
|
-
|
165
|
-
@arel_table = superclass.arel_table.dup.tap do |at|
|
166
|
-
at.name = arel_tn
|
167
|
-
end
|
168
|
-
|
169
|
-
end)
|
170
|
-
|
171
|
-
if dangerous_class_const?(constant)
|
172
|
-
raise ArgumentError, "You tried to define a frame named \"#{constant}\" " \
|
173
|
-
"on the model \"#{self.constant}\", but Active Record already defined " \
|
174
|
-
"a class method with the same name."
|
167
|
+
at = arel_table.clone.tap do |a_t|
|
168
|
+
a_t.name = arel_tn
|
175
169
|
end
|
170
|
+
# at = Arel::Table.new(arel_tn, arel_table.engine)
|
171
|
+
# at = Arel::Table.new(arel_tn, self)
|
176
172
|
|
173
|
+
frames[constant] = Proc.new do
|
174
|
+
build_frame([body].compact, at, relation, &block)
|
175
|
+
end
|
177
176
|
end
|
178
177
|
|
179
178
|
private
|
180
179
|
|
181
180
|
def valid_frame_name?(name)
|
182
|
-
if const_defined?(name) && logger
|
181
|
+
# if const_defined?(name) && logger
|
182
|
+
if frames.key?(name) && logger
|
183
183
|
logger.warn "Creating frame :#{name}. " \
|
184
184
|
"Overwriting existing const #{self.name}::#{name}."
|
185
185
|
end
|
@@ -4,29 +4,10 @@ module ActiveRecord
|
|
4
4
|
module Framing
|
5
5
|
module QueryMethods
|
6
6
|
|
7
|
-
|
8
|
-
|
9
|
-
elsif ::ActiveRecord.version >= Gem::Version.new("5.0") # 5.0+
|
10
|
-
require 'active_record/framing/compat/active_record_5_0'
|
11
|
-
elsif ::ActiveRecord.version >= Gem::Version.new("4.2") # 4.2+
|
12
|
-
require 'active_record/framing/compat/active_record_4_2'
|
13
|
-
else
|
14
|
-
raise NotImplementedError, "ActiveRecord::Framing does not support Rails #{::ActiveRecord.version}"
|
7
|
+
def frame(value)
|
8
|
+
spawn.frame!(value)
|
15
9
|
end
|
16
10
|
|
17
|
-
# def from!(value, subquery_name = nil) # :nodoc:
|
18
|
-
# super.tap do |rel|
|
19
|
-
# if value.is_a?(::ActiveRecord::Relation) && value.frames_values.any?
|
20
|
-
# self.frames_values = self.frames_values.merge(value.frames_values)
|
21
|
-
# value.frames_values.clear
|
22
|
-
# end
|
23
|
-
# end
|
24
|
-
# end
|
25
|
-
|
26
|
-
# def frame(value)
|
27
|
-
# spawn.frame!(value)
|
28
|
-
# end
|
29
|
-
|
30
11
|
def frame!(value)
|
31
12
|
value = value.all if (value.is_a?(Class) && value < ::ActiveRecord::Base)
|
32
13
|
|
@@ -39,6 +20,8 @@ module ActiveRecord
|
|
39
20
|
{value.left.name => value}
|
40
21
|
when String
|
41
22
|
{arel_table.name => value}
|
23
|
+
when Hash
|
24
|
+
value
|
42
25
|
else
|
43
26
|
{}
|
44
27
|
end
|
@@ -4,15 +4,21 @@ require 'active_record/framing/query_methods'
|
|
4
4
|
require 'active_record/framing/spawn_methods'
|
5
5
|
require 'active_record/framing/attribute_methods'
|
6
6
|
require 'active_record/framing/relation'
|
7
|
+
require 'active_record/framing/join_dependency'
|
8
|
+
require 'active_record/framing/reflection'
|
9
|
+
require 'active_record/framing/compat'
|
7
10
|
|
8
11
|
module ActiveRecord::Framing
|
9
12
|
class Railtie < Rails::Railtie
|
10
13
|
initializer 'active_record-framing.load' do |_app|
|
11
14
|
ActiveSupport.on_load(:active_record) do
|
12
15
|
::ActiveRecord::Base.prepend(ActiveRecord::Framing)
|
16
|
+
|
13
17
|
::ActiveRecord::Relation.prepend(ActiveRecord::Framing::Relation)
|
14
18
|
::ActiveRecord::Relation.prepend(ActiveRecord::Framing::QueryMethods)
|
15
19
|
::ActiveRecord::Relation.prepend(ActiveRecord::Framing::SpawnMethods)
|
20
|
+
::ActiveRecord::Associations::JoinDependency.prepend(ActiveRecord::Framing::JoinDependency)
|
21
|
+
|
16
22
|
end
|
17
23
|
end
|
18
24
|
end
|
@@ -0,0 +1,36 @@
|
|
1
|
+
module ActiveRecord
|
2
|
+
module Framing
|
3
|
+
module Reflection
|
4
|
+
|
5
|
+
def self.prepended(subclass)
|
6
|
+
class << subclass
|
7
|
+
prepend ClassMethods
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module ClassMethods
|
12
|
+
|
13
|
+
def add_reflection(ar, name, reflection)
|
14
|
+
super.tap do |reflections|
|
15
|
+
#
|
16
|
+
# puts "foo"
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
module ActiveRecord
|
25
|
+
module Framing
|
26
|
+
module MacroReflection
|
27
|
+
|
28
|
+
def self.prepended(subclass)
|
29
|
+
class << subclass
|
30
|
+
attr_reader :frame
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -1,9 +1,18 @@
|
|
1
|
+
require 'active_record/framing/model_proxy'
|
2
|
+
|
1
3
|
module ActiveRecord
|
2
4
|
# = Active Record \Relation
|
3
5
|
module Framing
|
4
6
|
module Relation
|
5
7
|
|
8
|
+
def initialize(*args)
|
9
|
+
super.tap do |something|
|
10
|
+
@klass = ModelProxy.new(@klass, arel_table) unless @klass.class == ModelProxy
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
6
14
|
def build_arel(*)
|
15
|
+
@klass.reframe_values = reframe_values
|
7
16
|
super.tap do |ar|
|
8
17
|
unless ignore_default_frame?
|
9
18
|
build_frames(ar)
|
@@ -12,8 +21,30 @@ module ActiveRecord
|
|
12
21
|
end
|
13
22
|
end
|
14
23
|
|
24
|
+
# Prevents a scope from re-including the default_scope
|
25
|
+
# e.g. User::All.all.to_sql yielding
|
26
|
+
# WITH "all/users" AS (SELECT "users".* FROM "users"), "users" AS (SELECT "users".* FROM "users" WHERE "users"."kind" = 1) SELECT "all/users".* FROM "all/users"
|
27
|
+
# vs.
|
28
|
+
# WITH "all/users" AS (SELECT "users".* FROM "users") SELECT "all/users".* FROM "all/users"
|
29
|
+
def all
|
30
|
+
spawn
|
31
|
+
end
|
32
|
+
|
33
|
+
def table=(value)
|
34
|
+
@table = value
|
35
|
+
@klass.table = value
|
36
|
+
end
|
37
|
+
|
38
|
+
def aggregate_column(column_name)
|
39
|
+
framing { super }
|
40
|
+
end
|
41
|
+
|
42
|
+
def build_joins(*)
|
43
|
+
framing { super }
|
44
|
+
end
|
45
|
+
|
15
46
|
# This is all very unfortunate (rails 4.2):
|
16
|
-
# ActiveRecord
|
47
|
+
# ActiveRecord—in it's infinite wisdom—has decided
|
17
48
|
# to create JoinDependency objects with arel_tables using a
|
18
49
|
# generic engine (ActiveRecord::Base) as opposed to that of
|
19
50
|
# the driving class. For example:
|
@@ -33,7 +64,7 @@ module ActiveRecord
|
|
33
64
|
# TODO: cache known associations, (renable warning)
|
34
65
|
assocs = klass.reflect_on_all_associations.inject(Hash.new) do |assocs, assoc|
|
35
66
|
begin
|
36
|
-
assocs[assoc.
|
67
|
+
assocs[assoc.name] = assoc
|
37
68
|
rescue NameError => e
|
38
69
|
# warn <<~WARN.chomp
|
39
70
|
# ActiveRecord::Framing was trying to inspect the association #{assoc.name}
|
@@ -47,19 +78,28 @@ module ActiveRecord
|
|
47
78
|
|
48
79
|
manager.join_sources.each do |join_source|
|
49
80
|
next unless join_source&.left&.respond_to?(:name)
|
50
|
-
|
51
|
-
|
81
|
+
table = join_source.left
|
82
|
+
if (join_class = derive_engine(table, assocs))
|
83
|
+
frame!(join_class)
|
84
|
+
end
|
85
|
+
end
|
52
86
|
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
merge!(source)
|
58
|
-
end
|
87
|
+
reframe_values.each do |assoc_name, relation|
|
88
|
+
relation.frames_values.values.each do |value|
|
89
|
+
original = frames_values.delete(value.left.engine.table_name)
|
90
|
+
frame!(value)
|
59
91
|
end
|
60
92
|
end
|
61
93
|
end
|
62
94
|
|
95
|
+
def derive_engine(table, associations)
|
96
|
+
if table.engine == ::ActiveRecord::Base
|
97
|
+
associations[table.name]
|
98
|
+
else
|
99
|
+
table.engine
|
100
|
+
end
|
101
|
+
end
|
102
|
+
|
63
103
|
# Frame all queries to the current frame.
|
64
104
|
#
|
65
105
|
# Comment.where(post_id: 1).framing do
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: active_record-framing
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.0.pre.
|
4
|
+
version: 0.1.0.pre.10
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Dale Stevens
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2019-
|
11
|
+
date: 2019-12-24 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: activerecord
|
@@ -17,6 +17,9 @@ dependencies:
|
|
17
17
|
- - ">="
|
18
18
|
- !ruby/object:Gem::Version
|
19
19
|
version: '4.2'
|
20
|
+
- - "<"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: '6.1'
|
20
23
|
type: :runtime
|
21
24
|
prerelease: false
|
22
25
|
version_requirements: !ruby/object:Gem::Requirement
|
@@ -24,6 +27,9 @@ dependencies:
|
|
24
27
|
- - ">="
|
25
28
|
- !ruby/object:Gem::Version
|
26
29
|
version: '4.2'
|
30
|
+
- - "<"
|
31
|
+
- !ruby/object:Gem::Version
|
32
|
+
version: '6.1'
|
27
33
|
- !ruby/object:Gem::Dependency
|
28
34
|
name: pg
|
29
35
|
requirement: !ruby/object:Gem::Requirement
|
@@ -136,6 +142,20 @@ dependencies:
|
|
136
142
|
- - ">="
|
137
143
|
- !ruby/object:Gem::Version
|
138
144
|
version: '0'
|
145
|
+
- !ruby/object:Gem::Dependency
|
146
|
+
name: colorize
|
147
|
+
requirement: !ruby/object:Gem::Requirement
|
148
|
+
requirements:
|
149
|
+
- - ">="
|
150
|
+
- !ruby/object:Gem::Version
|
151
|
+
version: '0'
|
152
|
+
type: :development
|
153
|
+
prerelease: false
|
154
|
+
version_requirements: !ruby/object:Gem::Requirement
|
155
|
+
requirements:
|
156
|
+
- - ">="
|
157
|
+
- !ruby/object:Gem::Version
|
158
|
+
version: '0'
|
139
159
|
description: Allows for larger level scoping (framing) that affect complicated queries
|
140
160
|
more holistically
|
141
161
|
email:
|
@@ -148,16 +168,21 @@ files:
|
|
148
168
|
- LICENSE
|
149
169
|
- README.md
|
150
170
|
- lib/active_record-framing.rb
|
151
|
-
- lib/active_record/framing.rb
|
152
171
|
- lib/active_record/framing/attribute_methods.rb
|
172
|
+
- lib/active_record/framing/compat.rb
|
153
173
|
- lib/active_record/framing/compat/active_record_4_2.rb
|
154
174
|
- lib/active_record/framing/compat/active_record_5_0.rb
|
155
175
|
- lib/active_record/framing/compat/active_record_5_1.rb
|
176
|
+
- lib/active_record/framing/compat/active_record_6_0.rb
|
177
|
+
- lib/active_record/framing/compat/arel.rb
|
156
178
|
- lib/active_record/framing/core_extension.rb
|
157
179
|
- lib/active_record/framing/default.rb
|
180
|
+
- lib/active_record/framing/join_dependency.rb
|
181
|
+
- lib/active_record/framing/model_proxy.rb
|
158
182
|
- lib/active_record/framing/named.rb
|
159
183
|
- lib/active_record/framing/query_methods.rb
|
160
184
|
- lib/active_record/framing/railtie.rb
|
185
|
+
- lib/active_record/framing/reflection.rb
|
161
186
|
- lib/active_record/framing/relation.rb
|
162
187
|
- lib/active_record/framing/spawn_methods.rb
|
163
188
|
- lib/active_record/framing/version.rb
|
@@ -181,7 +206,8 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
181
206
|
- !ruby/object:Gem::Version
|
182
207
|
version: 1.3.1
|
183
208
|
requirements: []
|
184
|
-
|
209
|
+
rubyforge_project:
|
210
|
+
rubygems_version: 2.7.6.2
|
185
211
|
signing_key:
|
186
212
|
specification_version: 4
|
187
213
|
summary: Provides larger level scopes (frames) through the use of common table expressions.
|