cheat 1.2.1 → 1.3.0
Sign up to get free protection for your applications and to get access to all the features.
- data/README +5 -0
- data/bin/cheat +8 -2
- data/lib/ambition/LICENSE +18 -0
- data/lib/ambition/README +143 -0
- data/lib/ambition/Rakefile +29 -0
- data/lib/ambition/init.rb +1 -0
- data/lib/ambition/lib/ambition.rb +21 -0
- data/lib/ambition/lib/ambition/count.rb +8 -0
- data/lib/ambition/lib/ambition/enumerable.rb +9 -0
- data/lib/ambition/lib/ambition/limit.rb +34 -0
- data/lib/ambition/lib/ambition/order.rb +52 -0
- data/lib/ambition/lib/ambition/processor.rb +54 -0
- data/lib/ambition/lib/ambition/query.rb +85 -0
- data/lib/ambition/lib/ambition/where.rb +164 -0
- data/lib/ambition/lib/proc_to_ruby.rb +36 -0
- data/lib/ambition/test/chaining_test.rb +34 -0
- data/lib/ambition/test/count_test.rb +17 -0
- data/lib/ambition/test/enumerable_test.rb +51 -0
- data/lib/ambition/test/helper.rb +30 -0
- data/lib/ambition/test/join_test.rb +32 -0
- data/lib/ambition/test/limit_test.rb +37 -0
- data/lib/ambition/test/order_test.rb +48 -0
- data/lib/ambition/test/types_test.rb +56 -0
- data/lib/ambition/test/where_test.rb +131 -0
- data/lib/cheat.rb +81 -11
- data/lib/{diffr.rb → cheat/diffr.rb} +0 -1
- data/lib/{responder.rb → cheat/responder.rb} +3 -3
- data/lib/cheat/rv_harness.rb +41 -0
- data/lib/{site.rb → cheat/site.rb} +114 -93
- data/lib/cheat/version.rb +3 -0
- data/lib/{wrap.rb → cheat/wrap.rb} +0 -0
- metadata +77 -42
- data/lib/cheat/commands.rb +0 -41
- data/lib/new_cheat.rb +0 -163
- data/test/test_cheat.rb +0 -50
data/README
CHANGED
@@ -19,6 +19,11 @@ To add a cheat sheet, use the --add switch.
|
|
19
19
|
|
20
20
|
$ cheat readme --add
|
21
21
|
|
22
|
+
To execute a sheet, use the --execute or -x switch.
|
23
|
+
|
24
|
+
$ cheat rspec_rails_install_edge --execute
|
25
|
+
$ cheat rspec_rails_install_edge --x
|
26
|
+
|
22
27
|
Special Thanks To:
|
23
28
|
- Evan Weaver
|
24
29
|
- Kevin Marsh
|
data/bin/cheat
CHANGED
@@ -0,0 +1,18 @@
|
|
1
|
+
Copyright (c) 2007 Chris Wanstrath
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining a copy of
|
4
|
+
this software and associated documentation files (the "Software"), to deal in
|
5
|
+
the Software without restriction, including without limitation the rights to
|
6
|
+
use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
|
7
|
+
the Software, and to permit persons to whom the Software is furnished to do so,
|
8
|
+
subject to the following conditions:
|
9
|
+
|
10
|
+
The above copyright notice and this permission notice shall be included in all
|
11
|
+
copies or substantial portions of the Software.
|
12
|
+
|
13
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
|
14
|
+
IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
|
15
|
+
FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
|
16
|
+
COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
|
17
|
+
IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
|
18
|
+
CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/lib/ambition/README
ADDED
@@ -0,0 +1,143 @@
|
|
1
|
+
== Ambitious SQL
|
2
|
+
|
3
|
+
A simple experiment and even simpler ActiveRecord library.
|
4
|
+
|
5
|
+
I could tell you all about how awesome the internals are, or
|
6
|
+
how fun it was to write, or how it'll make you rich and famous,
|
7
|
+
but instead I'm just going to show you some examples.
|
8
|
+
|
9
|
+
== Examples
|
10
|
+
|
11
|
+
Basically, you write your SQL in Ruby. No, not in Ruby. As Ruby.
|
12
|
+
|
13
|
+
User.select { |u| u.city == 'San Francisco' }.each do |user|
|
14
|
+
puts user.name
|
15
|
+
end
|
16
|
+
|
17
|
+
And that's it.
|
18
|
+
|
19
|
+
The key is the +each+ method. You build up a +Query+ using +select+, +detect+,
|
20
|
+
+limit+, and +sort_by+, then call +each+ on it. This'll run the query and enumerate
|
21
|
+
through the results.
|
22
|
+
|
23
|
+
Our +Query+ object has two useful methods: +to_sql+ and +to_hash+. With these, we can
|
24
|
+
check out what exactly we're building.
|
25
|
+
|
26
|
+
See, +to_sql+:
|
27
|
+
>> User.select { |m| m.name == 'jon' }.to_sql
|
28
|
+
=> "SELECT * FROM users WHERE users.`name` = 'jon'"
|
29
|
+
|
30
|
+
See, +to_hash+:
|
31
|
+
>> User.select { |m| m.name == 'jon' }.to_hash
|
32
|
+
=> {:conditions=>"users.`name` = 'jon'"}
|
33
|
+
|
34
|
+
== Limitations
|
35
|
+
|
36
|
+
You can use variables, but any more complex Ruby (right now) won't work
|
37
|
+
inside your blocks. Just do it outside the block and assign it to a variable, okay?
|
38
|
+
|
39
|
+
Instead of:
|
40
|
+
User.select { |m| m.date == 2.days.ago }
|
41
|
+
|
42
|
+
Just do:
|
43
|
+
date = 2.days.ago
|
44
|
+
User.select { |m| m.date == date }
|
45
|
+
|
46
|
+
Instance variables and globals work, too.
|
47
|
+
|
48
|
+
== Equality -- select { |u| u.field == 'bob' }
|
49
|
+
|
50
|
+
>> User.select { |m| m.name == 'jon' }.to_sql
|
51
|
+
=> "SELECT * FROM users WHERE users.`name` = 'jon'"
|
52
|
+
|
53
|
+
>> User.select { |m| m.name != 'jon' }.to_sql
|
54
|
+
=> "SELECT * FROM users WHERE users.`name` <> 'jon'"
|
55
|
+
|
56
|
+
>> User.select { |m| m.name == 'jon' && m.age == 21 }.to_sql
|
57
|
+
=> "SELECT * FROM users WHERE (users.`name` = 'jon' AND users.`age` = 21)"
|
58
|
+
|
59
|
+
>> User.select { |m| m.name == 'jon' || m.age == 21 }.to_sql
|
60
|
+
=> "SELECT * FROM users WHERE (users.`name` = 'jon' OR users.`age` = 21)"
|
61
|
+
|
62
|
+
>> User.select { |m| m.name == 'jon' || m.age == 21 && m.password == 'pass' }.to_sql
|
63
|
+
=> "SELECT * FROM users WHERE (users.`name` = 'jon' OR (users.`age` = 21 AND users.`password` = 'pass'))"
|
64
|
+
|
65
|
+
>> User.select { |m| (m.name == 'jon' || m.name == 'rick') && m.age == 21 }.to_sql
|
66
|
+
=> "SELECT * FROM users WHERE ((users.`name` = 'jon' OR users.`name` = 'rick') AND users.`age` = 21)"
|
67
|
+
|
68
|
+
== Comparisons -- select { |u| u.age > 21 }
|
69
|
+
|
70
|
+
>> User.select { |m| m.age > 21 }.to_sql
|
71
|
+
=> "SELECT * FROM users WHERE users.`age` > 21"
|
72
|
+
|
73
|
+
>> User.select { |m| m.age < 21 }.to_sql
|
74
|
+
=> "SELECT * FROM users WHERE users.`age` < 21"
|
75
|
+
|
76
|
+
>> sql = User.select { |m| [1, 2, 3, 4].include? m.id }.to_sql
|
77
|
+
=> "SELECT * FROM users WHERE users.`id` IN (1, 2, 3, 4)"
|
78
|
+
|
79
|
+
== LIKE and REGEXP (RLIKE) -- select { |m| m.name =~ 'chris' }
|
80
|
+
|
81
|
+
>> User.select { |m| m.name =~ 'chris' }.to_sql
|
82
|
+
=> "SELECT * FROM users WHERE users.`name` LIKE 'chris'"
|
83
|
+
|
84
|
+
>> User.select { |m| m.name =~ 'chri%' }.to_sql
|
85
|
+
=> "SELECT * FROM users WHERE users.`name` LIKE 'chri%'"
|
86
|
+
|
87
|
+
>> User.select { |m| m.name !~ 'chris' }.to_sql
|
88
|
+
=> "SELECT * FROM users WHERE users.`name` NOT LIKE 'chris'"
|
89
|
+
|
90
|
+
>> User.select { |m| !(m.name =~ 'chris') }.to_sql
|
91
|
+
=> "SELECT * FROM users WHERE users.`name` NOT LIKE 'chris'"
|
92
|
+
|
93
|
+
>> User.select { |m| m.name =~ /chris/ }.to_sql
|
94
|
+
=> "SELECT * FROM users WHERE users.`name` REGEXP 'chris'"
|
95
|
+
|
96
|
+
== #detect
|
97
|
+
|
98
|
+
>> User.detect { |m| m.name == 'chris' }.to_sql
|
99
|
+
=> "SELECT * FROM users WHERE users.`name` = 'chris' LIMIT 1"
|
100
|
+
|
101
|
+
== LIMITs -- first, first(x), [offset, limit]
|
102
|
+
|
103
|
+
>> User.select { |m| m.name == 'jon' }.first.to_sql
|
104
|
+
=> "SELECT * FROM users WHERE users.`name` = 'jon' LIMIT 1"
|
105
|
+
|
106
|
+
>> User.select { |m| m.name == 'jon' }.first(5).to_sql
|
107
|
+
=> "SELECT * FROM users WHERE users.`name` = 'jon' LIMIT 5"
|
108
|
+
|
109
|
+
>> User.select { |m| m.name == 'jon' }[10, 20].to_sql
|
110
|
+
=> "SELECT * FROM users WHERE users.`name` = 'jon' LIMIT 10, 20"
|
111
|
+
|
112
|
+
== ORDER -- sort_by { |u| u.field }
|
113
|
+
|
114
|
+
>> User.select { |m| m.name == 'jon' }.sort_by { |m| m.name }.to_sql
|
115
|
+
=> "SELECT * FROM users WHERE users.`name` = 'jon' ORDER BY name"
|
116
|
+
|
117
|
+
>> User.select { |m| m.name == 'jon' }.sort_by { |m| [ m.name, m.age ] }.to_sql
|
118
|
+
=> "SELECT * FROM users WHERE users.`name` = 'jon' ORDER BY name, age"
|
119
|
+
|
120
|
+
>> User.select { |m| m.name == 'jon' }.sort_by { |m| [ m.name, -m.age ] }.to_sql
|
121
|
+
=> "SELECT * FROM users WHERE users.`name` = 'jon' ORDER BY name, age DESC"
|
122
|
+
|
123
|
+
>> User.select { |m| m.name == 'jon' }.sort_by { |m| [ -m.name, -m.age ] }.to_sql
|
124
|
+
=> "SELECT * FROM users WHERE users.`name` = 'jon' ORDER BY name DESC, age DESC"
|
125
|
+
|
126
|
+
>> User.select { |m| m.name == 'jon' }.sort_by { |m| -m.age }.to_sql
|
127
|
+
=> "SELECT * FROM users WHERE users.`name` = 'jon' ORDER BY age DESC"
|
128
|
+
|
129
|
+
>> User.select { |m| m.name == 'jon' }.sort_by { rand }.to_sql
|
130
|
+
=> "SELECT * FROM users WHERE users.`name` = 'jon' ORDER BY RAND()"
|
131
|
+
|
132
|
+
== COUNT -- select { |u| u.name == 'jon' }.size
|
133
|
+
|
134
|
+
>> User.select { |m| m.name == 'jon' }.size
|
135
|
+
=> 21
|
136
|
+
|
137
|
+
== SELECT * FROM bugs
|
138
|
+
|
139
|
+
Found a bug? Sweet. Add it at the Lighthouse: http://err.lighthouseapp.com/projects/466-plugins/tickets/new
|
140
|
+
|
141
|
+
Feature requests are welcome. Ideas for cross-table stuff and joins, especially.
|
142
|
+
|
143
|
+
* Chris Wanstrath [ chris@ozmm.org ]
|
@@ -0,0 +1,29 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'rake/testtask'
|
3
|
+
require 'rake/rdoctask'
|
4
|
+
|
5
|
+
desc 'Default: run unit tests.'
|
6
|
+
task :default => :test
|
7
|
+
|
8
|
+
desc 'Test it!'
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
10
|
+
t.pattern = 'test/**/*_test.rb'
|
11
|
+
t.verbose = true
|
12
|
+
end
|
13
|
+
|
14
|
+
desc 'Generate RDoc documentation'
|
15
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
16
|
+
files = ['README', 'LICENSE', 'lib/**/*.rb']
|
17
|
+
rdoc.rdoc_files.add(files)
|
18
|
+
rdoc.main = "README" # page to start on
|
19
|
+
rdoc.title = "ambition"
|
20
|
+
rdoc.template = File.exists?(t="/Users/chris/ruby/projects/err/rock/template.rb") ? t : "/var/www/rock/template.rb"
|
21
|
+
rdoc.rdoc_dir = 'doc' # rdoc output folder
|
22
|
+
rdoc.options << '--inline-source'
|
23
|
+
end
|
24
|
+
|
25
|
+
desc 'Generate coverage reports'
|
26
|
+
task :rcov do
|
27
|
+
`rcov -e gems test/*_test.rb`
|
28
|
+
puts 'Generated coverage reports.'
|
29
|
+
end
|
@@ -0,0 +1 @@
|
|
1
|
+
require 'ambition'
|
@@ -0,0 +1,21 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'proc_to_ruby'
|
3
|
+
require 'ambition/processor'
|
4
|
+
require 'ambition/query'
|
5
|
+
require 'ambition/where'
|
6
|
+
require 'ambition/order'
|
7
|
+
require 'ambition/limit'
|
8
|
+
require 'ambition/count'
|
9
|
+
require 'ambition/enumerable'
|
10
|
+
|
11
|
+
module Ambition
|
12
|
+
include Where, Order, Limit, Enumerable, Count
|
13
|
+
|
14
|
+
attr_accessor :query_context
|
15
|
+
|
16
|
+
def query_context
|
17
|
+
@query_context || Query.new(self)
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
ActiveRecord::Base.extend Ambition
|
@@ -0,0 +1,34 @@
|
|
1
|
+
module Ambition
|
2
|
+
module Limit
|
3
|
+
def first(limit = 1, offset = nil)
|
4
|
+
query_context.add LimitProcessor.new(limit, offset)
|
5
|
+
find(limit == 1 ? :first : :all, query_context.to_hash)
|
6
|
+
end
|
7
|
+
|
8
|
+
def [](offset, limit = nil)
|
9
|
+
return first(offset, limit) if limit
|
10
|
+
|
11
|
+
if offset.is_a? Range
|
12
|
+
limit = offset.end
|
13
|
+
limit -= 1 if offset.exclude_end?
|
14
|
+
first(offset.first, limit - offset.first)
|
15
|
+
else
|
16
|
+
first(offset, 1)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
class LimitProcessor
|
22
|
+
def initialize(*args)
|
23
|
+
@args = args
|
24
|
+
end
|
25
|
+
|
26
|
+
def key
|
27
|
+
:limit
|
28
|
+
end
|
29
|
+
|
30
|
+
def to_s
|
31
|
+
@args.compact * ', '
|
32
|
+
end
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module Ambition
|
2
|
+
module Order
|
3
|
+
def sort_by(&block)
|
4
|
+
query_context.add OrderProcessor.new(table_name, block)
|
5
|
+
end
|
6
|
+
end
|
7
|
+
|
8
|
+
class OrderProcessor < Processor
|
9
|
+
def initialize(table_name, block)
|
10
|
+
super()
|
11
|
+
@receiver = nil
|
12
|
+
@table_name = table_name
|
13
|
+
@block = block
|
14
|
+
@key = :order
|
15
|
+
end
|
16
|
+
|
17
|
+
##
|
18
|
+
# Sexp Processing Methods
|
19
|
+
def process_call(exp)
|
20
|
+
receiver, method, other = *exp
|
21
|
+
exp.clear
|
22
|
+
|
23
|
+
translation(receiver, method, other)
|
24
|
+
end
|
25
|
+
|
26
|
+
def process_vcall(exp)
|
27
|
+
if (method = exp.shift) == :rand
|
28
|
+
'RAND()'
|
29
|
+
else
|
30
|
+
raise "Not implemented: :vcall for #{method}"
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
def process_masgn(exp)
|
35
|
+
exp.clear
|
36
|
+
''
|
37
|
+
end
|
38
|
+
|
39
|
+
##
|
40
|
+
# Helpers!
|
41
|
+
def translation(receiver, method, other)
|
42
|
+
case method
|
43
|
+
when :-@
|
44
|
+
"#{process(receiver)} DESC"
|
45
|
+
when :__send__
|
46
|
+
"#{@table_name}.#{eval('to_s', @block)}"
|
47
|
+
else
|
48
|
+
"#{@table_name}.#{method}"
|
49
|
+
end
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'active_record/connection_adapters/abstract/quoting'
|
2
|
+
|
3
|
+
module Ambition
|
4
|
+
class Processor < SexpProcessor
|
5
|
+
include ActiveRecord::ConnectionAdapters::Quoting
|
6
|
+
|
7
|
+
attr_reader :key, :join_string, :prefix
|
8
|
+
|
9
|
+
def initialize
|
10
|
+
super()
|
11
|
+
@strict = false
|
12
|
+
@expected = String
|
13
|
+
@auto_shift_type = true
|
14
|
+
@warn_on_default = false
|
15
|
+
@default_method = :process_error
|
16
|
+
end
|
17
|
+
|
18
|
+
##
|
19
|
+
# Processing methods
|
20
|
+
def process_error(exp)
|
21
|
+
raise "Missing process method for sexp: #{exp.inspect}"
|
22
|
+
end
|
23
|
+
|
24
|
+
def process_proc(exp)
|
25
|
+
receiver, body = process(exp.shift), exp.shift
|
26
|
+
return process(body)
|
27
|
+
end
|
28
|
+
|
29
|
+
def process_dasgn_curr(exp)
|
30
|
+
@receiver = exp.shift
|
31
|
+
return @receiver.to_s
|
32
|
+
end
|
33
|
+
|
34
|
+
def process_array(exp)
|
35
|
+
arrayed = exp.map { |m| process(m) }
|
36
|
+
exp.clear
|
37
|
+
return arrayed.join(', ')
|
38
|
+
end
|
39
|
+
|
40
|
+
##
|
41
|
+
# Helper methods
|
42
|
+
def to_s
|
43
|
+
process(@block.to_sexp).squeeze(' ')
|
44
|
+
end
|
45
|
+
|
46
|
+
def sanitize(value)
|
47
|
+
case value.to_s
|
48
|
+
when 'true' then '1'
|
49
|
+
when 'false' then '0'
|
50
|
+
else ActiveRecord::Base.connection.quote(value) rescue quote(value)
|
51
|
+
end
|
52
|
+
end
|
53
|
+
end
|
54
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module Ambition
|
2
|
+
class Query
|
3
|
+
@@select = 'SELECT * FROM %s %s'
|
4
|
+
|
5
|
+
def initialize(owner)
|
6
|
+
@table_name = owner.table_name
|
7
|
+
@owner = owner
|
8
|
+
@clauses = []
|
9
|
+
end
|
10
|
+
|
11
|
+
def add(clause)
|
12
|
+
@clauses << clause
|
13
|
+
self
|
14
|
+
end
|
15
|
+
|
16
|
+
def query_context
|
17
|
+
self
|
18
|
+
end
|
19
|
+
|
20
|
+
def method_missing(method, *args, &block)
|
21
|
+
with_context do
|
22
|
+
@owner.send(method, *args, &block)
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def with_context
|
27
|
+
@owner.query_context = self
|
28
|
+
ret = yield
|
29
|
+
ensure
|
30
|
+
@owner.query_context = nil
|
31
|
+
ret
|
32
|
+
end
|
33
|
+
|
34
|
+
def to_hash
|
35
|
+
keyed = keyed_clauses
|
36
|
+
hash = {}
|
37
|
+
|
38
|
+
unless (where = keyed[:conditions]).blank?
|
39
|
+
hash[:conditions] = Array(where)
|
40
|
+
hash[:conditions] *= ' AND '
|
41
|
+
end
|
42
|
+
|
43
|
+
unless (includes = keyed[:includes]).blank?
|
44
|
+
hash[:includes] = includes.flatten
|
45
|
+
end
|
46
|
+
|
47
|
+
if order = keyed[:order]
|
48
|
+
hash[:order] = order.join(', ')
|
49
|
+
end
|
50
|
+
|
51
|
+
if limit = keyed[:limit]
|
52
|
+
hash[:limit] = limit.join(', ')
|
53
|
+
end
|
54
|
+
|
55
|
+
hash
|
56
|
+
end
|
57
|
+
|
58
|
+
def to_s
|
59
|
+
hash = keyed_clauses
|
60
|
+
|
61
|
+
sql = []
|
62
|
+
sql << "JOIN #{hash[:includes].join(', ')}" unless hash[:includes].blank?
|
63
|
+
sql << "WHERE #{hash[:conditions].join(' AND ')}" unless hash[:conditions].blank?
|
64
|
+
sql << "ORDER BY #{hash[:order].join(', ')}" unless hash[:order].blank?
|
65
|
+
sql << "LIMIT #{hash[:limit].join(', ')}" unless hash[:limit].blank?
|
66
|
+
|
67
|
+
@@select % [ @table_name, sql.join(' ') ]
|
68
|
+
end
|
69
|
+
alias_method :to_sql, :to_s
|
70
|
+
|
71
|
+
def keyed_clauses
|
72
|
+
@clauses.inject({}) do |hash, clause|
|
73
|
+
hash[clause.key] ||= []
|
74
|
+
hash[clause.key] << clause.to_s
|
75
|
+
|
76
|
+
if clause.respond_to?(:includes) && !clause.includes.blank?
|
77
|
+
hash[:includes] ||= []
|
78
|
+
hash[:includes] << clause.includes
|
79
|
+
end
|
80
|
+
|
81
|
+
hash
|
82
|
+
end
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|