active_record-framing 0.1.0.pre.1 → 0.1.0.pre.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.
- checksums.yaml +4 -4
- data/CHANGELOG.md +3 -52
- data/README.md +0 -16
- data/lib/active_record/framing/core_extension.rb +9 -5
- data/lib/active_record/framing/default.rb +6 -7
- data/lib/active_record/framing/named.rb +161 -209
- data/lib/active_record/framing/railtie.rb +1 -1
- data/lib/active_record/framing/relation.rb +0 -19
- data/lib/active_record/framing/version.rb +1 -1
- metadata +1 -1
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8dd6c0a413bc426a0a22dee85cb7077ffb21c47fb77e5cf411a47478961002d1
|
4
|
+
data.tar.gz: 5c7b93bcd554161bf4020f5f3d3cec8882d31ec0b9da86ef26b72b08f70d146d
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 04f3c99ff11c241b3a0915d06e4c32cf171bd31b18314ac23f7c2536ad8d35aefaac73bdcfe4cbb4c6476a04db3bfcf03fdbde902865ea8dc761671d9894efed
|
7
|
+
data.tar.gz: b19fcf8ef79262680551b65f0dffe47d5c0cb2b1216d5c7c149dd7d7037067f3b5785dd87d2aa6d5ce0dc8d749d440f0864a6c4360b7bf1d30273475d42f0ebb
|
data/CHANGELOG.md
CHANGED
@@ -1,53 +1,4 @@
|
|
1
|
-
#
|
1
|
+
# ActiveRecord::Framing
|
2
2
|
|
3
|
-
## 0.
|
4
|
-
-
|
5
|
-
- Dropped support for Ruby 2.0, 2.1, 2.2
|
6
|
-
- Dropped support for Rails 4.1
|
7
|
-
- Default `active_record-framing` options using `Proc`
|
8
|
-
|
9
|
-
## 0.4.0 _(Never Released)_
|
10
|
-
- Specs for Rails 4.0-5.1
|
11
|
-
- Uses `combustion` gem for cleaner and more comprehensive testing
|
12
|
-
- Added badges to ReadMe
|
13
|
-
- Using `:prepend` to leverage ancestry chain
|
14
|
-
- Add logger for internal use
|
15
|
-
- DRYd up init code
|
16
|
-
- Removed partially supported features
|
17
|
-
- Added DSL in migrations/schema for adding `active_record-framing` timestamps to tables
|
18
|
-
|
19
|
-
## 0.3.0 _(May 10, 2017)_
|
20
|
-
- Add specs
|
21
|
-
- Clean up dependencies
|
22
|
-
- Auto-init models after installing views
|
23
|
-
- Remove chained `create` methods
|
24
|
-
|
25
|
-
## 0.2.6 _(April 06, 2017)_
|
26
|
-
- Add warning when no DB connection present
|
27
|
-
|
28
|
-
## 0.2.5 _(March 28, 2017)_
|
29
|
-
- Extract injections to `.load` method
|
30
|
-
|
31
|
-
## 0.2.4 _(February 03, 2017)_
|
32
|
-
- Use `becomes` to mask `::All` etc classes
|
33
|
-
|
34
|
-
## 0.2.3 _(February 03, 2017)_
|
35
|
-
- Chain `create!` method to work properly
|
36
|
-
|
37
|
-
## 0.2.2 _(February 03, 2017)_
|
38
|
-
- Chain `create` method to work properly
|
39
|
-
|
40
|
-
## 0.2.1 _(February 03, 2017)_
|
41
|
-
- More reliable table name handling
|
42
|
-
- Changed API for installing views (e.g. `destroy_deleted_view`, `uninstall_deleted_view`)
|
43
|
-
|
44
|
-
## 0.1.1 _(January 31, 2017)_
|
45
|
-
- Added instructions to readme
|
46
|
-
- Fixes stack-too-deep edge-case (by moving to `:include` over `:prepend`)
|
47
|
-
|
48
|
-
## 0.1.0 _(January 30, 2017)_
|
49
|
-
- Renames primary table to `model_name/all`
|
50
|
-
- Creates views for each model using `active_record-framing`
|
51
|
-
- `model_name/deleted`
|
52
|
-
- `model_name/present`
|
53
|
-
- Classes created to read from views (`::All`, `::Present`, `::Deleted`)
|
3
|
+
## 0.1.0 _(May 08, 2019)_
|
4
|
+
- Initial Release
|
data/README.md
CHANGED
@@ -62,22 +62,6 @@ WITH "admins" AS
|
|
62
62
|
(SELECT "users".* )
|
63
63
|
```
|
64
64
|
|
65
|
-
If you're starting with a brand-new table, the existing `timestamps` DSL has been extended to accept `deleted_at: true` as an option, for convenience. Or you can do it seperately as shown above.
|
66
|
-
|
67
|
-
```ruby
|
68
|
-
class CreatCommentsTable < ActiveRecord::Migration
|
69
|
-
|
70
|
-
def change
|
71
|
-
create_table :comments do |t|
|
72
|
-
# ...
|
73
|
-
# to the `timestamps` DSL
|
74
|
-
t.timestamps null: false, deleted_at: true
|
75
|
-
end
|
76
|
-
end
|
77
|
-
|
78
|
-
end
|
79
|
-
```
|
80
|
-
|
81
65
|
## Development
|
82
66
|
|
83
67
|
After checking out the repo, run `bundle` to install dependencies. Then, run `bundle exec rspec` to run the tests.
|
@@ -4,12 +4,16 @@ require "active_support/per_thread_registry"
|
|
4
4
|
|
5
5
|
module ActiveRecord
|
6
6
|
module Framing
|
7
|
-
extend ActiveSupport::Concern
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
8
|
+
def self.prepended(subclass)
|
9
|
+
subclass.class_eval do
|
10
|
+
singleton_class.alias_method :unframed_all, :all
|
11
|
+
end
|
12
|
+
|
13
|
+
subclass.include Default
|
14
|
+
subclass.extend Named
|
15
|
+
subclass.include AttributeMethods
|
16
|
+
subclass.extend ClassMethods
|
13
17
|
end
|
14
18
|
|
15
19
|
module ClassMethods # :nodoc:
|
@@ -34,7 +34,7 @@ module ActiveRecord
|
|
34
34
|
# Post.limit(10) # Fires "SELECT * FROM posts LIMIT 10"
|
35
35
|
# }
|
36
36
|
def unframed
|
37
|
-
block_given? ?
|
37
|
+
block_given? ? unframed_all.framing { yield } : unframed_all
|
38
38
|
end
|
39
39
|
|
40
40
|
# Are there attributes associated with this frame?
|
@@ -116,12 +116,11 @@ module ActiveRecord
|
|
116
116
|
|
117
117
|
if default_frame_override
|
118
118
|
# The user has defined their own default frame method, so call that
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
warn "come back to me!"
|
119
|
+
evaluate_default_frame do
|
120
|
+
if frame = default_frame
|
121
|
+
(base_rel ||= relation).frame!(frame)
|
122
|
+
end
|
123
|
+
end
|
125
124
|
elsif default_frames.any?
|
126
125
|
# cte_table = arel_table
|
127
126
|
cte_table = Arel::Table.new(table_name)
|
@@ -4,236 +4,188 @@ module ActiveRecord
|
|
4
4
|
# = Active Record \Named \Frames
|
5
5
|
module Framing
|
6
6
|
module Named
|
7
|
-
extend ActiveSupport::Concern
|
8
7
|
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
# end
|
27
|
-
# end
|
28
|
-
# end
|
29
|
-
|
30
|
-
# alias this method?, framed_all
|
31
|
-
# def all
|
32
|
-
# # if current_scope
|
33
|
-
# # current_scope.clone
|
34
|
-
# # else
|
35
|
-
# # default_scoped
|
36
|
-
# # end
|
37
|
-
# if current_frame
|
38
|
-
# puts "#{name} has a current_frame: #{current_frame.to_sql}"
|
39
|
-
# super.frame(current_frame.clone)
|
40
|
-
# else
|
41
|
-
# puts "#{name} is using a default_frame: #{default_framed.to_sql}" if default_framed
|
42
|
-
# super.frame(default_framed)
|
43
|
-
# end
|
44
|
-
# end
|
45
|
-
|
46
|
-
def all
|
47
|
-
framed_all(super)
|
48
|
-
end
|
49
|
-
|
50
|
-
def framed_all(rel)
|
51
|
-
if current_frame = self.current_frame
|
52
|
-
if self == current_frame.klass
|
53
|
-
current_frame.clone
|
54
|
-
else
|
55
|
-
rel.merge!(current_frame)
|
56
|
-
end
|
8
|
+
# Returns an ActiveRecord::Relation frame object.
|
9
|
+
#
|
10
|
+
# posts = Post.all
|
11
|
+
# posts.size # Fires "select count(*) from posts" and returns the count
|
12
|
+
# posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects
|
13
|
+
#
|
14
|
+
# fruits = Fruit.all
|
15
|
+
# fruits = fruits.where(color: 'red') if options[:red_only]
|
16
|
+
# fruits = fruits.limit(10) if limited?
|
17
|
+
#
|
18
|
+
# You can define a frame that applies to all finders using
|
19
|
+
# {default_frame}[rdoc-ref:Framing::Default::ClassMethods#default_frame].
|
20
|
+
def all
|
21
|
+
rel = unframed_all
|
22
|
+
if current_frame = self.current_frame
|
23
|
+
if self == current_frame.klass
|
24
|
+
current_frame.clone
|
57
25
|
else
|
58
|
-
|
26
|
+
rel.merge!(current_frame)
|
59
27
|
end
|
28
|
+
else
|
29
|
+
default_framed(rel)
|
60
30
|
end
|
31
|
+
end
|
61
32
|
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
def registered_frames
|
67
|
-
@registered_frames ||= {}
|
68
|
-
end
|
69
|
-
|
70
|
-
def frame_for_association(frame = relation) # :nodoc:
|
71
|
-
current_frame = self.current_frame
|
72
|
-
|
73
|
-
if current_frame && current_frame.empty_frame?
|
74
|
-
frame
|
75
|
-
else
|
76
|
-
default_framed(frame)
|
77
|
-
end
|
78
|
-
end
|
33
|
+
# def default_framed(frame = relation) # :nodoc:
|
34
|
+
def default_framed(frame = nil) # :nodoc:
|
35
|
+
!ignore_default_frame? && build_default_frame(frame) || frame
|
36
|
+
end
|
79
37
|
|
80
|
-
|
81
|
-
|
82
|
-
|
38
|
+
# Adds a class method for retrieving and querying objects.
|
39
|
+
# The method is intended to return an ActiveRecord::Relation
|
40
|
+
# object, which is composable with other frames.
|
41
|
+
# If it returns +nil+ or +false+, an
|
42
|
+
# {all}[rdoc-ref:Framing::Named::ClassMethods#all] frame is returned instead.
|
43
|
+
#
|
44
|
+
# A \frame represents a narrowing of a database query, such as
|
45
|
+
# <tt>where(color: :red).select('shirts.*').includes(:washing_instructions)</tt>.
|
46
|
+
#
|
47
|
+
# class Shirt < ActiveRecord::Base
|
48
|
+
# frame :red, -> { where(color: 'red') }
|
49
|
+
# frame :dry_clean_only, -> { joins(:washing_instructions).where('washing_instructions.dry_clean_only = ?', true) }
|
50
|
+
# end
|
51
|
+
#
|
52
|
+
# The above calls to #frame define class methods <tt>Shirt.red</tt> and
|
53
|
+
# <tt>Shirt::DryCleanOnly</tt>. <tt>Shirt::Red</tt>, in effect,
|
54
|
+
# represents the query <tt>Shirt.where(color: 'red')</tt>.
|
55
|
+
#
|
56
|
+
# You should always pass a callable object to the frames defined
|
57
|
+
# with #frame. This ensures that the frame is re-evaluated each
|
58
|
+
# time it is called.
|
59
|
+
#
|
60
|
+
# Note that this is simply 'syntactic sugar' for defining an actual
|
61
|
+
# class method:
|
62
|
+
#
|
63
|
+
# class Shirt < ActiveRecord::Base
|
64
|
+
# def self.red
|
65
|
+
# where(color: 'red')
|
66
|
+
# end
|
67
|
+
# end
|
68
|
+
#
|
69
|
+
# Unlike <tt>Shirt.find(...)</tt>, however, the object returned by
|
70
|
+
# <tt>Shirt.red</tt> is not an Array but an ActiveRecord::Relation,
|
71
|
+
# which is composable with other frames; it resembles the association object
|
72
|
+
# constructed by a {has_many}[rdoc-ref:Associations::ClassMethods#has_many]
|
73
|
+
# declaration. For instance, you can invoke <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>,
|
74
|
+
# <tt>Shirt.red.where(size: 'small')</tt>. Also, just as with the
|
75
|
+
# association objects, named \frames act like an Array, implementing
|
76
|
+
# Enumerable; <tt>Shirt.red.each(&block)</tt>, <tt>Shirt.red.first</tt>,
|
77
|
+
# and <tt>Shirt.red.inject(memo, &block)</tt> all behave as if
|
78
|
+
# <tt>Shirt.red</tt> really was an array.
|
79
|
+
#
|
80
|
+
# These named \frames are composable. For instance,
|
81
|
+
# <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are
|
82
|
+
# both red and dry clean only. Nested finds and calculations also work
|
83
|
+
# with these compositions: <tt>Shirt.red.dry_clean_only.count</tt>
|
84
|
+
# returns the number of garments for which these criteria obtain.
|
85
|
+
# Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
|
86
|
+
#
|
87
|
+
# All frames are available as class methods on the ActiveRecord::Base
|
88
|
+
# descendant upon which the \frames were defined. But they are also
|
89
|
+
# available to {has_many}[rdoc-ref:Associations::ClassMethods#has_many]
|
90
|
+
# associations. If,
|
91
|
+
#
|
92
|
+
# class Person < ActiveRecord::Base
|
93
|
+
# has_many :shirts
|
94
|
+
# end
|
95
|
+
#
|
96
|
+
# then <tt>elton.shirts.red.dry_clean_only</tt> will return all of
|
97
|
+
# Elton's red, dry clean only shirts.
|
98
|
+
#
|
99
|
+
# \Named frames can also have extensions, just as with
|
100
|
+
# {has_many}[rdoc-ref:Associations::ClassMethods#has_many] declarations:
|
101
|
+
#
|
102
|
+
# class Shirt < ActiveRecord::Base
|
103
|
+
# frame :red, -> { where(color: 'red') } do
|
104
|
+
# def dom_id
|
105
|
+
# 'red_shirts'
|
106
|
+
# end
|
107
|
+
# end
|
108
|
+
# end
|
109
|
+
#
|
110
|
+
# Frames cannot be used while creating/building a record.
|
111
|
+
#
|
112
|
+
# class Article < ActiveRecord::Base
|
113
|
+
# frame :published, -> { where(published: true) }
|
114
|
+
# end
|
115
|
+
#
|
116
|
+
# Article.published.new.published # => nil
|
117
|
+
# Article.published.create.published # => nil
|
118
|
+
#
|
119
|
+
# \Class methods on your model are automatically available
|
120
|
+
# on frames. Assuming the following setup:
|
121
|
+
#
|
122
|
+
# class Article < ActiveRecord::Base
|
123
|
+
# frame :published, -> { where(published: true) }
|
124
|
+
# frame :featured, -> { where(featured: true) }
|
125
|
+
#
|
126
|
+
# def self.latest_article
|
127
|
+
# order('published_at desc').first
|
128
|
+
# end
|
129
|
+
#
|
130
|
+
# def self.titles
|
131
|
+
# pluck(:title)
|
132
|
+
# end
|
133
|
+
# end
|
134
|
+
#
|
135
|
+
# We are able to call the methods like this:
|
136
|
+
#
|
137
|
+
# Article.published.featured.latest_article
|
138
|
+
# Article.featured.titles
|
139
|
+
def frame(frame_name, body, &block)
|
140
|
+
unless body.respond_to?(:call)
|
141
|
+
raise ArgumentError, "The frame body needs to be callable."
|
83
142
|
end
|
84
143
|
|
85
|
-
|
86
|
-
|
87
|
-
# object, which is composable with other frames.
|
88
|
-
# If it returns +nil+ or +false+, an
|
89
|
-
# {all}[rdoc-ref:Framing::Named::ClassMethods#all] frame is returned instead.
|
90
|
-
#
|
91
|
-
# A \frame represents a narrowing of a database query, such as
|
92
|
-
# <tt>where(color: :red).select('shirts.*').includes(:washing_instructions)</tt>.
|
93
|
-
#
|
94
|
-
# class Shirt < ActiveRecord::Base
|
95
|
-
# frame :red, -> { where(color: 'red') }
|
96
|
-
# frame :dry_clean_only, -> { joins(:washing_instructions).where('washing_instructions.dry_clean_only = ?', true) }
|
97
|
-
# end
|
98
|
-
#
|
99
|
-
# The above calls to #frame define class methods <tt>Shirt.red</tt> and
|
100
|
-
# <tt>Shirt::DryCleanOnly</tt>. <tt>Shirt::Red</tt>, in effect,
|
101
|
-
# represents the query <tt>Shirt.where(color: 'red')</tt>.
|
102
|
-
#
|
103
|
-
# You should always pass a callable object to the frames defined
|
104
|
-
# with #frame. This ensures that the frame is re-evaluated each
|
105
|
-
# time it is called.
|
106
|
-
#
|
107
|
-
# Note that this is simply 'syntactic sugar' for defining an actual
|
108
|
-
# class method:
|
109
|
-
#
|
110
|
-
# class Shirt < ActiveRecord::Base
|
111
|
-
# def self.red
|
112
|
-
# where(color: 'red')
|
113
|
-
# end
|
114
|
-
# end
|
115
|
-
#
|
116
|
-
# Unlike <tt>Shirt.find(...)</tt>, however, the object returned by
|
117
|
-
# <tt>Shirt.red</tt> is not an Array but an ActiveRecord::Relation,
|
118
|
-
# which is composable with other frames; it resembles the association object
|
119
|
-
# constructed by a {has_many}[rdoc-ref:Associations::ClassMethods#has_many]
|
120
|
-
# declaration. For instance, you can invoke <tt>Shirt.red.first</tt>, <tt>Shirt.red.count</tt>,
|
121
|
-
# <tt>Shirt.red.where(size: 'small')</tt>. Also, just as with the
|
122
|
-
# association objects, named \frames act like an Array, implementing
|
123
|
-
# Enumerable; <tt>Shirt.red.each(&block)</tt>, <tt>Shirt.red.first</tt>,
|
124
|
-
# and <tt>Shirt.red.inject(memo, &block)</tt> all behave as if
|
125
|
-
# <tt>Shirt.red</tt> really was an array.
|
126
|
-
#
|
127
|
-
# These named \frames are composable. For instance,
|
128
|
-
# <tt>Shirt.red.dry_clean_only</tt> will produce all shirts that are
|
129
|
-
# both red and dry clean only. Nested finds and calculations also work
|
130
|
-
# with these compositions: <tt>Shirt.red.dry_clean_only.count</tt>
|
131
|
-
# returns the number of garments for which these criteria obtain.
|
132
|
-
# Similarly with <tt>Shirt.red.dry_clean_only.average(:thread_count)</tt>.
|
133
|
-
#
|
134
|
-
# All frames are available as class methods on the ActiveRecord::Base
|
135
|
-
# descendant upon which the \frames were defined. But they are also
|
136
|
-
# available to {has_many}[rdoc-ref:Associations::ClassMethods#has_many]
|
137
|
-
# associations. If,
|
138
|
-
#
|
139
|
-
# class Person < ActiveRecord::Base
|
140
|
-
# has_many :shirts
|
141
|
-
# end
|
142
|
-
#
|
143
|
-
# then <tt>elton.shirts.red.dry_clean_only</tt> will return all of
|
144
|
-
# Elton's red, dry clean only shirts.
|
145
|
-
#
|
146
|
-
# \Named frames can also have extensions, just as with
|
147
|
-
# {has_many}[rdoc-ref:Associations::ClassMethods#has_many] declarations:
|
148
|
-
#
|
149
|
-
# class Shirt < ActiveRecord::Base
|
150
|
-
# frame :red, -> { where(color: 'red') } do
|
151
|
-
# def dom_id
|
152
|
-
# 'red_shirts'
|
153
|
-
# end
|
154
|
-
# end
|
155
|
-
# end
|
156
|
-
#
|
157
|
-
# Frames cannot be used while creating/building a record.
|
158
|
-
#
|
159
|
-
# class Article < ActiveRecord::Base
|
160
|
-
# frame :published, -> { where(published: true) }
|
161
|
-
# end
|
162
|
-
#
|
163
|
-
# Article.published.new.published # => nil
|
164
|
-
# Article.published.create.published # => nil
|
165
|
-
#
|
166
|
-
# \Class methods on your model are automatically available
|
167
|
-
# on frames. Assuming the following setup:
|
168
|
-
#
|
169
|
-
# class Article < ActiveRecord::Base
|
170
|
-
# frame :published, -> { where(published: true) }
|
171
|
-
# frame :featured, -> { where(featured: true) }
|
172
|
-
#
|
173
|
-
# def self.latest_article
|
174
|
-
# order('published_at desc').first
|
175
|
-
# end
|
176
|
-
#
|
177
|
-
# def self.titles
|
178
|
-
# pluck(:title)
|
179
|
-
# end
|
180
|
-
# end
|
181
|
-
#
|
182
|
-
# We are able to call the methods like this:
|
183
|
-
#
|
184
|
-
# Article.published.featured.latest_article
|
185
|
-
# Article.featured.titles
|
186
|
-
def frame(frame_name, body, &block)
|
187
|
-
unless body.respond_to?(:call)
|
188
|
-
raise ArgumentError, "The frame body needs to be callable."
|
189
|
-
end
|
190
|
-
|
191
|
-
constant = frame_name.to_s.classify.to_sym
|
192
|
-
arel_tn = "#{frame_name}/#{self.table_name}"
|
144
|
+
constant = frame_name.to_s.classify.to_sym
|
145
|
+
arel_tn = "#{frame_name}/#{self.table_name}"
|
193
146
|
|
194
|
-
|
195
|
-
|
147
|
+
the_frame = body.respond_to?(:to_proc) ? body : body.method(:call)
|
148
|
+
cte_relation = relation.merge!(relation.instance_exec(&the_frame) || relation)
|
196
149
|
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
150
|
+
# self.const_set constant, Class.new(DelegateClass(self)) do |klass|
|
151
|
+
delegator = self.name.to_sym
|
152
|
+
self.const_set(constant, self.dup).class_eval do |klass|
|
153
|
+
# self.const_set constant, Class.new do |klass|
|
154
|
+
extend SingleForwardable
|
155
|
+
def_delegators delegator, :type_caster, :table_name, :discriminate_class_for_record
|
203
156
|
|
204
|
-
|
157
|
+
klass.default_frames = []
|
205
158
|
|
206
|
-
|
207
|
-
|
208
|
-
end
|
209
|
-
|
210
|
-
klass.current_frame = build_frame(cte_relation, &block)
|
159
|
+
@arel_table = klass.arel_table.dup.tap do |at|
|
160
|
+
at.name = arel_tn
|
211
161
|
end
|
212
162
|
|
213
|
-
|
214
|
-
|
215
|
-
"on the model \"#{self.constant}\", but Active Record already defined " \
|
216
|
-
"a class method with the same name."
|
217
|
-
end
|
163
|
+
klass.current_frame = build_frame(cte_relation, &block)
|
164
|
+
end
|
218
165
|
|
166
|
+
if dangerous_class_const?(constant)
|
167
|
+
raise ArgumentError, "You tried to define a frame named \"#{constant}\" " \
|
168
|
+
"on the model \"#{self.constant}\", but Active Record already defined " \
|
169
|
+
"a class method with the same name."
|
219
170
|
end
|
220
171
|
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
172
|
+
end
|
173
|
+
|
174
|
+
def build_frame(frame, &block)
|
175
|
+
extension = Module.new(&block) if block
|
176
|
+
relation.frame!(Arel::Nodes::As.new(arel_table, frame.arel)).tap do |rel|
|
177
|
+
rel.extending!(extension) if extension
|
226
178
|
end
|
179
|
+
end
|
227
180
|
|
228
|
-
|
181
|
+
private
|
229
182
|
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
end
|
183
|
+
def valid_frame_name?(name)
|
184
|
+
if respond_to?(name, true) && logger
|
185
|
+
logger.warn "Creating frame :#{name}. " \
|
186
|
+
"Overwriting existing method #{self.name}.#{name}."
|
235
187
|
end
|
236
|
-
|
188
|
+
end
|
237
189
|
end
|
238
190
|
end
|
239
191
|
end
|
@@ -9,7 +9,7 @@ module ActiveRecord::Framing
|
|
9
9
|
class Railtie < Rails::Railtie
|
10
10
|
initializer 'active_record-framing.load' do |_app|
|
11
11
|
ActiveSupport.on_load(:active_record) do
|
12
|
-
::ActiveRecord::Base.
|
12
|
+
::ActiveRecord::Base.prepend(ActiveRecord::Framing)
|
13
13
|
::ActiveRecord::Relation.prepend(ActiveRecord::Framing::Relation)
|
14
14
|
::ActiveRecord::Relation.include(ActiveRecord::Framing::QueryMethods)
|
15
15
|
::ActiveRecord::Relation.prepend(ActiveRecord::Framing::SpawnMethods)
|
@@ -3,20 +3,6 @@ module ActiveRecord
|
|
3
3
|
module Framing
|
4
4
|
module Relation
|
5
5
|
|
6
|
-
# def arel_without_frames
|
7
|
-
# klass.ignore_default_frame
|
8
|
-
# Thread.currently(:without_frames, true) do
|
9
|
-
# @arel_without_frames ||= build_arel_without_frames
|
10
|
-
# end
|
11
|
-
# end
|
12
|
-
|
13
|
-
# def build_arel_without_frames
|
14
|
-
# @arel, old = nil, @arel
|
15
|
-
# arel
|
16
|
-
# ensure
|
17
|
-
# @arel = old
|
18
|
-
# end
|
19
|
-
|
20
6
|
def build_arel(*)
|
21
7
|
super.tap do |ar|
|
22
8
|
unless ignore_default_frame?
|
@@ -26,11 +12,6 @@ module ActiveRecord
|
|
26
12
|
# reflection.klass.type_caster
|
27
13
|
# )
|
28
14
|
build_frames(ar)
|
29
|
-
|
30
|
-
frames_values.each do |k,v|
|
31
|
-
puts "#{k} => #{v.to_sql}"
|
32
|
-
end
|
33
|
-
|
34
15
|
ar.with(*frames_values.values) if frames_values.any?
|
35
16
|
end
|
36
17
|
end
|