better_ar 0.0.6 → 0.0.7
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.
- data/HISTORY.txt +4 -0
- data/README.markdown +8 -0
- data/better_ar.gemspec +0 -2
- data/lib/better_ar.rb +4 -106
- data/lib/better_ar/association_collection.rb +49 -0
- data/lib/better_ar/calculations.rb +28 -0
- data/lib/better_ar/finder_methods.rb +84 -0
- data/lib/better_ar/version.rb +1 -1
- data/test/helper.rb +7 -1
- data/test/test_finder_methods.rb +71 -1
- metadata +5 -2
data/HISTORY.txt
CHANGED
@@ -1,3 +1,7 @@
|
|
1
|
+
## 0.0.7
|
2
|
+
Added support for association collections
|
3
|
+
Moved ActiveRecord::Relation included methods into proper modules
|
4
|
+
|
1
5
|
## 0.0.6
|
2
6
|
Moved methods as higher level module in ActiveRecord::Relation than ActiveRecord::FinderMethods. So the BetterAR modules take precedence.
|
3
7
|
|
data/README.markdown
CHANGED
@@ -23,6 +23,14 @@ is the same as
|
|
23
23
|
|
24
24
|
User.joins(:records).where(:age => 10, :records => {:name => 'test'}).limit(5).offset(10).order(:name)
|
25
25
|
|
26
|
+
It also works on association collections:
|
27
|
+
|
28
|
+
User.first.records.all(:level => 2, :limit! => 5, :offset! => 10, :order! => :name, :reports => {:name => 'test'})
|
29
|
+
|
30
|
+
This would be the same as:
|
31
|
+
|
32
|
+
User.first.records.joins(:reports).where(:level => 2, :reports => {:name => 'test'}).limit(5).offset(10).order(:name)
|
33
|
+
|
26
34
|
While this may seem less concise the advantage is being able to dynamically construct the query with a single hash in code.
|
27
35
|
|
28
36
|
### Legacy
|
data/better_ar.gemspec
CHANGED
data/lib/better_ar.rb
CHANGED
@@ -1,107 +1,5 @@
|
|
1
|
-
module BetterAr
|
1
|
+
module BetterAr; end
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
# User.all(:age => 10, :limit! => 2, :offset! => 3, :order! => :name)
|
7
|
-
#
|
8
|
-
# is the same as:
|
9
|
-
# User.where(:age => 10).limit(2).offset(3).order(:name)
|
10
|
-
#
|
11
|
-
# if the key :conditions is present it will fall back to legacy
|
12
|
-
#
|
13
|
-
# any key with the '!' at the end will be assumed to be a sql operator. The key should match either an {ActiveRecord::Relation} instance method or an ARel predicate.
|
14
|
-
#
|
15
|
-
# Implicit joins are supported.
|
16
|
-
# example:
|
17
|
-
# User.all(:records => {:name => 'test'})
|
18
|
-
#
|
19
|
-
# is the same as:
|
20
|
-
# User.joins(:records).where(:records => {:name => 'test'})
|
21
|
-
#
|
22
|
-
# @param [Hash]
|
23
|
-
# Optional
|
24
|
-
# @return [ActiveRecord::Relation]
|
25
|
-
def all(opts = {})
|
26
|
-
if opts.empty?
|
27
|
-
super()
|
28
|
-
elsif opts.key?(:conditions)
|
29
|
-
super(opts)
|
30
|
-
else
|
31
|
-
relation = clone
|
32
|
-
|
33
|
-
unless opts.key?(:conditions)
|
34
|
-
unless opts.empty?
|
35
|
-
opts.keys.select { |key| key.to_s =~ /!$/ }.each do |predicate|
|
36
|
-
if value = opts.delete(predicate)
|
37
|
-
relation = relation.send(predicate.to_s.sub('!',''), value)
|
38
|
-
end
|
39
|
-
end
|
40
|
-
|
41
|
-
reflect_on_all_associations.map(&:name).each do |name|
|
42
|
-
if opts.key?(name)
|
43
|
-
relation = relation.joins(name)
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
relation.where(opts)
|
48
|
-
end
|
49
|
-
|
50
|
-
if opts.empty?
|
51
|
-
relation
|
52
|
-
else
|
53
|
-
relation.where(opts)
|
54
|
-
end
|
55
|
-
end
|
56
|
-
end
|
57
|
-
end
|
58
|
-
|
59
|
-
# Forces a limit of 1 on the collection
|
60
|
-
#
|
61
|
-
# example:
|
62
|
-
# User.first(:age => 10, :name => 'Brian')
|
63
|
-
#
|
64
|
-
# is the same as:
|
65
|
-
# User.where(:age => 10, :name => 'Brian').limit(1).first
|
66
|
-
#
|
67
|
-
# if the key :conditions is present it will fall back to legacy
|
68
|
-
#
|
69
|
-
# @param [Hash]
|
70
|
-
# Optional follows same convention as {#all}
|
71
|
-
# @return [ActiveRecord::Base]
|
72
|
-
def first(opts = {})
|
73
|
-
if opts.empty?
|
74
|
-
super()
|
75
|
-
elsif opts.key?(:conditions)
|
76
|
-
super(opts)
|
77
|
-
else
|
78
|
-
all(opts.merge(:limit! => 1)).first
|
79
|
-
end
|
80
|
-
end
|
81
|
-
|
82
|
-
# Does a count on the query
|
83
|
-
#
|
84
|
-
# example:
|
85
|
-
# User.count(:age => 20)
|
86
|
-
#
|
87
|
-
# is the same as:
|
88
|
-
# User.where(:age => 20).count
|
89
|
-
#
|
90
|
-
# if the key :conditions is present it will fall back to legacy
|
91
|
-
#
|
92
|
-
# @param [Hash]
|
93
|
-
# Optional follows same convention as {#all}
|
94
|
-
# @return [Integer]
|
95
|
-
def count(opts = {})
|
96
|
-
if opts.empty?
|
97
|
-
super()
|
98
|
-
elsif opts.key?(:conditions)
|
99
|
-
super(opts)
|
100
|
-
else
|
101
|
-
all(opts).count
|
102
|
-
end
|
103
|
-
end
|
104
|
-
|
105
|
-
end
|
106
|
-
|
107
|
-
ActiveRecord::Relation.send(:include, BetterAr)
|
3
|
+
require 'better_ar/finder_methods'
|
4
|
+
require 'better_ar/calculations'
|
5
|
+
require 'better_ar/association_collection'
|
@@ -0,0 +1,49 @@
|
|
1
|
+
module BetterAr
|
2
|
+
module AssociationCollection
|
3
|
+
|
4
|
+
def self.included(base)
|
5
|
+
base.class_eval do
|
6
|
+
alias_method_chain :first, :better_ar
|
7
|
+
alias_method_chain :count, :better_ar
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# Allows for the same interface as {BetterAr::Relation#first} on association collections
|
12
|
+
#
|
13
|
+
# example:
|
14
|
+
# User.first.records.first(:level => 2, :order! => :name)
|
15
|
+
#
|
16
|
+
# @param [Hash]
|
17
|
+
# Optional
|
18
|
+
# @returns [ActiveRecord::Base]
|
19
|
+
def first_with_better_ar(opts = {})
|
20
|
+
if opts.empty?
|
21
|
+
first_without_better_ar
|
22
|
+
elsif opts.key?(:conditions)
|
23
|
+
first_without_better_ar(opts)
|
24
|
+
else
|
25
|
+
scoped.first(opts)
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# Allows for the same interface as {BetterAr::Relation#count} on association collections
|
30
|
+
#
|
31
|
+
# example:
|
32
|
+
# User.first.records.count(:level => 2)
|
33
|
+
#
|
34
|
+
# @param [Hash]
|
35
|
+
# Optional
|
36
|
+
# @returns [Integer]
|
37
|
+
def count_with_better_ar(opts = {})
|
38
|
+
if opts.empty?
|
39
|
+
count_without_better_ar
|
40
|
+
elsif opts.key?(:conditions)
|
41
|
+
count_without_better_ar(opts)
|
42
|
+
else
|
43
|
+
scoped.count(opts)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
ActiveRecord::Associations::AssociationCollection.send(:include, BetterAr::AssociationCollection)
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module BetterAr
|
2
|
+
module Calculations
|
3
|
+
# Does a count on the query
|
4
|
+
#
|
5
|
+
# example:
|
6
|
+
# User.count(:age => 20)
|
7
|
+
#
|
8
|
+
# is the same as:
|
9
|
+
# User.where(:age => 20).count
|
10
|
+
#
|
11
|
+
# if the key :conditions is present it will fall back to legacy
|
12
|
+
#
|
13
|
+
# @param [Hash]
|
14
|
+
# Optional follows same convention as {#all}
|
15
|
+
# @return [Integer]
|
16
|
+
def count(opts = {}, extra = {})
|
17
|
+
if opts.nil? || opts.empty?
|
18
|
+
super(nil, extra)
|
19
|
+
elsif opts.key?(:conditions)
|
20
|
+
super(opts, extra)
|
21
|
+
else
|
22
|
+
all(opts).count
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
ActiveRecord::Relation.send(:include, BetterAr::Calculations)
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module BetterAr
|
2
|
+
module FinderMethods
|
3
|
+
# Breaks down the hash to do a {ActiveRecord::Relation} query
|
4
|
+
#
|
5
|
+
# example:
|
6
|
+
# User.all(:age => 10, :limit! => 2, :offset! => 3, :order! => :name)
|
7
|
+
#
|
8
|
+
# is the same as:
|
9
|
+
# User.where(:age => 10).limit(2).offset(3).order(:name)
|
10
|
+
#
|
11
|
+
# if the key :conditions is present it will fall back to legacy
|
12
|
+
#
|
13
|
+
# any key with the '!' at the end will be assumed to be a sql operator. The key should match either an {ActiveRecord::Relation} instance method or an ARel predicate.
|
14
|
+
#
|
15
|
+
# Implicit joins are supported.
|
16
|
+
# example:
|
17
|
+
# User.all(:records => {:name => 'test'})
|
18
|
+
#
|
19
|
+
# is the same as:
|
20
|
+
# User.joins(:records).where(:records => {:name => 'test'})
|
21
|
+
#
|
22
|
+
# @param [Hash]
|
23
|
+
# Optional
|
24
|
+
# @return [ActiveRecord::Relation]
|
25
|
+
def all(opts = {})
|
26
|
+
if opts.empty?
|
27
|
+
super()
|
28
|
+
elsif opts.key?(:conditions)
|
29
|
+
super(opts)
|
30
|
+
else
|
31
|
+
relation = clone
|
32
|
+
|
33
|
+
unless opts.key?(:conditions)
|
34
|
+
unless opts.empty?
|
35
|
+
opts.keys.select { |key| key.to_s =~ /!$/ }.each do |predicate|
|
36
|
+
if value = opts.delete(predicate)
|
37
|
+
relation = relation.send(predicate.to_s.sub('!',''), value)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
reflect_on_all_associations.map(&:name).each do |name|
|
42
|
+
if opts.key?(name)
|
43
|
+
relation = relation.joins(name)
|
44
|
+
end
|
45
|
+
end
|
46
|
+
|
47
|
+
relation.where(opts)
|
48
|
+
end
|
49
|
+
|
50
|
+
if opts.empty?
|
51
|
+
relation
|
52
|
+
else
|
53
|
+
relation.where(opts)
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
end
|
58
|
+
|
59
|
+
# Forces a limit of 1 on the collection
|
60
|
+
#
|
61
|
+
# example:
|
62
|
+
# User.first(:age => 10, :name => 'Brian')
|
63
|
+
#
|
64
|
+
# is the same as:
|
65
|
+
# User.where(:age => 10, :name => 'Brian').limit(1).first
|
66
|
+
#
|
67
|
+
# if the key :conditions is present it will fall back to legacy
|
68
|
+
#
|
69
|
+
# @param [Hash]
|
70
|
+
# Optional follows same convention as {#all}
|
71
|
+
# @return [ActiveRecord::Base]
|
72
|
+
def first(opts = {})
|
73
|
+
if opts.empty?
|
74
|
+
super()
|
75
|
+
elsif opts.key?(:conditions)
|
76
|
+
super(opts)
|
77
|
+
else
|
78
|
+
all(opts.merge(:limit! => 1)).first
|
79
|
+
end
|
80
|
+
end
|
81
|
+
end
|
82
|
+
end
|
83
|
+
|
84
|
+
ActiveRecord::Relation.send(:include, BetterAr::FinderMethods)
|
data/lib/better_ar/version.rb
CHANGED
data/test/helper.rb
CHANGED
@@ -1,7 +1,6 @@
|
|
1
1
|
require 'rubygems'
|
2
2
|
require 'ruby-debug'
|
3
3
|
require 'minitest/autorun'
|
4
|
-
# require 'mocha'
|
5
4
|
require 'active_support/core_ext'
|
6
5
|
require 'active_record'
|
7
6
|
require 'better_ar'
|
@@ -19,8 +18,10 @@ ActiveRecord::Base.establish_connection(
|
|
19
18
|
|
20
19
|
users_table = %{CREATE TABLE users (id INTEGER PRIMARY KEY, age INTEGER, name TEXT);}
|
21
20
|
records_table = %{CREATE TABLE records (id INTEGER PRIMARY KEY, user_id INTEGER, name TEXT);}
|
21
|
+
reports_table = %{CREATE TABLE reports (id INTEGER PRIMARY KEY, record_id INTEGER, name TEXT);}
|
22
22
|
ActiveRecord::Base.connection.execute(users_table)
|
23
23
|
ActiveRecord::Base.connection.execute(records_table)
|
24
|
+
ActiveRecord::Base.connection.execute(reports_table)
|
24
25
|
|
25
26
|
class User < ActiveRecord::Base
|
26
27
|
has_many :records
|
@@ -28,5 +29,10 @@ end
|
|
28
29
|
|
29
30
|
class Record < ActiveRecord::Base
|
30
31
|
belongs_to :user
|
32
|
+
has_many :reports
|
33
|
+
end
|
34
|
+
|
35
|
+
class Report < ActiveRecord::Base
|
36
|
+
belongs_to :record
|
31
37
|
end
|
32
38
|
|
data/test/test_finder_methods.rb
CHANGED
@@ -1,6 +1,6 @@
|
|
1
1
|
require 'helper'
|
2
2
|
|
3
|
-
describe '
|
3
|
+
describe 'ActiveRecord::Relation Finder Methods' do
|
4
4
|
after do
|
5
5
|
User.destroy_all
|
6
6
|
end
|
@@ -72,3 +72,73 @@ describe 'Enhanced Finder Methods' do
|
|
72
72
|
|
73
73
|
end
|
74
74
|
|
75
|
+
describe 'ActiveRecord::Associations::AssociationCollection Finder Methods' do
|
76
|
+
before do
|
77
|
+
@user = User.create
|
78
|
+
@record_1 = @user.records.create(:name => 'one')
|
79
|
+
@record_2 = @user.records.create(:name => 'two')
|
80
|
+
end
|
81
|
+
|
82
|
+
after do
|
83
|
+
User.destroy_all
|
84
|
+
Record.destroy_all
|
85
|
+
end
|
86
|
+
|
87
|
+
describe '.all' do
|
88
|
+
it 'extracts the non-where scopes and applies' do
|
89
|
+
test_sql = @user.records.all(:limit! => 1, :offset! => 2, :order! => :name, :name => 'one').to_sql
|
90
|
+
expected_sql = @user.records.where(:name => 'one').limit(1).offset(2).order(:name).to_sql
|
91
|
+
test_sql.must_be_like expected_sql
|
92
|
+
end
|
93
|
+
|
94
|
+
it 'finds implicit joins by reflection' do
|
95
|
+
test_sql = @user.records.all(:reports => { :name => 'test' }).to_sql
|
96
|
+
expected_sql = @user.records.joins(:reports).where(:reports => { :name => 'test' }).to_sql
|
97
|
+
test_sql.must_be_like expected_sql
|
98
|
+
end
|
99
|
+
end
|
100
|
+
|
101
|
+
describe '.first' do
|
102
|
+
it 'calls #first on .all' do
|
103
|
+
@user.records.first(:name => 'two').must_equal @record_2
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
describe '.count' do
|
108
|
+
it 'calls #count on .all' do
|
109
|
+
@user.records.count(:name => 'one').must_equal 1
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
113
|
+
describe 'hash contains :conditions' do
|
114
|
+
describe 'with conditions' do
|
115
|
+
it 'falls back for .all' do
|
116
|
+
@user.records.all(:conditions => "name = 'one'").must_equal [@record_1]
|
117
|
+
end
|
118
|
+
|
119
|
+
it 'falls back for .first' do
|
120
|
+
@user.records.create(:name => 'one')
|
121
|
+
@user.records.first(:conditions => "name = 'one'").must_equal @record_1
|
122
|
+
end
|
123
|
+
|
124
|
+
# Note a valid test... need to rewrite.
|
125
|
+
# it 'falls back for .count' do
|
126
|
+
# @user.records.count(:conditions => "name = 'one'").must_equal 1
|
127
|
+
# end
|
128
|
+
end
|
129
|
+
|
130
|
+
describe 'without conditions' do
|
131
|
+
it 'falls back for .all' do
|
132
|
+
@user.records.all.must_equal [@record_1, @record_2]
|
133
|
+
end
|
134
|
+
|
135
|
+
it 'falls back for .first' do
|
136
|
+
@user.records.first.must_equal @record_1
|
137
|
+
end
|
138
|
+
|
139
|
+
it 'falls back for .count' do
|
140
|
+
@user.records.count.must_equal 2
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
metadata
CHANGED
@@ -5,8 +5,8 @@ version: !ruby/object:Gem::Version
|
|
5
5
|
segments:
|
6
6
|
- 0
|
7
7
|
- 0
|
8
|
-
-
|
9
|
-
version: 0.0.
|
8
|
+
- 7
|
9
|
+
version: 0.0.7
|
10
10
|
platform: ruby
|
11
11
|
authors:
|
12
12
|
- Brian Cardarella
|
@@ -51,6 +51,9 @@ files:
|
|
51
51
|
- Rakefile
|
52
52
|
- better_ar.gemspec
|
53
53
|
- lib/better_ar.rb
|
54
|
+
- lib/better_ar/association_collection.rb
|
55
|
+
- lib/better_ar/calculations.rb
|
56
|
+
- lib/better_ar/finder_methods.rb
|
54
57
|
- lib/better_ar/version.rb
|
55
58
|
- test/helper.rb
|
56
59
|
- test/test_finder_methods.rb
|