active_record-framing 0.1.0.pre.1 → 0.1.0.pre.2
Sign up to get free protection for your applications and to get access to all the features.
- 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
|