mongrep 0.2.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.
- checksums.yaml +7 -0
- data/.editorconfig +15 -0
- data/.gitignore +22 -0
- data/.rspec +2 -0
- data/.rubocop.yml +6 -0
- data/.ruby-version +1 -0
- data/.simplecov +9 -0
- data/.travis.yml +5 -0
- data/.yardopts +6 -0
- data/Gemfile +5 -0
- data/Guardfile +31 -0
- data/README.md +31 -0
- data/README.md.erb +31 -0
- data/Rakefile +29 -0
- data/bin/console +15 -0
- data/bin/setup +8 -0
- data/lib/mongrep/core_ext/hash.rb +36 -0
- data/lib/mongrep/core_ext/mongo/error/operation_failure.rb +21 -0
- data/lib/mongrep/model.rb +144 -0
- data/lib/mongrep/mongo_model.rb +32 -0
- data/lib/mongrep/query.rb +70 -0
- data/lib/mongrep/query_result.rb +44 -0
- data/lib/mongrep/read_only_repository.rb +22 -0
- data/lib/mongrep/repository.rb +198 -0
- data/lib/mongrep/version.rb +5 -0
- data/lib/mongrep.rb +24 -0
- data/mongrep.gemspec +47 -0
- metadata +336 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: 69914b4493a7de6bb8566f431244fc57f17a8a20
|
4
|
+
data.tar.gz: 5f24300241239d49e2bacbbcf6d3ce99d8c12327
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 96db1d696abc053ea35d709f4cb7694bf555fbdd1d969265a19cac103eefb2f889174c63f8e79158d699abe474a3b94ee3601ad06e1d76a16bc88a1ac5f5ae99
|
7
|
+
data.tar.gz: d695e3946bed16a8caa294fa96971a51073226ae44f78a2f5afecd95cb06296f8b423e856ca696e65513f9f9f5c3466b37d39b7d14d7a3e9ffb98d2751642d6b
|
data/.editorconfig
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
root = true
|
2
|
+
|
3
|
+
[*]
|
4
|
+
end_of_line = lf
|
5
|
+
trim_trailing_whitespace = true
|
6
|
+
insert_final_newline = true
|
7
|
+
charset = utf-8
|
8
|
+
indent_style = space
|
9
|
+
|
10
|
+
[{Gemfile,*.{rb,gemspec}}]
|
11
|
+
indent_size = 2
|
12
|
+
|
13
|
+
[*.md]
|
14
|
+
trim_trailing_whitespace = false
|
15
|
+
indent_size = 2
|
data/.gitignore
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
/.bundle/
|
2
|
+
/.yardoc
|
3
|
+
/Gemfile.lock
|
4
|
+
/_yardoc/
|
5
|
+
/coverage/*
|
6
|
+
!/coverage/.last_run.json
|
7
|
+
/pkg/
|
8
|
+
/spec/reports/
|
9
|
+
/tmp/
|
10
|
+
/vendor/bundle/
|
11
|
+
|
12
|
+
# for example coverage
|
13
|
+
tmp
|
14
|
+
|
15
|
+
# exuberant ctags
|
16
|
+
tags
|
17
|
+
|
18
|
+
# yardoc
|
19
|
+
.yardoc/
|
20
|
+
doc/yardoc
|
21
|
+
|
22
|
+
log
|
data/.rspec
ADDED
data/.rubocop.yml
ADDED
data/.ruby-version
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
2.3.1
|
data/.simplecov
ADDED
data/.travis.yml
ADDED
data/.yardopts
ADDED
data/Gemfile
ADDED
data/Guardfile
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
guard :rspec, cmd: 'bundle exec rspec' do
|
4
|
+
require 'guard/rspec/dsl'
|
5
|
+
dsl = Guard::RSpec::Dsl.new(self)
|
6
|
+
|
7
|
+
# RSpec files
|
8
|
+
rspec = dsl.rspec
|
9
|
+
watch(rspec.spec_helper) { rspec.spec_dir }
|
10
|
+
watch(rspec.spec_support) { rspec.spec_dir }
|
11
|
+
watch(rspec.spec_files)
|
12
|
+
|
13
|
+
# Ruby files
|
14
|
+
ruby = dsl.ruby
|
15
|
+
watch(ruby.lib_files) do |m|
|
16
|
+
spec_path = m[1][%r{(?<=lib/).*}]
|
17
|
+
[
|
18
|
+
rspec.spec.call("unit/#{spec_path}"),
|
19
|
+
rspec.spec.call("integration/#{spec_path}")
|
20
|
+
]
|
21
|
+
end
|
22
|
+
|
23
|
+
# Shared examples
|
24
|
+
watch(%r{(spec/.+)/shared_examples/.*\.rb}) { |m| m[1] }
|
25
|
+
end
|
26
|
+
|
27
|
+
guard 'yard' do
|
28
|
+
watch(%r{app/.+\.rb})
|
29
|
+
watch(%r{lib/.+\.rb})
|
30
|
+
watch(%r{ext/.+\.c})
|
31
|
+
end
|
data/README.md
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Mongrep
|
2
|
+
|
3
|
+
This gem provides classes and modules for implementing persistance layers using
|
4
|
+
the repository pattern
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'mongrep', '~> 0.2'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
## Documentation
|
19
|
+
|
20
|
+
API docs are available at https://rubydocs.info/mongrep
|
21
|
+
|
22
|
+
## Development
|
23
|
+
|
24
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
25
|
+
`rake test` to run the tests. You can also run `bin/console` for an interactive
|
26
|
+
prompt that will allow you to experiment.
|
27
|
+
|
28
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To
|
29
|
+
release a new version, update the version number in `version.rb`, and then run
|
30
|
+
`bundle exec rake release`, which will create a git tag for the version, push
|
31
|
+
git commits and tags and push the gem to rubygems.org.
|
data/README.md.erb
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
# Mongrep
|
2
|
+
|
3
|
+
This gem provides classes and modules for implementing persistance layers using
|
4
|
+
the repository pattern
|
5
|
+
|
6
|
+
## Installation
|
7
|
+
|
8
|
+
Add this line to your application's Gemfile:
|
9
|
+
|
10
|
+
```ruby
|
11
|
+
gem 'mongrep', '~> <%= Mongrep::VERSION.split('.').first(2).join('.') %>'
|
12
|
+
```
|
13
|
+
|
14
|
+
And then execute:
|
15
|
+
|
16
|
+
$ bundle
|
17
|
+
|
18
|
+
## Documentation
|
19
|
+
|
20
|
+
API docs are available at https://rubydocs.info/mongrep
|
21
|
+
|
22
|
+
## Development
|
23
|
+
|
24
|
+
After checking out the repo, run `bin/setup` to install dependencies. Then, run
|
25
|
+
`rake test` to run the tests. You can also run `bin/console` for an interactive
|
26
|
+
prompt that will allow you to experiment.
|
27
|
+
|
28
|
+
To install this gem onto your local machine, run `bundle exec rake install`. To
|
29
|
+
release a new version, update the version number in `version.rb`, and then run
|
30
|
+
`bundle exec rake release`, which will create a git tag for the version, push
|
31
|
+
git commits and tags and push the gem to rubygems.org.
|
data/Rakefile
ADDED
@@ -0,0 +1,29 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'bundler/gem_tasks'
|
3
|
+
require 'rspec/core/rake_task'
|
4
|
+
require 'rubocop/rake_task'
|
5
|
+
require 'erb'
|
6
|
+
require 'yard'
|
7
|
+
require 'json'
|
8
|
+
|
9
|
+
require 'mongrep/version'
|
10
|
+
|
11
|
+
RSpec::Core::RakeTask.new(:spec)
|
12
|
+
|
13
|
+
task default: :test
|
14
|
+
|
15
|
+
task test: [:spec, :rubocop]
|
16
|
+
|
17
|
+
RuboCop::RakeTask.new
|
18
|
+
|
19
|
+
Rake::Task['release:guard_clean'].enhance do
|
20
|
+
begin
|
21
|
+
system('git stash')
|
22
|
+
erb = ERB.new(File.read(File.expand_path('../README.md.erb', __FILE__)))
|
23
|
+
File.write(File.expand_path('../README.md', __FILE__), erb.result)
|
24
|
+
system('git add README.md')
|
25
|
+
system('git commit -m "Update gem version in readme"')
|
26
|
+
ensure
|
27
|
+
system('git stash pop')
|
28
|
+
end
|
29
|
+
end
|
data/bin/console
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
# frozen_string_literal: true
|
3
|
+
|
4
|
+
require 'bundler/setup'
|
5
|
+
require 'mongrep'
|
6
|
+
|
7
|
+
# You can add fixtures and/or initialization code here to make experimenting
|
8
|
+
# with your gem easier. You can also use a different console, if you like.
|
9
|
+
|
10
|
+
# (If you use this, don't forget to add pry to your Gemfile!)
|
11
|
+
# require 'pry'
|
12
|
+
# Pry.start
|
13
|
+
|
14
|
+
require 'pry'
|
15
|
+
Pry.start
|
data/bin/setup
ADDED
@@ -0,0 +1,36 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
3
|
+
|
4
|
+
module Mongrep
|
5
|
+
# Top level namespace for all core extensions
|
6
|
+
module CoreExt
|
7
|
+
# Core extensions to Hash class
|
8
|
+
module Hash
|
9
|
+
# Produces a hash containing only given keys which can be in dot
|
10
|
+
# notation
|
11
|
+
# @param keys [<String, Symbol>] the keys to include in the resulting
|
12
|
+
# hash. Note that they are stringified and self will be accessed
|
13
|
+
# with indifferent access.
|
14
|
+
# @return [{String => Object}] the hash including only the selected
|
15
|
+
# stringified keys
|
16
|
+
# @example
|
17
|
+
# hash = { foo: { bar: 'foobar' }, bar: 'foo' }
|
18
|
+
# hash.slice_with_dot_notation('foo.bar')
|
19
|
+
# #=> { 'foo.bar' => 'foobar' }
|
20
|
+
def slice_with_dot_notation(*keys)
|
21
|
+
keys.map(&:to_s).each_with_object(self.class.new) do |key, hash|
|
22
|
+
path = key.to_s.split('.')
|
23
|
+
|
24
|
+
catch :missing_key do
|
25
|
+
hash[key] = path.reduce(with_indifferent_access) do |level, part|
|
26
|
+
throw :missing_key unless level.key?(part)
|
27
|
+
level[part]
|
28
|
+
end
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
::Hash.include(self)
|
34
|
+
end
|
35
|
+
end
|
36
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'mongo'
|
3
|
+
|
4
|
+
module Mongrep
|
5
|
+
module CoreExt
|
6
|
+
# Core extensions to Mongo
|
7
|
+
module Mongo
|
8
|
+
# Core extensions to Mongo::Error
|
9
|
+
module Error
|
10
|
+
# Core extensions to Mongo::Error::OperationFailure
|
11
|
+
module OperationFailure
|
12
|
+
def duplicate_key_error?
|
13
|
+
message.start_with?('E11000')
|
14
|
+
end
|
15
|
+
|
16
|
+
::Mongo::Error::OperationFailure.include(self)
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,144 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'virtus'
|
3
|
+
|
4
|
+
module Mongrep
|
5
|
+
# A mixin providing Virtus.model functionality and a recursive to_h method
|
6
|
+
module Model
|
7
|
+
class << self
|
8
|
+
private
|
9
|
+
|
10
|
+
# The included method is used here to ensure correct order
|
11
|
+
# of inclusion and method overrides
|
12
|
+
def included(base)
|
13
|
+
base.include(Virtus.model(strict: true))
|
14
|
+
base.include(VirtusExtensions)
|
15
|
+
base.extend(ClassMethods)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
# Static methods of Shells
|
20
|
+
module Shell
|
21
|
+
# @return [String] A human readable string representation of the
|
22
|
+
# instance
|
23
|
+
def inspect
|
24
|
+
instance_vars = instance_variables.map do |var|
|
25
|
+
"#{var}=#{instance_variable_get(var).inspect}"
|
26
|
+
end
|
27
|
+
"#<#{self.class.inspect}:#{object_id} #{instance_vars.join(' ')}>"
|
28
|
+
end
|
29
|
+
|
30
|
+
# Static class methods for Shells
|
31
|
+
module ClassMethods
|
32
|
+
# @return [String] A human readable string representation of the
|
33
|
+
# class instance
|
34
|
+
def inspect
|
35
|
+
name
|
36
|
+
end
|
37
|
+
end
|
38
|
+
end
|
39
|
+
|
40
|
+
# Class methods for models
|
41
|
+
# TODO: Clean this up
|
42
|
+
# @!classmethods
|
43
|
+
module ClassMethods
|
44
|
+
# Returns a new model that includes just the provided subset of fields
|
45
|
+
# from the original model
|
46
|
+
# @overload partial(*field_names)
|
47
|
+
# @param field_names [Array(Symbol)] a list of fields to include in the
|
48
|
+
# resulting model
|
49
|
+
# @overload partial(*field_names, nested_fields)
|
50
|
+
# @param field_names [Array(Symbol)] a list of fields to include in the
|
51
|
+
# resulting model
|
52
|
+
# @param nested_fields [Hash(Symbol => Array(Symbol, Hash))]
|
53
|
+
# nested fields to include in the resulting model
|
54
|
+
# @return [Model] the generated model class
|
55
|
+
# @example
|
56
|
+
# MyModel.partial(:foo, :bar)
|
57
|
+
# @example With nested fields
|
58
|
+
# MyModel.partial(:foo, :bar, nested: [:foo, { deep: [:bar] }])
|
59
|
+
def partial(*fields)
|
60
|
+
class_name = "#{name}::Partial[#{fields.map(&:inspect).join(', ')}]"
|
61
|
+
attributes = partial_attributes(*fields)
|
62
|
+
Class.new do
|
63
|
+
include Model
|
64
|
+
include Shell
|
65
|
+
extend Shell::ClassMethods
|
66
|
+
|
67
|
+
define_singleton_method(:name) { class_name }
|
68
|
+
attributes.each { |attribute| attribute_set << attribute }
|
69
|
+
end
|
70
|
+
end
|
71
|
+
|
72
|
+
private
|
73
|
+
|
74
|
+
def partial_attributes(*fields)
|
75
|
+
field_hash = fields.last.is_a?(Hash) ? fields.pop : {}
|
76
|
+
|
77
|
+
(fields + field_hash.keys).map do |name|
|
78
|
+
attribute = attribute_set[name]
|
79
|
+
nested_fields = field_hash[name]
|
80
|
+
if nested_fields
|
81
|
+
nested_partial_attribute(attribute, *nested_fields)
|
82
|
+
else
|
83
|
+
attribute
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def nested_partial_attribute(attribute, *fields)
|
89
|
+
type = nested_partial(attribute.type, *fields)
|
90
|
+
options = attribute.options.slice(
|
91
|
+
:primitive, :default, :strict, :required, :finalize,
|
92
|
+
:nullify_blank, :reader, :writer, :name, :coerce
|
93
|
+
)
|
94
|
+
Virtus::Attribute.build(type, options)
|
95
|
+
end
|
96
|
+
|
97
|
+
def nested_partial(type, *fields)
|
98
|
+
case type
|
99
|
+
when Virtus::Attribute::Collection::Type
|
100
|
+
type.primitive[type.member_type.partial(*fields)]
|
101
|
+
when Virtus::Attribute::Hash::Type
|
102
|
+
Hash[type.key_type.primitive => type.value_type.partial(*fields)]
|
103
|
+
else type.primitive.partial(*fields)
|
104
|
+
end
|
105
|
+
end
|
106
|
+
end
|
107
|
+
|
108
|
+
# @!parse include VirtusExtensions
|
109
|
+
|
110
|
+
# Extensions to Virtus models
|
111
|
+
module VirtusExtensions
|
112
|
+
# Converts the model into a hash. This supports nested models.
|
113
|
+
# @return [Hash] A Hash representation of the models attributes
|
114
|
+
def to_h
|
115
|
+
result = {}
|
116
|
+
super.each { |key, value| result[key] = hashify_value(value) }
|
117
|
+
result
|
118
|
+
end
|
119
|
+
|
120
|
+
# Checks for equality between self and other
|
121
|
+
# @param other [Model] Another Model
|
122
|
+
# @return [true] If other has equal attributes
|
123
|
+
# @return [false] otherwise
|
124
|
+
def ==(other)
|
125
|
+
return false unless other.is_a?(self.class)
|
126
|
+
to_h == other.to_h
|
127
|
+
end
|
128
|
+
|
129
|
+
private
|
130
|
+
|
131
|
+
def hashify_value(value)
|
132
|
+
case value
|
133
|
+
when Array then value.map(&method(:hashify_value))
|
134
|
+
when Hash
|
135
|
+
result = {}
|
136
|
+
value.each { |k, v| result[k] = hashify_value(v) }
|
137
|
+
result
|
138
|
+
when Model then value.to_h
|
139
|
+
else value
|
140
|
+
end
|
141
|
+
end
|
142
|
+
end
|
143
|
+
end
|
144
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'abstractize'
|
3
|
+
require 'active_support/core_ext/hash/indifferent_access'
|
4
|
+
|
5
|
+
require 'mongrep/model'
|
6
|
+
|
7
|
+
module Mongrep
|
8
|
+
# @abstract The base class for all models
|
9
|
+
class MongoModel
|
10
|
+
include Abstractize
|
11
|
+
include Model
|
12
|
+
|
13
|
+
define_abstract_method :_id
|
14
|
+
|
15
|
+
# An alias for #_id
|
16
|
+
def id
|
17
|
+
_id
|
18
|
+
end
|
19
|
+
|
20
|
+
# Used by Mongo to convert the model into BSON
|
21
|
+
# @api private
|
22
|
+
def bson_type
|
23
|
+
Hash::BSON_TYPE
|
24
|
+
end
|
25
|
+
|
26
|
+
# Used by Mongo to convert the model into BSON
|
27
|
+
# @api private
|
28
|
+
def to_bson(*args)
|
29
|
+
to_h.to_bson(*args)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,70 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Mongrep
|
3
|
+
# A mongodb query object
|
4
|
+
class Query
|
5
|
+
# @param query_hash [Hash] A hash representing the query
|
6
|
+
# @example
|
7
|
+
# Query.new(name: 'test')
|
8
|
+
def initialize(query_hash = {})
|
9
|
+
@query_hash = query_hash.to_h
|
10
|
+
end
|
11
|
+
|
12
|
+
# Combines two queries by merging their underlying query hashes
|
13
|
+
# @param other [Query] The query to combine self with
|
14
|
+
# @return [Query] A new Query instance resulting in the combination of
|
15
|
+
# self and other
|
16
|
+
# @example
|
17
|
+
# first = Query.new(name: 'test', value: 5)
|
18
|
+
# second = Query.new(value: 6)
|
19
|
+
# (first & second).to_h #=> { name: 'test', value: 6 }
|
20
|
+
def &(other)
|
21
|
+
self.class.new(@query_hash.merge(other.to_h))
|
22
|
+
end
|
23
|
+
|
24
|
+
# Combines two queries by using the MongoDB $or operator
|
25
|
+
# @param other [Query] The query to combine self with
|
26
|
+
# @return [Query] A new Query instance resulting in the combination of
|
27
|
+
# self and other
|
28
|
+
# @example
|
29
|
+
# first = Query.new(name: 'foo')
|
30
|
+
# second = Query.new(name: 'bar')
|
31
|
+
# (first | second).to_h
|
32
|
+
# #=> { :$or => [{ name: 'foo' }, { name: 'bar' }] }
|
33
|
+
def |(other)
|
34
|
+
self.class.new(:$or => [@query_hash, other.to_h])
|
35
|
+
end
|
36
|
+
|
37
|
+
# Combines self with the given query hash by merging it to the
|
38
|
+
# existing one
|
39
|
+
# @param query_hash [Hash] The query hash to merge into the query
|
40
|
+
# @return [Query] A new Query resulting in the combination of the
|
41
|
+
# given query hash and the existing one
|
42
|
+
# @example
|
43
|
+
# query.where(name: 'test')
|
44
|
+
# @note This is mainly for usage in Repository#find using a block
|
45
|
+
# @see #&
|
46
|
+
# @see Repository Repository#find
|
47
|
+
def where(query_hash)
|
48
|
+
self & self.class.new(query_hash)
|
49
|
+
end
|
50
|
+
|
51
|
+
# Combines self with the given query hash by using the MongoDB $or
|
52
|
+
# operator
|
53
|
+
# @param query_hash [Hash] The query hash to combine with the query
|
54
|
+
# @return [Query] A new Query resulting in the combination of the
|
55
|
+
# given query hash and the existing one
|
56
|
+
# @see #|
|
57
|
+
# @example
|
58
|
+
# query.where(name: 'foo').or(name: 'bar')
|
59
|
+
def or(query_hash)
|
60
|
+
self | self.class.new(query_hash)
|
61
|
+
end
|
62
|
+
|
63
|
+
alias and where
|
64
|
+
|
65
|
+
# @return [Hash] The underlying query hash
|
66
|
+
def to_h
|
67
|
+
@query_hash
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
@@ -0,0 +1,44 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
module Mongrep
|
3
|
+
# A wrapper around mongo cursors
|
4
|
+
class QueryResult
|
5
|
+
include Enumerable
|
6
|
+
|
7
|
+
# The methods to be delegated to the underlying mongo cursor
|
8
|
+
# @api private
|
9
|
+
DELEGATED_METHODS = %i(limit projection skip sort).freeze
|
10
|
+
|
11
|
+
attr_reader :model_class
|
12
|
+
# @api private
|
13
|
+
def initialize(collection_view, model_class)
|
14
|
+
@model_class = model_class
|
15
|
+
@collection_view = collection_view
|
16
|
+
end
|
17
|
+
|
18
|
+
DELEGATED_METHODS.each do |method|
|
19
|
+
aggregation_stage = method == :projection ? :project : method
|
20
|
+
|
21
|
+
define_method(method) do |param|
|
22
|
+
if @collection_view.is_a?(Mongo::Collection::View::Aggregation)
|
23
|
+
@collection_view.pipeline << { :"$#{aggregation_stage}" => param }
|
24
|
+
else
|
25
|
+
@collection_view = @collection_view.public_send(method, param)
|
26
|
+
end
|
27
|
+
|
28
|
+
self
|
29
|
+
end
|
30
|
+
end
|
31
|
+
|
32
|
+
# Iterates over the query result
|
33
|
+
# @yieldparam item [Model] A model representing a document from the
|
34
|
+
# query result
|
35
|
+
# @return [void]
|
36
|
+
def each
|
37
|
+
return enum_for(:each) unless block_given?
|
38
|
+
|
39
|
+
@collection_view.each do |document|
|
40
|
+
yield @model_class.new(document)
|
41
|
+
end
|
42
|
+
end
|
43
|
+
end
|
44
|
+
end
|
@@ -0,0 +1,22 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
|
3
|
+
require 'mongrep/repository'
|
4
|
+
module Mongrep
|
5
|
+
# A mixin providing overwrites for write methods in read-only repositories
|
6
|
+
module ReadOnlyRepository
|
7
|
+
# An error signaling that the write operation isn't possible
|
8
|
+
class WriteError < ArgumentError; end
|
9
|
+
|
10
|
+
# @!method insert(*)
|
11
|
+
# @raise [WriteError] - Always raises
|
12
|
+
# @!method update(*)
|
13
|
+
# @raise [WriteError] - Always raises
|
14
|
+
# @!method delete(*)
|
15
|
+
# @raise [WriteError] - Always raises
|
16
|
+
%i(insert update delete).each do |method|
|
17
|
+
define_method(method) do |*|
|
18
|
+
raise WriteError, 'this repository is read-only'
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,198 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'active_support/inflector'
|
3
|
+
require 'active_support/core_ext/hash/except'
|
4
|
+
require 'active_support/core_ext/hash/slice'
|
5
|
+
require 'mongrep/core_ext/hash'
|
6
|
+
require 'abstractize'
|
7
|
+
require 'mongrep/query'
|
8
|
+
require 'mongrep/query_result'
|
9
|
+
require 'mongrep/core_ext/mongo/error/operation_failure'
|
10
|
+
|
11
|
+
module Mongrep
|
12
|
+
# @abstract The base class for all repositories
|
13
|
+
class Repository
|
14
|
+
include Abstractize
|
15
|
+
|
16
|
+
# An error signaling that a document could not be found
|
17
|
+
class DocumentNotFoundError < RuntimeError; end
|
18
|
+
# An error signaling that a model has not been persisted
|
19
|
+
class UnpersistedModelError < ArgumentError; end
|
20
|
+
# An error signaling that a document already exists
|
21
|
+
class DocumentExistsError < ArgumentError; end
|
22
|
+
|
23
|
+
attr_reader :collection
|
24
|
+
|
25
|
+
# @param mongo_client [Mongo::Client] The mongodb client to use
|
26
|
+
# @param collection_name [String] The name of the collection to use
|
27
|
+
# defaults to the demodulized and underscored class name
|
28
|
+
def initialize(
|
29
|
+
mongo_client,
|
30
|
+
collection_name = self.class.name.demodulize.underscore
|
31
|
+
)
|
32
|
+
@collection = mongo_client[collection_name]
|
33
|
+
end
|
34
|
+
|
35
|
+
# Derives the model class from the class name, requires and returns it
|
36
|
+
# @return [Model.class] The model class for this repository
|
37
|
+
# @example
|
38
|
+
# repository = Shop::ShoppingCarts.new(mongo_client)
|
39
|
+
# repository.model_class #=> Shop::Models::ShoppingCart
|
40
|
+
def model_class
|
41
|
+
model_name = self.class.name.demodulize.singularize
|
42
|
+
Mongrep.models_namespace.const_get(model_name)
|
43
|
+
end
|
44
|
+
|
45
|
+
# Finds documents matching the given query
|
46
|
+
# @overload find(query)
|
47
|
+
# @param query [Hash, Query] The mongodb query to perform
|
48
|
+
# @overload find(query, options)
|
49
|
+
# @param query [Hash, Query] The mongodb query to perform
|
50
|
+
# @param options [Hash] Options to pass to the query
|
51
|
+
# @overload find
|
52
|
+
# @yieldparam query [Query] A new query
|
53
|
+
# @yieldreturn [Query] The query to be used
|
54
|
+
# @overload find(query)
|
55
|
+
# @param query [Hash, Query] The initial query
|
56
|
+
# @yieldparam query [Query] The query object
|
57
|
+
# @yieldreturn [Query] The final query to be used
|
58
|
+
# @overload find(query, options)
|
59
|
+
# @param query [Hash, Query] The initial query
|
60
|
+
# @param options [Hash] Options to pass to the query
|
61
|
+
# @yieldparam query [Query] The query object
|
62
|
+
# @yieldreturn [Query] The final query to be used
|
63
|
+
# @return [QueryResult<Model>] An enumerable query result
|
64
|
+
# @example With query hash
|
65
|
+
# result = repository.find(name: 'test')
|
66
|
+
# @example With Query object
|
67
|
+
# repeating_query = Query.new(name: 'test')
|
68
|
+
# result = repository.find(repeating_query)
|
69
|
+
# @example With code block
|
70
|
+
# result = repository.find do |query|
|
71
|
+
# query.where(name: 'test 1').or(name: 'test 2')
|
72
|
+
# end
|
73
|
+
# @example With query hash and options
|
74
|
+
# result = repository.find({ name: 'test' }, limit: 1)
|
75
|
+
# @see Query
|
76
|
+
# @see QueryResult
|
77
|
+
def find(query = {}, options = {})
|
78
|
+
query_object = query.is_a?(Hash) ? Query.new(query) : query
|
79
|
+
query_object = yield(query_object) if block_given?
|
80
|
+
execute_query(query_object, options)
|
81
|
+
end
|
82
|
+
|
83
|
+
# Finds a single document matching the given query
|
84
|
+
# @overload find_one(query)
|
85
|
+
# @param query [Hash, Query] The mongodb query to perform
|
86
|
+
# @overload find_one(query, options)
|
87
|
+
# @param query [Hash, Query] The mongodb query to perform
|
88
|
+
# @param options [Hash] Options to pass to the query
|
89
|
+
# @overload find_one
|
90
|
+
# @yieldparam query [Query] A new query
|
91
|
+
# @yieldreturn [Query] The query to be used
|
92
|
+
# @overload find_one(query)
|
93
|
+
# @param query [Hash, Query] The initial query
|
94
|
+
# @yieldparam query [Query] The query object
|
95
|
+
# @yieldreturn [Query] The final query to be used
|
96
|
+
# @overload find_one(query, options)
|
97
|
+
# @param query [Hash, Query] The initial query
|
98
|
+
# @param options [Hash] Options to pass to the query
|
99
|
+
# @yieldparam query [Query] The query object
|
100
|
+
# @yieldreturn [Query] The final query to be used
|
101
|
+
# @raise [DocumentNotFoundError] if no matching document could be found
|
102
|
+
# @return [Model] The single model instance representing the document
|
103
|
+
# matching the query
|
104
|
+
def find_one(query = {}, options = {}, &block)
|
105
|
+
# TODO: Pass some context to DocumentNotFoundError
|
106
|
+
find(query, options, &block).first || raise(DocumentNotFoundError)
|
107
|
+
end
|
108
|
+
|
109
|
+
# Inserts a document into the database
|
110
|
+
# @param model [Model] The model representing the document to be inserted
|
111
|
+
# @return [Mongo::Operation::Write::Insert::Result] The result of the
|
112
|
+
# insert operation
|
113
|
+
def insert(model)
|
114
|
+
collection.insert_one(model.to_h)
|
115
|
+
rescue Mongo::Error::OperationFailure => error
|
116
|
+
# TODO: Pass relevant info to DocumentExistsError message
|
117
|
+
raise(error.duplicate_key_error? ? DocumentExistsError : error)
|
118
|
+
end
|
119
|
+
|
120
|
+
# Update an existing document in the database
|
121
|
+
# @param model [Model] The model representing the document to be updated
|
122
|
+
# @option options [Array<String>] :fields The specific fields to update.
|
123
|
+
# If this option is omitted the whole document is updated
|
124
|
+
# @raise [UnpersistedModelError] if the model is not persisted
|
125
|
+
# (has no value for _id)
|
126
|
+
# @raise [DocumentNotFoundError] if nothing was updated
|
127
|
+
# (no document found for _id)
|
128
|
+
# @return [Mongo::Operation::Write::Update::Result] The result of the
|
129
|
+
# update operation
|
130
|
+
def update(model, options = {})
|
131
|
+
check_persistence!(model)
|
132
|
+
result = collection.update_one(
|
133
|
+
id_query(model),
|
134
|
+
update_hash(model, options[:fields])
|
135
|
+
)
|
136
|
+
# TODO: Pass some context to DocumentNotFoundError
|
137
|
+
raise(DocumentNotFoundError) if result.documents.first['n'].zero?
|
138
|
+
result
|
139
|
+
end
|
140
|
+
|
141
|
+
# TODO: implement upsert
|
142
|
+
|
143
|
+
# Delete an existing document from the database
|
144
|
+
# @param model [Model] The model representing the document to be updated
|
145
|
+
# @raise [UnpersistedModelError] if the model is not persisted
|
146
|
+
# (has no value for _id)
|
147
|
+
# @raise [DocumentNotFoundError] if nothing was deleted
|
148
|
+
# (no document found for _id)
|
149
|
+
# @return [Mongo::Operation::Write::Delete::Result] The result of the
|
150
|
+
# delete operation
|
151
|
+
def delete(model)
|
152
|
+
check_persistence!(model)
|
153
|
+
result = collection.delete_one(id_query(model))
|
154
|
+
# TODO: Pass some context to DocumentNotFoundError
|
155
|
+
raise(DocumentNotFoundError) if result.documents.first['n'].zero?
|
156
|
+
result
|
157
|
+
end
|
158
|
+
|
159
|
+
# Get a distinct list of values for the given field over all documents
|
160
|
+
# in the collection.
|
161
|
+
# @param field [Symbol, String] The field or dot notated path to the
|
162
|
+
# field
|
163
|
+
# @return [Array] An array with the distinct values
|
164
|
+
def distinct(field)
|
165
|
+
collection.distinct(field)
|
166
|
+
end
|
167
|
+
|
168
|
+
private
|
169
|
+
|
170
|
+
def update_hash(model, fields_to_set = nil)
|
171
|
+
model_fields = model.to_h.except(:_id)
|
172
|
+
return model_fields unless fields_to_set
|
173
|
+
{ :$set => model_fields.slice_with_dot_notation(*fields_to_set) }
|
174
|
+
end
|
175
|
+
|
176
|
+
def id_query(model)
|
177
|
+
{ _id: model._id }
|
178
|
+
end
|
179
|
+
|
180
|
+
def execute_query(query_object, options)
|
181
|
+
check_query_type!(query_object)
|
182
|
+
QueryResult.new(collection.find(query_object.to_h, options), model_class)
|
183
|
+
end
|
184
|
+
|
185
|
+
def check_persistence!(model)
|
186
|
+
return if model._id
|
187
|
+
raise UnpersistedModelError, 'model is not yet persisted'
|
188
|
+
end
|
189
|
+
|
190
|
+
protected
|
191
|
+
|
192
|
+
def check_query_type!(query_object)
|
193
|
+
return if query_object.is_a?(Query)
|
194
|
+
raise ArgumentError, 'Invalid type for query ' \
|
195
|
+
"(#{query_object.class.name})"
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
data/lib/mongrep.rb
ADDED
@@ -0,0 +1,24 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
require 'mongrep/version'
|
3
|
+
|
4
|
+
# The top level namespace
|
5
|
+
module Mongrep
|
6
|
+
# An error signaling an error with the configuration of the gem
|
7
|
+
class ConfigurationError < RuntimeError; end
|
8
|
+
|
9
|
+
module_function
|
10
|
+
|
11
|
+
# @overload models_namespace
|
12
|
+
# Get the namespace where models are defined
|
13
|
+
# @overload models_namespace(namespace)
|
14
|
+
# Set the namespace where models are defined
|
15
|
+
# @param namespace [Module] The namespace module to be set
|
16
|
+
# @return [Module] the models namespace
|
17
|
+
def models_namespace(namespace = nil)
|
18
|
+
unless namespace || @models_namespace
|
19
|
+
raise ConfigurationError, 'models namespace is unset'
|
20
|
+
end
|
21
|
+
|
22
|
+
namespace ? @models_namespace = namespace : @models_namespace
|
23
|
+
end
|
24
|
+
end
|
data/mongrep.gemspec
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
# frozen_string_literal: true
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'mongrep/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |spec|
|
7
|
+
spec.name = 'mongrep'
|
8
|
+
spec.version = Mongrep::VERSION
|
9
|
+
spec.authors = ['Joakim Reinert']
|
10
|
+
spec.email = ['reinert@meso.net']
|
11
|
+
|
12
|
+
spec.summary =
|
13
|
+
'A library for utilizing the repository pattern for MongoDB'
|
14
|
+
spec.description =
|
15
|
+
'Mongrep provides base classes and modules for implementing persistance ' \
|
16
|
+
'layers for MongoDB using the repository pattern'
|
17
|
+
spec.homepage = 'https://github.com/meso-unimpressed/mongrep'
|
18
|
+
|
19
|
+
spec.files = `git ls-files -z`.split("\x0").reject do |f|
|
20
|
+
f.match(%r{^(test|spec|features)/})
|
21
|
+
end
|
22
|
+
|
23
|
+
spec.bindir = 'exe'
|
24
|
+
spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
|
25
|
+
spec.require_paths = ['lib']
|
26
|
+
|
27
|
+
spec.add_development_dependency 'bundler', '~> 1.1'
|
28
|
+
spec.add_development_dependency 'rake', '~> 11.2'
|
29
|
+
spec.add_development_dependency 'rspec', '~> 3.5'
|
30
|
+
spec.add_development_dependency 'rubocop', '~> 0.4'
|
31
|
+
spec.add_development_dependency 'rubocop-rspec', '~> 1.7'
|
32
|
+
spec.add_development_dependency 'pry', '~> 0.1'
|
33
|
+
spec.add_development_dependency 'pry-byebug', '~> 3.4'
|
34
|
+
spec.add_development_dependency 'simplecov', '~> 0.1'
|
35
|
+
spec.add_development_dependency 'guard', '~> 2.1'
|
36
|
+
spec.add_development_dependency 'guard-rspec', '~> 4.7'
|
37
|
+
spec.add_development_dependency 'guard-yard', '~> 2.1'
|
38
|
+
spec.add_development_dependency 'libnotify', '~> 0.9'
|
39
|
+
spec.add_development_dependency 'yard', '~> 0.9'
|
40
|
+
spec.add_development_dependency 'yard-classmethods', '~> 1.0'
|
41
|
+
spec.add_development_dependency 'factory_girl', '~> 4.7'
|
42
|
+
|
43
|
+
spec.add_dependency 'abstractize', '~> 0.1'
|
44
|
+
spec.add_dependency 'mongo', '~> 2.3'
|
45
|
+
spec.add_dependency 'virtus', '~> 1.0'
|
46
|
+
spec.add_dependency 'activesupport', '~> 5.0'
|
47
|
+
end
|
metadata
ADDED
@@ -0,0 +1,336 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: mongrep
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joakim Reinert
|
8
|
+
autorequire:
|
9
|
+
bindir: exe
|
10
|
+
cert_chain: []
|
11
|
+
date: 2016-11-10 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: bundler
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - "~>"
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '1.1'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - "~>"
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '1.1'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: rake
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - "~>"
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '11.2'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - "~>"
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '11.2'
|
41
|
+
- !ruby/object:Gem::Dependency
|
42
|
+
name: rspec
|
43
|
+
requirement: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - "~>"
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: '3.5'
|
48
|
+
type: :development
|
49
|
+
prerelease: false
|
50
|
+
version_requirements: !ruby/object:Gem::Requirement
|
51
|
+
requirements:
|
52
|
+
- - "~>"
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '3.5'
|
55
|
+
- !ruby/object:Gem::Dependency
|
56
|
+
name: rubocop
|
57
|
+
requirement: !ruby/object:Gem::Requirement
|
58
|
+
requirements:
|
59
|
+
- - "~>"
|
60
|
+
- !ruby/object:Gem::Version
|
61
|
+
version: '0.4'
|
62
|
+
type: :development
|
63
|
+
prerelease: false
|
64
|
+
version_requirements: !ruby/object:Gem::Requirement
|
65
|
+
requirements:
|
66
|
+
- - "~>"
|
67
|
+
- !ruby/object:Gem::Version
|
68
|
+
version: '0.4'
|
69
|
+
- !ruby/object:Gem::Dependency
|
70
|
+
name: rubocop-rspec
|
71
|
+
requirement: !ruby/object:Gem::Requirement
|
72
|
+
requirements:
|
73
|
+
- - "~>"
|
74
|
+
- !ruby/object:Gem::Version
|
75
|
+
version: '1.7'
|
76
|
+
type: :development
|
77
|
+
prerelease: false
|
78
|
+
version_requirements: !ruby/object:Gem::Requirement
|
79
|
+
requirements:
|
80
|
+
- - "~>"
|
81
|
+
- !ruby/object:Gem::Version
|
82
|
+
version: '1.7'
|
83
|
+
- !ruby/object:Gem::Dependency
|
84
|
+
name: pry
|
85
|
+
requirement: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - "~>"
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: '0.1'
|
90
|
+
type: :development
|
91
|
+
prerelease: false
|
92
|
+
version_requirements: !ruby/object:Gem::Requirement
|
93
|
+
requirements:
|
94
|
+
- - "~>"
|
95
|
+
- !ruby/object:Gem::Version
|
96
|
+
version: '0.1'
|
97
|
+
- !ruby/object:Gem::Dependency
|
98
|
+
name: pry-byebug
|
99
|
+
requirement: !ruby/object:Gem::Requirement
|
100
|
+
requirements:
|
101
|
+
- - "~>"
|
102
|
+
- !ruby/object:Gem::Version
|
103
|
+
version: '3.4'
|
104
|
+
type: :development
|
105
|
+
prerelease: false
|
106
|
+
version_requirements: !ruby/object:Gem::Requirement
|
107
|
+
requirements:
|
108
|
+
- - "~>"
|
109
|
+
- !ruby/object:Gem::Version
|
110
|
+
version: '3.4'
|
111
|
+
- !ruby/object:Gem::Dependency
|
112
|
+
name: simplecov
|
113
|
+
requirement: !ruby/object:Gem::Requirement
|
114
|
+
requirements:
|
115
|
+
- - "~>"
|
116
|
+
- !ruby/object:Gem::Version
|
117
|
+
version: '0.1'
|
118
|
+
type: :development
|
119
|
+
prerelease: false
|
120
|
+
version_requirements: !ruby/object:Gem::Requirement
|
121
|
+
requirements:
|
122
|
+
- - "~>"
|
123
|
+
- !ruby/object:Gem::Version
|
124
|
+
version: '0.1'
|
125
|
+
- !ruby/object:Gem::Dependency
|
126
|
+
name: guard
|
127
|
+
requirement: !ruby/object:Gem::Requirement
|
128
|
+
requirements:
|
129
|
+
- - "~>"
|
130
|
+
- !ruby/object:Gem::Version
|
131
|
+
version: '2.1'
|
132
|
+
type: :development
|
133
|
+
prerelease: false
|
134
|
+
version_requirements: !ruby/object:Gem::Requirement
|
135
|
+
requirements:
|
136
|
+
- - "~>"
|
137
|
+
- !ruby/object:Gem::Version
|
138
|
+
version: '2.1'
|
139
|
+
- !ruby/object:Gem::Dependency
|
140
|
+
name: guard-rspec
|
141
|
+
requirement: !ruby/object:Gem::Requirement
|
142
|
+
requirements:
|
143
|
+
- - "~>"
|
144
|
+
- !ruby/object:Gem::Version
|
145
|
+
version: '4.7'
|
146
|
+
type: :development
|
147
|
+
prerelease: false
|
148
|
+
version_requirements: !ruby/object:Gem::Requirement
|
149
|
+
requirements:
|
150
|
+
- - "~>"
|
151
|
+
- !ruby/object:Gem::Version
|
152
|
+
version: '4.7'
|
153
|
+
- !ruby/object:Gem::Dependency
|
154
|
+
name: guard-yard
|
155
|
+
requirement: !ruby/object:Gem::Requirement
|
156
|
+
requirements:
|
157
|
+
- - "~>"
|
158
|
+
- !ruby/object:Gem::Version
|
159
|
+
version: '2.1'
|
160
|
+
type: :development
|
161
|
+
prerelease: false
|
162
|
+
version_requirements: !ruby/object:Gem::Requirement
|
163
|
+
requirements:
|
164
|
+
- - "~>"
|
165
|
+
- !ruby/object:Gem::Version
|
166
|
+
version: '2.1'
|
167
|
+
- !ruby/object:Gem::Dependency
|
168
|
+
name: libnotify
|
169
|
+
requirement: !ruby/object:Gem::Requirement
|
170
|
+
requirements:
|
171
|
+
- - "~>"
|
172
|
+
- !ruby/object:Gem::Version
|
173
|
+
version: '0.9'
|
174
|
+
type: :development
|
175
|
+
prerelease: false
|
176
|
+
version_requirements: !ruby/object:Gem::Requirement
|
177
|
+
requirements:
|
178
|
+
- - "~>"
|
179
|
+
- !ruby/object:Gem::Version
|
180
|
+
version: '0.9'
|
181
|
+
- !ruby/object:Gem::Dependency
|
182
|
+
name: yard
|
183
|
+
requirement: !ruby/object:Gem::Requirement
|
184
|
+
requirements:
|
185
|
+
- - "~>"
|
186
|
+
- !ruby/object:Gem::Version
|
187
|
+
version: '0.9'
|
188
|
+
type: :development
|
189
|
+
prerelease: false
|
190
|
+
version_requirements: !ruby/object:Gem::Requirement
|
191
|
+
requirements:
|
192
|
+
- - "~>"
|
193
|
+
- !ruby/object:Gem::Version
|
194
|
+
version: '0.9'
|
195
|
+
- !ruby/object:Gem::Dependency
|
196
|
+
name: yard-classmethods
|
197
|
+
requirement: !ruby/object:Gem::Requirement
|
198
|
+
requirements:
|
199
|
+
- - "~>"
|
200
|
+
- !ruby/object:Gem::Version
|
201
|
+
version: '1.0'
|
202
|
+
type: :development
|
203
|
+
prerelease: false
|
204
|
+
version_requirements: !ruby/object:Gem::Requirement
|
205
|
+
requirements:
|
206
|
+
- - "~>"
|
207
|
+
- !ruby/object:Gem::Version
|
208
|
+
version: '1.0'
|
209
|
+
- !ruby/object:Gem::Dependency
|
210
|
+
name: factory_girl
|
211
|
+
requirement: !ruby/object:Gem::Requirement
|
212
|
+
requirements:
|
213
|
+
- - "~>"
|
214
|
+
- !ruby/object:Gem::Version
|
215
|
+
version: '4.7'
|
216
|
+
type: :development
|
217
|
+
prerelease: false
|
218
|
+
version_requirements: !ruby/object:Gem::Requirement
|
219
|
+
requirements:
|
220
|
+
- - "~>"
|
221
|
+
- !ruby/object:Gem::Version
|
222
|
+
version: '4.7'
|
223
|
+
- !ruby/object:Gem::Dependency
|
224
|
+
name: abstractize
|
225
|
+
requirement: !ruby/object:Gem::Requirement
|
226
|
+
requirements:
|
227
|
+
- - "~>"
|
228
|
+
- !ruby/object:Gem::Version
|
229
|
+
version: '0.1'
|
230
|
+
type: :runtime
|
231
|
+
prerelease: false
|
232
|
+
version_requirements: !ruby/object:Gem::Requirement
|
233
|
+
requirements:
|
234
|
+
- - "~>"
|
235
|
+
- !ruby/object:Gem::Version
|
236
|
+
version: '0.1'
|
237
|
+
- !ruby/object:Gem::Dependency
|
238
|
+
name: mongo
|
239
|
+
requirement: !ruby/object:Gem::Requirement
|
240
|
+
requirements:
|
241
|
+
- - "~>"
|
242
|
+
- !ruby/object:Gem::Version
|
243
|
+
version: '2.3'
|
244
|
+
type: :runtime
|
245
|
+
prerelease: false
|
246
|
+
version_requirements: !ruby/object:Gem::Requirement
|
247
|
+
requirements:
|
248
|
+
- - "~>"
|
249
|
+
- !ruby/object:Gem::Version
|
250
|
+
version: '2.3'
|
251
|
+
- !ruby/object:Gem::Dependency
|
252
|
+
name: virtus
|
253
|
+
requirement: !ruby/object:Gem::Requirement
|
254
|
+
requirements:
|
255
|
+
- - "~>"
|
256
|
+
- !ruby/object:Gem::Version
|
257
|
+
version: '1.0'
|
258
|
+
type: :runtime
|
259
|
+
prerelease: false
|
260
|
+
version_requirements: !ruby/object:Gem::Requirement
|
261
|
+
requirements:
|
262
|
+
- - "~>"
|
263
|
+
- !ruby/object:Gem::Version
|
264
|
+
version: '1.0'
|
265
|
+
- !ruby/object:Gem::Dependency
|
266
|
+
name: activesupport
|
267
|
+
requirement: !ruby/object:Gem::Requirement
|
268
|
+
requirements:
|
269
|
+
- - "~>"
|
270
|
+
- !ruby/object:Gem::Version
|
271
|
+
version: '5.0'
|
272
|
+
type: :runtime
|
273
|
+
prerelease: false
|
274
|
+
version_requirements: !ruby/object:Gem::Requirement
|
275
|
+
requirements:
|
276
|
+
- - "~>"
|
277
|
+
- !ruby/object:Gem::Version
|
278
|
+
version: '5.0'
|
279
|
+
description: Mongrep provides base classes and modules for implementing persistance
|
280
|
+
layers for MongoDB using the repository pattern
|
281
|
+
email:
|
282
|
+
- reinert@meso.net
|
283
|
+
executables: []
|
284
|
+
extensions: []
|
285
|
+
extra_rdoc_files: []
|
286
|
+
files:
|
287
|
+
- ".editorconfig"
|
288
|
+
- ".gitignore"
|
289
|
+
- ".rspec"
|
290
|
+
- ".rubocop.yml"
|
291
|
+
- ".ruby-version"
|
292
|
+
- ".simplecov"
|
293
|
+
- ".travis.yml"
|
294
|
+
- ".yardopts"
|
295
|
+
- Gemfile
|
296
|
+
- Guardfile
|
297
|
+
- README.md
|
298
|
+
- README.md.erb
|
299
|
+
- Rakefile
|
300
|
+
- bin/console
|
301
|
+
- bin/setup
|
302
|
+
- lib/mongrep.rb
|
303
|
+
- lib/mongrep/core_ext/hash.rb
|
304
|
+
- lib/mongrep/core_ext/mongo/error/operation_failure.rb
|
305
|
+
- lib/mongrep/model.rb
|
306
|
+
- lib/mongrep/mongo_model.rb
|
307
|
+
- lib/mongrep/query.rb
|
308
|
+
- lib/mongrep/query_result.rb
|
309
|
+
- lib/mongrep/read_only_repository.rb
|
310
|
+
- lib/mongrep/repository.rb
|
311
|
+
- lib/mongrep/version.rb
|
312
|
+
- mongrep.gemspec
|
313
|
+
homepage: https://github.com/meso-unimpressed/mongrep
|
314
|
+
licenses: []
|
315
|
+
metadata: {}
|
316
|
+
post_install_message:
|
317
|
+
rdoc_options: []
|
318
|
+
require_paths:
|
319
|
+
- lib
|
320
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
321
|
+
requirements:
|
322
|
+
- - ">="
|
323
|
+
- !ruby/object:Gem::Version
|
324
|
+
version: '0'
|
325
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
326
|
+
requirements:
|
327
|
+
- - ">="
|
328
|
+
- !ruby/object:Gem::Version
|
329
|
+
version: '0'
|
330
|
+
requirements: []
|
331
|
+
rubyforge_project:
|
332
|
+
rubygems_version: 2.5.1
|
333
|
+
signing_key:
|
334
|
+
specification_version: 4
|
335
|
+
summary: A library for utilizing the repository pattern for MongoDB
|
336
|
+
test_files: []
|