benhoskings-ambition 0.5.4

Sign up to get free protection for your applications and to get access to all the features.
Files changed (44) hide show
  1. data/LICENSE +18 -0
  2. data/Manifest +42 -0
  3. data/README +24 -0
  4. data/ambition.gemspec +120 -0
  5. data/app_generators/ambition_adapter/USAGE +1 -0
  6. data/app_generators/ambition_adapter/ambition_adapter_generator.rb +66 -0
  7. data/app_generators/ambition_adapter/templates/LICENSE +18 -0
  8. data/app_generators/ambition_adapter/templates/README +6 -0
  9. data/app_generators/ambition_adapter/templates/Rakefile +31 -0
  10. data/app_generators/ambition_adapter/templates/lib/adapter/base.rb.erb +12 -0
  11. data/app_generators/ambition_adapter/templates/lib/adapter/query.rb.erb +52 -0
  12. data/app_generators/ambition_adapter/templates/lib/adapter/select.rb.erb +100 -0
  13. data/app_generators/ambition_adapter/templates/lib/adapter/slice.rb.erb +19 -0
  14. data/app_generators/ambition_adapter/templates/lib/adapter/sort.rb.erb +43 -0
  15. data/app_generators/ambition_adapter/templates/lib/init.rb.erb +22 -0
  16. data/app_generators/ambition_adapter/templates/test/helper.rb.erb +9 -0
  17. data/app_generators/ambition_adapter/templates/test/select_test.rb.erb +157 -0
  18. data/app_generators/ambition_adapter/templates/test/slice_test.rb.erb +36 -0
  19. data/app_generators/ambition_adapter/templates/test/sort_test.rb.erb +53 -0
  20. data/bin/ambition_adapter +13 -0
  21. data/lib/ambition.rb +11 -0
  22. data/lib/ambition/api.rb +116 -0
  23. data/lib/ambition/context.rb +66 -0
  24. data/lib/ambition/core_ext.rb +21 -0
  25. data/lib/ambition/enumerable.rb +6 -0
  26. data/lib/ambition/processors/base.rb +134 -0
  27. data/lib/ambition/processors/ruby.rb +26 -0
  28. data/lib/ambition/processors/select.rb +105 -0
  29. data/lib/ambition/processors/slice.rb +15 -0
  30. data/lib/ambition/processors/sort.rb +51 -0
  31. data/lib/ambition/sexp_translator.rb +16 -0
  32. data/test/adapters/exemplar/association_test.rb +34 -0
  33. data/test/adapters/exemplar/count_test.rb +0 -0
  34. data/test/adapters/exemplar/detect_test.rb +9 -0
  35. data/test/adapters/exemplar/enumerable_test.rb +0 -0
  36. data/test/adapters/exemplar/helper.rb +3 -0
  37. data/test/adapters/exemplar/index_operator.rb +6 -0
  38. data/test/adapters/exemplar/reject_test.rb +0 -0
  39. data/test/adapters/exemplar/select_test.rb +151 -0
  40. data/test/adapters/exemplar/slice_test.rb +0 -0
  41. data/test/adapters/exemplar/sort_test.rb +0 -0
  42. data/test/debug +9 -0
  43. data/test/helper.rb +4 -0
  44. metadata +142 -0
@@ -0,0 +1,19 @@
1
+ module Ambition
2
+ module Adapters
3
+ module <%= adapter_module %>
4
+ class Slice < Base
5
+ # >> User.first(5)
6
+ # => #slice(0, 5)
7
+ #
8
+ # >> User.first
9
+ # => #slice(0, 1)
10
+ #
11
+ # >> User[10, 20]
12
+ # => #slice(10, 20)
13
+ def slice(start, length)
14
+ raise "Not implemented."
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,43 @@
1
+ module Ambition
2
+ module Adapters
3
+ module <%= adapter_module %>
4
+ class Sort < Base
5
+ # >> sort_by { |u| u.age }
6
+ # => #sort_by(:age)
7
+ def sort_by(method)
8
+ raise "Not implemented."
9
+ end
10
+
11
+ # >> sort_by { |u| -u.age }
12
+ # => #reverse_sort_by(:age)
13
+ def reverse_sort_by(method)
14
+ raise "Not implemented."
15
+ end
16
+
17
+ # >> sort_by { |u| u.profile.name }
18
+ # => #chained_sort_by(:profile, :name)
19
+ def chained_sort_by(receiver, method)
20
+ raise "Not implemented."
21
+ end
22
+
23
+ # >> sort_by { |u| -u.profile.name }
24
+ # => #chained_reverse_sort_by(:profile, :name)
25
+ def chained_reverse_sort_by(receiver, method)
26
+ raise "Not implemented."
27
+ end
28
+
29
+ # >> sort_by(&:name)
30
+ # => #to_proc(:name)
31
+ def to_proc(symbol)
32
+ raise "Not implemented."
33
+ end
34
+
35
+ # >> sort_by { rand }
36
+ # => #rand
37
+ def rand
38
+ raise "Not implemented."
39
+ end
40
+ end
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,22 @@
1
+ require 'ambition'
2
+ require '<%= adapter_name %>'
3
+ require 'ambition/adapters/<%= adapter_name %>/base'
4
+ require 'ambition/adapters/<%= adapter_name %>/query'
5
+ require 'ambition/adapters/<%= adapter_name %>/select'
6
+ require 'ambition/adapters/<%= adapter_name %>/sort'
7
+ require 'ambition/adapters/<%= adapter_name %>/slice'
8
+
9
+ ##
10
+ # This is where you inject Ambition into your target.
11
+ #
12
+ # Use `extend' if you are injecting a class, `include' if you are
13
+ # injecting instances of that class.
14
+ #
15
+ # You must also set the `ambition_adapter' class variable on your target
16
+ # class, regardless of whether you are injecting instances or the class itself.
17
+ #
18
+ # You probably want something like this:
19
+ #
20
+ # <%= adapter_module %>::Base.extend Ambition::API
21
+ # <%= adapter_module %>::Base.ambition_adapter = Ambition::Adapters::<%= adapter_module %>
22
+ #
@@ -0,0 +1,9 @@
1
+ %w( rubygems test/spec mocha English ).each { |f| require f }
2
+
3
+ begin
4
+ require 'redgreen'
5
+ rescue LoadError
6
+ end
7
+
8
+ $LOAD_PATH.unshift File.dirname(__FILE__) + '/../lib'
9
+ require 'ambition/adapters/<%= adapter_name %>'
@@ -0,0 +1,157 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ context "<%= adapter_module %> Adapter :: Select" do
4
+ setup do
5
+ @klass = User
6
+ end
7
+
8
+ xspecify "==" do
9
+ translator = @klass.select { |m| m.name == 'jon' }
10
+ translator.to_s.should == %Q(foo)
11
+ end
12
+
13
+ xspecify "!=" do
14
+ translator = @klass.select { |m| m.name != 'jon' }
15
+ translator.to_s.should == %Q(foo)
16
+ end
17
+
18
+ xspecify "== && ==" do
19
+ translator = @klass.select { |m| m.name == 'jon' && m.age == 21 }
20
+ translator.to_s.should == %Q(foo)
21
+ end
22
+
23
+ xspecify "== || ==" do
24
+ translator = @klass.select { |m| m.name == 'jon' || m.age == 21 }
25
+ translator.to_s.should == %Q(foo)
26
+ end
27
+
28
+ xspecify "mixed && and ||" do
29
+ translator = @klass.select { |m| m.name == 'jon' || m.age == 21 && m.password == 'pass' }
30
+ translator.to_s.should == %Q(foo)
31
+ end
32
+
33
+ xspecify "grouped && and ||" do
34
+ translator = @klass.select { |m| (m.name == 'jon' || m.name == 'rick') && m.age == 21 }
35
+ translator.to_s.should == %Q(foo)
36
+ end
37
+
38
+ xspecify ">/<" do
39
+ translator = @klass.select { |m| m.age > 21 }
40
+ translator.to_s.should == %Q(foo)
41
+
42
+ translator = @klass.select { |m| m.age >= 21 }
43
+ translator.to_s.should == %Q(foo)
44
+
45
+ translator = @klass.select { |m| m.age < 21 }
46
+ translator.to_s.should == %Q(foo)
47
+ end
48
+
49
+ xspecify "array.include? item" do
50
+ translator = @klass.select { |m| [1, 2, 3, 4].include? m.id }
51
+ translator.to_s.should == %Q(foo)
52
+ end
53
+
54
+ xspecify "variabled array.include? item" do
55
+ array = [1, 2, 3, 4]
56
+ translator = @klass.select { |m| array.include? m.id }
57
+ translator.to_s.should == %Q(foo)
58
+ end
59
+
60
+ xspecify "== with variables" do
61
+ me = 'chris'
62
+ translator = @klass.select { |m| m.name == me }
63
+ translator.to_s.should == %Q(foo)
64
+ end
65
+
66
+ xspecify "== with method arguments" do
67
+ def test_it(name)
68
+ translator = @klass.select { |m| m.name == name }
69
+ translator.to_s.should == %Q(foo)
70
+ end
71
+
72
+ test_it('chris')
73
+ end
74
+
75
+ xspecify "== with instance variables" do
76
+ @me = 'chris'
77
+ translator = @klass.select { |m| m.name == @me }
78
+ translator.to_s.should == %Q(foo)
79
+ end
80
+
81
+ xspecify "== with instance variable method call" do
82
+ require 'ostruct'
83
+ @person = OpenStruct.new(:name => 'chris')
84
+
85
+ translator = @klass.select { |m| m.name == @person.name }
86
+ translator.to_s.should == %Q(foo)
87
+ end
88
+
89
+ xspecify "== with global variables" do
90
+ $my_name = 'boston'
91
+ translator = @klass.select { |m| m.name == $my_name }
92
+ translator.to_s.should == %Q(foo)
93
+ end
94
+
95
+ xspecify "== with method call" do
96
+ def band
97
+ 'skinny puppy'
98
+ end
99
+
100
+ translator = @klass.select { |m| m.name == band }
101
+ translator.to_s.should == %Q(foo)
102
+ end
103
+
104
+ xspecify "=~ with string" do
105
+ translator = @klass.select { |m| m.name =~ 'chris' }
106
+ translator.to_s.should == %Q(foo)
107
+
108
+ translator = @klass.select { |m| m.name =~ 'chri%' }
109
+ translator.to_s.should == %Q(foo)
110
+ end
111
+
112
+ xspecify "!~ with string" do
113
+ translator = @klass.select { |m| m.name !~ 'chris' }
114
+ translator.to_s.should == %Q(foo)
115
+
116
+ translator = @klass.select { |m| !(m.name =~ 'chris') }
117
+ translator.to_s.should == %Q(foo)
118
+ end
119
+
120
+ xspecify "=~ with regexp" do
121
+ translator = @klass.select { |m| m.name =~ /chris/ }
122
+ translator.to_s.should == %Q(foo)
123
+ end
124
+
125
+ xspecify "=~ with regexp flags" do
126
+ translator = @klass.select { |m| m.name =~ /chris/i }
127
+ translator.to_s.should == %Q(foo)
128
+ end
129
+
130
+ xspecify "downcase" do
131
+ translator = @klass.select { |m| m.name.downcase =~ 'chris%' }
132
+ translator.to_s.should == %Q(foo)
133
+ end
134
+
135
+ xspecify "upcase" do
136
+ translator = @klass.select { |m| m.name.upcase =~ 'chris%' }
137
+ translator.to_s.should == %Q(foo)
138
+ end
139
+
140
+ xspecify "undefined equality symbol" do
141
+ should.raise { @klass.select { |m| m.name =* /chris/ } }
142
+ end
143
+
144
+ xspecify "block variable / assigning variable conflict" do
145
+ m = @klass.select { |m| m.name == 'chris' }
146
+ m.should == %Q(foo)
147
+ end
148
+
149
+ xspecify "== with inline ruby" do
150
+ translator = @klass.select { |m| m.created_at == Time.now.to_s }
151
+ translator.to_s.should == %Q(foo)
152
+ end
153
+
154
+ xspecify "inspect" do
155
+ @klass.select { |u| u.name }.inspect.should.match %r(call #to_s or #to_hash)
156
+ end
157
+ end
@@ -0,0 +1,36 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ context "<%= adapter_module %> Adapter :: Slice" do
4
+ setup do
5
+ @klass = User
6
+ @block = @klass.select { |m| m.name == 'jon' }
7
+ end
8
+
9
+ xspecify "first" do
10
+ @klass.expects(:find).with(:limit => 1, :name => 'jon')
11
+ @block.first
12
+ end
13
+
14
+ xspecify "first with argument" do
15
+ @klass.expects(:find).with(:limit => 5, :name => 'jon')
16
+ @block.first(5).entries
17
+ end
18
+
19
+ xspecify "[] with two elements" do
20
+ @klass.expects(:find).with(:limit => 20, :offset => 10, :name => 'jon')
21
+ @block[10, 20].entries
22
+
23
+ @klass.expects(:find).with(:limit => 20, :offset => 20, :name => 'jon')
24
+ @block[20, 20].entries
25
+ end
26
+
27
+ xspecify "slice is an alias of []" do
28
+ @klass.expects(:find).with(:limit => 20, :offset => 10, :name => 'jon')
29
+ @block.slice(10, 20).entries
30
+ end
31
+
32
+ xspecify "[] with range" do
33
+ @klass.expects(:find).with(:limit => 10, :offset => 10, :name => 'jon')
34
+ @block[11..20].entries
35
+ end
36
+ end
@@ -0,0 +1,53 @@
1
+ require File.dirname(__FILE__) + '/helper'
2
+
3
+ context "<%= adapter_module %> Adapter :: Sort" do
4
+ setup do
5
+ @klass = User
6
+ @block = @klass.select { |m| m.name == 'jon' }
7
+ end
8
+
9
+ xspecify "order" do
10
+ string = @block.sort_by { |m| m.name }.to_s
11
+ string.should == "foo"
12
+ end
13
+
14
+ xspecify "combined order" do
15
+ string = @block.sort_by { |m| [ m.name, m.age ] }.to_s
16
+ string.should == "foo"
17
+ end
18
+
19
+ xspecify "combined order with single reverse" do
20
+ string = @block.sort_by { |m| [ m.name, -m.age ] }.to_s
21
+ string.should == "foo"
22
+ end
23
+
24
+ xspecify "combined order with two reverses" do
25
+ string = @block.sort_by { |m| [ -m.name, -m.age ] }.to_s
26
+ string.should == "foo"
27
+ end
28
+
29
+ xspecify "reverse order with -" do
30
+ string = @block.sort_by { |m| -m.age }.to_s
31
+ string.should == "foo"
32
+ end
33
+
34
+ xspecify "reverse order with #reverse" do
35
+ # TODO: not implemented
36
+ string = @block.sort_by { |m| m.age }.reverse.to_s
37
+ string.should == "foo"
38
+ end
39
+
40
+ xspecify "random order" do
41
+ string = @block.sort_by { rand }.to_s
42
+ string.should == "foo"
43
+ end
44
+
45
+ xspecify "non-existent method to sort by" do
46
+ should.raise(NoMethodError) { @block.sort_by { foo }.to_s }
47
+ end
48
+
49
+ xspecify "Symbol#to_proc" do
50
+ string = @klass.sort_by(&:name).to_s
51
+ string.should == "foo"
52
+ end
53
+ end
@@ -0,0 +1,13 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'rubigen'
4
+
5
+ if %w(-v --version).include? ARGV.first
6
+ version = File.read('./Rakefile').scan(/Version = '(.+)'/).first.first
7
+ puts "#{File.basename($0)} #{version}"
8
+ exit(0)
9
+ end
10
+
11
+ require 'rubigen/scripts/generate'
12
+ RubiGen::Base.use_application_sources! :ambition_adapter
13
+ RubiGen::Scripts::Generate.new.run(ARGV, :generator => 'ambition_adapter')
data/lib/ambition.rb ADDED
@@ -0,0 +1,11 @@
1
+ require 'ambition/enumerable'
2
+ require 'ambition/api'
3
+ require 'ambition/context'
4
+ require 'ambition/core_ext'
5
+ require 'ambition/sexp_translator'
6
+
7
+ require 'ambition/processors/base'
8
+ require 'ambition/processors/select'
9
+ require 'ambition/processors/sort'
10
+ require 'ambition/processors/slice'
11
+ require 'ambition/processors/ruby'
@@ -0,0 +1,116 @@
1
+ module Ambition #:nodoc:
2
+ # Module that you will extend from in your adapters in your toplevel file.
3
+ #
4
+ # For example, for ambitious_sphinx in lib/ambition/adapters/ambitious_sphinx.rb, we have:
5
+ # ActiveRecord::Base.extend Ambition::API
6
+ module API
7
+ include Enumerable
8
+
9
+ def chain(other_context)
10
+ other_context.clauses.inject(ambition_context) {|context,(k,v)|
11
+ context.clauses[k].concat(other_context.clauses[k]).uniq!
12
+ context
13
+ } unless other_context.nil?
14
+ end
15
+
16
+ ##
17
+ # Entry methods
18
+ def select(&block)
19
+ context = ambition_context
20
+ context << Processors::Select.new(context, block)
21
+ end
22
+
23
+ def sort_by(&block)
24
+ context = ambition_context
25
+ context << Processors::Sort.new(context, block)
26
+ end
27
+
28
+ # Entries that our context is able to find.
29
+ def entries
30
+ ambition_context.kick
31
+ end
32
+ alias_method :to_a, :entries
33
+
34
+ def size
35
+ context = ambition_context
36
+ context == self ? super : context.size
37
+ end
38
+
39
+ def slice(start, length = nil)
40
+ context = ambition_context
41
+ context << Processors::Slice.new(context, start, length)
42
+ end
43
+ alias_method :[], :slice
44
+
45
+ ##
46
+ # Convenience methods
47
+
48
+ # See Enumerable#detect
49
+ def detect(&block)
50
+ select(&block).first
51
+ end
52
+
53
+ # See Array#first
54
+ def first(count = 1)
55
+ sliced = slice(0, count)
56
+ count == 1 ? Array(sliced.kick).first : sliced
57
+ end
58
+
59
+ # See Array#each, applied to +entries+
60
+ def each(&block)
61
+ entries.each(&block)
62
+ end
63
+
64
+ # See Enumerable#any?
65
+ def any?(&block)
66
+ select(&block).size > 0
67
+ end
68
+
69
+ # See Enumerable#all?
70
+ def all?(&block)
71
+ size == select(&block).size
72
+ end
73
+
74
+ # See Array#empty?
75
+ def empty?
76
+ size.zero?
77
+ end
78
+
79
+ # Builds a new +Context+.
80
+ def ambition_context
81
+ Context.new(self)
82
+ end
83
+
84
+ # Gives you the current ambitious adapter.
85
+ def ambition_adapter
86
+ own_name = respond_to?(:name) ? name : self.class.name
87
+ parent = respond_to?(:superclass) ? superclass : self.class.superclass
88
+ @@ambition_adapter[own_name] || @@ambition_adapter[parent.name] || infer_ambition_adapter
89
+ end
90
+
91
+ # If we're in a class that doesn't have an adapter assigned, but descends from a class
92
+ # that does, use that adapter (storing it for next time, since ancestors.include? is
93
+ # expensive to run constantly).
94
+ def infer_ambition_adapter
95
+ famous_ancestor = @@ambition_adapter.keys.detect {|key|
96
+ ancestors.include?(Object.recursive_const_get(key))
97
+ }
98
+ self.ambition_adapter = @@ambition_adapter[famous_ancestor] unless famous_ancestor.nil?
99
+ end
100
+
101
+ # Assign the ambition adapter. Typically, you use this in the toplevel file of your adapter.
102
+ #
103
+ # For example, for ambitious_sphinx, in our lib/ambition/adapters/ambitious_sphinx.rb:
104
+ #
105
+ # ActiveRecord::Base.ambition_adapter = Ambition::Adapters::AmbitiousSphinx
106
+ def ambition_adapter=(klass)
107
+ @@ambition_adapter ||= {}
108
+ # should this be doing the same check for respond_to?(:name) like above?
109
+ @@ambition_adapter[name] = klass
110
+ end
111
+
112
+ def ambition_owner
113
+ @owner || self
114
+ end
115
+ end
116
+ end