daodalus 0.1.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.gitignore +7 -0
- data/.travis.yml +7 -0
- data/Gemfile +10 -0
- data/Gemfile.lock +65 -0
- data/LICENCE.md +9 -0
- data/README.md +316 -0
- data/Rakefile +8 -0
- data/daodalus.gemspec +23 -0
- data/lib/daodalus.rb +29 -0
- data/lib/daodalus/connection.rb +19 -0
- data/lib/daodalus/dao.rb +41 -0
- data/lib/daodalus/dsl.rb +21 -0
- data/lib/daodalus/dsl/aggregation/group.rb +88 -0
- data/lib/daodalus/dsl/aggregation/limit.rb +23 -0
- data/lib/daodalus/dsl/aggregation/match.rb +35 -0
- data/lib/daodalus/dsl/aggregation/project.rb +79 -0
- data/lib/daodalus/dsl/aggregation/skip.rb +23 -0
- data/lib/daodalus/dsl/aggregation/sort.rb +29 -0
- data/lib/daodalus/dsl/aggregation/unwind.rb +23 -0
- data/lib/daodalus/dsl/aggregations.rb +44 -0
- data/lib/daodalus/dsl/clause.rb +19 -0
- data/lib/daodalus/dsl/matchers.rb +87 -0
- data/lib/daodalus/dsl/queries.rb +29 -0
- data/lib/daodalus/dsl/query.rb +46 -0
- data/lib/daodalus/dsl/select.rb +43 -0
- data/lib/daodalus/dsl/update.rb +86 -0
- data/lib/daodalus/dsl/updates.rb +71 -0
- data/lib/daodalus/dsl/where.rb +30 -0
- data/lib/daodalus/invalid_connection_error.rb +7 -0
- data/lib/daodalus/invalid_query_error.rb +4 -0
- data/spec/lib/daodalus/connection_spec.rb +22 -0
- data/spec/lib/daodalus/dao_spec.rb +34 -0
- data/spec/lib/daodalus/dsl/aggregation/group_spec.rb +92 -0
- data/spec/lib/daodalus/dsl/aggregation/limit_spec.rb +22 -0
- data/spec/lib/daodalus/dsl/aggregation/match_spec.rb +32 -0
- data/spec/lib/daodalus/dsl/aggregation/project_spec.rb +74 -0
- data/spec/lib/daodalus/dsl/aggregation/skip_spec.rb +22 -0
- data/spec/lib/daodalus/dsl/aggregation/sort_spec.rb +36 -0
- data/spec/lib/daodalus/dsl/aggregation/unwind_spec.rb +24 -0
- data/spec/lib/daodalus/dsl/clause_spec.rb +22 -0
- data/spec/lib/daodalus/dsl/query_spec.rb +35 -0
- data/spec/lib/daodalus/dsl/select_spec.rb +46 -0
- data/spec/lib/daodalus/dsl/update_spec.rb +113 -0
- data/spec/lib/daodalus/dsl/where_spec.rb +133 -0
- data/spec/lib/daodalus/dsl_spec.rb +12 -0
- data/spec/pointless_coverage_spec.rb +9 -0
- data/spec/spec_helper.rb +18 -0
- data/spec/support/mongo_cleaner.rb +12 -0
- metadata +208 -0
@@ -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,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
|