CloudSesame 0.4.6 → 0.5.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (39) hide show
  1. checksums.yaml +4 -4
  2. data/Gemfile.lock +4 -1
  3. data/cloud_sesame.gemspec +2 -2
  4. data/lib/cloud_sesame/domain/base.rb +40 -30
  5. data/lib/cloud_sesame/query/ast/field_array.rb +4 -5
  6. data/lib/cloud_sesame/query/ast/literal.rb +11 -12
  7. data/lib/cloud_sesame/query/ast/multi_expression_operator.rb +4 -9
  8. data/lib/cloud_sesame/query/ast/near.rb +3 -1
  9. data/lib/cloud_sesame/query/ast/not.rb +3 -1
  10. data/lib/cloud_sesame/query/ast/root.rb +1 -1
  11. data/lib/cloud_sesame/query/ast/single_expression_operator.rb +3 -3
  12. data/lib/cloud_sesame/query/builder.rb +20 -12
  13. data/lib/cloud_sesame/query/domain/block.rb +65 -0
  14. data/lib/cloud_sesame/query/domain/chaining_block.rb +44 -0
  15. data/lib/cloud_sesame/query/domain/literal.rb +39 -0
  16. data/lib/cloud_sesame/query/dsl/block_methods.rb +6 -27
  17. data/lib/cloud_sesame/query/dsl/field_accessors.rb +24 -0
  18. data/lib/cloud_sesame/query/dsl/field_array_methods.rb +5 -9
  19. data/lib/cloud_sesame/query/dsl/filter_query_methods.rb +3 -3
  20. data/lib/cloud_sesame/query/dsl/operator_methods.rb +8 -15
  21. data/lib/cloud_sesame/query/dsl/range_methods.rb +4 -4
  22. data/lib/cloud_sesame/query/dsl/response_methods.rb +1 -1
  23. data/lib/cloud_sesame/query/dsl/scope_accessors.rb +28 -0
  24. data/lib/cloud_sesame/query/error/scope_not_defined.rb +8 -0
  25. data/lib/cloud_sesame/query/scope.rb +21 -0
  26. data/lib/cloud_sesame.rb +14 -7
  27. data/spec/cloud_sesame/query/ast/single_expression_operator_spec.rb +1 -1
  28. data/spec/cloud_sesame/query/dsl/block_methods_spec.rb +54 -70
  29. data/spec/cloud_sesame/query/dsl/{field_methods_spec.rb → field_accessors_spec.rb} +1 -1
  30. data/spec/cloud_sesame_spec.rb +35 -22
  31. metadata +30 -34
  32. data/lib/cloud_sesame/query/ast/block_chaining_relation.rb +0 -30
  33. data/lib/cloud_sesame/query/dsl/base.rb +0 -27
  34. data/lib/cloud_sesame/query/dsl/block_chaining_methods.rb +0 -26
  35. data/lib/cloud_sesame/query/dsl/field_methods.rb +0 -16
  36. data/lib/cloud_sesame/query/dsl/scope_methods.rb +0 -33
  37. data/spec/cloud_sesame/query/ast/block_chaining_relation_spec.rb +0 -44
  38. data/spec/cloud_sesame/query/dsl/base_spec.rb +0 -31
  39. data/spec/cloud_sesame/query/dsl/block_chaining_methods_spec.rb +0 -64
@@ -5,7 +5,7 @@ module CloudSesame
5
5
 
6
6
  def included?(field, value = nil)
7
7
  !!(
8
- (field_options = dsl_context[:fields][field.to_sym]) &&
8
+ (field_options = _context[:fields][field.to_sym]) &&
9
9
  (applied = field_options[:applied]) &&
10
10
  (
11
11
  (!value && applied.values.any?) ||
@@ -19,7 +19,7 @@ module CloudSesame
19
19
 
20
20
  def excluded?(field, value = nil)
21
21
  !!(
22
- (field_options = dsl_context[:fields][field.to_sym]) &&
22
+ (field_options = _context[:fields][field.to_sym]) &&
23
23
  (applied = field_options[:applied]) &&
24
24
  (
25
25
  (!value && !applied.values.all?) ||
@@ -33,7 +33,7 @@ module CloudSesame
33
33
 
34
34
  def applied_filters
35
35
  applied = {}
36
- dsl_context[:fields].each do |field, options|
36
+ _context[:fields].each do |field, options|
37
37
  if options && options[:applied] &&
38
38
  !(values = options[:applied].select { |k, v| v }.keys).empty?
39
39
  applied[field] = values
@@ -6,7 +6,7 @@ module CloudSesame
6
6
  # NEAR: creates a single NEAR node
7
7
  # =======================================
8
8
  def near(value, options = {})
9
- create_literal AST::Near, options, value
9
+ _build_operator AST::Near, options, value
10
10
  end
11
11
 
12
12
  alias_method :sloppy, :near
@@ -14,7 +14,7 @@ module CloudSesame
14
14
  # PREFIX: creates a single PREFIX node
15
15
  # =======================================
16
16
  def prefix(value, options = {})
17
- create_literal AST::Prefix, options, value
17
+ _build_operator AST::Prefix, options, value
18
18
  end
19
19
 
20
20
  alias_method :start_with, :prefix
@@ -23,28 +23,21 @@ module CloudSesame
23
23
  # PHRASE: creates a single PHRASE node
24
24
  # =======================================
25
25
  def phrase(value, options = {})
26
- create_literal AST::Phrase, options, value
26
+ _build_operator AST::Phrase, options, value
27
27
  end
28
28
 
29
29
  # TERM: creates a single TERM node
30
30
  # =======================================
31
31
  def term(value, options = {})
32
- create_literal AST::Term, options, value
32
+ _build_operator AST::Term, options, value
33
33
  end
34
34
 
35
35
  private
36
36
 
37
- def fields
38
- dsl_context[:fields]
39
- end
40
-
41
- def create_literal(klass, options, value)
42
- (node = klass.new dsl_context, options) << fieldless_literal(value)
43
- return node
44
- end
45
-
46
- def fieldless_literal(value)
47
- AST::Literal.new nil, value
37
+ def _build_operator(klass, options, value)
38
+ node = klass.new _context, options
39
+ node << AST::Literal.new(nil, value)
40
+ node
48
41
  end
49
42
 
50
43
  end
@@ -4,19 +4,19 @@ module CloudSesame
4
4
  module RangeMethods
5
5
 
6
6
  def gt(input)
7
- AST::RangeValue.new.gt(input)
7
+ AST::RangeValue.new.gt input
8
8
  end
9
9
 
10
10
  def gte(input)
11
- AST::RangeValue.new.gte(input)
11
+ AST::RangeValue.new.gte input
12
12
  end
13
13
 
14
14
  def lt(input)
15
- AST::RangeValue.new.lt(input)
15
+ AST::RangeValue.new.lt input
16
16
  end
17
17
 
18
18
  def lte(input)
19
- AST::RangeValue.new.lte(input)
19
+ AST::RangeValue.new.lte input
20
20
  end
21
21
 
22
22
  end
@@ -29,7 +29,7 @@ module CloudSesame
29
29
  def search
30
30
  compiled = request.compile
31
31
  raise Error::MissingQuery.new("Query or FilterQuery can not be empty!") if !compiled[:query] || compiled[:query].empty?
32
- @response = searchable.cloudsearch.client.search compiled
32
+ @response = @searchable.cloudsearch.client.search compiled
33
33
  end
34
34
 
35
35
  end
@@ -0,0 +1,28 @@
1
+ module CloudSesame
2
+ module Query
3
+ module DSL
4
+ module ScopeAccessors
5
+
6
+ def scopes(name = nil, *args)
7
+ defined_scopes = _scope.context[:scopes]
8
+ return _return if name.nil?
9
+ if defined_scopes && (block = defined_scopes[name.to_sym])
10
+ instance_exec *args, &block
11
+ _return
12
+ else
13
+ raise Error::ScopeNotDefined
14
+ end
15
+ end
16
+
17
+ private
18
+
19
+ def method_missing(name, *args, &block)
20
+ scopes name, *args
21
+ rescue Error::ScopeNotDefined
22
+ super
23
+ end
24
+
25
+ end
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,8 @@
1
+ module CloudSesame
2
+ module Query
3
+ module Error
4
+ class ScopeNotDefined < Exception
5
+ end
6
+ end
7
+ end
8
+ end
@@ -0,0 +1,21 @@
1
+ module CloudSesame
2
+ module Query
3
+ class Scope
4
+
5
+ attr_reader :_scope, :_return
6
+
7
+ def initialize(_scope, _return)
8
+ @_scope, @_return = _scope, _return
9
+ end
10
+
11
+ def _context
12
+ _scope.context
13
+ end
14
+
15
+ def <<(object)
16
+ _scope << object
17
+ end
18
+
19
+ end
20
+ end
21
+ end
data/lib/cloud_sesame.rb CHANGED
@@ -14,14 +14,13 @@ require 'cloud_sesame/config/credential'
14
14
  require 'cloud_sesame/query/error/invalid_syntax'
15
15
  require 'cloud_sesame/query/error/missing_operator_symbol'
16
16
  require 'cloud_sesame/query/error/missing_query'
17
+ require 'cloud_sesame/query/error/scope_not_defined'
17
18
 
18
19
  # Query DSL Methods
19
20
  # ===============================================
20
- require 'cloud_sesame/query/dsl/base'
21
21
  require 'cloud_sesame/query/dsl/block_methods'
22
- require 'cloud_sesame/query/dsl/block_chaining_methods'
23
22
  require 'cloud_sesame/query/dsl/field_array_methods'
24
- require 'cloud_sesame/query/dsl/field_methods'
23
+ require 'cloud_sesame/query/dsl/field_accessors'
25
24
  require 'cloud_sesame/query/dsl/filter_query_methods'
26
25
  require 'cloud_sesame/query/dsl/operator_methods'
27
26
  require 'cloud_sesame/query/dsl/page_methods'
@@ -29,15 +28,20 @@ require 'cloud_sesame/query/dsl/query_methods'
29
28
  require 'cloud_sesame/query/dsl/range_methods'
30
29
  require 'cloud_sesame/query/dsl/response_methods'
31
30
  require 'cloud_sesame/query/dsl/return_methods'
32
- require 'cloud_sesame/query/dsl/scope_methods'
31
+ require 'cloud_sesame/query/dsl/scope_accessors'
33
32
  require 'cloud_sesame/query/dsl/sort_methods'
34
33
 
34
+ # Query Query Domain Objects
35
+ # ===============================================
36
+ require 'cloud_sesame/query/domain/block'
37
+ require 'cloud_sesame/query/domain/chaining_block'
38
+ require 'cloud_sesame/query/domain/literal'
39
+
35
40
  # Query Query Filter Query AST Tree
36
41
  # ===============================================
37
42
  require 'cloud_sesame/query/ast/operator'
38
43
  require 'cloud_sesame/query/ast/multi_expression_operator'
39
44
  require 'cloud_sesame/query/ast/single_expression_operator'
40
- require 'cloud_sesame/query/ast/block_chaining_relation'
41
45
  require 'cloud_sesame/query/ast/field_array'
42
46
  require 'cloud_sesame/query/ast/and'
43
47
  require 'cloud_sesame/query/ast/or'
@@ -71,6 +75,7 @@ require 'cloud_sesame/query/node/return'
71
75
  # Query Builder Interface
72
76
  # ===============================================
73
77
  require 'cloud_sesame/query/builder'
78
+ require 'cloud_sesame/query/scope'
74
79
 
75
80
  # Domain Objects
76
81
  # ===============================================
@@ -95,13 +100,15 @@ module CloudSesame
95
100
 
96
101
  def define_cloudsearch(&block)
97
102
  if block_given?
98
- Domain::Base::DEFINITIONS[self] = block
103
+ Domain::Base.definitions[self] = block
104
+ cloudsearch._caller = block.binding.eval "self"
99
105
  cloudsearch.instance_eval &block
106
+ cloudsearch._caller = nil
100
107
  end
101
108
  end
102
109
 
103
110
  def load_definition_from(klass)
104
- if (definition = Domain::Base::DEFINITIONS[self])
111
+ if (definition = Domain::Base.definitions[self])
105
112
  cloudsearch.instance_eval &definition
106
113
  end
107
114
  end
@@ -53,7 +53,7 @@ module CloudSesame
53
53
  describe '#compile' do
54
54
  before {
55
55
  subject.child = OpenStruct.new compile: ""
56
- allow(subject.child).to receive(:compile)
56
+ allow(subject.child).to receive(:compile).and_return(" ")
57
57
  }
58
58
  it 'should compile it\'s child and detailed set to false' do
59
59
  expect(subject.child).to receive(:compile).with(SingleExpressionOperator::DETAILED)
@@ -5,7 +5,9 @@ module CloudSesame
5
5
  module DSL
6
6
  describe BlockMethods do
7
7
 
8
- # Setup Test Class
8
+ # Setup
9
+ # =================================================
10
+
9
11
  class Product
10
12
  include CloudSesame
11
13
  define_cloudsearch {}
@@ -13,99 +15,81 @@ module CloudSesame
13
15
 
14
16
  subject(:cloudsearch) { Product.cloudsearch.builder }
15
17
 
16
- # AND
17
- # =======================================================
18
- describe '#and' do
18
+ # Block Style Clause
19
+ # =================================================
20
+ shared_examples 'block styled clause' do
21
+
22
+ let(:node) { klass1.new({}) }
23
+ let(:root) { subject.request.filter_query.root }
24
+
25
+ let(:empty_block) { Proc.new { } }
26
+ let(:nested_block) { Proc.new { send(method, empty_block) } }
27
+
28
+ let(:clause_call) { subject.send(clause1, &empty_block) }
19
29
 
20
- it 'should create an AND node' do
21
- expect(AST::And).to receive(:new).once
22
- subject.and
30
+ it "should create an multi-expression node" do
31
+ expect(klass1).to receive(:new).once
32
+ subject.send(clause1)
23
33
  end
24
34
 
25
35
  context 'when given a block' do
36
+ before { allow(klass1).to receive(:new).and_return(node) }
26
37
 
27
- context 'when called from cloudsearch' do
28
- it 'should become a child of root node' do
29
- node = nil
30
- subject.and { node = self; }
31
- expect(subject.request.filter_query.root.children).to include(node)
38
+ context 'when called from cloudsearch build' do
39
+ it 'should add to root\'s children' do
40
+ expect{ clause_call }.to change{ root.children.size }.by(1)
41
+ expect(root.children).to include(node)
32
42
  end
33
- it 'should return cloudsearch' do
34
- expect(subject.and {}).to eq subject
35
- end
36
- it 'should use self as the dsl scope in the block' do
37
- block = ->(scope, node) { expect(scope).to eq node }
38
- subject.and { block.call(dsl_scope, self) }
43
+ it 'should return the build itself' do
44
+ expect(clause_call).to eq(subject)
39
45
  end
40
46
  end
41
-
42
- context 'when called from inside a block' do
43
- it 'should become a child of the dsl_scope' do
44
- parent = nil
45
- child = nil
46
- subject.or { parent = self; child = and! {} }
47
- expect(parent.children).to include(child)
47
+ context 'when called within a nested block' do
48
+ it 'should add to scope\'s children' do
49
+ subject.send(clause2) do
50
+ child = send(clause1, &empty_block)
51
+ expect(_scope.children).to include(child)
52
+ end
48
53
  end
49
54
  it 'should return the node it self' do
50
- node = AST::And.new({})
51
- return_node = nil
52
- allow(AST::And).to receive(:new).and_return(node)
53
- subject.or { return_node = and! {} }
54
- expect(return_node).to eq node
55
+ subject.send(clause2) {
56
+ child = send(clause1, &empty_block)
57
+ expect(child).to eq(node)
58
+ }
55
59
  end
56
60
  end
57
-
58
61
  end
59
62
 
60
63
  context 'when not given a block' do
61
- it 'should reutnr a block chaining relation object' do
62
- expect(subject.and).to be_kind_of(AST::BlockChainingRelation)
64
+ it 'should return a chaining block domain object' do
65
+ expect(subject.send(clause1)).to be_kind_of(Domain::ChainingBlock)
63
66
  end
64
- it 'should not add a child to it\'s dsl scope' do
65
- expect{ subject.and }.to_not change { subject.request.filter_query.root.children.size }
67
+ it 'should NOT add any children to the scope node' do
68
+ expect{ subject.send(clause1) }.to_not change{ root.children.size }
66
69
  end
67
70
  end
71
+ end
72
+
68
73
 
74
+ # AND
75
+ # =======================================================
76
+ describe '#and' do
77
+ it_behaves_like 'block styled clause' do
78
+ let(:klass1) { AST::And }
79
+ let(:klass2) { AST::Or }
80
+ let(:clause1) { :and! }
81
+ let(:clause2) { :or! }
82
+ end
69
83
  end
70
84
 
71
85
  # OR
72
86
  # =======================================================
73
87
  describe '#or' do
74
-
75
- it 'should create an Or node' do
76
- expect(AST::Or).to receive(:new).once
77
- subject.or
78
- end
79
-
80
- context 'when called from cloudsearch' do
81
- it 'should become a child of root node' do
82
- node = nil
83
- subject.or { node = self; }
84
- expect(subject.request.filter_query.root.children).to include(node)
85
- end
86
- it 'should return cloudsearch' do
87
- expect(subject.or {}).to eq subject
88
- end
89
- it 'should use self as the dsl scope in the block' do
90
- block = ->(scope, node) { expect(scope).to eq node }
91
- subject.and { block.call(dsl_scope, self) }
92
- end
93
- end
94
-
95
- context 'when called from inside a block' do
96
- it 'should become a child of the dsl_scope' do
97
- parent = nil
98
- child = nil
99
- subject.and { parent = self; child = or! {} }
100
- expect(parent.children).to include(child)
101
- end
102
- it 'should return the node it self' do
103
- node = AST::Or.new({})
104
- return_node = nil
105
- allow(AST::Or).to receive(:new).and_return(node)
106
- subject.and { return_node = or! {} }
107
- expect(return_node).to eq node
108
- end
88
+ it_behaves_like 'block styled clause' do
89
+ let(:klass1) { AST::Or }
90
+ let(:klass2) { AST::And }
91
+ let(:clause1) { :or! }
92
+ let(:clause2) { :and! }
109
93
  end
110
94
  end
111
95
 
@@ -3,7 +3,7 @@ require 'spec_helper'
3
3
  module CloudSesame
4
4
  module Query
5
5
  module DSL
6
- describe FieldMethods do
6
+ describe FieldAccessors do
7
7
 
8
8
  # Setup Test Class
9
9
  class Product
@@ -25,6 +25,10 @@
25
25
  # class Product
26
26
  # include CloudSesame
27
27
 
28
+ # def self.greeting
29
+ # "hello world!"
30
+ # end
31
+
28
32
  # define_cloudsearch do
29
33
  # # Product CloudSesame Config
30
34
  # config.endpoint = ENV['AWS_ENDPOINT']
@@ -62,26 +66,31 @@
62
66
  # end
63
67
 
64
68
 
65
- # # n = 10_000
66
- # # q = nil
67
- # # result = RubyProf.profile do
68
- # # n.times do
69
- # q = Product.cloudsearch.query("black jacket").sort(price: :asc).page(1).size(1000).and {
69
+ # @tags = [1, 2]
70
+ # n = 10_000
71
+ # q = nil
72
+ # result = RubyProf.profile do
73
+ # n.times do
74
+ # q = Product.cloudsearch.query("black jacket").sort(price: :asc).page(1).size(1000)
75
+ # .price { gt 100 }
76
+ # .and {
70
77
  # or! {
71
- # tags "1", "2"
78
+ # tags *@tags
79
+ # tags
80
+ # tags nil
72
81
  # and! {
73
82
  # tags.not "3", "4"
74
83
  # }
75
84
  # and!.not {
76
85
  # tags.start_with "5", "6"
77
- # tags.not.start_with "7"
78
- # tags.not.near "8"
86
+ # tags.not.start_with("7")
87
+ # tags.not.near("8", distance: 7)
79
88
  # tags start_with("9"), near("10")
80
- # tags term "11"
81
- # tags phrase "12"
89
+ # tags term("11", boost: 2)
90
+ # tags.not phrase "12"
82
91
  # }
83
92
  # or!.not {
84
- # price 25..100
93
+ # price(25..100)
85
94
  # price 100...200
86
95
  # price gte(200).lt(300)
87
96
  # price gte(300)
@@ -93,30 +102,34 @@
93
102
  # }
94
103
  # }
95
104
  # }
96
- # # q.applied_filters
105
+ # q.applied_filters
97
106
 
98
- # # end
99
- # # end
100
- # # printer = RubyProf::FlatPrinter.new(result)
101
- # # printer.print(STDOUT, {})
107
+ # end
108
+ # end
109
+ # printer = RubyProf::FlatPrinter.new(result)
110
+ # printer.print(STDOUT, {})
102
111
 
103
112
  # binding.pry
104
113
 
105
- # # class TestClass
106
- # # def initialize
114
+ # # class ProductController
115
+
116
+ # # def load_user
107
117
  # # @name = "scott"
108
118
  # # end
119
+
109
120
  # # def greeting
110
121
  # # "hello world!"
111
122
  # # end
112
- # # def test_method
113
- # # local = :test
123
+
124
+ # # def search
125
+ # # load_user
114
126
  # # q = Product.cloudsearch.and {
115
127
  # # binding.pry
116
128
  # # }
117
129
  # # end
130
+
118
131
  # # end
119
132
 
120
- # # test = TestClass.new
121
- # # test.test_method
133
+ # # test = ProductController.new
134
+ # # test.search
122
135
  # end