ambition 0.2.0 → 0.2.1

Sign up to get free protection for your applications and to get access to all the features.
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