ddollar-pacecar 1.1.6
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/MIT-LICENSE +22 -0
- data/README.rdoc +202 -0
- data/init.rb +1 -0
- data/lib/pacecar.rb +32 -0
- data/lib/pacecar/associations.rb +39 -0
- data/lib/pacecar/boolean.rb +34 -0
- data/lib/pacecar/datetime.rb +64 -0
- data/lib/pacecar/duration.rb +28 -0
- data/lib/pacecar/helpers.rb +54 -0
- data/lib/pacecar/limit.rb +22 -0
- data/lib/pacecar/order.rb +24 -0
- data/lib/pacecar/polymorph.rb +17 -0
- data/lib/pacecar/presence.rb +23 -0
- data/lib/pacecar/ranking.rb +26 -0
- data/lib/pacecar/search.rb +42 -0
- data/lib/pacecar/state.rb +30 -0
- metadata +71 -0
data/MIT-LICENSE
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2008, Matt Jankowski & thoughtbot, inc.
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person
|
4
|
+
obtaining a copy of this software and associated documentation
|
5
|
+
files (the "Software"), to deal in the Software without
|
6
|
+
restriction, including without limitation the rights to use,
|
7
|
+
copy, modify, merge, publish, distribute, sublicense, and/or sell
|
8
|
+
copies of the Software, and to permit persons to whom the
|
9
|
+
Software is furnished to do so, subject to the following
|
10
|
+
conditions:
|
11
|
+
|
12
|
+
The above copyright notice and this permission notice shall be
|
13
|
+
included in all copies or substantial portions of the Software.
|
14
|
+
|
15
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
16
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES
|
17
|
+
OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
18
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT
|
19
|
+
HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY,
|
20
|
+
WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING
|
21
|
+
FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR
|
22
|
+
OTHER DEALINGS IN THE SOFTWARE.
|
data/README.rdoc
ADDED
@@ -0,0 +1,202 @@
|
|
1
|
+
== Pacecar
|
2
|
+
|
3
|
+
Pacecar adds named_scope methods and other common functionality to ActiveRecord classes via database column introspection.
|
4
|
+
|
5
|
+
Pacecar automatically includes the Pacecar::Helpers module into all ActiveRecord::Base classes.
|
6
|
+
|
7
|
+
To get all Pacecar functionality, you need to "include Pacecar" in your class.
|
8
|
+
|
9
|
+
To get some subset (for example, only the state functionality), you can do something like "include Pacecar::State" to get only the module(s) you want.
|
10
|
+
|
11
|
+
== Usage
|
12
|
+
|
13
|
+
Assuming a database schema...
|
14
|
+
|
15
|
+
class CreateSchema < ActiveRecord::Migration
|
16
|
+
def self.up
|
17
|
+
create_table :users, :force => true do |t|
|
18
|
+
t.boolean :admin, :default => false, :null => false
|
19
|
+
t.datetime :approved_at
|
20
|
+
t.datetime :rejected_at
|
21
|
+
t.string :first_name
|
22
|
+
t.string :last_name
|
23
|
+
t.text :description
|
24
|
+
t.timestamps
|
25
|
+
end
|
26
|
+
create_table :posts, :force => true do |t|
|
27
|
+
t.string :owner_type
|
28
|
+
t.integer :owner_id
|
29
|
+
t.string :publication_state
|
30
|
+
t.string :post_type
|
31
|
+
t.timestamps
|
32
|
+
end
|
33
|
+
create_table :comments, :force => true do |t|
|
34
|
+
t.integer :user_id
|
35
|
+
t.text :description
|
36
|
+
t.timestamps
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
And some basic model declarations...
|
42
|
+
|
43
|
+
class User < ActiveRecord::Base
|
44
|
+
include Pacecar
|
45
|
+
has_many :posts, :as => :owner
|
46
|
+
has_many :comments
|
47
|
+
has_many :articles
|
48
|
+
has_ranking :comments
|
49
|
+
has_recent_records :comments
|
50
|
+
has_recent_records :articles, :comments
|
51
|
+
end
|
52
|
+
|
53
|
+
class Post < ActiveRecord::Base
|
54
|
+
include Pacecar
|
55
|
+
PUBLICATION_STATES = %w(Draft Submitted Rejected Accepted)
|
56
|
+
TYPES = %w(Free Open Private Anonymous PostModern)
|
57
|
+
belongs_to :owner, :polymorphic => true
|
58
|
+
has_state :publication_state
|
59
|
+
has_state :post_type, :with => TYPES
|
60
|
+
has_polymorph :owner
|
61
|
+
end
|
62
|
+
|
63
|
+
class Comment < ActiveRecord::Base
|
64
|
+
include Pacecar
|
65
|
+
belongs_to :user
|
66
|
+
end
|
67
|
+
|
68
|
+
class Article < ActiveRecord::Base
|
69
|
+
belongs_to :user
|
70
|
+
end
|
71
|
+
|
72
|
+
= All columns
|
73
|
+
|
74
|
+
Records where approved_at is not null, or where it is null...
|
75
|
+
|
76
|
+
User.approved_at_present
|
77
|
+
User.approved_at_missing
|
78
|
+
|
79
|
+
Records where first_name is not null, or where it is null...
|
80
|
+
|
81
|
+
User.first_name_present
|
82
|
+
User.first_name_missing
|
83
|
+
|
84
|
+
Records ordered by first_name (default to 'asc', can specify to override)...
|
85
|
+
|
86
|
+
User.by_first_name
|
87
|
+
User.by_first_name(:asc)
|
88
|
+
User.by_first_name(:desc)
|
89
|
+
|
90
|
+
Records where an attribute matches a search term (column LIKE "%term%")...
|
91
|
+
|
92
|
+
User.first_name_matches('John')
|
93
|
+
|
94
|
+
Records where an attribute starts or ends with a search term...
|
95
|
+
|
96
|
+
User.first_name_starts_with('A')
|
97
|
+
User.first_name_ends_with('a')
|
98
|
+
|
99
|
+
Records where any non-state text or string column matches term...
|
100
|
+
|
101
|
+
User.search_for('test')
|
102
|
+
|
103
|
+
Records where any of a list of columns match the term...
|
104
|
+
|
105
|
+
User.search_for 'test', :on => [:first_name, :last_name]
|
106
|
+
|
107
|
+
Records where all of a list of columns match the term...
|
108
|
+
|
109
|
+
User.search_for 'test', :on => [:first_name, :last_name], :require => :all
|
110
|
+
|
111
|
+
= Boolean columns
|
112
|
+
|
113
|
+
Records that are all admins or non-admins...
|
114
|
+
|
115
|
+
User.admin
|
116
|
+
User.not_admin
|
117
|
+
|
118
|
+
The "balance" (count of true minus false for column in question)...
|
119
|
+
|
120
|
+
User.admin_balance
|
121
|
+
|
122
|
+
= Datetime columns
|
123
|
+
|
124
|
+
Records approved before or after certain times...
|
125
|
+
|
126
|
+
User.approved_at_before(5.days.ago)
|
127
|
+
User.approved_at_after(4.weeks.ago)
|
128
|
+
|
129
|
+
Records with approved_at in the past or future...
|
130
|
+
|
131
|
+
User.approved_at_in_past
|
132
|
+
User.approved_at_in_future
|
133
|
+
|
134
|
+
Records with approved_at inside or outside of two times...
|
135
|
+
|
136
|
+
User.approved_at_inside(10.days.ago, 1.day.ago)
|
137
|
+
User.approved_at_outside(2.days.ago, 1.day.ago)
|
138
|
+
|
139
|
+
Records with certain year, month or day...
|
140
|
+
|
141
|
+
User.approved_at_in_year(2000)
|
142
|
+
User.approved_at_in_month(01)
|
143
|
+
User.approved_at_in_day(01)
|
144
|
+
|
145
|
+
Records with a duration (time delta between two columns) of, over or under a certain number of days...
|
146
|
+
|
147
|
+
User.with_duration_of(14, :approved_at, :rejected_at)
|
148
|
+
User.with_duration_over(14, :approved_at, :rejected_at)
|
149
|
+
User.with_duration_under(14, :approved_at, :rejected_at)
|
150
|
+
|
151
|
+
= Polymorphic relationships
|
152
|
+
|
153
|
+
Records which have an owner_type of User...
|
154
|
+
|
155
|
+
Post.for_owner_type(User)
|
156
|
+
|
157
|
+
= Associations
|
158
|
+
|
159
|
+
Records with the most and least associated records...
|
160
|
+
|
161
|
+
User.maximum_comments
|
162
|
+
User.minimum_comments
|
163
|
+
|
164
|
+
Records with associated records since a certain time...
|
165
|
+
|
166
|
+
User.recent_comments_since(2.days.ago)
|
167
|
+
User.recent_comments_and_posts_since(3.days.ago)
|
168
|
+
User.recent_comments_or_posts_since(4.days.ago)
|
169
|
+
|
170
|
+
= State columns
|
171
|
+
|
172
|
+
Records which are in a particular state, or not in a state...
|
173
|
+
|
174
|
+
Post.publication_state_draft
|
175
|
+
Post.post_type_not_open
|
176
|
+
|
177
|
+
Query methods on instances to check state...
|
178
|
+
|
179
|
+
Post.first.publication_state_draft?
|
180
|
+
Post.last.post_type_not_open?
|
181
|
+
|
182
|
+
= Limits
|
183
|
+
|
184
|
+
First x records...
|
185
|
+
|
186
|
+
User.limited(10)
|
187
|
+
|
188
|
+
= Named scopes
|
189
|
+
|
190
|
+
Because these are all named_scope, you can combine them.
|
191
|
+
|
192
|
+
To get all users that have a first_name set, who are admins and approved more than 2 weeks ago, ordered by their first name...
|
193
|
+
|
194
|
+
User.first_name_present.admin.approved_at_before(2.weeks.ago).by_first_name
|
195
|
+
|
196
|
+
To get the top 10 commenters...
|
197
|
+
|
198
|
+
User.maximim_comments.limited(10)
|
199
|
+
|
200
|
+
= License
|
201
|
+
|
202
|
+
Pacecar is free software, and may be redistributed under the terms specified in the MIT-LICENSE file.
|
data/init.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require 'pacecar'
|
data/lib/pacecar.rb
ADDED
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'pacecar/associations'
|
2
|
+
require 'pacecar/boolean'
|
3
|
+
require 'pacecar/datetime'
|
4
|
+
require 'pacecar/duration'
|
5
|
+
require 'pacecar/helpers'
|
6
|
+
require 'pacecar/limit'
|
7
|
+
require 'pacecar/order'
|
8
|
+
require 'pacecar/polymorph'
|
9
|
+
require 'pacecar/presence'
|
10
|
+
require 'pacecar/ranking'
|
11
|
+
require 'pacecar/search'
|
12
|
+
require 'pacecar/state'
|
13
|
+
|
14
|
+
module Pacecar
|
15
|
+
def self.included(base)
|
16
|
+
base.class_eval do
|
17
|
+
include Pacecar::Associations
|
18
|
+
include Pacecar::Boolean
|
19
|
+
include Pacecar::Datetime
|
20
|
+
include Pacecar::Duration
|
21
|
+
include Pacecar::Limit
|
22
|
+
include Pacecar::Order
|
23
|
+
include Pacecar::Polymorph
|
24
|
+
include Pacecar::Presence
|
25
|
+
include Pacecar::Ranking
|
26
|
+
include Pacecar::Search
|
27
|
+
include Pacecar::State
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
ActiveRecord::Base.send :include, Pacecar::Helpers
|
@@ -0,0 +1,39 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module Associations
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
|
9
|
+
def has_recent_records(*names)
|
10
|
+
names.each do |name|
|
11
|
+
named_scope "recent_#{name}_since".to_sym, lambda { |since|
|
12
|
+
{
|
13
|
+
:conditions => [conditions_for_name(name), { :since_time => since }]
|
14
|
+
}
|
15
|
+
}
|
16
|
+
end
|
17
|
+
unless names.first == names.last
|
18
|
+
named_scope "recent_#{names.join('_or_')}_since".to_sym, lambda { |since|
|
19
|
+
{
|
20
|
+
:conditions => [names.collect { |name| conditions_for_name(name) }.join(' or '), { :since_time => since }]
|
21
|
+
}
|
22
|
+
}
|
23
|
+
named_scope "recent_#{names.join('_and_')}_since".to_sym, lambda { |since|
|
24
|
+
{
|
25
|
+
:conditions => [names.collect { |name| conditions_for_name(name) }.join(' and '), { :since_time => since }]
|
26
|
+
}
|
27
|
+
}
|
28
|
+
end
|
29
|
+
end
|
30
|
+
|
31
|
+
protected
|
32
|
+
|
33
|
+
def conditions_for_name(name)
|
34
|
+
"((select count(*) from \"#{name}\" where \"#{name}\".#{reflections[name].primary_key_name} = #{quoted_table_name}.id and \"#{name}\".created_at > :since_time) > 0)"
|
35
|
+
end
|
36
|
+
|
37
|
+
end
|
38
|
+
end
|
39
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module Boolean
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def self.extended(base)
|
9
|
+
base.send :define_boolean_scopes
|
10
|
+
base.send :define_balance_count
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def define_boolean_scopes
|
16
|
+
boolean_column_names.each do |name|
|
17
|
+
named_scope name.to_sym, :conditions => ["#{quoted_table_name}.#{name} = ?", true]
|
18
|
+
named_scope "not_#{name}".to_sym, :conditions => ["#{quoted_table_name}.#{name} = ?", false]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def define_balance_count
|
23
|
+
boolean_column_names.each do |name|
|
24
|
+
self.class_eval %Q{
|
25
|
+
def self.#{name}_balance
|
26
|
+
#{name}.count - not_#{name}.count
|
27
|
+
end
|
28
|
+
}
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,64 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module Datetime
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def self.extended(base)
|
9
|
+
base.send :define_datetime_scopes
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def define_datetime_scopes
|
15
|
+
datetime_column_names.each do |name|
|
16
|
+
define_before_after_scopes(name)
|
17
|
+
define_past_future_scopes(name)
|
18
|
+
define_inside_outside_scopes(name)
|
19
|
+
define_in_date_scopes(name)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def define_before_after_scopes(name)
|
24
|
+
named_scope "#{name}_before".to_sym, lambda { |time|
|
25
|
+
{ :conditions => ["#{quoted_table_name}.#{name} < ?", time] }
|
26
|
+
}
|
27
|
+
named_scope "#{name}_after".to_sym, lambda { |time|
|
28
|
+
{ :conditions => ["#{quoted_table_name}.#{name} > ?", time] }
|
29
|
+
}
|
30
|
+
end
|
31
|
+
|
32
|
+
def define_past_future_scopes(name)
|
33
|
+
named_scope "#{name}_in_past", lambda {
|
34
|
+
{ :conditions => ["#{quoted_table_name}.#{name} < ?", Time.now] }
|
35
|
+
}
|
36
|
+
named_scope "#{name}_in_future", lambda {
|
37
|
+
{ :conditions => ["#{quoted_table_name}.#{name} > ?", Time.now] }
|
38
|
+
}
|
39
|
+
end
|
40
|
+
|
41
|
+
def define_inside_outside_scopes(name)
|
42
|
+
named_scope "#{name}_inside".to_sym, lambda { |start, stop|
|
43
|
+
{ :conditions => ["#{quoted_table_name}.#{name} > ? and #{quoted_table_name}.#{name} < ?", start, stop] }
|
44
|
+
}
|
45
|
+
named_scope "#{name}_outside".to_sym, lambda { |start, stop|
|
46
|
+
{ :conditions => ["#{quoted_table_name}.#{name} < ? and #{quoted_table_name}.#{name} > ?", start, stop] }
|
47
|
+
}
|
48
|
+
end
|
49
|
+
|
50
|
+
def define_in_date_scopes(name)
|
51
|
+
named_scope "#{name}_in_year".to_sym, lambda { |year|
|
52
|
+
{ :conditions => ["year(#{quoted_table_name}.#{name}) = ?", year] }
|
53
|
+
}
|
54
|
+
named_scope "#{name}_in_month".to_sym, lambda { |month|
|
55
|
+
{ :conditions => ["month(#{quoted_table_name}.#{name}) = ?", month] }
|
56
|
+
}
|
57
|
+
named_scope "#{name}_in_day".to_sym, lambda { |day|
|
58
|
+
{ :conditions => ["day(#{quoted_table_name}.#{name}) = ?", day] }
|
59
|
+
}
|
60
|
+
end
|
61
|
+
|
62
|
+
end
|
63
|
+
end
|
64
|
+
end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module Duration
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def self.extended(base)
|
9
|
+
base.send :define_duration_scopes
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def define_duration_scopes
|
15
|
+
named_scope :with_duration_of, lambda { |duration, start, stop|
|
16
|
+
{ :conditions => ["datediff(#{quoted_table_name}.#{start}, #{quoted_table_name}.#{stop}) = ?", duration] }
|
17
|
+
}
|
18
|
+
named_scope :with_duration_over, lambda { |duration, start, stop|
|
19
|
+
{ :conditions => ["datediff(#{quoted_table_name}.#{start}, #{quoted_table_name}.#{stop}) > ?", duration] }
|
20
|
+
}
|
21
|
+
named_scope :with_duration_under, lambda { |duration, start, stop|
|
22
|
+
{ :conditions => ["datediff(#{quoted_table_name}.#{start}, #{quoted_table_name}.#{stop}) < ?", duration] }
|
23
|
+
}
|
24
|
+
end
|
25
|
+
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module Helpers
|
3
|
+
|
4
|
+
mattr_accessor :options
|
5
|
+
self.options = {
|
6
|
+
:state_pattern => /_(type|state)$/i,
|
7
|
+
:default_limit => 10
|
8
|
+
}
|
9
|
+
|
10
|
+
def self.included(base)
|
11
|
+
base.extend ClassMethods
|
12
|
+
end
|
13
|
+
|
14
|
+
module ClassMethods
|
15
|
+
|
16
|
+
def safe_columns
|
17
|
+
begin
|
18
|
+
columns
|
19
|
+
rescue ActiveRecord::StatementInvalid # If the table does not exist
|
20
|
+
Array.new
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def column_names
|
25
|
+
safe_columns.collect(&:name)
|
26
|
+
end
|
27
|
+
|
28
|
+
def column_names_for_type(*types)
|
29
|
+
safe_columns.select { |column| types.include? column.type }.collect(&:name)
|
30
|
+
end
|
31
|
+
|
32
|
+
def column_names_without_type(*types)
|
33
|
+
safe_columns.select { |column| ! types.include? column.type }.collect(&:name)
|
34
|
+
end
|
35
|
+
|
36
|
+
def boolean_column_names
|
37
|
+
column_names_for_type :boolean
|
38
|
+
end
|
39
|
+
|
40
|
+
def datetime_column_names
|
41
|
+
column_names_for_type :datetime, :date
|
42
|
+
end
|
43
|
+
|
44
|
+
def text_and_string_column_names
|
45
|
+
column_names_for_type :text, :string
|
46
|
+
end
|
47
|
+
|
48
|
+
def non_state_text_and_string_columns
|
49
|
+
text_and_string_column_names.reject { |name| name =~ Pacecar::Helpers.options[:state_pattern] }
|
50
|
+
end
|
51
|
+
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module Limit
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def self.extended(base)
|
9
|
+
base.send :define_limit_scopes
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def define_limit_scopes
|
15
|
+
named_scope :limited, lambda { |*args|
|
16
|
+
{ :limit => args.flatten.first || (defined?(per_page) ? per_page : Pacecar::Helpers.options[:default_limit]) }
|
17
|
+
}
|
18
|
+
end
|
19
|
+
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,24 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module Order
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def self.extended(base)
|
9
|
+
base.send :define_order_scopes
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def define_order_scopes
|
15
|
+
column_names.each do |name|
|
16
|
+
named_scope "by_#{name}".to_sym, lambda { |*args|
|
17
|
+
{ :order => "#{quoted_table_name}.#{name} #{args.flatten.first || 'asc'}" }
|
18
|
+
}
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module Polymorph
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
|
9
|
+
def has_polymorph(name)
|
10
|
+
named_scope "for_#{name}_type".to_sym, lambda { |type|
|
11
|
+
{ :conditions => ["#{quoted_table_name}.#{name}_type = ?", type.to_s] }
|
12
|
+
}
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module Presence
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def self.extended(base)
|
9
|
+
base.send :define_presence_scopes
|
10
|
+
end
|
11
|
+
|
12
|
+
protected
|
13
|
+
|
14
|
+
def define_presence_scopes
|
15
|
+
column_names_without_type(:boolean).each do |name|
|
16
|
+
named_scope "#{name}_present".to_sym, :conditions => "#{quoted_table_name}.#{name} is not null"
|
17
|
+
named_scope "#{name}_missing".to_sym, :conditions => "#{quoted_table_name}.#{name} is null"
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module Ranking
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
|
9
|
+
def has_ranking(association)
|
10
|
+
define_ranking_scope association, :maximum, :desc
|
11
|
+
define_ranking_scope association, :minimum, :asc
|
12
|
+
end
|
13
|
+
|
14
|
+
protected
|
15
|
+
|
16
|
+
def define_ranking_scope(association, name, direction)
|
17
|
+
named_scope "#{name}_#{association}",
|
18
|
+
:joins => "inner join #{association} on #{association}.#{reflections[association].primary_key_name} = #{quoted_table_name}.#{primary_key}",
|
19
|
+
:select => "#{quoted_table_name}.*, count(#{quoted_table_name}.#{primary_key}) as #{association}_count",
|
20
|
+
:group => "#{association}.#{reflections[association].primary_key_name}",
|
21
|
+
:order => "#{association}_count #{direction}"
|
22
|
+
end
|
23
|
+
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module Search
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
def self.extended(base)
|
9
|
+
base.send :define_search_scopes
|
10
|
+
base.send :define_basic_search_scope
|
11
|
+
end
|
12
|
+
|
13
|
+
protected
|
14
|
+
|
15
|
+
def define_search_scopes
|
16
|
+
text_and_string_column_names.each do |name|
|
17
|
+
named_scope "#{name}_matches".to_sym, lambda { |query|
|
18
|
+
{ :conditions => ["upper(#{quoted_table_name}.#{name}) like upper(:query)", { :query => "%#{query}%" }] }
|
19
|
+
}
|
20
|
+
named_scope "#{name}_starts_with".to_sym, lambda { |query|
|
21
|
+
{ :conditions => ["upper(#{quoted_table_name}.#{name}) like upper(:query)", { :query => "#{query}%" }] }
|
22
|
+
}
|
23
|
+
named_scope "#{name}_ends_with".to_sym, lambda { |query|
|
24
|
+
{ :conditions => ["upper(#{quoted_table_name}.#{name}) like upper(:query)", { :query => "%#{query}" }] }
|
25
|
+
}
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
def define_basic_search_scope
|
30
|
+
named_scope :search_for, lambda { |*args|
|
31
|
+
opts = args.extract_options!
|
32
|
+
query = args.flatten.first
|
33
|
+
columns = opts[:on] || non_state_text_and_string_columns
|
34
|
+
joiner = opts[:require].eql?(:all) ? 'and' : 'or'
|
35
|
+
match = columns.collect { |name| "upper(#{quoted_table_name}.#{name}) like upper(:query)" }.join(" #{joiner} ")
|
36
|
+
{ :conditions => [match, { :query => "%#{query}%" } ] }
|
37
|
+
}
|
38
|
+
end
|
39
|
+
|
40
|
+
end
|
41
|
+
end
|
42
|
+
end
|
@@ -0,0 +1,30 @@
|
|
1
|
+
module Pacecar
|
2
|
+
module State
|
3
|
+
def self.included(base)
|
4
|
+
base.extend ClassMethods
|
5
|
+
end
|
6
|
+
|
7
|
+
module ClassMethods
|
8
|
+
|
9
|
+
def has_state(*names)
|
10
|
+
opts = names.extract_options!
|
11
|
+
names.each do |name|
|
12
|
+
constant = opts[:with] || const_get(name.to_s.pluralize.upcase)
|
13
|
+
constant.each do |state|
|
14
|
+
named_scope "#{name}_#{state.downcase}".to_sym, :conditions => ["#{quoted_table_name}.#{name} = ?", state]
|
15
|
+
named_scope "#{name}_not_#{state.downcase}".to_sym, :conditions => ["#{quoted_table_name}.#{name} <> ?", state]
|
16
|
+
self.class_eval %Q{
|
17
|
+
def #{name}_#{state.downcase}?
|
18
|
+
#{name} == '#{state}'
|
19
|
+
end
|
20
|
+
def #{name}_not_#{state.downcase}?
|
21
|
+
#{name} != '#{state}'
|
22
|
+
end
|
23
|
+
}
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
metadata
ADDED
@@ -0,0 +1,71 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ddollar-pacecar
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.1.6
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Matt Jankowski
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2009-07-30 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies: []
|
15
|
+
|
16
|
+
description: Generated scopes for ActiveRecord classes.
|
17
|
+
email: mjankowski@thoughtbot.com
|
18
|
+
executables: []
|
19
|
+
|
20
|
+
extensions: []
|
21
|
+
|
22
|
+
extra_rdoc_files:
|
23
|
+
- README.rdoc
|
24
|
+
files:
|
25
|
+
- init.rb
|
26
|
+
- lib
|
27
|
+
- lib/pacecar
|
28
|
+
- lib/pacecar/associations.rb
|
29
|
+
- lib/pacecar/boolean.rb
|
30
|
+
- lib/pacecar/datetime.rb
|
31
|
+
- lib/pacecar/duration.rb
|
32
|
+
- lib/pacecar/helpers.rb
|
33
|
+
- lib/pacecar/limit.rb
|
34
|
+
- lib/pacecar/order.rb
|
35
|
+
- lib/pacecar/polymorph.rb
|
36
|
+
- lib/pacecar/presence.rb
|
37
|
+
- lib/pacecar/ranking.rb
|
38
|
+
- lib/pacecar/search.rb
|
39
|
+
- lib/pacecar/state.rb
|
40
|
+
- lib/pacecar.rb
|
41
|
+
- MIT-LICENSE
|
42
|
+
- README.rdoc
|
43
|
+
has_rdoc: true
|
44
|
+
homepage: http://github.com/thoughtbot/pacecar
|
45
|
+
licenses:
|
46
|
+
post_install_message:
|
47
|
+
rdoc_options: []
|
48
|
+
|
49
|
+
require_paths:
|
50
|
+
- lib
|
51
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
52
|
+
requirements:
|
53
|
+
- - ">="
|
54
|
+
- !ruby/object:Gem::Version
|
55
|
+
version: "0"
|
56
|
+
version:
|
57
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - ">="
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: "0"
|
62
|
+
version:
|
63
|
+
requirements: []
|
64
|
+
|
65
|
+
rubyforge_project:
|
66
|
+
rubygems_version: 1.3.5
|
67
|
+
signing_key:
|
68
|
+
specification_version: 2
|
69
|
+
summary: Pacecar adds named_scope methods to ActiveRecord classes via database column introspection.
|
70
|
+
test_files: []
|
71
|
+
|