cheat 1.2.1 → 1.3.0
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/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
|