ambition 0.1.5 → 0.1.6

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,18 +1,41 @@
1
1
  ./init.rb
2
2
  ./lib/ambition/count.rb
3
+ ./lib/ambition/database_statements.rb
3
4
  ./lib/ambition/enumerable.rb
4
5
  ./lib/ambition/limit.rb
5
6
  ./lib/ambition/order.rb
7
+ ./lib/ambition/proc_to_ruby.rb
6
8
  ./lib/ambition/processor.rb
7
9
  ./lib/ambition/query.rb
8
10
  ./lib/ambition/where.rb
9
11
  ./lib/ambition.rb
10
- ./lib/proc_to_ruby.rb
11
12
  ./LICENSE
13
+ ./Manifest
12
14
  ./Rakefile
13
15
  ./README
14
16
  ./test/chaining_test.rb
17
+ ./test/console
18
+ ./test/constructive_test.rb
15
19
  ./test/count_test.rb
20
+ ./test/databases/boot.rb
21
+ ./test/databases/database.yml
22
+ ./test/databases/fixtures/admin.rb
23
+ ./test/databases/fixtures/companies.yml
24
+ ./test/databases/fixtures/company.rb
25
+ ./test/databases/fixtures/developer.rb
26
+ ./test/databases/fixtures/developers_projects.yml
27
+ ./test/databases/fixtures/project.rb
28
+ ./test/databases/fixtures/projects.yml
29
+ ./test/databases/fixtures/replies.yml
30
+ ./test/databases/fixtures/reply.rb
31
+ ./test/databases/fixtures/topic.rb
32
+ ./test/databases/fixtures/topics.yml
33
+ ./test/databases/fixtures/user.rb
34
+ ./test/databases/fixtures/users.yml
35
+ ./test/databases/lib/activerecord_test_connector.rb
36
+ ./test/databases/lib/load_fixtures.rb
37
+ ./test/databases/lib/schema.rb
38
+ ./test/destructive_test.rb
16
39
  ./test/enumerable_test.rb
17
40
  ./test/helper.rb
18
41
  ./test/join_test.rb
@@ -20,4 +43,3 @@
20
43
  ./test/order_test.rb
21
44
  ./test/types_test.rb
22
45
  ./test/where_test.rb
23
- ./Manifest
data/Rakefile CHANGED
@@ -2,7 +2,7 @@ require 'rake'
2
2
  require 'rake/testtask'
3
3
  require 'rake/rdoctask'
4
4
 
5
- Version = '0.1.5'
5
+ Version = '0.1.6'
6
6
 
7
7
  module Rake::TaskManager
8
8
  def redefine_task(task_class, args, &block)
@@ -0,0 +1,31 @@
1
+ module Ambition
2
+ module DatabaseStatements
3
+ def self.const_missing(*args)
4
+ Abstract
5
+ end
6
+
7
+ class Abstract
8
+ def regexp(options)
9
+ 'REGEXP'
10
+ end
11
+ end
12
+
13
+ class PostgreSQL < Abstract
14
+ def regexp(regexp)
15
+ if regexp.options == 1
16
+ '~*'
17
+ else
18
+ '~'
19
+ end
20
+ end
21
+
22
+ def negated_regexp(regexp)
23
+ if regexp.options == 1
24
+ '!~*'
25
+ else
26
+ '!~'
27
+ end
28
+ end
29
+ end
30
+ end
31
+ end
@@ -3,7 +3,7 @@ module Ambition
3
3
  include ::Enumerable
4
4
 
5
5
  def each(&block)
6
- find(:all, query_context.to_hash).each(&block)
6
+ entries.each(&block)
7
7
  end
8
8
 
9
9
  def any?(&block)
@@ -1,27 +1,35 @@
1
1
  module Ambition
2
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)
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
6
11
  end
7
12
 
8
- def [](offset, limit = nil)
9
- return first(offset, limit) if limit
13
+ def slice(offset, limit = nil)
10
14
 
11
15
  if offset.is_a? Range
12
16
  limit = offset.end
13
17
  limit -= 1 if offset.exclude_end?
14
- first(offset.first, limit - offset.first)
15
- else
16
- first(offset, 1)
18
+ offset = offset.first
19
+ limit -= offset
20
+ elsif limit.nil?
21
+ return find(offset)
17
22
  end
23
+
24
+ query_context.add OffsetProcessor.new(offset)
25
+ query_context.add LimitProcessor.new(limit)
18
26
  end
19
- alias_method :slice, :[]
27
+ alias_method :[], :slice
20
28
  end
21
29
 
22
30
  class LimitProcessor
23
- def initialize(*args)
24
- @args = args
31
+ def initialize(limit)
32
+ @limit = limit
25
33
  end
26
34
 
27
35
  def key
@@ -29,7 +37,21 @@ module Ambition
29
37
  end
30
38
 
31
39
  def to_s
32
- @args.compact * ', '
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
33
55
  end
34
56
  end
35
57
  end
@@ -0,0 +1,25 @@
1
+ ##
2
+ # Taken from ruby2ruby, Copyright (c) 2006 Ryan Davis under the MIT License
3
+ require 'parse_tree'
4
+ require 'sexp_processor'
5
+
6
+ class ProcHolder
7
+ end
8
+
9
+ class Method
10
+ def to_sexp
11
+ ParseTree.translate(ProcHolder, :proc_to_method)
12
+ end
13
+ end
14
+
15
+ class Proc
16
+ def to_method
17
+ ProcHolder.send(:define_method, :proc_to_method, self)
18
+ ProcHolder.new.method(:proc_to_method)
19
+ end
20
+
21
+ def to_sexp
22
+ body = to_method.to_sexp[2][1..-1]
23
+ [:proc, *body]
24
+ end
25
+ end
@@ -45,15 +45,41 @@ module Ambition
45
45
  end
46
46
 
47
47
  def sanitize(value)
48
- case value.to_s
49
- when 'true' then '1'
50
- when 'false' then '0'
51
- else ActiveRecord::Base.connection.quote(value) rescue quote(value)
48
+ if value.is_a? Array
49
+ return value.map { |v| sanitize(v) }.join(', ')
52
50
  end
51
+
52
+ case value
53
+ when true, 'true'
54
+ '1'
55
+ when false, 'false'
56
+ '0'
57
+ when Regexp
58
+ "'#{value.source}'"
59
+ else
60
+ ActiveRecord::Base.connection.quote(value)
61
+ end
62
+ rescue ActiveRecord::ConnectionNotEstablished
63
+ quote(value)
64
+ rescue
65
+ "'#{value}'"
53
66
  end
54
67
 
55
68
  def quote_column_name(value)
56
- ActiveRecord::Base.connection.quote_column_name(value) rescue value.to_s
69
+ ActiveRecord::Base.connection.quote_column_name(value)
70
+ rescue ActiveRecord::ConnectionNotEstablished
71
+ value.to_s
72
+ end
73
+
74
+ def statement(*args)
75
+ @statement_instnace ||= DatabaseStatements.const_get(adapter_name).new
76
+ @statement_instnace.send(*args)
77
+ end
78
+
79
+ def adapter_name
80
+ ActiveRecord::Base.connection.adapter_name
81
+ rescue ActiveRecord::ConnectionNotEstablished
82
+ 'Abstract'
57
83
  end
58
84
 
59
85
  def extract_includes(receiver, method)
@@ -45,7 +45,11 @@ module Ambition
45
45
  end
46
46
 
47
47
  if limit = keyed[:limit]
48
- hash[:limit] = limit.join(', ')
48
+ hash[:limit] = limit.last
49
+ end
50
+
51
+ if offset = keyed[:offset]
52
+ hash[:offset] = offset.last
49
53
  end
50
54
 
51
55
  hash
@@ -58,7 +62,8 @@ module Ambition
58
62
  sql << "JOIN #{hash[:includes].join(', ')}" unless hash[:includes].blank?
59
63
  sql << "WHERE #{hash[:conditions].join(' AND ')}" unless hash[:conditions].blank?
60
64
  sql << "ORDER BY #{hash[:order].join(', ')}" unless hash[:order].blank?
61
- sql << "LIMIT #{hash[:limit].join(', ')}" unless hash[:limit].blank?
65
+ sql << "LIMIT #{hash[:limit]}" unless hash[:limit].blank?
66
+ sql << "OFFSET #{hash[:limit]}" unless hash[:offset].blank?
62
67
 
63
68
  @@select % [ @table_name, sql.join(' ') ]
64
69
  end
@@ -30,9 +30,16 @@ module Ambition
30
30
  end
31
31
 
32
32
  def process_not(exp)
33
- _, receiver, method, other = *exp.first
33
+ type, receiver, method, other = *exp.first
34
34
  exp.clear
35
- return translation(receiver, negate(method), other)
35
+
36
+ case type
37
+ when :call
38
+ translation(receiver, negate(method, other), other)
39
+ when :match3
40
+ regexp = receiver.last
41
+ "#{process(method)} #{statement(:negated_regexp, regexp)} #{sanitize(regexp)}"
42
+ end
36
43
  end
37
44
 
38
45
  def process_call(exp)
@@ -63,8 +70,8 @@ module Ambition
63
70
  end
64
71
 
65
72
  def process_match3(exp)
66
- regexp, target = exp.shift.last.inspect.gsub(/\/([^\/]+)\/\S*/, '\1'), process(exp.shift)
67
- "#{target} REGEXP '#{regexp}'"
73
+ regexp, target = exp.shift.last, process(exp.shift)
74
+ "#{target} #{statement(:regexp, regexp)} #{sanitize(regexp)}"
68
75
  end
69
76
 
70
77
  def process_dvar(exp)
@@ -111,23 +118,29 @@ module Ambition
111
118
  sanitize eval(variable, @block)
112
119
  end
113
120
 
114
- def negate(method)
121
+ def negate(method, target = nil)
122
+ if Array(target).last == [:nil]
123
+ return 'IS NOT'
124
+ end
125
+
115
126
  case method
116
127
  when :==
117
128
  '<>'
118
129
  when :=~
119
130
  '!~'
120
131
  else
121
- raise "Not implemented: #{method}"
132
+ raise "Not implemented: #{method.inspect}"
122
133
  end
123
134
  end
124
135
 
125
- def translation(receiver, method, other)
136
+ def translation(receiver, method, other = nil)
126
137
  case method.to_s
138
+ when 'IS NOT'
139
+ "#{process(receiver)} IS NOT #{process(other)}"
127
140
  when '=='
128
141
  case other_value = process(other)
129
142
  when "NULL"
130
- "#{process(receiver)} is #{other_value}"
143
+ "#{process(receiver)} IS #{other_value}"
131
144
  else
132
145
  "#{process(receiver)} = #{other_value}"
133
146
  end
data/lib/ambition.rb CHANGED
@@ -1,6 +1,8 @@
1
- require 'rubygems'
2
- require 'active_record' unless defined? ActiveRecord
3
- require 'proc_to_ruby'
1
+ unless defined? ActiveRecord
2
+ require 'rubygems'
3
+ require 'active_record'
4
+ end
5
+ require 'ambition/proc_to_ruby'
4
6
  require 'ambition/processor'
5
7
  require 'ambition/query'
6
8
  require 'ambition/where'
@@ -8,6 +10,7 @@ require 'ambition/order'
8
10
  require 'ambition/limit'
9
11
  require 'ambition/count'
10
12
  require 'ambition/enumerable'
13
+ require 'ambition/database_statements'
11
14
 
12
15
  module Ambition
13
16
  include Where, Order, Limit, Enumerable, Count
data/test/console ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env ruby
2
+ irb = RUBY_PLATFORM =~ /(:?mswin|mingw)/ ? 'irb.bat' : 'irb'
3
+ libs = []
4
+ dirname = File.dirname(__FILE__)
5
+
6
+ libs << 'irb/completion'
7
+ libs << File.join(dirname, 'databases', 'boot')
8
+
9
+ exec "#{irb} #{libs.map{|l| " -r #{l}" }.join} --simple-prompt"
@@ -0,0 +1,3 @@
1
+ require File.dirname(__FILE__) + '/lib/load_fixtures'
2
+ $:.unshift File.dirname(__FILE__) + '/../../lib'
3
+ require 'ambition'
@@ -0,0 +1,17 @@
1
+ sqlite3:
2
+ adapter: sqlite3
3
+ database: db/development.sqlite3
4
+
5
+ mysql:
6
+ adapter: mysql
7
+ database: ambition_development
8
+ username: root
9
+ password:
10
+ host: localhost
11
+
12
+ postgres:
13
+ adapter: postgresql
14
+ database: ambition_development
15
+ username: root
16
+ password:
17
+ host: localhost
@@ -0,0 +1,3 @@
1
+ class Admin < User
2
+ has_many :companies, :finder_sql => 'SELECT * FROM companies'
3
+ end
@@ -0,0 +1,24 @@
1
+ thirty_seven_signals:
2
+ id: 1
3
+ name: 37Signals
4
+ rating: 4
5
+
6
+ TextDrive:
7
+ id: 2
8
+ name: TextDrive
9
+ rating: 3
10
+
11
+ PlanetArgon:
12
+ id: 3
13
+ name: Planet Argon
14
+ rating: 3
15
+
16
+ Google:
17
+ id: 4
18
+ name: Google
19
+ rating: 5
20
+
21
+ Ionist:
22
+ id: 5
23
+ name: Ioni.st
24
+ rating: 4
@@ -0,0 +1,23 @@
1
+ class Company < ActiveRecord::Base
2
+ attr_protected :rating
3
+ set_sequence_name :companies_nonstd_seq
4
+
5
+ validates_presence_of :name
6
+ def validate
7
+ errors.add('rating', 'rating should not be 2') if rating == 2
8
+ end
9
+
10
+ def self.with_best
11
+ with_scope :find => { :conditions => ['companies.rating > ?', 3] } do
12
+ yield
13
+ end
14
+ end
15
+
16
+ def self.find_best(*args)
17
+ with_best { find(*args) }
18
+ end
19
+
20
+ def self.calculate_best(*args)
21
+ with_best { calculate(*args) }
22
+ end
23
+ end
@@ -0,0 +1,11 @@
1
+ class Developer < User
2
+ has_and_belongs_to_many :projects, :include => :topics, :order => 'projects.name'
3
+
4
+ def self.with_poor_ones(&block)
5
+ with_scope :find => { :conditions => ['salary <= ?', 80000], :order => 'salary' } do
6
+ yield
7
+ end
8
+ end
9
+
10
+ def self.per_page() 10 end
11
+ end
@@ -0,0 +1,13 @@
1
+ david_action_controller:
2
+ developer_id: 1
3
+ project_id: 2
4
+ joined_on: 2004-10-10
5
+
6
+ david_active_record:
7
+ developer_id: 1
8
+ project_id: 1
9
+ joined_on: 2004-10-10
10
+
11
+ jamis_active_record:
12
+ developer_id: 2
13
+ project_id: 1
@@ -0,0 +1,4 @@
1
+ class Project < ActiveRecord::Base
2
+ has_and_belongs_to_many :developers, :uniq => true
3
+ has_many :topics
4
+ end
@@ -0,0 +1,7 @@
1
+ action_controller:
2
+ id: 2
3
+ name: Active Controller
4
+
5
+ active_record:
6
+ id: 1
7
+ name: Active Record
@@ -0,0 +1,20 @@
1
+ witty_retort:
2
+ id: 1
3
+ topic_id: 1
4
+ content: Birdman is better!
5
+ created_at: <%= 6.hours.ago.to_s(:db) %>
6
+ updated_at: nil
7
+
8
+ another:
9
+ id: 2
10
+ topic_id: 2
11
+ content: Nuh uh!
12
+ created_at: <%= 1.hour.ago.to_s(:db) %>
13
+ updated_at: nil
14
+
15
+ spam:
16
+ id: 3
17
+ topic_id: 1
18
+ content: Nice site!
19
+ created_at: <%= 1.hour.ago.to_s(:db) %>
20
+ updated_at: nil
@@ -0,0 +1,5 @@
1
+ class Reply < ActiveRecord::Base
2
+ belongs_to :topic, :include => [:replies]
3
+
4
+ validates_presence_of :content
5
+ end
@@ -0,0 +1,19 @@
1
+ class Topic < ActiveRecord::Base
2
+ has_many :replies, :dependent => :destroy, :order => 'replies.created_at DESC'
3
+ belongs_to :project
4
+
5
+ # pretend find and count were extended and accept an extra option
6
+ # if there is a :foo option, prepend its value to collection
7
+ def self.find(*args)
8
+ more = []
9
+ more << args.last.delete(:foo) if args.last.is_a?(Hash) and args.last[:foo]
10
+ res = super
11
+ more.empty?? res : more + res
12
+ end
13
+
14
+ # if there is a :foo option, always return 100
15
+ def self.count(*args)
16
+ return 100 if args.last.is_a?(Hash) and args.last[:foo]
17
+ super
18
+ end
19
+ end
@@ -0,0 +1,32 @@
1
+ futurama:
2
+ id: 1
3
+ title: Isnt futurama awesome?
4
+ subtitle: It really is, isnt it.
5
+ content: I like futurama
6
+ created_at: <%= 1.day.ago.to_s(:db) %>
7
+ updated_at: <%= 1.day.ago.to_s(:db) %>
8
+
9
+ harvey_birdman:
10
+ id: 2
11
+ title: Harvey Birdman is the king of all men
12
+ subtitle: yup
13
+ content: He really is
14
+ created_at: <%= 2.hours.ago.to_s(:db) %>
15
+ updated_at: <%= 2.hours.ago.to_s(:db) %>
16
+
17
+ rails:
18
+ id: 3
19
+ project_id: 1
20
+ title: Rails is nice
21
+ subtitle: It makes me happy
22
+ content: except when I have to hack internals to fix pagination. even then really.
23
+ created_at: <%= 20.minutes.ago.to_s(:db) %>
24
+ updated_at: <%= 20.minutes.ago.to_s(:db) %>
25
+
26
+ ar:
27
+ id: 4
28
+ project_id: 1
29
+ title: ActiveRecord sometimes freaks me out
30
+ content: "I mean, what's the deal with eager loading?"
31
+ created_at: <%= 15.minutes.ago.to_s(:db) %>
32
+ updated_at: <%= 15.minutes.ago.to_s(:db) %>
@@ -0,0 +1,2 @@
1
+ class User < ActiveRecord::Base
2
+ end
@@ -0,0 +1,35 @@
1
+ david:
2
+ id: 1
3
+ name: David
4
+ salary: 80000
5
+ type: Developer
6
+
7
+ jamis:
8
+ id: 2
9
+ name: Jamis
10
+ salary: 150000
11
+ type: Developer
12
+
13
+ <% for digit in 3..10 %>
14
+ dev_<%= digit %>:
15
+ id: <%= digit %>
16
+ name: fixture_<%= digit %>
17
+ salary: 100000
18
+ type: Developer
19
+ <% end %>
20
+
21
+ poor_jamis:
22
+ id: 11
23
+ name: Jamis
24
+ salary: 9000
25
+ type: Developer
26
+
27
+ admin:
28
+ id: 12
29
+ name: admin
30
+ type: Admin
31
+
32
+ goofy:
33
+ id: 13
34
+ name: Goofy
35
+ type: Admin
@@ -0,0 +1,65 @@
1
+ class ActiveRecordTestConnector
2
+ cattr_accessor :able_to_connect
3
+ cattr_accessor :connected
4
+
5
+ # Set our defaults
6
+ self.connected = false
7
+ self.able_to_connect = true
8
+
9
+ def self.setup
10
+ unless connected || !able_to_connect
11
+ setup_connection
12
+ load_schema
13
+ require_fixture_models
14
+ self.connected = true
15
+ end
16
+ rescue Exception => e # errors from ActiveRecord setup
17
+ if e.to_s =~ /unknown database/i
18
+ puts "\nPlease create an `ambition_development' database to play!"
19
+ else
20
+ $stderr.puts "\nSkipping ActiveRecord assertion tests: #{e}"
21
+ end
22
+ self.able_to_connect = false
23
+ end
24
+
25
+ private
26
+
27
+ def self.setup_connection
28
+ if Object.const_defined?(:ActiveRecord)
29
+ config_file = File.dirname(__FILE__) + '/../database.yml'
30
+ ActiveRecord::Base.logger = Logger.new STDOUT
31
+
32
+ case adapter = ENV['ADAPTER'] || 'mysql'
33
+ when 'sqlite3'
34
+ options = { :database => ':memory:', :adapter => 'sqlite3', :timeout => 500 }
35
+ ActiveRecord::Base.configurations = { 'sqlite3_ar_integration' => options }
36
+ else
37
+ options = YAML.load_file(config_file)[adapter]
38
+ end
39
+
40
+ puts "Using #{adapter}"
41
+
42
+ ActiveRecord::Base.establish_connection(options)
43
+ ActiveRecord::Base.connection
44
+
45
+ unless Object.const_defined?(:QUOTED_TYPE)
46
+ Object.send :const_set, :QUOTED_TYPE, ActiveRecord::Base.connection.quote_column_name('type')
47
+ end
48
+ else
49
+ raise "Can't setup connection since ActiveRecord isn't loaded."
50
+ end
51
+ end
52
+
53
+ # Load actionpack sqlite tables
54
+ def self.load_schema
55
+ ActiveRecord::Base.silence do
56
+ load File.dirname(__FILE__) + "/schema.rb"
57
+ end
58
+ end
59
+
60
+ def self.require_fixture_models
61
+ models = Dir.glob(File.dirname(__FILE__) + "/../fixtures/*.rb")
62
+ models = (models.grep(/user.rb/) + models).uniq
63
+ models.each {|f| require f}
64
+ end
65
+ end
@@ -0,0 +1,13 @@
1
+ dirname = File.dirname(__FILE__)
2
+ %w(rubygems active_record active_record/version active_record/fixtures).each {|f| require f}
3
+ puts " (ActiveRecord v#{ActiveRecord::VERSION::STRING})"
4
+ require File.join(dirname, 'activerecord_test_connector')
5
+
6
+ # setup the connection
7
+ ActiveRecordTestConnector.setup
8
+
9
+ # load all fixtures
10
+ fixture_path = File.join(dirname, '..', 'fixtures')
11
+ Fixtures.create_fixtures(fixture_path, tables = ActiveRecord::Base.connection.tables - %w(schema_info))
12
+
13
+ puts "Available models: #{Dir[fixture_path+'/*.rb'].map{|f|File.basename(f,'.rb')}.map(&:classify).to_sentence}"
@@ -0,0 +1,41 @@
1
+ ActiveRecord::Schema.define(:version => 0) do
2
+ create_table :users, :force => true do |t|
3
+ t.column :name, :string
4
+ t.column :type, :string
5
+ t.column :salary, :integer, :default => 70_000
6
+ t.column :created_at, :datetime, :null => false
7
+ t.column :updated_at, :datetime, :null => false
8
+ end
9
+
10
+ create_table :replies, :force => true do |t|
11
+ t.column :content, :string
12
+ t.column :topic_id, :integer
13
+ t.column :created_at, :datetime, :null => false
14
+ t.column :updated_at, :datetime, :null => false
15
+ end
16
+
17
+ create_table :topics, :force => true do |t|
18
+ t.column :project_id, :integer
19
+ t.column :title, :string
20
+ t.column :subtitle, :string
21
+ t.column :content, :text
22
+ t.column :created_at, :datetime, :null => false
23
+ t.column :updated_at, :datetime, :null => false
24
+ end
25
+
26
+ create_table :projects, :force => true do |t|
27
+ t.column :name, :string
28
+ end
29
+
30
+ create_table :developers_projects, :force => true do |t|
31
+ t.column :developer_id, :integer
32
+ t.column :project_id, :integer
33
+ t.column :joined_on, :datetime
34
+ t.column :access_level, :integer, :default => 1
35
+ end
36
+
37
+ create_table :companies, :force => true do |t|
38
+ t.column :name, :string
39
+ t.column :rating, :integer
40
+ end
41
+ end
@@ -10,7 +10,7 @@ context "Each" do
10
10
  end
11
11
 
12
12
  specify "limit and conditions" do
13
- hash = { :limit => '5', :conditions => "users.age = 21" }
13
+ hash = { :limit => 5, :conditions => "users.age = 21" }
14
14
  User.expects(:find).with(:all, hash).returns([])
15
15
  User.select { |m| m.age == 21 }.first(5).each do |user|
16
16
  puts user.name
@@ -18,7 +18,7 @@ context "Each" do
18
18
  end
19
19
 
20
20
  specify "limit and conditions and order" do
21
- hash = { :limit => '5', :conditions => "users.age = 21", :order => 'users.name' }
21
+ hash = { :limit => 5, :conditions => "users.age = 21", :order => 'users.name' }
22
22
  User.expects(:find).with(:all, hash).returns([])
23
23
  User.select { |m| m.age == 21 }.sort_by { |m| m.name }.first(5).each do |user|
24
24
  puts user.name
@@ -26,7 +26,7 @@ context "Each" do
26
26
  end
27
27
 
28
28
  specify "limit and order" do
29
- hash = { :limit => '5', :order => 'users.name' }
29
+ hash = { :limit => 5, :order => 'users.name' }
30
30
  User.expects(:find).with(:all, hash).returns([])
31
31
  User.sort_by { |m| m.name }.first(5).each do |user|
32
32
  puts user.name
data/test/helper.rb CHANGED
@@ -1,22 +1,20 @@
1
1
  require 'rubygems'
2
2
  begin
3
- require 'test/spec'
3
+ require 'test/spec' unless $NO_TEST
4
4
  require 'mocha'
5
- require 'active_support'
6
5
  rescue LoadError
7
- puts "=> You need the test-spec, mocha, and activesupport gems to run these tests."
6
+ puts "=> You need the test-spec and mocha gems to run these tests."
8
7
  exit
9
8
  end
9
+ require 'active_support'
10
10
  require 'active_record'
11
11
 
12
- begin require 'redgreen'; rescue LoadError; end
12
+ begin require 'redgreen'; rescue LoadError; end unless $NO_TEST
13
13
 
14
14
  $:.unshift File.dirname(__FILE__) + '/../lib'
15
15
  require 'ambition'
16
16
 
17
- class User
18
- extend Ambition
19
-
17
+ class User < ActiveRecord::Base
20
18
  def self.reflections
21
19
  return @reflections if @reflections
22
20
  @reflections = {}
@@ -34,3 +32,23 @@ end
34
32
 
35
33
  class Reflection < Struct.new(:macro, :primary_key_name, :name, :table_name)
36
34
  end
35
+
36
+ module ActiveRecord
37
+ module ConnectionAdapters
38
+ class MysqlAdapter
39
+ def connect(*args)
40
+ true
41
+ end
42
+ end
43
+
44
+ class PostgreSQLAdapter
45
+ def connect(*args)
46
+ true
47
+ end
48
+ class PGError; end
49
+ end
50
+
51
+ class FakeAdapter < AbstractAdapter
52
+ end
53
+ end
54
+ end
data/test/limit_test.rb CHANGED
@@ -6,38 +6,36 @@ context "Limit" do
6
6
  end
7
7
 
8
8
  specify "first" do
9
- conditions = { :conditions => "users.name = 'jon'", :limit => '1' }
9
+ conditions = { :conditions => "users.name = 'jon'", :limit => 1 }
10
10
  User.expects(:find).with(:first, conditions)
11
11
  @sql.first
12
12
  end
13
13
 
14
14
  specify "first with argument" do
15
- conditions = { :conditions => "users.name = 'jon'", :limit => '5' }
15
+ conditions = { :conditions => "users.name = 'jon'", :limit => 5 }
16
16
  User.expects(:find).with(:all, conditions)
17
- @sql.first(5)
18
- end
19
-
20
- specify "[] with one element" do
21
- conditions = { :conditions => "users.name = 'jon'", :limit => '10, 1' }
22
- User.expects(:find).with(:all, conditions)
23
- @sql[10]
17
+ @sql.first(5).entries
24
18
  end
25
19
 
26
20
  specify "[] with two elements" do
27
- conditions = { :conditions => "users.name = 'jon'", :limit => '10, 20' }
21
+ conditions = { :conditions => "users.name = 'jon'", :limit => 20, :offset => 10 }
22
+ User.expects(:find).with(:all, conditions)
23
+ @sql[10, 20].entries
24
+
25
+ conditions = { :conditions => "users.name = 'jon'", :limit => 20, :offset => 20 }
28
26
  User.expects(:find).with(:all, conditions)
29
- @sql[10, 20]
27
+ @sql[20, 20].entries
30
28
  end
31
29
 
32
30
  specify "slice is an alias of []" do
33
- conditions = { :conditions => "users.name = 'jon'", :limit => '10, 20' }
31
+ conditions = { :conditions => "users.name = 'jon'", :limit => 20, :offset => 10 }
34
32
  User.expects(:find).with(:all, conditions)
35
- @sql.slice(10, 20)
33
+ @sql.slice(10, 20).entries
36
34
  end
37
35
 
38
36
  specify "[] with range" do
39
- conditions = { :conditions => "users.name = 'jon'", :limit => '10, 10' }
37
+ conditions = { :conditions => "users.name = 'jon'", :limit => 10, :offset => 10 }
40
38
  User.expects(:find).with(:all, conditions)
41
- @sql[10..20]
39
+ @sql[10..20].entries
42
40
  end
43
41
  end
data/test/types_test.rb CHANGED
@@ -44,7 +44,12 @@ context "Different types" do
44
44
 
45
45
  specify "nil" do
46
46
  sql = User.select { |m| m.name == nil }.to_sql
47
- sql.should == "SELECT * FROM users WHERE users.name is NULL"
47
+ sql.should == "SELECT * FROM users WHERE users.name IS NULL"
48
+ end
49
+
50
+ specify "not nil" do
51
+ sql = User.select { |m| m.name != nil }.to_sql
52
+ sql.should == "SELECT * FROM users WHERE users.name IS NOT NULL"
48
53
  end
49
54
 
50
55
  xspecify "Time" do
data/test/where_test.rb CHANGED
@@ -47,6 +47,12 @@ context "Where (using select)" do
47
47
  sql.should == "SELECT * FROM users WHERE users.id IN (1, 2, 3, 4)"
48
48
  end
49
49
 
50
+ specify "variable'd array.include? item" do
51
+ array = [1, 2, 3, 4]
52
+ sql = User.select { |m| array.include? m.id }.to_sql
53
+ sql.should == "SELECT * FROM users WHERE users.id IN (1, 2, 3, 4)"
54
+ end
55
+
50
56
  specify "simple == with variables" do
51
57
  me = 'chris'
52
58
  sql = User.select { |m| m.name == me }.to_sql
@@ -144,14 +150,92 @@ end
144
150
 
145
151
  context "Where (using detect)" do
146
152
  specify "simple ==" do
147
- conditions = { :conditions => "users.name = 'chris'", :limit => '1' }
148
- User.expects(:find).with(:first, conditions)
153
+ User.expects(:select).returns(mock(:first => true))
149
154
  User.detect { |m| m.name == 'chris' }
150
155
  end
151
156
 
152
157
  specify "nothing found" do
153
- conditions = { :conditions => "users.name = 'chris'", :limit => '1' }
154
- User.expects(:find).with(:first, conditions).returns(nil)
158
+ User.expects(:select).returns(mock(:first => nil))
155
159
  User.detect { |m| m.name == 'chris' }.should.be.nil
156
160
  end
157
161
  end
162
+
163
+ context "Where (using [])" do
164
+ specify "finds a single row" do
165
+ User.expects(:find).with(1)
166
+ User[1]
167
+ end
168
+ end
169
+
170
+ context "PostgreSQL specific" do
171
+ setup do
172
+ ActiveRecord::Base.connection = ActiveRecord::ConnectionAdapters::PostgreSQLAdapter.new 'fake_connection', 'fake_logger'
173
+ end
174
+
175
+ teardown do
176
+ ActiveRecord::Base.remove_connection
177
+ end
178
+
179
+ specify "quoting of column name" do
180
+ me = 'chris'
181
+ sql = User.select { |m| m.name == me }.to_sql
182
+ sql.should == %(SELECT * FROM users WHERE users."name" = '#{me}')
183
+ end
184
+
185
+ specify "simple =~ with regexp" do
186
+ sql = User.select { |m| m.name =~ /chris/ }.to_sql
187
+ sql.should == %(SELECT * FROM users WHERE users."name" ~ 'chris')
188
+ end
189
+
190
+ specify "insensitive =~" do
191
+ sql = User.select { |m| m.name =~ /chris/i }.to_sql
192
+ sql.should == %(SELECT * FROM users WHERE users."name" ~* 'chris')
193
+ end
194
+
195
+ specify "negated =~" do
196
+ sql = User.select { |m| m.name !~ /chris/ }.to_sql
197
+ sql.should == %(SELECT * FROM users WHERE users."name" !~ 'chris')
198
+ end
199
+
200
+ specify "negated insensitive =~" do
201
+ sql = User.select { |m| m.name !~ /chris/i }.to_sql
202
+ sql.should == %(SELECT * FROM users WHERE users."name" !~* 'chris')
203
+ end
204
+ end
205
+
206
+ context "MySQL specific" do
207
+ setup do
208
+ ActiveRecord::Base.connection = ActiveRecord::ConnectionAdapters::MysqlAdapter.new('connection', 'logger', 'options', 'config')
209
+ end
210
+
211
+ teardown do
212
+ ActiveRecord::Base.remove_connection
213
+ end
214
+
215
+ specify "quoting of column name" do
216
+ me = 'chris'
217
+ sql = User.select { |m| m.name == me }.to_sql
218
+ sql.should == "SELECT * FROM users WHERE users.`name` = '#{me}'"
219
+ end
220
+
221
+ specify "simple =~ with regexp" do
222
+ sql = User.select { |m| m.name =~ /chris/ }.to_sql
223
+ sql.should == "SELECT * FROM users WHERE users.`name` REGEXP 'chris'"
224
+ end
225
+ end
226
+
227
+ context "Adapter without overrides" do
228
+ setup do
229
+ ActiveRecord::Base.connection = ActiveRecord::ConnectionAdapters::FakeAdapter.new('connection', 'logger')
230
+ end
231
+
232
+ teardown do
233
+ ActiveRecord::Base.remove_connection
234
+ end
235
+
236
+ specify "quoting of column name" do
237
+ me = 'chris'
238
+ sql = User.select { |m| m.name == me }.to_sql
239
+ sql.should == "SELECT * FROM users WHERE users.name = '#{me}'"
240
+ end
241
+ end
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.1.5
7
- date: 2007-08-30 00:00:00 -07:00
6
+ version: 0.1.6
7
+ date: 2007-09-02 00:00:00 -07:00
8
8
  summary: Ambition builds SQL from plain jane Ruby.
9
9
  require_paths:
10
10
  - lib
@@ -31,19 +31,42 @@ authors:
31
31
  files:
32
32
  - ./init.rb
33
33
  - ./lib/ambition/count.rb
34
+ - ./lib/ambition/database_statements.rb
34
35
  - ./lib/ambition/enumerable.rb
35
36
  - ./lib/ambition/limit.rb
36
37
  - ./lib/ambition/order.rb
38
+ - ./lib/ambition/proc_to_ruby.rb
37
39
  - ./lib/ambition/processor.rb
38
40
  - ./lib/ambition/query.rb
39
41
  - ./lib/ambition/where.rb
40
42
  - ./lib/ambition.rb
41
- - ./lib/proc_to_ruby.rb
42
43
  - ./LICENSE
44
+ - ./Manifest
43
45
  - ./Rakefile
44
46
  - ./README
45
47
  - ./test/chaining_test.rb
48
+ - ./test/console
49
+ - ./test/constructive_test.rb
46
50
  - ./test/count_test.rb
51
+ - ./test/databases/boot.rb
52
+ - ./test/databases/database.yml
53
+ - ./test/databases/fixtures/admin.rb
54
+ - ./test/databases/fixtures/companies.yml
55
+ - ./test/databases/fixtures/company.rb
56
+ - ./test/databases/fixtures/developer.rb
57
+ - ./test/databases/fixtures/developers_projects.yml
58
+ - ./test/databases/fixtures/project.rb
59
+ - ./test/databases/fixtures/projects.yml
60
+ - ./test/databases/fixtures/replies.yml
61
+ - ./test/databases/fixtures/reply.rb
62
+ - ./test/databases/fixtures/topic.rb
63
+ - ./test/databases/fixtures/topics.yml
64
+ - ./test/databases/fixtures/user.rb
65
+ - ./test/databases/fixtures/users.yml
66
+ - ./test/databases/lib/activerecord_test_connector.rb
67
+ - ./test/databases/lib/load_fixtures.rb
68
+ - ./test/databases/lib/schema.rb
69
+ - ./test/destructive_test.rb
47
70
  - ./test/enumerable_test.rb
48
71
  - ./test/helper.rb
49
72
  - ./test/join_test.rb
@@ -51,7 +74,6 @@ files:
51
74
  - ./test/order_test.rb
52
75
  - ./test/types_test.rb
53
76
  - ./test/where_test.rb
54
- - ./Manifest
55
77
  test_files:
56
78
  - test/chaining_test.rb
57
79
  - test/constructive_test.rb
data/lib/proc_to_ruby.rb DELETED
@@ -1,36 +0,0 @@
1
- ##
2
- # Taken from ruby2ruby, Copyright (c) 2006 Ryan Davis under the MIT License
3
- require 'parse_tree'
4
- require 'unique'
5
- require 'sexp_processor'
6
-
7
- class Method
8
- def with_class_and_method_name
9
- if self.inspect =~ /<Method: (.*)\#(.*)>/ then
10
- klass = eval $1
11
- method = $2.intern
12
- raise "Couldn't determine class from #{self.inspect}" if klass.nil?
13
- return yield(klass, method)
14
- else
15
- raise "Can't parse signature: #{self.inspect}"
16
- end
17
- end
18
-
19
- def to_sexp
20
- with_class_and_method_name do |klass, method|
21
- ParseTree.new(false).parse_tree_for_method(klass, method)
22
- end
23
- end
24
- end
25
-
26
- class Proc
27
- def to_method
28
- Unique.send(:define_method, :proc_to_method, self)
29
- Unique.new.method(:proc_to_method)
30
- end
31
-
32
- def to_sexp
33
- body = self.to_method.to_sexp[2][1..-1]
34
- [:proc, *body]
35
- end
36
- end