CloudSesame 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (74) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +4 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +3 -0
  5. data/Gemfile.lock +76 -0
  6. data/Guardfile +70 -0
  7. data/cloud_sesame.gemspec +25 -0
  8. data/lib/abstract_object.rb +67 -0
  9. data/lib/cloud_sesame.rb +77 -0
  10. data/lib/cloud_sesame/config/credential.rb +8 -0
  11. data/lib/cloud_sesame/domain/base.rb +70 -0
  12. data/lib/cloud_sesame/domain/client.rb +28 -0
  13. data/lib/cloud_sesame/domain/config.rb +8 -0
  14. data/lib/cloud_sesame/domain/context.rb +35 -0
  15. data/lib/cloud_sesame/query/ast/and.rb +9 -0
  16. data/lib/cloud_sesame/query/ast/compound_array.rb +73 -0
  17. data/lib/cloud_sesame/query/ast/leaf.rb +8 -0
  18. data/lib/cloud_sesame/query/ast/literal.rb +27 -0
  19. data/lib/cloud_sesame/query/ast/multi_branch.rb +27 -0
  20. data/lib/cloud_sesame/query/ast/not.rb +13 -0
  21. data/lib/cloud_sesame/query/ast/operator.rb +24 -0
  22. data/lib/cloud_sesame/query/ast/or.rb +9 -0
  23. data/lib/cloud_sesame/query/ast/prefix_literal.rb +17 -0
  24. data/lib/cloud_sesame/query/ast/root.rb +24 -0
  25. data/lib/cloud_sesame/query/ast/single_branch.rb +24 -0
  26. data/lib/cloud_sesame/query/ast/value.rb +38 -0
  27. data/lib/cloud_sesame/query/builder.rb +78 -0
  28. data/lib/cloud_sesame/query/dsl.rb +62 -0
  29. data/lib/cloud_sesame/query/dsl/and.rb +19 -0
  30. data/lib/cloud_sesame/query/dsl/base.rb +23 -0
  31. data/lib/cloud_sesame/query/dsl/filter_query.rb +47 -0
  32. data/lib/cloud_sesame/query/dsl/literal.rb +41 -0
  33. data/lib/cloud_sesame/query/dsl/or.rb +19 -0
  34. data/lib/cloud_sesame/query/dsl/range.rb +41 -0
  35. data/lib/cloud_sesame/query/dsl/scope.rb +24 -0
  36. data/lib/cloud_sesame/query/error/missing_operator_symbol.rb +8 -0
  37. data/lib/cloud_sesame/query/error/missing_query.rb +8 -0
  38. data/lib/cloud_sesame/query/node/abstract.rb +14 -0
  39. data/lib/cloud_sesame/query/node/facet.rb +14 -0
  40. data/lib/cloud_sesame/query/node/filter_query.rb +17 -0
  41. data/lib/cloud_sesame/query/node/page.rb +27 -0
  42. data/lib/cloud_sesame/query/node/query.rb +29 -0
  43. data/lib/cloud_sesame/query/node/query_options.rb +37 -0
  44. data/lib/cloud_sesame/query/node/query_options_field.rb +20 -0
  45. data/lib/cloud_sesame/query/node/query_parser.rb +27 -0
  46. data/lib/cloud_sesame/query/node/request.rb +71 -0
  47. data/lib/cloud_sesame/query/node/sort.rb +37 -0
  48. data/spec/abstract_object_spec.rb +103 -0
  49. data/spec/cloud_sesame/domain/base_spec.rb +27 -0
  50. data/spec/cloud_sesame/domain/context_spec.rb +24 -0
  51. data/spec/cloud_sesame/query/ast/and_spec.rb +13 -0
  52. data/spec/cloud_sesame/query/ast/literal_spec.rb +37 -0
  53. data/spec/cloud_sesame/query/ast/multi_branch_spec.rb +65 -0
  54. data/spec/cloud_sesame/query/ast/operator_spec.rb +30 -0
  55. data/spec/cloud_sesame/query/ast/or_spec.rb +13 -0
  56. data/spec/cloud_sesame/query/ast/root_spec.rb +43 -0
  57. data/spec/cloud_sesame/query/ast/value_spec.rb +40 -0
  58. data/spec/cloud_sesame/query/builder_spec.rb +12 -0
  59. data/spec/cloud_sesame/query/dsl/base_spec.rb +31 -0
  60. data/spec/cloud_sesame/query/dsl/filter_query_spec.rb +158 -0
  61. data/spec/cloud_sesame/query/dsl_spec.rb +96 -0
  62. data/spec/cloud_sesame/query/node/abstract_spec.rb +19 -0
  63. data/spec/cloud_sesame/query/node/facet_spec.rb +42 -0
  64. data/spec/cloud_sesame/query/node/filter_query_spec.rb +29 -0
  65. data/spec/cloud_sesame/query/node/page_spec.rb +58 -0
  66. data/spec/cloud_sesame/query/node/query_options_field_spec.rb +27 -0
  67. data/spec/cloud_sesame/query/node/query_options_spec.rb +56 -0
  68. data/spec/cloud_sesame/query/node/query_parser_spec.rb +37 -0
  69. data/spec/cloud_sesame/query/node/query_spec.rb +46 -0
  70. data/spec/cloud_sesame/query/node/request_spec.rb +71 -0
  71. data/spec/cloud_sesame/query/node/sort_spec.rb +76 -0
  72. data/spec/cloud_sesame_spec.rb +58 -0
  73. data/spec/spec_helper.rb +12 -0
  74. metadata +218 -0
@@ -0,0 +1,71 @@
1
+ module CloudSesame
2
+ module Query
3
+ module Node
4
+ class Request < Abstract
5
+
6
+ # CHILDREN
7
+ # =========================================
8
+
9
+ def query
10
+ @query ||= Query.new(context[:query, true])
11
+ end
12
+
13
+ def query_options
14
+ @query_options ||= QueryOptions.new(context[:query_options, true])
15
+ end
16
+
17
+ def query_parser
18
+ @query_parser ||= QueryParser.new(context[:query_parser, true])
19
+ end
20
+
21
+ def filter_query
22
+ @filter_query ||= FilterQuery.new(context[:filter_query, true])
23
+ end
24
+
25
+ def facet
26
+ @facet ||= Facet.new(context[:facet, true])
27
+ end
28
+
29
+ def page
30
+ @page ||= Page.new(context[:page, true])
31
+ end
32
+
33
+ def sort
34
+ @sort ||= Sort.new(context[:sort, true])
35
+ end
36
+
37
+ # EVALUATION
38
+ # =========================================
39
+
40
+ def compile
41
+ compiled = [
42
+ query,
43
+ query_options,
44
+ query_parser,
45
+ filter_query,
46
+ page,
47
+ sort
48
+ ].each_with_object({}) do |node, compiled|
49
+ compiled.merge!(node.compile || {})
50
+ end
51
+
52
+ convert_to_structured_query(compiled) if compiled[:query].empty?
53
+
54
+ compiled
55
+ end
56
+
57
+ private
58
+
59
+ def convert_to_structured_query(compiled)
60
+ replace(compiled, :query, :filter_query).merge!(query_parser.structured.compile)
61
+ end
62
+
63
+ def replace(hash, key1, key2)
64
+ hash[key1] = hash.delete key2
65
+ hash
66
+ end
67
+
68
+ end
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,37 @@
1
+ module CloudSesame
2
+ module Query
3
+ module Node
4
+ class Sort < Abstract
5
+
6
+ attr_reader :sorting_attributes
7
+
8
+ def sorting_attributes
9
+ @sorting_attributes ||= deserialize context[:sort]
10
+ end
11
+
12
+ def [](attribute)
13
+ sorting_attributes[attribute.to_sym]
14
+ end
15
+
16
+ def []=(attribute, order = nil)
17
+ sorting_attributes[attribute.to_sym] = order.to_sym if order
18
+ end
19
+
20
+ def compile
21
+ (result = serialize(sorting_attributes)).empty? ? {} : { sort: result }
22
+ end
23
+
24
+ private
25
+
26
+ def serialize(hash = {})
27
+ hash.to_a.map { |i| i.join(' ') }.join(',')
28
+ end
29
+
30
+ def deserialize(string)
31
+ Hash[*((string || "").split(',').map { |i| i.strip.split(' ').map(&:to_sym) }.flatten)]
32
+ end
33
+
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,103 @@
1
+ require 'spec_helper'
2
+
3
+ describe AbstractObject do
4
+
5
+ # define TestConfig Class
6
+ class TestConfig < AbstractObject; end
7
+
8
+ describe '.accept' do
9
+ before { TestConfig.accept :access_key }
10
+
11
+ it 'should define attribute accessor using attribute name' do
12
+ expect(TestConfig.new).to respond_to(:access_key, :access_key=)
13
+ end
14
+
15
+ it 'should store attribute name in definitions' do
16
+ definitions = TestConfig.instance_variable_get(:@definitions)
17
+ expect(definitions).to include(access_key: :access_key)
18
+ end
19
+
20
+ context 'when accept attribute aliases' do
21
+ before { TestConfig.accept :access_key, as: [:access_key_id] }
22
+
23
+ it 'should define attribute accessor using default attribute name' do
24
+ expect(TestConfig.new).to respond_to(:access_key, :access_key=)
25
+ end
26
+ it 'should define attribute accessor using aliases' do
27
+ expect(TestConfig.new).to respond_to(:access_key_id, :access_key_id=)
28
+ end
29
+ it 'should store aliases in definitions' do
30
+ definitions = TestConfig.instance_variable_get(:@definitions)
31
+ expect(definitions).to include(access_key: :access_key, access_key_id: :access_key)
32
+ end
33
+ end
34
+
35
+ context 'when accept attribute with default value' do
36
+ let(:default_value) { SecureRandom.hex }
37
+ before { TestConfig.accept :access_key, default: default_value }
38
+
39
+ it 'should return default value if attribute hasn\'t being set' do
40
+ expect(TestConfig.new.access_key).to eq default_value
41
+ end
42
+ end
43
+
44
+ end
45
+
46
+ describe '#initialize' do
47
+
48
+ shared_examples 'acceptable attributes' do
49
+ it 'should set the acceptable attributes' do
50
+ expect(TestConfig.new(attributes_passed_in).to_hash).to include attributes_accepted
51
+ end
52
+ end
53
+
54
+ shared_examples 'unacceptable attributes' do
55
+ it 'should filter out the unacceptable attributes' do
56
+ expect(TestConfig.new(attributes_passed_in).to_hash).to_not include attributes_not_accepted
57
+ end
58
+ end
59
+
60
+ context 'when attributes passed is a hash' do
61
+ let(:attributes_accepted) { { access_key: 1 } }
62
+
63
+ context 'and attribute name is used' do
64
+ let(:attributes_passed_in) { { access_key: 1 } }
65
+ include_examples 'acceptable attributes'
66
+ end
67
+
68
+ context 'and attribute alias is used' do
69
+ let(:attributes_passed_in) { { access_key_id: 1 } }
70
+ include_examples 'acceptable attributes'
71
+ end
72
+
73
+ context 'and attribute is not in the definitions' do
74
+ let(:attributes_not_accepted) { { secret_key: 2 } }
75
+ let(:attributes_passed_in) { { secret_key: 2 } }
76
+ include_examples 'unacceptable attributes'
77
+ end
78
+
79
+
80
+ end
81
+ context 'when attributes passed is a config object' do
82
+ let(:attributes_accepted) { { access_key: 1 } }
83
+ let(:attributes_not_accepted) { { secret_key: 2 } }
84
+ let(:attributes_passed_in) { TestConfig.new(access_key: 1, secret_key: 2) }
85
+ include_examples 'acceptable attributes'
86
+ include_examples 'unacceptable attributes'
87
+ end
88
+ end
89
+
90
+ describe 'to_hash' do
91
+ let(:config) { TestConfig.new(access_key: 1) }
92
+
93
+ it 'should return config attributes in hash' do
94
+ expect(config.to_hash).to be_a(Hash)
95
+ end
96
+
97
+ it 'should not be able to manipulate the config through the hash' do
98
+ config.to_hash[:secret_key] = 2
99
+ expect(config.to_hash).to_not include(secret_key: 2)
100
+ end
101
+ end
102
+
103
+ end
@@ -0,0 +1,27 @@
1
+ require 'spec_helper'
2
+
3
+ module CloudSesame
4
+ module Domain
5
+ describe Base do
6
+
7
+ let(:searchable_class) { "Test" }
8
+ subject { Base.new(searchable_class) }
9
+
10
+ describe '#initalize' do
11
+ it 'should set searchable class' do
12
+ expect(subject.searchable_class).to eq "Test"
13
+ end
14
+ end
15
+
16
+ describe '#field' do
17
+ end
18
+
19
+ describe '#client' do
20
+ end
21
+
22
+ describe '#query' do
23
+ end
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,24 @@
1
+ require 'spec_helper'
2
+
3
+ module CloudSesame
4
+ module Domain
5
+ describe Context do
6
+
7
+ describe '#initialize' do
8
+ it 'should set default value to nil' do
9
+ expect(subject.table).to eq({})
10
+ end
11
+ it 'should accept an default value' do
12
+ context = Context.new(:default_value)
13
+ expect(context.table).to eq(:default_value)
14
+ end
15
+ end
16
+
17
+ describe '#[]' do
18
+ # it 'should return an '
19
+
20
+ end
21
+
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,13 @@
1
+ require "spec_helper"
2
+
3
+ module CloudSesame
4
+ module Query
5
+ module AST
6
+ describe And do
7
+ it 'should set the symbol to :and' do
8
+ expect(And.symbol).to eq :and
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,37 @@
1
+ require "spec_helper"
2
+
3
+ module CloudSesame
4
+ module Query
5
+ module AST
6
+ describe Literal do
7
+ let(:literal) { Literal.new('description', 'Shoes') }
8
+ let(:value) { Value.new("Shoes") }
9
+
10
+ # before { allow(Value).to receive(:new).and_return(value) }
11
+
12
+ describe '#initialize' do
13
+ it 'should store field' do
14
+ expect(literal.field).to eq('description')
15
+ end
16
+ it 'should create value node and store it' do
17
+ expect(Value).to receive(:new).with('Shoes').and_return(value)
18
+ expect(literal.value).to be_kind_of Value
19
+ end
20
+ end
21
+
22
+ describe '#compile' do
23
+ context 'when options is empty' do
24
+ it 'should compile value' do
25
+ expect_any_instance_of(Value).to receive(:compile).and_return("'Shoes'")
26
+ literal.compile
27
+ end
28
+ it 'should join the field and compiled value with colon' do
29
+ expect(literal.compile).to eq("description:'Shoes'")
30
+ end
31
+ end
32
+ end
33
+
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,65 @@
1
+ require "spec_helper"
2
+
3
+ module CloudSesame
4
+ module Query
5
+ module AST
6
+ describe MultiBranch do
7
+ let(:context) { {} }
8
+ let(:proc) { Proc.new { } }
9
+ let(:node) { MultiBranch.new(context, &proc) }
10
+
11
+ describe '#initialize' do
12
+ it 'should store the context' do
13
+ expect(node.context).to eq context
14
+ end
15
+ it 'should accepts an block and eval it' do
16
+ proc = Proc.new { test }
17
+ expect_any_instance_of(MultiBranch).to receive(:test)
18
+ MultiBranch.new(context, &proc)
19
+ end
20
+ end
21
+
22
+ describe '#children' do
23
+ it 'should instantiate an empty array' do
24
+ expect(CompoundArray).to receive(:new).and_call_original
25
+ node.children
26
+ end
27
+ it 'should return the array' do
28
+ node.children.concat([1,2,3])
29
+ expect(node.children).to eq [1,2,3]
30
+ end
31
+ end
32
+
33
+ describe '#compile_children' do
34
+ let(:children) {
35
+ [Value.new('value1'),
36
+ Value.new('value2')]
37
+ }
38
+ it 'should compile its children' do
39
+ children.each do |child|
40
+ expect(child).to receive(:compile)
41
+ end
42
+
43
+ node.children.concat(children)
44
+ node.compile_children
45
+ end
46
+
47
+ it 'should join its compiled children with a space' do
48
+ children.each do |child|
49
+ expect(child).to receive(:compile).and_call_original
50
+ end
51
+ node.children.concat(children)
52
+ expect(node.compile_children).to eq("'value1' 'value2'")
53
+ end
54
+ end
55
+
56
+ describe 'Query DSL' do
57
+ it 'should be included' do
58
+ expect(node).to respond_to(:and, :or, :literal)
59
+ end
60
+ end
61
+
62
+ end
63
+ end
64
+ end
65
+ end
@@ -0,0 +1,30 @@
1
+ require "spec_helper"
2
+
3
+ module CloudSesame
4
+ module Query
5
+ module AST
6
+ describe Operator do
7
+ let(:proc) { Proc.new {} }
8
+ let(:operator) { Operator.new({}, &proc )}
9
+ before { Operator.symbol = :symbol }
10
+
11
+ describe '#compile' do
12
+ it 'should raise an error if operator symbol has not being set' do
13
+ Operator.symbol = nil
14
+ expect{ operator.compile }.to raise_error(Error::MissingOperatorSymbol)
15
+ end
16
+
17
+ it 'should return nil if children are empty' do
18
+ expect(operator.compile).to eq(nil)
19
+ end
20
+
21
+ it 'should return a string with the symbol and compile children' do
22
+ operator.children.push Literal.new(:price, 10)
23
+ expect(operator.compile).to eq("(symbol price:10)")
24
+ end
25
+
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,13 @@
1
+ require "spec_helper"
2
+
3
+ module CloudSesame
4
+ module Query
5
+ module AST
6
+ describe Or do
7
+ it 'should set the symbol to :or' do
8
+ expect(Or.symbol).to eq :or
9
+ end
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,43 @@
1
+ require "spec_helper"
2
+
3
+ module CloudSesame
4
+ module Query
5
+ module AST
6
+ describe Root do
7
+
8
+ let(:root) { Root.new({}) }
9
+
10
+ describe '#compile' do
11
+ before { root.children.concat(children) }
12
+
13
+ context 'when it has multiple children' do
14
+ let(:children) {
15
+ [Literal.new(:tags, "flash_deal"),
16
+ Literal.new(:tags, "sales")]
17
+ }
18
+ it 'should inject an default operator' do
19
+ expect(root.compile).to eq "(and tags:'flash_deal' tags:'sales')"
20
+ end
21
+ end
22
+ context 'when it has 1 child' do
23
+ let(:children) {
24
+ [Literal.new(:tags, "flash_deal")]
25
+ }
26
+ it 'should compile the child' do
27
+ expect(root).to receive(:compile_children)
28
+ root.compile
29
+ end
30
+ end
31
+ context 'when it has no children' do
32
+ let(:children) { [] }
33
+ it 'should compile the child' do
34
+ expect(root).to receive(:compile_children)
35
+ root.compile
36
+ end
37
+ end
38
+ end
39
+
40
+ end
41
+ end
42
+ end
43
+ end