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 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