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 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
@@ -1,4 +1,10 @@
1
1
  #!/usr/bin/env ruby
2
- require 'rubygems'
3
- require 'cheat'
2
+
3
+ begin
4
+ require 'cheat'
5
+ rescue LoadError
6
+ require 'rubygems'
7
+ require 'cheat'
8
+ end
9
+
4
10
  Cheat.sheets(ARGV)
@@ -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.
@@ -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,8 @@
1
+ module Ambition
2
+ module Count
3
+ def size
4
+ count(query_context.to_hash)
5
+ end
6
+ alias_method :length, :size
7
+ end
8
+ end
@@ -0,0 +1,9 @@
1
+ module Ambition
2
+ module Enumerable
3
+ include ::Enumerable
4
+
5
+ def each(&block)
6
+ find(:all, query_context.to_hash).each(&block)
7
+ end
8
+ end
9
+ end
@@ -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