ambition 0.2.0 → 0.2.1

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/Manifest CHANGED
@@ -1,21 +1,20 @@
1
1
  ./init.rb
2
- ./lib/ambition/count.rb
2
+ ./lib/ambition/api.rb
3
3
  ./lib/ambition/database_statements.rb
4
- ./lib/ambition/enumerable.rb
5
- ./lib/ambition/limit.rb
6
- ./lib/ambition/order.rb
7
4
  ./lib/ambition/proc_to_ruby.rb
8
5
  ./lib/ambition/processor.rb
9
6
  ./lib/ambition/query.rb
10
- ./lib/ambition/where.rb
7
+ ./lib/ambition/select_processor.rb
8
+ ./lib/ambition/simple_processor.rb
9
+ ./lib/ambition/sort_processor.rb
11
10
  ./lib/ambition.rb
12
11
  ./LICENSE
13
12
  ./Manifest
14
13
  ./Rakefile
15
14
  ./README
15
+ ./test/benchmark.rb
16
16
  ./test/chaining_test.rb
17
17
  ./test/console
18
- ./test/constructive_test.rb
19
18
  ./test/count_test.rb
20
19
  ./test/databases/boot.rb
21
20
  ./test/databases/database.yml
@@ -35,11 +34,12 @@
35
34
  ./test/databases/lib/activerecord_test_connector.rb
36
35
  ./test/databases/lib/load_fixtures.rb
37
36
  ./test/databases/lib/schema.rb
38
- ./test/destructive_test.rb
39
37
  ./test/enumerable_test.rb
40
38
  ./test/helper.rb
41
39
  ./test/join_test.rb
42
40
  ./test/limit_test.rb
41
+ ./test/mocks_test.rb
43
42
  ./test/order_test.rb
43
+ ./test/profiler.rb
44
44
  ./test/types_test.rb
45
45
  ./test/where_test.rb
data/Rakefile CHANGED
@@ -2,7 +2,7 @@ require 'rake'
2
2
  require 'rake/testtask'
3
3
  require 'rake/rdoctask'
4
4
 
5
- Version = '0.2.0'
5
+ Version = '0.2.1'
6
6
 
7
7
  module Rake::TaskManager
8
8
  def redefine_task(task_class, args, &block)
@@ -2,24 +2,12 @@ unless defined? ActiveRecord
2
2
  require 'rubygems'
3
3
  require 'active_record'
4
4
  end
5
- require 'ambition/proc_to_ruby'
5
+ require 'ambition/api'
6
6
  require 'ambition/processor'
7
+ require 'ambition/select_processor'
8
+ require 'ambition/sort_processor'
9
+ require 'ambition/simple_processor'
7
10
  require 'ambition/query'
8
- require 'ambition/where'
9
- require 'ambition/order'
10
- require 'ambition/limit'
11
- require 'ambition/count'
12
- require 'ambition/enumerable'
13
11
  require 'ambition/database_statements'
14
12
 
15
- module Ambition
16
- include Where, Order, Limit, Enumerable, Count
17
-
18
- attr_accessor :query_context
19
-
20
- def query_context
21
- @query_context || Query.new(self)
22
- end
23
- end
24
-
25
- ActiveRecord::Base.extend Ambition
13
+ ActiveRecord::Base.extend Ambition::API
@@ -0,0 +1,73 @@
1
+ module Ambition
2
+ module API
3
+ include Enumerable
4
+
5
+ attr_accessor :query_context
6
+ def query_context
7
+ @query_context || Query.new(self)
8
+ end
9
+
10
+ def entries
11
+ find(:all, query_context.to_hash)
12
+ end
13
+ alias_method :to_a, :entries
14
+
15
+ def select(*args, &block)
16
+ query_context.add SelectProcessor.new(self, block)
17
+ end
18
+
19
+ def first(limit = 1)
20
+ query_context.add SimpleProcessor.new(:limit => limit)
21
+
22
+ if limit == 1
23
+ find(:first, query_context.to_hash)
24
+ else
25
+ query_context
26
+ end
27
+ end
28
+
29
+ def slice(offset, limit = nil)
30
+ if offset.is_a? Range
31
+ limit = offset.end
32
+ limit -= 1 if offset.exclude_end?
33
+ offset = offset.first
34
+ limit -= offset
35
+ elsif limit.nil?
36
+ return find(offset)
37
+ end
38
+
39
+ query_context.add SimpleProcessor.new(:offset => offset)
40
+ query_context.add SimpleProcessor.new(:limit => limit)
41
+ end
42
+ alias_method :[], :slice
43
+
44
+ def sort_by(&block)
45
+ query_context.add SortProcessor.new(self, block)
46
+ end
47
+
48
+ def detect(&block)
49
+ select(&block).first
50
+ end
51
+
52
+ def size
53
+ count(query_context.to_hash)
54
+ end
55
+ alias_method :length, :size
56
+
57
+ def each(&block)
58
+ entries.each(&block)
59
+ end
60
+
61
+ def any?(&block)
62
+ select(&block).size > 0
63
+ end
64
+
65
+ def all?(&block)
66
+ size == select(&block).size
67
+ end
68
+
69
+ def empty?
70
+ size.zero?
71
+ end
72
+ end
73
+ end
@@ -1,7 +1,6 @@
1
1
  ##
2
2
  # Taken from ruby2ruby, Copyright (c) 2006 Ryan Davis under the MIT License
3
3
  require 'parse_tree'
4
- require 'sexp_processor'
5
4
 
6
5
  class ProcHolder
7
6
  end
@@ -1,4 +1,5 @@
1
1
  require 'active_record/connection_adapters/abstract/quoting'
2
+ require 'ambition/proc_to_ruby'
2
3
 
3
4
  module Ambition
4
5
  class Processor
@@ -29,7 +30,6 @@ module Ambition
29
30
 
30
31
  def process_array(exp)
31
32
  arrayed = exp.map { |m| process(m) }
32
- exp.clear
33
33
  return arrayed.join(', ')
34
34
  end
35
35
 
@@ -52,18 +52,26 @@ module Ambition
52
52
  when Regexp
53
53
  "'#{value.source}'"
54
54
  else
55
- ActiveRecord::Base.connection.quote(value)
55
+ if active_connection?
56
+ ActiveRecord::Base.connection.quote(value)
57
+ else
58
+ quote(value)
59
+ end
56
60
  end
57
- rescue ActiveRecord::ConnectionNotEstablished
58
- quote(value)
59
61
  rescue
60
62
  "'#{value}'"
61
63
  end
62
64
 
63
65
  def quote_column_name(value)
64
- ActiveRecord::Base.connection.quote_column_name(value)
65
- rescue ActiveRecord::ConnectionNotEstablished
66
- value.to_s
66
+ if active_connection?
67
+ ActiveRecord::Base.connection.quote_column_name(value)
68
+ else
69
+ value.to_s
70
+ end
71
+ end
72
+
73
+ def active_connection?
74
+ ActiveRecord::Base.active_connection_name
67
75
  end
68
76
 
69
77
  def statement(*args)
@@ -1,15 +1,5 @@
1
1
  module Ambition
2
- module Where
3
- def select(*args, &block)
4
- query_context.add WhereProcessor.new(self, block)
5
- end
6
-
7
- def detect(&block)
8
- select(&block).first
9
- end
10
- end
11
-
12
- class WhereProcessor < Processor
2
+ class SelectProcessor < Processor
13
3
  def initialize(owner, block)
14
4
  super()
15
5
  @receiver = nil
@@ -31,7 +21,6 @@ module Ambition
31
21
 
32
22
  def process_not(exp)
33
23
  type, receiver, method, other = *exp.first
34
- exp.clear
35
24
 
36
25
  case type
37
26
  when :call
@@ -39,14 +28,15 @@ module Ambition
39
28
  when :match3
40
29
  regexp = receiver.last
41
30
  "#{process(method)} #{statement(:negated_regexp, regexp)} #{sanitize(regexp)}"
31
+ else
32
+ process_error(exp)
42
33
  end
43
34
  end
44
35
 
45
36
  def process_call(exp)
46
37
  receiver, method, other = *exp
47
- exp.clear
48
38
 
49
- return translation(receiver, method, other)
39
+ translation(receiver, method, other)
50
40
  end
51
41
 
52
42
  def process_lit(exp)
@@ -77,9 +67,9 @@ module Ambition
77
67
  def process_dvar(exp)
78
68
  target = exp.shift
79
69
  if target == @receiver
80
- return @table_name
70
+ @table_name
81
71
  else
82
- return value(target.to_s[0..-1])
72
+ value(target.to_s[0..-1])
83
73
  end
84
74
  end
85
75
 
@@ -100,7 +90,6 @@ module Ambition
100
90
  end
101
91
 
102
92
  def process_attrasgn(exp)
103
- exp.clear
104
93
  raise "Assignment not supported. Maybe you meant ==?"
105
94
  end
106
95
 
@@ -111,7 +100,7 @@ module Ambition
111
100
  while clause = exp.shift
112
101
  clauses << clause
113
102
  end
114
- return "(" + clauses.map { |c| process(c) }.join(" #{with} ") + ")"
103
+ "(" + clauses.map { |c| process(c) }.join(" #{with} ") + ")"
115
104
  end
116
105
 
117
106
  def value(variable)
@@ -0,0 +1,10 @@
1
+ module Ambition
2
+ class SimpleProcessor
3
+ attr_reader :key, :value
4
+ alias_method :to_s, :value
5
+
6
+ def initialize(info = {})
7
+ @key, @value = info.to_a.first
8
+ end
9
+ end
10
+ end
@@ -1,11 +1,5 @@
1
1
  module Ambition
2
- module Order
3
- def sort_by(&block)
4
- query_context.add OrderProcessor.new(self, block)
5
- end
6
- end
7
-
8
- class OrderProcessor < Processor
2
+ class SortProcessor < Processor
9
3
  def initialize(owner, block)
10
4
  super()
11
5
  @receiver = nil
@@ -0,0 +1,62 @@
1
+ $:.unshift File.dirname(__FILE__) + '/../lib'
2
+ $NO_TEST = true
3
+ %w( ambition rubygems active_record benchmark ).each { |f| require f }
4
+
5
+ class User < ActiveRecord::Base
6
+ def self.reflections
7
+ return @reflections if @reflections
8
+ @reflections = {}
9
+ @reflections[:ideas] = Reflection.new(:has_many, 'user_id', :ideas, 'ideas')
10
+ @reflections[:invites] = Reflection.new(:has_many, 'referrer_id', :invites, 'invites')
11
+ @reflections[:profile] = Reflection.new(:has_one, 'user_id', :profile, 'profiles')
12
+ @reflections[:account] = Reflection.new(:belongs_to, 'account_id', :account, 'accounts')
13
+ @reflections
14
+ end
15
+
16
+ def self.table_name
17
+ 'users'
18
+ end
19
+ end
20
+
21
+ class Reflection < Struct.new(:macro, :primary_key_name, :name, :table_name)
22
+ end
23
+
24
+ Times = 1000
25
+
26
+ Benchmark.bm(30) do |x|
27
+ x.report 'simple select' do
28
+ Times.times do
29
+ User.select { |u| u.id == 20 }.to_hash
30
+ end
31
+ end
32
+
33
+ x.report 'dual select' do
34
+ Times.times do
35
+ User.select { |u| u.id == 20 && u.age > 20 }.to_hash
36
+ end
37
+ end
38
+
39
+ x.report 'join select' do
40
+ Times.times do
41
+ User.select { |u| u.id == 20 && u.ideas.name =~ /stuff/ }.to_hash
42
+ end
43
+ end
44
+
45
+ x.report 'dual select w/ sort' do
46
+ Times.times do
47
+ User.select { |u| u.id == 20 && u.age > 20 }.sort_by { |u| u.id }.to_hash
48
+ end
49
+ end
50
+
51
+ x.report 'dual select w/ sort & first' do
52
+ Times.times do
53
+ User.select { |u| u.id == 20 && u.age > 20 }.sort_by { |u| u.id }.first(20).to_hash
54
+ end
55
+ end
56
+
57
+ x.report "it's complicated" do
58
+ Times.times do
59
+ User.select { |u| (u.id == 20 && u.age > 20) || u.profile.name == 'Jon' }.sort_by { |u| [u.id, -u.name] }.first(20).to_hash
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,29 @@
1
+ #require File.dirname(__FILE__) + '/helper'
2
+ #
3
+ #context "Setting the ambition_source" do
4
+ # setup do
5
+ # @users = [
6
+ # OpenStruct.new(:name => 'Chris', :age => 22),
7
+ # OpenStruct.new(:name => 'PJ', :age => 24),
8
+ # OpenStruct.new(:name => 'Kevin', :age => 23),
9
+ # OpenStruct.new(:name => '_why', :age => 65)
10
+ # ]
11
+ # User.ambition_source = @users
12
+ # end
13
+ #
14
+ # teardown do
15
+ # User.ambition_source = nil
16
+ # end
17
+ #
18
+ # xspecify "should run all selects / detects against that collection" do
19
+ # User.detect { |u| u.name == 'Chris' }.should == @users.first
20
+ # end
21
+ #
22
+ # specify "should run all sorts against that collection" do
23
+ # User.sort_by { |u| -u.age }.entries.should == @users.sort_by { |u| -u.age }
24
+ # end
25
+ #
26
+ # xspecify "should chain successfully" do
27
+ # User.select { |u| u.age > 22 }.sort_by { |u| -u.age }.entries.should == [ @users[3], @users[1], @users[2] ]
28
+ # end
29
+ #end
@@ -0,0 +1,34 @@
1
+ $:.unshift File.dirname(__FILE__) + '/../lib'
2
+ require 'ambition'
3
+ require 'ruby-prof'
4
+
5
+ class User < ActiveRecord::Base
6
+ def self.reflections
7
+ return @reflections if @reflections
8
+ @reflections = {}
9
+ @reflections[:ideas] = Reflection.new(:has_many, 'user_id', :ideas, 'ideas')
10
+ @reflections[:invites] = Reflection.new(:has_many, 'referrer_id', :invites, 'invites')
11
+ @reflections[:profile] = Reflection.new(:has_one, 'user_id', :profile, 'profiles')
12
+ @reflections[:account] = Reflection.new(:belongs_to, 'account_id', :account, 'accounts')
13
+ @reflections
14
+ end
15
+
16
+ def self.table_name
17
+ 'users'
18
+ end
19
+ end
20
+
21
+ class Reflection < Struct.new(:macro, :primary_key_name, :name, :table_name)
22
+ end
23
+
24
+ result = RubyProf.profile do
25
+ 1000.times do
26
+ User.select { |u| (u.id == 20 && u.age > 20) || u.profile.name == 'Jon' }.sort_by { |u| [u.id, -u.name] }.first(20).to_hash
27
+ end
28
+ end
29
+
30
+ printer = RubyProf::FlatPrinter.new(result)
31
+ #printer = RubyProf::GraphPrinter.new(result)
32
+ printer.print(STDOUT, 0)
33
+
34
+ puts User.select { |u| (u.id == 20 && u.age > 20) || u.profile.name == 'Jon' }.sort_by { |u| [u.id, -u.name] }.first(20).to_hash.inspect
metadata CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.0
3
3
  specification_version: 1
4
4
  name: ambition
5
5
  version: !ruby/object:Gem::Version
6
- version: 0.2.0
7
- date: 2007-09-04 00:00:00 -07:00
6
+ version: 0.2.1
7
+ date: 2007-09-10 00:00:00 -07:00
8
8
  summary: Ambition builds SQL from plain jane Ruby.
9
9
  require_paths:
10
10
  - lib
@@ -30,23 +30,22 @@ authors:
30
30
  - Chris Wanstrath
31
31
  files:
32
32
  - ./init.rb
33
- - ./lib/ambition/count.rb
33
+ - ./lib/ambition/api.rb
34
34
  - ./lib/ambition/database_statements.rb
35
- - ./lib/ambition/enumerable.rb
36
- - ./lib/ambition/limit.rb
37
- - ./lib/ambition/order.rb
38
35
  - ./lib/ambition/proc_to_ruby.rb
39
36
  - ./lib/ambition/processor.rb
40
37
  - ./lib/ambition/query.rb
41
- - ./lib/ambition/where.rb
38
+ - ./lib/ambition/select_processor.rb
39
+ - ./lib/ambition/simple_processor.rb
40
+ - ./lib/ambition/sort_processor.rb
42
41
  - ./lib/ambition.rb
43
42
  - ./LICENSE
44
43
  - ./Manifest
45
44
  - ./Rakefile
46
45
  - ./README
46
+ - ./test/benchmark.rb
47
47
  - ./test/chaining_test.rb
48
48
  - ./test/console
49
- - ./test/constructive_test.rb
50
49
  - ./test/count_test.rb
51
50
  - ./test/databases/boot.rb
52
51
  - ./test/databases/database.yml
@@ -66,22 +65,22 @@ files:
66
65
  - ./test/databases/lib/activerecord_test_connector.rb
67
66
  - ./test/databases/lib/load_fixtures.rb
68
67
  - ./test/databases/lib/schema.rb
69
- - ./test/destructive_test.rb
70
68
  - ./test/enumerable_test.rb
71
69
  - ./test/helper.rb
72
70
  - ./test/join_test.rb
73
71
  - ./test/limit_test.rb
72
+ - ./test/mocks_test.rb
74
73
  - ./test/order_test.rb
74
+ - ./test/profiler.rb
75
75
  - ./test/types_test.rb
76
76
  - ./test/where_test.rb
77
77
  test_files:
78
78
  - test/chaining_test.rb
79
- - test/constructive_test.rb
80
79
  - test/count_test.rb
81
- - test/destructive_test.rb
82
80
  - test/enumerable_test.rb
83
81
  - test/join_test.rb
84
82
  - test/limit_test.rb
83
+ - test/mocks_test.rb
85
84
  - test/order_test.rb
86
85
  - test/types_test.rb
87
86
  - test/where_test.rb
@@ -1,8 +0,0 @@
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
@@ -1,26 +0,0 @@
1
- module Ambition
2
- module Enumerable
3
- include ::Enumerable
4
-
5
- def each(&block)
6
- entries.each(&block)
7
- end
8
-
9
- def any?(&block)
10
- select(&block).size > 0
11
- end
12
-
13
- def all?(&block)
14
- size == select(&block).size
15
- end
16
-
17
- def empty?
18
- size.zero?
19
- end
20
-
21
- def entries
22
- find(:all, query_context.to_hash)
23
- end
24
- alias_method :to_a, :entries
25
- end
26
- end
@@ -1,57 +0,0 @@
1
- module Ambition
2
- module Limit
3
- def first(limit = 1)
4
- query_context.add LimitProcessor.new(limit)
5
-
6
- if limit == 1
7
- find(:first, query_context.to_hash)
8
- else
9
- query_context
10
- end
11
- end
12
-
13
- def slice(offset, limit = nil)
14
-
15
- if offset.is_a? Range
16
- limit = offset.end
17
- limit -= 1 if offset.exclude_end?
18
- offset = offset.first
19
- limit -= offset
20
- elsif limit.nil?
21
- return find(offset)
22
- end
23
-
24
- query_context.add OffsetProcessor.new(offset)
25
- query_context.add LimitProcessor.new(limit)
26
- end
27
- alias_method :[], :slice
28
- end
29
-
30
- class LimitProcessor
31
- def initialize(limit)
32
- @limit = limit
33
- end
34
-
35
- def key
36
- :limit
37
- end
38
-
39
- def to_s
40
- @limit
41
- end
42
- end
43
-
44
- class OffsetProcessor
45
- def initialize(offset)
46
- @offset = offset
47
- end
48
-
49
- def key
50
- :offset
51
- end
52
-
53
- def to_s
54
- @offset
55
- end
56
- end
57
- end
@@ -1,16 +0,0 @@
1
- require File.dirname(__FILE__) + '/helper'
2
-
3
-
4
- context "Constructive" do
5
- xspecify "concat" do
6
- end
7
-
8
- xspecify "<<" do
9
- end
10
-
11
- xspecify "+=" do
12
- end
13
-
14
- xspecify "push" do
15
- end
16
- end
@@ -1,21 +0,0 @@
1
- require File.dirname(__FILE__) + '/helper'
2
-
3
- context "Destructive" do
4
- xspecify "clear" do
5
- end
6
-
7
- xspecify "delete" do
8
- end
9
-
10
- xspecify "delete_if" do
11
- end
12
-
13
- xspecify "<<" do
14
- end
15
-
16
- xspecify "concat" do
17
- end
18
-
19
- xspecify "delete_at" do
20
- end
21
- end