CloudSesame 0.1.0

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.
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