daodalus 0.1.1

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 (49) hide show
  1. data/.gitignore +7 -0
  2. data/.travis.yml +7 -0
  3. data/Gemfile +10 -0
  4. data/Gemfile.lock +65 -0
  5. data/LICENCE.md +9 -0
  6. data/README.md +316 -0
  7. data/Rakefile +8 -0
  8. data/daodalus.gemspec +23 -0
  9. data/lib/daodalus.rb +29 -0
  10. data/lib/daodalus/connection.rb +19 -0
  11. data/lib/daodalus/dao.rb +41 -0
  12. data/lib/daodalus/dsl.rb +21 -0
  13. data/lib/daodalus/dsl/aggregation/group.rb +88 -0
  14. data/lib/daodalus/dsl/aggregation/limit.rb +23 -0
  15. data/lib/daodalus/dsl/aggregation/match.rb +35 -0
  16. data/lib/daodalus/dsl/aggregation/project.rb +79 -0
  17. data/lib/daodalus/dsl/aggregation/skip.rb +23 -0
  18. data/lib/daodalus/dsl/aggregation/sort.rb +29 -0
  19. data/lib/daodalus/dsl/aggregation/unwind.rb +23 -0
  20. data/lib/daodalus/dsl/aggregations.rb +44 -0
  21. data/lib/daodalus/dsl/clause.rb +19 -0
  22. data/lib/daodalus/dsl/matchers.rb +87 -0
  23. data/lib/daodalus/dsl/queries.rb +29 -0
  24. data/lib/daodalus/dsl/query.rb +46 -0
  25. data/lib/daodalus/dsl/select.rb +43 -0
  26. data/lib/daodalus/dsl/update.rb +86 -0
  27. data/lib/daodalus/dsl/updates.rb +71 -0
  28. data/lib/daodalus/dsl/where.rb +30 -0
  29. data/lib/daodalus/invalid_connection_error.rb +7 -0
  30. data/lib/daodalus/invalid_query_error.rb +4 -0
  31. data/spec/lib/daodalus/connection_spec.rb +22 -0
  32. data/spec/lib/daodalus/dao_spec.rb +34 -0
  33. data/spec/lib/daodalus/dsl/aggregation/group_spec.rb +92 -0
  34. data/spec/lib/daodalus/dsl/aggregation/limit_spec.rb +22 -0
  35. data/spec/lib/daodalus/dsl/aggregation/match_spec.rb +32 -0
  36. data/spec/lib/daodalus/dsl/aggregation/project_spec.rb +74 -0
  37. data/spec/lib/daodalus/dsl/aggregation/skip_spec.rb +22 -0
  38. data/spec/lib/daodalus/dsl/aggregation/sort_spec.rb +36 -0
  39. data/spec/lib/daodalus/dsl/aggregation/unwind_spec.rb +24 -0
  40. data/spec/lib/daodalus/dsl/clause_spec.rb +22 -0
  41. data/spec/lib/daodalus/dsl/query_spec.rb +35 -0
  42. data/spec/lib/daodalus/dsl/select_spec.rb +46 -0
  43. data/spec/lib/daodalus/dsl/update_spec.rb +113 -0
  44. data/spec/lib/daodalus/dsl/where_spec.rb +133 -0
  45. data/spec/lib/daodalus/dsl_spec.rb +12 -0
  46. data/spec/pointless_coverage_spec.rb +9 -0
  47. data/spec/spec_helper.rb +18 -0
  48. data/spec/support/mongo_cleaner.rb +12 -0
  49. metadata +208 -0
@@ -0,0 +1,19 @@
1
+ module Daodalus
2
+ module DSL
3
+ module Clause
4
+
5
+ def to_query
6
+ query.wheres
7
+ end
8
+
9
+ def to_projection
10
+ query.selects
11
+ end
12
+
13
+ def to_update
14
+ query.updates
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,87 @@
1
+ module Daodalus
2
+ module DSL
3
+ module Matchers
4
+
5
+ def eq value
6
+ add_clause value
7
+ end
8
+ alias_method :equals, :eq
9
+
10
+ def ne value
11
+ add_clause '$ne' => value
12
+ end
13
+ alias_method :not_equal, :ne
14
+
15
+ def lt value
16
+ add_clause '$lt' => value
17
+ end
18
+ alias_method :less_than, :lt
19
+
20
+ def gt value
21
+ add_clause '$gt' => value
22
+ end
23
+ alias_method :greater_than, :gt
24
+
25
+ def lte value
26
+ add_clause '$lte' => value
27
+ end
28
+ alias_method :less_than_or_equal, :lte
29
+
30
+ def gte value
31
+ add_clause '$gte' => value
32
+ end
33
+ alias_method :greater_than_or_equal, :gte
34
+
35
+ def in *values
36
+ add_clause '$in' => values
37
+ end
38
+
39
+ def nin *values
40
+ add_clause '$nin' => values
41
+ end
42
+ alias_method :not_in, :nin
43
+
44
+ def all *values
45
+ add_clause '$all' => values
46
+ end
47
+
48
+ def size value
49
+ add_clause '$size' => value
50
+ end
51
+
52
+ def exists value=true
53
+ add_clause '$exists' => value
54
+ end
55
+
56
+ def does_not_exist *values
57
+ exists false
58
+ end
59
+
60
+ def not
61
+ define_singleton_method :add_clause do |clause|
62
+ add_logical_clause field.to_s => { '$not' => clause }
63
+ end
64
+ define_singleton_method :eq do |value|
65
+ add_logical_clause field.to_s => { '$ne' => value}
66
+ end
67
+ self
68
+ end
69
+
70
+ def any *clauses
71
+ add_logical_clause '$or' => clauses.map(&:to_query)
72
+ end
73
+ alias_method :or, :any
74
+
75
+ def none *clauses
76
+ add_logical_clause '$nor' => clauses.map(&:to_query)
77
+ end
78
+ alias_method :nor, :none
79
+
80
+ def elem_match clause
81
+ add_clause '$elemMatch' => clause.to_query
82
+ end
83
+
84
+ end
85
+ end
86
+ end
87
+
@@ -0,0 +1,29 @@
1
+ module Daodalus
2
+ module DSL
3
+ module Queries
4
+
5
+ def find(options = {})
6
+ options = options.merge(fields: query.selects) if query.has_selects?
7
+ dao.find(query.wheres, options)
8
+ end
9
+
10
+ def find_one(options = {})
11
+ options = options.merge(fields: query.selects) if query.has_selects?
12
+ dao.find_one(query.wheres, options)
13
+ end
14
+
15
+ def where field=nil
16
+ if field.is_a? Hash
17
+ Where.new(dao, query.where(field), nil)
18
+ else
19
+ Where.new(dao, query, field)
20
+ end
21
+ end
22
+
23
+ def select *fields
24
+ Select.new(dao, query, fields)
25
+ end
26
+
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,46 @@
1
+ module Daodalus
2
+ module DSL
3
+ class Query
4
+
5
+ def initialize(wheres={}, selects={}, updates={})
6
+ @wheres = wheres
7
+ @selects = selects
8
+ @updates = updates
9
+ end
10
+ attr_reader :wheres, :updates
11
+
12
+ def where clause
13
+ Query.new(add_where(clause), selects, updates)
14
+ end
15
+
16
+ def update clause
17
+ Query.new(wheres, selects, updates.merge(clause))
18
+ end
19
+
20
+ def select clause
21
+ Query.new(wheres, selects.merge(clause), updates)
22
+ end
23
+
24
+ def selects
25
+ @selects.empty? ? @selects : {'_id' => 0}.merge(@selects)
26
+ end
27
+
28
+ def has_selects?
29
+ !selects.empty?
30
+ end
31
+
32
+ private
33
+
34
+ def add_where clause
35
+ wheres.merge(clause) do |f, a, b|
36
+ if a.respond_to?(:merge) && b.respond_to?(:merge)
37
+ a.merge(b)
38
+ else
39
+ raise InvalidQueryError.new("Invalid Query: Cannot merge clauses '#{a}' and '#{b}' for field '#{f}'")
40
+ end
41
+ end
42
+ end
43
+
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,43 @@
1
+ module Daodalus
2
+ module DSL
3
+ class Select
4
+ include Clause
5
+ include Queries
6
+
7
+ def initialize(dao, query, fields)
8
+ @dao = dao
9
+ @query = query
10
+ @fields = fields
11
+ end
12
+
13
+ alias_method :and, :select
14
+
15
+ def by_position
16
+ raise InvalidQueryError, "Too many fields for positional operator: #{fields.inspect}" if fields.size > 1
17
+ Select.new(dao, @query.select("#{fields.first}.$" => 1), [])
18
+ end
19
+ alias_method :positional, :by_position
20
+
21
+ def slice skip=false, limit
22
+ raise InvalidQueryError, "Too many fields for slice operator: #{fields.inspect}" if fields.size > 1
23
+ if skip
24
+ Select.new(dao, @query.select(fields.first.to_s => { '$slice' => [skip, limit]}), [])
25
+ else
26
+ Select.new(dao, @query.select(fields.first.to_s => { '$slice' => limit}), [])
27
+ end
28
+ end
29
+
30
+ def elem_match clause
31
+ Select.new(dao, @query.select(fields.first.to_s => { '$elemMatch' => clause.to_query}), [])
32
+ end
33
+
34
+ private
35
+
36
+ def query
37
+ @query.select(fields.reduce({}) { |acc, f| acc.merge(f.to_s => 1) })
38
+ end
39
+
40
+ attr_reader :dao, :fields
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,86 @@
1
+ module Daodalus
2
+ module DSL
3
+ class Update
4
+ include Clause
5
+ include Queries
6
+ include Updates
7
+
8
+ def initialize(dao, query)
9
+ @dao = dao
10
+ @query = query
11
+ end
12
+
13
+ def set(fields)
14
+ with_clause '$set' => fields
15
+ end
16
+
17
+ def unset(fields)
18
+ with_clause '$unset' => fields.reduce({}) { |acc, f| acc.merge(f => 1) }
19
+ end
20
+
21
+ def inc(field, amount)
22
+ with_clause '$inc' => { field => amount }
23
+ end
24
+
25
+ def dec(field, amount)
26
+ with_clause '$inc' => { field => -amount }
27
+ end
28
+
29
+ def rename(field, value)
30
+ with_clause '$rename' => { field => value.to_s }
31
+ end
32
+
33
+ def push(field, values)
34
+ if values.length == 1
35
+ with_clause '$push' => { field => values.first }
36
+ else
37
+ push_all field, values
38
+ end
39
+ end
40
+
41
+ def push_all(field, values)
42
+ with_clause '$pushAll' => { field => values }
43
+ end
44
+
45
+ def add_to_set(field, values)
46
+ if values.length == 1
47
+ with_clause '$addToSet' => { field => values.first }
48
+ else
49
+ add_each_to_set field, values
50
+ end
51
+ end
52
+
53
+ def add_each_to_set(field, values)
54
+ with_clause '$addToSet' => { field => { '$each' => values } }
55
+ end
56
+
57
+ def pop_first(field)
58
+ with_clause '$pop' => { field => -1 }
59
+ end
60
+
61
+ def pop_last(field)
62
+ with_clause '$pop' => { field => 1 }
63
+ end
64
+
65
+ def pull(field, values)
66
+ if values.length == 1
67
+ with_clause '$pull' => { field => values.first }
68
+ else
69
+ pull_all field, values
70
+ end
71
+ end
72
+
73
+ def pull_all(field, values)
74
+ with_clause '$pullAll' => { field => values }
75
+ end
76
+
77
+ private
78
+
79
+ def with_clause clause
80
+ Update.new(dao, query.update(clause))
81
+ end
82
+
83
+ attr_reader :dao, :query
84
+ end
85
+ end
86
+ end
@@ -0,0 +1,71 @@
1
+ module Daodalus
2
+ module DSL
3
+ module Updates
4
+
5
+ def update(options = {})
6
+ dao.update(query.wheres, query.updates, options)
7
+ end
8
+
9
+ def upsert(options = {})
10
+ update(options.merge(upsert: true))
11
+ end
12
+
13
+ def find_and_modify(options = {})
14
+ dao.find_and_modify(options.merge(query: query.wheres, update: query.updates))
15
+ end
16
+
17
+ def set(fields)
18
+ Update.new(dao, query).set(fields)
19
+ end
20
+
21
+ def unset(*fields)
22
+ Update.new(dao, query).unset(fields)
23
+ end
24
+
25
+ def inc(field, amount=1)
26
+ Update.new(dao, query).inc(field, amount)
27
+ end
28
+
29
+ def dec(field, amount=1)
30
+ Update.new(dao, query).dec(field, amount)
31
+ end
32
+
33
+ def rename(field, value)
34
+ Update.new(dao, query).rename(field, value)
35
+ end
36
+
37
+ def push(field, *values)
38
+ Update.new(dao, query).push(field, values)
39
+ end
40
+
41
+ def push_all(field, values)
42
+ Update.new(dao, query).push_all(field, values)
43
+ end
44
+
45
+ def add_to_set(field, *values)
46
+ Update.new(dao, query).add_to_set(field, values)
47
+ end
48
+
49
+ def add_each_to_set(field, values)
50
+ Update.new(dao, query).add_each_to_set(field, values)
51
+ end
52
+
53
+ def pop_first(field)
54
+ Update.new(dao, query).pop_first(field)
55
+ end
56
+
57
+ def pop_last(field)
58
+ Update.new(dao, query).pop_last(field)
59
+ end
60
+
61
+ def pull(field, *values)
62
+ Update.new(dao, query).pull(field, values)
63
+ end
64
+
65
+ def pull_all(field, values)
66
+ Update.new(dao, query).pull_all(field, values)
67
+ end
68
+
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,30 @@
1
+ module Daodalus
2
+ module DSL
3
+ class Where
4
+ include Clause
5
+ include Updates
6
+ include Queries
7
+ include Matchers
8
+
9
+ def initialize(dao, query, field)
10
+ @dao = dao
11
+ @query = query
12
+ @field = field
13
+ end
14
+
15
+ alias_method :and, :where
16
+
17
+ private
18
+
19
+ def add_clause clause
20
+ Where.new(dao, query.where(field.to_s => clause), field)
21
+ end
22
+
23
+ def add_logical_clause clause
24
+ Where.new(dao, query.where(clause), field)
25
+ end
26
+
27
+ attr_reader :dao, :query, :field
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,7 @@
1
+ module Daodalus
2
+ class InvalidConnectionError < StandardError
3
+ def initialize(name)
4
+ super("Daodalus::InvalidConnectionError - #{name.inspect}")
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ module Daodalus
2
+ class InvalidQueryError < StandardError
3
+ end
4
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec_helper'
2
+
3
+ module Daodalus
4
+ describe Connection do
5
+
6
+ let (:conn) { Mongo::MongoClient.new('localhost', 27017, pool_size: 5) }
7
+
8
+ it 'allows connections to be registered and fetched by name' do
9
+ Daodalus::Connection.register(conn, :animalhouse)
10
+ Daodalus::Connection.fetch(:animalhouse).should eq conn
11
+ end
12
+
13
+ it 'allows the default connection to be registered and fetched without a name' do
14
+ Daodalus::Connection.register(conn)
15
+ Daodalus::Connection.fetch.should eq conn
16
+ end
17
+
18
+ it 'raises an invalid connection error if asked for a non-existent connection' do
19
+ expect { Daodalus::Connection.fetch(:wrong) }.to raise_error InvalidConnectionError
20
+ end
21
+ end
22
+ end