rmodel 0.2.2.pre.dev → 0.3.0.pre.dev

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 1b2245246230da601f1327a85f31eb1620b845d0
4
- data.tar.gz: fba4de0431ac5f5bb38db8c946dcb789e6fc919d
3
+ metadata.gz: 790c3d9b1e832db25110443277f43ecc7dc8c195
4
+ data.tar.gz: 075fb90b667ce3efc49e772366039173439c3941
5
5
  SHA512:
6
- metadata.gz: 83e9ca4326098fae9e620cd853d9f05558937b81813c4f85f9b15d06640b6b98605dd397911df9c6c171660a8cb28d53390fbcec643ce74f575e70aef74e88d3
7
- data.tar.gz: 13e959f27b6654e2c6658689e2f00c13a1789047a65c1aaff0d76bb86f61805ab3d54999991a70a8b5c68171af79f10a7bb670e8e7722236d25086f4f6057f11
6
+ metadata.gz: c70ec6448d74876b0fc8e82598e9ba695d03b6d57321925b66db79928789c6c89a09c3d9d87f6f680b9cc60abf77a185eb33c139782e21d4c4744f3c998b59f5
7
+ data.tar.gz: f6137976d4574443696e365ad4165d5bff58cd2960174776c7baafe4faa6fd7c77b286194617c49b9c967d3bdf2ae2ef55205723985c081fbe13894272ad2615
data/.gitignore CHANGED
@@ -20,3 +20,4 @@ tmp
20
20
  *.o
21
21
  *.a
22
22
  mkmf.log
23
+ rmodel_test.sqlite3
data/README.md CHANGED
@@ -9,10 +9,11 @@
9
9
  * [Timestamps](#timestamps)
10
10
  * [Sugar methods](#sugar-methods)
11
11
  * [Advanced creation of repository](#advanced-creation-of-repository)
12
+ * [SQL repository](#sql-repository)
12
13
 
13
14
  Rmodel is an ORM library, which tends to follow the SOLID principles.
14
15
 
15
- **Currently works with MongoDB only.**
16
+ **Currently works with MongoDB and SQL databases supported by Sequel.**
16
17
 
17
18
  The main thoughts behind it are:
18
19
 
@@ -228,6 +229,58 @@ repo.find(1)
228
229
 
229
230
  The `factory` is an object, which has 2 methods: `#fromHash(hash)` and `#toHash(object)`.
230
231
 
232
+ ### SQL repository
233
+
234
+ SQL amenities is based on the Sequel gem (http://sequel.jeremyevans.net/).
235
+ So the big range of SQL databases is supported.
236
+
237
+ > Sequel currently has adapters for ADO, Amalgalite, CUBRID, DataObjects, IBM_DB, JDBC, MySQL, Mysql2, ODBC, Oracle, PostgreSQL, SQLAnywhere, SQLite3, Swift, and TinyTDS.
238
+
239
+ Below you can the the example how to setup Rmodel for any supported SQL database.
240
+
241
+ ```ruby
242
+ require 'rmodel'
243
+
244
+ Rmodel.setup do
245
+ client :default, { adapter: 'sqlite', database: 'rmodel_test.sqlite3' }
246
+ end
247
+
248
+ client = Rmodel.setup.establish_sequel_client(:default)
249
+ client.drop_table? :things
250
+ client.create_table :things do
251
+ primary_key :id
252
+ String :name
253
+ Float :price
254
+ end
255
+
256
+ class Thing
257
+ attr_accessor :id, :name, :price
258
+
259
+ def initialize(name = nil, price = nil)
260
+ self.name = name
261
+ self.price = price
262
+ end
263
+ end
264
+
265
+ class ThingRepository < Rmodel::Sequel::Repository
266
+ simple_factory Thing, :name, :price
267
+
268
+ scope :worth_more_than do |amount|
269
+ # use Sequel dataset filtering http://sequel.jeremyevans.net/rdoc/files/doc/dataset_filtering_rdoc.html
270
+ where { price >= amount }
271
+ end
272
+ end
273
+
274
+ repo = ThingRepository.new
275
+ repo.insert Thing.new('iPod', 200)
276
+ repo.insert Thing.new('iPhone', 300)
277
+ repo.insert Thing.new('iPad', 500)
278
+
279
+ p repo.query.count # 3
280
+ p repo.query.worth_more_than(400).count # 1
281
+ p repo.query.worth_more_than(400).to_sql
282
+ ```
283
+
231
284
  ## Contributing
232
285
 
233
286
  1. Fork it ( https://github.com/alexei-lexx/rmodel/fork )
@@ -0,0 +1,42 @@
1
+ require 'rmodel'
2
+
3
+ Rmodel.setup do
4
+ # see more examples of connection options
5
+ # http://sequel.jeremyevans.net/rdoc/files/doc/opening_databases_rdoc.html#label-Passing+a+block+to+either+method
6
+ client :default, { adapter: 'sqlite', database: 'rmodel_test.sqlite3' }
7
+ end
8
+
9
+ client = Rmodel.setup.establish_sequel_client(:default)
10
+ client.drop_table? :things
11
+ client.create_table :things do
12
+ primary_key :id
13
+ String :name
14
+ Float :price
15
+ end
16
+
17
+ class Thing
18
+ attr_accessor :id, :name, :price
19
+
20
+ def initialize(name = nil, price = nil)
21
+ self.name = name
22
+ self.price = price
23
+ end
24
+ end
25
+
26
+ class ThingRepository < Rmodel::Sequel::Repository
27
+ simple_factory Thing, :name, :price
28
+
29
+ scope :worth_more_than do |amount|
30
+ # use Sequel dataset filtering http://sequel.jeremyevans.net/rdoc/files/doc/dataset_filtering_rdoc.html
31
+ where { price >= amount }
32
+ end
33
+ end
34
+
35
+ repo = ThingRepository.new
36
+ repo.insert Thing.new('iPod', 200)
37
+ repo.insert Thing.new('iPhone', 300)
38
+ repo.insert Thing.new('iPad', 500)
39
+
40
+ p repo.query.count # 3
41
+ p repo.query.worth_more_than(400).count # 1
42
+ p repo.query.worth_more_than(400).to_sql
@@ -0,0 +1,61 @@
1
+ require 'sequel'
2
+ require 'rmodel/base/repository'
3
+ require 'rmodel/sequel/repository_ext/queryable'
4
+
5
+ module Rmodel::Sequel
6
+ class Repository < Rmodel::Base::Repository
7
+ include RepositoryExt::Queryable
8
+
9
+ def initialize(client = nil, table = nil, factory = nil)
10
+ @client = client || Rmodel.setup.establish_sequel_client(self.class.client_name || :default) or
11
+ raise ArgumentError.new('Client driver is not setup')
12
+
13
+ @table = table || self.class.setting_table ||
14
+ self.class.table_by_convention or
15
+ raise ArgumentError.new('Table can not be guessed')
16
+
17
+ @factory = factory || self.class.setting_factory or
18
+ raise ArgumentError.new('Factory can not be guessed')
19
+ end
20
+
21
+ def find(id)
22
+ result = @client[@table].where(id: id).first
23
+ result && @factory.fromHash(result)
24
+ end
25
+
26
+ def insert(object)
27
+ id = @client[@table].insert(@factory.toHash(object, true))
28
+ object.id ||= id
29
+ end
30
+
31
+ def update(object)
32
+ @client[@table].where(id: object.id).update(@factory.toHash(object, false))
33
+ end
34
+
35
+ def remove(object)
36
+ @client[@table].where(id: object.id).delete
37
+ end
38
+
39
+ class << self
40
+ attr_reader :client_name, :setting_table, :setting_factory
41
+
42
+ def client(name)
43
+ @client_name = name
44
+ end
45
+
46
+ def table(name)
47
+ @setting_table = name
48
+ end
49
+
50
+ def table_by_convention
51
+ if name =~ /(.*)Repository$/
52
+ ActiveSupport::Inflector.tableize($1).to_sym
53
+ end
54
+ end
55
+
56
+ def simple_factory(klass, *attributes)
57
+ @setting_factory = SimpleFactory.new(klass, *attributes)
58
+ end
59
+ end
60
+ end
61
+ end
@@ -0,0 +1,34 @@
1
+ require 'origin'
2
+
3
+ module Rmodel::Sequel
4
+ module RepositoryExt
5
+ class Query
6
+ include Enumerable
7
+
8
+ def initialize(repo, dataset)
9
+ @repo = repo
10
+ @dataset = dataset
11
+ end
12
+
13
+ def each(&block)
14
+ @repo.find_by_query(@dataset).each(&block)
15
+ self
16
+ end
17
+
18
+ def remove
19
+ @dataset.delete
20
+ end
21
+
22
+ def to_sql
23
+ @dataset.sql
24
+ end
25
+
26
+ def self.define_scope(name, &block)
27
+ define_method name do |*args|
28
+ new_dataset = @dataset.instance_exec(*args, &block)
29
+ self.class.new(@repo, new_dataset)
30
+ end
31
+ end
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,30 @@
1
+ require 'rmodel/sequel/repository_ext/query'
2
+
3
+ module Rmodel::Sequel
4
+ module RepositoryExt
5
+ module Queryable
6
+ def self.included(base)
7
+ base.extend ClassMethods
8
+ end
9
+
10
+ def query
11
+ (self.class.query_klass ||= Class.new(Query)).new(self, @client[@table])
12
+ end
13
+
14
+ def find_by_query(dataset)
15
+ dataset.map do |hash|
16
+ @factory.fromHash(hash)
17
+ end
18
+ end
19
+
20
+ module ClassMethods
21
+ attr_accessor :query_klass
22
+
23
+ def scope(name, &block)
24
+ self.query_klass ||= Class.new(Query)
25
+ self.query_klass.define_scope(name, &block)
26
+ end
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,12 @@
1
+ module Rmodel::Sequel
2
+ module Setup
3
+ def establish_sequel_client(name)
4
+ config = @clients_config[name]
5
+ config && establish_client(name) { Sequel.connect(config) }
6
+ end
7
+ end
8
+ end
9
+
10
+ class Rmodel::Setup
11
+ include Rmodel::Sequel::Setup
12
+ end
@@ -0,0 +1,28 @@
1
+ module Rmodel::Sequel
2
+ class SimpleFactory
3
+ def initialize(klass, *attributes)
4
+ @klass = klass
5
+ @attributes = attributes
6
+ end
7
+
8
+ def fromHash(hash)
9
+ object = @klass.new
10
+ object.id = hash[:id]
11
+ @attributes.each do |attribute|
12
+ object.public_send "#{attribute}=", hash[attribute.to_sym]
13
+ end
14
+ object
15
+ end
16
+
17
+ def toHash(object, id_included)
18
+ hash = {}
19
+ @attributes.each do |attribute|
20
+ hash[attribute.to_sym] = object.public_send(attribute)
21
+ end
22
+ if id_included
23
+ hash[:id] = object.id
24
+ end
25
+ hash
26
+ end
27
+ end
28
+ end
data/lib/rmodel/setup.rb CHANGED
@@ -15,6 +15,7 @@ module Rmodel
15
15
 
16
16
  def clear
17
17
  @clients_config.clear
18
+ @established_clients.clear
18
19
  end
19
20
 
20
21
  private
@@ -1,3 +1,3 @@
1
1
  module Rmodel
2
- VERSION = "0.2.2-dev"
2
+ VERSION = "0.3.0-dev"
3
3
  end
data/lib/rmodel.rb CHANGED
@@ -4,6 +4,9 @@ require 'rmodel/errors'
4
4
  require 'rmodel/mongo/setup'
5
5
  require 'rmodel/mongo/simple_factory'
6
6
  require 'rmodel/mongo/repository'
7
+ require 'rmodel/sequel/setup'
8
+ require 'rmodel/sequel/simple_factory'
9
+ require 'rmodel/sequel/repository'
7
10
 
8
11
  module Rmodel
9
12
 
data/rmodel.gemspec CHANGED
@@ -21,10 +21,12 @@ Gem::Specification.new do |spec|
21
21
  spec.add_dependency 'mongo', '~> 2.1'
22
22
  spec.add_dependency 'activesupport'
23
23
  spec.add_dependency 'origin'
24
+ spec.add_dependency 'sequel'
24
25
 
25
26
  spec.add_development_dependency 'bundler', '~> 1.6'
26
27
  spec.add_development_dependency 'rake'
27
28
  spec.add_development_dependency 'rspec'
29
+ spec.add_development_dependency 'sqlite3'
28
30
 
29
31
  spec.required_ruby_version = '>= 2.0.0'
30
32
  end
@@ -1,5 +1,5 @@
1
1
  RSpec.describe Rmodel::Mongo::Repository do
2
- include_context 'clean Mongo database'
2
+ include_context 'clean mongo database'
3
3
 
4
4
  before do
5
5
  stub_const('Thing', Struct.new(:id, :a, :b))
@@ -0,0 +1,50 @@
1
+ RSpec.describe Rmodel::Mongo::Repository do
2
+ it_behaves_like 'repository crud' do
3
+ include_context 'clean mongo database'
4
+
5
+ before do
6
+ stub_const('ThingRepository', Class.new(Rmodel::Mongo::Repository))
7
+ end
8
+
9
+ let(:factory) { Rmodel::Mongo::SimpleFactory.new(Thing, :name) }
10
+ subject { ThingRepository.new(mongo_session, :things, factory) }
11
+ let(:unique_constraint_error) { Mongo::Error::OperationFailure }
12
+
13
+ def insert_record(id, columns)
14
+ record = columns.dup
15
+ record['_id'] = id
16
+ mongo_session[:things].insert_one(record)
17
+ end
18
+ end
19
+
20
+ it_behaves_like 'sugarable repository' do
21
+ include_context 'clean mongo database'
22
+
23
+ before do
24
+ stub_const('ThingRepository', Class.new(Rmodel::Mongo::Repository))
25
+ end
26
+
27
+ subject do
28
+ factory = Rmodel::Mongo::SimpleFactory.new(Thing, :name)
29
+ ThingRepository.new(mongo_session, :things, factory)
30
+ end
31
+ end
32
+
33
+ it_behaves_like 'timestampable repository' do
34
+ include_context 'clean mongo database'
35
+
36
+ before do
37
+ stub_const('ThingRepository', Class.new(Rmodel::Mongo::Repository))
38
+ end
39
+
40
+ let(:repo_w_timestamps) do
41
+ factory = Rmodel::Mongo::SimpleFactory.new(Thing, :name, :created_at, :updated_at)
42
+ ThingRepository.new(mongo_session, :things, factory)
43
+ end
44
+
45
+ let(:repo_wo_timestamps) do
46
+ factory = Rmodel::Mongo::SimpleFactory.new(Thing, :name)
47
+ ThingRepository.new(mongo_session, :things, factory)
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,114 @@
1
+ RSpec.describe Rmodel::Sequel::Repository do
2
+ include_context 'clean sequel database'
3
+
4
+ before do
5
+ create_database
6
+ stub_const('Thing', Struct.new(:id, :a, :b))
7
+ stub_const('ThingRepository', Class.new(Rmodel::Sequel::Repository))
8
+ end
9
+
10
+ subject do
11
+ factory = Rmodel::Sequel::SimpleFactory.new(Thing, :a, :b)
12
+ ThingRepository.new(sequel_conn, :things, factory)
13
+ end
14
+
15
+ before do
16
+ subject.insert(Thing.new(nil, 2, 3))
17
+ subject.insert(Thing.new(nil, 2, 4))
18
+ subject.insert(Thing.new(nil, 5, 6))
19
+ end
20
+
21
+ describe '.scope' do
22
+ context 'when a scope w/o arguments is defined' do
23
+ before do
24
+ ThingRepository.class_eval do
25
+ scope :a_equals_2 do
26
+ where(a: 2)
27
+ end
28
+ end
29
+ end
30
+
31
+ it 'works!' do
32
+ expect(subject.query.a_equals_2.count).to eq 2
33
+ end
34
+
35
+ it 'returns an array of instances of the appropriate class' do
36
+ expect(subject.query.a_equals_2.first).to be_an_instance_of Thing
37
+ end
38
+ end
39
+
40
+ context 'when a scope w/ arguments is defined' do
41
+ before do
42
+ ThingRepository.class_eval do
43
+ scope :a_equals do |n|
44
+ where(a: n)
45
+ end
46
+ end
47
+ end
48
+
49
+ it 'works!' do
50
+ expect(subject.query.a_equals(2).count).to eq 2
51
+ end
52
+ end
53
+
54
+ context 'when two scopes are defined and chained' do
55
+ before do
56
+ ThingRepository.class_eval do
57
+ scope :a_equals do |n|
58
+ where(a: n)
59
+ end
60
+
61
+ scope :b_equals do |n|
62
+ where(b: n)
63
+ end
64
+ end
65
+ end
66
+
67
+ it 'works!' do
68
+ expect(subject.query.a_equals(2).b_equals(4).count).to eq 1
69
+ end
70
+ end
71
+
72
+ context 'when an unknown scope is used' do
73
+ it 'raises the NoMethodError' do
74
+ expect {
75
+ subject.query.something
76
+ }.to raise_error NoMethodError
77
+ end
78
+ end
79
+ end
80
+
81
+ describe '.query' do
82
+ describe '#remove' do
83
+ context 'when no scope is given' do
84
+ it 'removes all objects' do
85
+ subject.query.remove
86
+ expect(subject.query.count).to eq 0
87
+ end
88
+ end
89
+
90
+ context 'when the scope filters 2 objects from 3' do
91
+ before do
92
+ ThingRepository.class_eval do
93
+ scope :a_equals_2 do
94
+ where(a: 2)
95
+ end
96
+ end
97
+ end
98
+
99
+ it 'removes 2 objects' do
100
+ subject.query.a_equals_2.remove
101
+ expect(subject.query.count).to eq 1
102
+ end
103
+ end
104
+ end
105
+ end
106
+
107
+ def create_database
108
+ sequel_conn.create_table(:things) do
109
+ primary_key :id
110
+ Integer :a
111
+ Integer :b
112
+ end
113
+ end
114
+ end
@@ -0,0 +1,118 @@
1
+ RSpec.describe Rmodel::Sequel::Repository do
2
+ before do
3
+ stub_const('Thing', Struct.new(:id, :name))
4
+ stub_const('ThingRepository', Class.new(Rmodel::Sequel::Repository))
5
+ ThingRepository.send :attr_reader, :client, :table, :factory
6
+ end
7
+ let(:factory) { Rmodel::Sequel::SimpleFactory.new(Thing, :name) }
8
+
9
+ describe '.client(name)' do
10
+ conn_options = { adapter: 'sqlite', database: 'rmodel_test.sqlite3' }
11
+
12
+ subject { ThingRepository.new(nil, :things, factory) }
13
+
14
+ context 'when it is called with an existent name' do
15
+ before do
16
+ Rmodel.setup do
17
+ client :not_default, conn_options
18
+ end
19
+
20
+ ThingRepository.class_eval do
21
+ client :not_default
22
+ end
23
+ end
24
+ after { Rmodel::setup.clear }
25
+
26
+ it 'sets the appropriate #client' do
27
+ expect(subject.client).to be_a_kind_of Sequel::Database
28
+ end
29
+ end
30
+
31
+ context 'when it is called with a non-existent name' do
32
+ before do
33
+ ThingRepository.class_eval do
34
+ client :not_default
35
+ end
36
+ end
37
+
38
+ it 'makes #initialize raise the ArgumentError' do
39
+ expect { subject }.to raise_error ArgumentError
40
+ end
41
+ end
42
+
43
+ context 'when it is not called' do
44
+ context 'when the :default client is set' do
45
+ before do
46
+ Rmodel.setup do
47
+ client :default, conn_options
48
+ end
49
+ end
50
+ after { Rmodel::setup.clear }
51
+
52
+ it 'sets #client to be default' do
53
+ expect(subject.client).to be_a_kind_of Sequel::Database
54
+ end
55
+ end
56
+
57
+ context 'when the :default client is not set' do
58
+ it 'makes #initialize raise the ArgumentError' do
59
+ expect { subject }.to raise_error ArgumentError
60
+ end
61
+ end
62
+ end
63
+ end
64
+
65
+ describe '.table(name)' do
66
+ subject { ThingRepository.new(Object.new, nil, factory) }
67
+
68
+ context 'when the :people table is given' do
69
+ before do
70
+ ThingRepository.class_eval do
71
+ table :people
72
+ end
73
+ end
74
+
75
+ it 'uses the :people' do
76
+ expect(subject.table).to eq :people
77
+ end
78
+ end
79
+
80
+ context 'when no table is given' do
81
+ it 'gets the right name by convention' do
82
+ expect(subject.table).to eq :things
83
+ end
84
+ end
85
+ end
86
+
87
+ describe '.simple_factory(klass, attribute1, attribute2, ...)' do
88
+ subject { ThingRepository.new(Object.new, :things) }
89
+
90
+ context 'when it is called' do
91
+ before do
92
+ ThingRepository.class_eval do
93
+ simple_factory Thing, :name, :email
94
+ end
95
+ end
96
+
97
+ it 'sets the appropriate #factory' do
98
+ expect(subject.factory).to be_an_instance_of Rmodel::Sequel::SimpleFactory
99
+ end
100
+ end
101
+
102
+ context 'when it is not called' do
103
+ it 'make #initialize raise an error' do
104
+ expect { subject }.to raise_error ArgumentError
105
+ end
106
+ end
107
+ end
108
+
109
+ describe '#initialize(client, collection, factory)' do
110
+ context 'when all constructor arguments are passed' do
111
+ it 'works!' do
112
+ expect {
113
+ ThingRepository.new(Object.new, :users, factory)
114
+ }.not_to raise_error
115
+ end
116
+ end
117
+ end
118
+ end
@@ -0,0 +1,64 @@
1
+ RSpec.describe Rmodel::Sequel::Repository do
2
+ it_behaves_like 'repository crud' do
3
+ include_examples 'clean sequel database'
4
+
5
+ before do
6
+ create_database
7
+ stub_const('ThingRepository', Class.new(Rmodel::Sequel::Repository))
8
+ end
9
+
10
+ let(:factory) { Rmodel::Sequel::SimpleFactory.new(Thing, :name) }
11
+ subject { ThingRepository.new(sequel_conn, :things, factory) }
12
+ let(:unique_constraint_error) { Sequel::UniqueConstraintViolation }
13
+
14
+ def insert_record(id, columns)
15
+ record = columns.dup
16
+ record[:id] = id
17
+ sequel_conn[:things].insert(record)
18
+ end
19
+ end
20
+
21
+ it_behaves_like 'sugarable repository' do
22
+ include_examples 'clean sequel database'
23
+
24
+ before do
25
+ create_database
26
+ stub_const('ThingRepository', Class.new(Rmodel::Sequel::Repository))
27
+ end
28
+
29
+ subject do
30
+ factory = Rmodel::Sequel::SimpleFactory.new(Thing, :name)
31
+ ThingRepository.new(sequel_conn, :things, factory)
32
+ end
33
+ end
34
+
35
+ it_behaves_like 'timestampable repository' do
36
+ include_examples 'clean sequel database'
37
+
38
+ before do
39
+ create_database(true)
40
+ stub_const('ThingRepository', Class.new(Rmodel::Sequel::Repository))
41
+ end
42
+
43
+ let(:repo_w_timestamps) do
44
+ factory = Rmodel::Sequel::SimpleFactory.new(Thing, :name, :created_at, :updated_at)
45
+ ThingRepository.new(sequel_conn, :things, factory)
46
+ end
47
+
48
+ let(:repo_wo_timestamps) do
49
+ factory = Rmodel::Sequel::SimpleFactory.new(Thing, :name)
50
+ ThingRepository.new(sequel_conn, :things, factory)
51
+ end
52
+ end
53
+
54
+ def create_database(timestamps = false)
55
+ sequel_conn.create_table(:things) do
56
+ primary_key :id
57
+ String :name
58
+ if timestamps
59
+ Time :created_at
60
+ Time :updated_at
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,55 @@
1
+ RSpec.describe Rmodel::Sequel::SimpleFactory do
2
+ context 'when the User(id, name, email) class is defined' do
3
+ before { stub_const('User', Struct.new(:id, :name, :email)) }
4
+
5
+ subject { described_class.new(User, :name, :email) }
6
+
7
+ describe '#fromHash' do
8
+ context 'when the hash with id, name and email is given' do
9
+ let(:hash) { { id: 1, name: 'John', email: 'john@example.com' } }
10
+ let(:result) { subject.fromHash(hash) }
11
+
12
+ it 'returns an instance of User' do
13
+ expect(result).to be_an_instance_of User
14
+ end
15
+
16
+ it 'sets the attributes correctly' do
17
+ expect(result.name).to eq 'John'
18
+ expect(result.email).to eq 'john@example.com'
19
+ end
20
+
21
+ it 'sets the User#id correctly' do
22
+ expect(result.id).to eq 1
23
+ end
24
+ end
25
+ end
26
+
27
+ describe '#toHash' do
28
+ let(:user) { User.new(1, 'John', 'john@example.com') }
29
+ context 'when id_included is false' do
30
+ let(:result) { subject.toHash(user, false) }
31
+
32
+ it 'returns an instance of Hash' do
33
+ expect(result).to be_an_instance_of Hash
34
+ end
35
+
36
+ it 'sets the keys correctly' do
37
+ expect(result[:name]).to eq 'John'
38
+ expect(result[:email]).to eq 'john@example.com'
39
+ end
40
+
41
+ it 'has no the "id" key' do
42
+ expect(result.has_key?(:id)).to be false
43
+ end
44
+ end
45
+
46
+ context 'when id_included is true' do
47
+ let(:result) { subject.toHash(user, true) }
48
+
49
+ it 'sets the "id" key' do
50
+ expect(result[:id]).to eq 1
51
+ end
52
+ end
53
+ end
54
+ end
55
+ end
@@ -1,4 +1,4 @@
1
- RSpec.shared_context 'clean Mongo database' do
1
+ RSpec.shared_context 'clean mongo database' do
2
2
  let(:mongo_session) { Mongo::Client.new([ '127.0.0.1:27017' ], database: 'rmodel_test') }
3
3
 
4
4
  before(:all) do
@@ -0,0 +1,11 @@
1
+ RSpec.shared_context 'clean sequel database' do
2
+ let(:sequel_conn) { Sequel.connect(adapter: 'sqlite', database: 'rmodel_test.sqlite3') }
3
+
4
+ before(:all) do
5
+ Mongo::Logger.logger.level = Logger::ERROR
6
+ sequel_conn = Sequel.sqlite('rmodel_test.sqlite3')
7
+ sequel_conn.drop_table?(:users, :things)
8
+ end
9
+
10
+ after { sequel_conn.drop_table?(:users, :things) }
11
+ end
@@ -0,0 +1,83 @@
1
+ RSpec.shared_examples 'repository crud' do
2
+ before do
3
+ stub_const('Thing', Struct.new(:id, :name))
4
+ end
5
+
6
+ describe '#find' do
7
+ before { insert_record(1, name: 'chair') }
8
+
9
+ context 'when an existent id is given' do
10
+ it 'returns the instance of correct type' do
11
+ expect(subject.find(1)).to be_an_instance_of Thing
12
+ end
13
+
14
+ it 'returns the correct attributes' do
15
+ expect(subject.find(1).name).to eq 'chair'
16
+ end
17
+ end
18
+
19
+ context 'when a non-existent id is given' do
20
+ it 'returns nil' do
21
+ expect(subject.find(2)).to be_nil
22
+ end
23
+ end
24
+ end
25
+
26
+ describe '#insert' do
27
+ context 'when the id is not provided' do
28
+ let(:thing) { Thing.new(nil, 'chair') }
29
+
30
+ it 'sets the id before insert' do
31
+ subject.insert(thing)
32
+ expect(thing.id).not_to be_nil
33
+ end
34
+
35
+ it 'persists the object' do
36
+ subject.insert(thing)
37
+ expect(subject.find(thing.id).name).to eq 'chair'
38
+ end
39
+ end
40
+
41
+ context 'when the id is provided' do
42
+ let(:thing) { Thing.new(1000, 'chair') }
43
+
44
+ it 'uses the existent id' do
45
+ subject.insert(thing)
46
+ expect(thing.id).to eq 1000
47
+ end
48
+ end
49
+
50
+ context 'when the given id already exists' do
51
+ let(:thing) { Thing.new }
52
+ before { subject.insert(thing) }
53
+
54
+ it 'raises the error' do
55
+ expect { subject.insert(thing) }.to raise_error unique_constraint_error
56
+ end
57
+ end
58
+ end
59
+
60
+ describe '#update' do
61
+ let(:thing) { Thing.new(nil) }
62
+
63
+ before do
64
+ subject.insert(thing)
65
+ thing.name = 'chair'
66
+ end
67
+
68
+ it 'updates the record' do
69
+ subject.update(thing)
70
+ expect(subject.find(thing.id).name).to eq 'chair'
71
+ end
72
+ end
73
+
74
+ describe '#remove' do
75
+ let(:thing) { Thing.new }
76
+ before { subject.insert(thing) }
77
+
78
+ it 'removes the record' do
79
+ subject.remove(thing)
80
+ expect(subject.find(thing.id)).to be_nil
81
+ end
82
+ end
83
+ end
@@ -1,45 +1,43 @@
1
1
  RSpec.shared_examples 'sugarable repository' do
2
- describe Rmodel::Base::RepositoryExt::Sugarable do
3
- before do
4
- stub_const('Thing', Struct.new(:id, :name))
5
- end
2
+ before do
3
+ stub_const('Thing', Struct.new(:id, :name))
4
+ end
6
5
 
7
- describe '#find!' do
8
- context 'when an existent id is given' do
9
- before { subject.insert(Thing.new(1)) }
6
+ describe '#find!' do
7
+ context 'when an existent id is given' do
8
+ before { subject.insert(Thing.new(1)) }
10
9
 
11
- it 'returns the right instance' do
12
- expect(subject.find!(1)).not_to be_nil
13
- end
10
+ it 'returns the right instance' do
11
+ expect(subject.find!(1)).not_to be_nil
14
12
  end
13
+ end
15
14
 
16
- context 'when a non-existent id is given' do
17
- it 'raises NotFound' do
18
- expect {
19
- subject.find!(1)
20
- }.to raise_error Rmodel::NotFound
21
- end
15
+ context 'when a non-existent id is given' do
16
+ it 'raises NotFound' do
17
+ expect {
18
+ subject.find!(1)
19
+ }.to raise_error Rmodel::NotFound
22
20
  end
23
21
  end
22
+ end
24
23
 
25
- describe 'save' do
26
- let(:thing) { Thing.new }
24
+ describe 'save' do
25
+ let(:thing) { Thing.new }
27
26
 
28
- context 'when a new object is given' do
29
- it 'gets inserted' do
30
- subject.save(thing)
31
- expect(subject.find(thing.id)).not_to be_nil
32
- end
27
+ context 'when a new object is given' do
28
+ it 'gets inserted' do
29
+ subject.save(thing)
30
+ expect(subject.find(thing.id)).not_to be_nil
33
31
  end
32
+ end
34
33
 
35
- context 'when an existent object is given' do
36
- before { subject.insert(thing) }
34
+ context 'when an existent object is given' do
35
+ before { subject.insert(thing) }
37
36
 
38
- it 'gets updated' do
39
- thing.name = 'chair'
40
- subject.save(thing)
41
- expect(subject.find(thing.id).name).to eq 'chair'
42
- end
37
+ it 'gets updated' do
38
+ thing.name = 'chair'
39
+ subject.save(thing)
40
+ expect(subject.find(thing.id).name).to eq 'chair'
43
41
  end
44
42
  end
45
43
  end
@@ -1,68 +1,66 @@
1
1
  RSpec.shared_examples 'timestampable repository' do
2
- describe Rmodel::Base::RepositoryExt::Timestampable do
3
- context 'when the entity object has attributes created_at and updated_at' do
4
- before do
5
- stub_const('Thing', Struct.new(:id, :name, :created_at, :updated_at))
6
- end
7
-
8
- subject { repo_w_timestamps }
9
-
10
- context 'when we insert(object)' do
11
- context 'and the object.created_at is already set' do
12
- let(:thing) { Thing.new(nil, 'chair', Time.now) }
2
+ context 'when the entity object has attributes created_at and updated_at' do
3
+ before do
4
+ stub_const('Thing', Struct.new(:id, :name, :created_at, :updated_at))
5
+ end
13
6
 
14
- it 'doesnt change the value of created_at' do
15
- set_created_at = thing.created_at
16
- subject.insert(thing)
17
- expect(thing.created_at).to eq set_created_at
18
- end
19
- end
7
+ subject { repo_w_timestamps }
20
8
 
21
- context 'and the object.created_at is not set yet' do
22
- let(:thing) { Thing.new(nil, 'chair') }
9
+ context 'when we insert(object)' do
10
+ context 'and the object.created_at is already set' do
11
+ let(:thing) { Thing.new(nil, 'chair', Time.now) }
23
12
 
24
- it 'sets the value of created_at' do
25
- subject.insert(thing)
26
- expect(thing.created_at).not_to be_nil
27
- end
13
+ it 'doesnt change the value of created_at' do
14
+ set_created_at = thing.created_at
15
+ subject.insert(thing)
16
+ expect(thing.created_at).to eq set_created_at
28
17
  end
29
18
  end
30
19
 
31
- context 'when we update(object)' do
20
+ context 'and the object.created_at is not set yet' do
32
21
  let(:thing) { Thing.new(nil, 'chair') }
33
- before { subject.insert(thing) }
34
22
 
35
23
  it 'sets the value of created_at' do
36
- thing.name = 'table'
37
- subject.update(thing)
38
- expect(thing.updated_at).not_to be_nil
24
+ subject.insert(thing)
25
+ expect(thing.created_at).not_to be_nil
39
26
  end
40
27
  end
41
28
  end
42
29
 
43
- context 'when the entity has no attributes :created_at and updated_at' do
44
- before do
45
- stub_const('Thing', Struct.new(:id, :name))
30
+ context 'when we update(object)' do
31
+ let(:thing) { Thing.new(nil, 'chair') }
32
+ before { subject.insert(thing) }
33
+
34
+ it 'sets the value of created_at' do
35
+ thing.name = 'table'
36
+ subject.update(thing)
37
+ expect(thing.updated_at).not_to be_nil
46
38
  end
39
+ end
40
+ end
47
41
 
48
- let(:thing) { Thing.new(nil, 'chair') }
49
- subject { repo_wo_timestamps }
42
+ context 'when the entity has no attributes :created_at and updated_at' do
43
+ before do
44
+ stub_const('Thing', Struct.new(:id, :name))
45
+ end
50
46
 
51
- context 'when we insert(object)' do
52
- it 'does nothing special' do
53
- subject.insert(thing)
54
- expect(thing.respond_to?(:created_at)).to be false
55
- end
47
+ let(:thing) { Thing.new(nil, 'chair') }
48
+ subject { repo_wo_timestamps }
49
+
50
+ context 'when we insert(object)' do
51
+ it 'does nothing special' do
52
+ subject.insert(thing)
53
+ expect(thing.respond_to?(:created_at)).to be false
56
54
  end
55
+ end
57
56
 
58
- context 'when we update(object)' do
59
- before { subject.insert(thing) }
57
+ context 'when we update(object)' do
58
+ before { subject.insert(thing) }
60
59
 
61
- it 'does nothing special' do
62
- thing.name = 'table'
63
- subject.update(thing)
64
- expect(thing.respond_to?(:updated_at)).to be false
65
- end
60
+ it 'does nothing special' do
61
+ thing.name = 'table'
62
+ subject.update(thing)
63
+ expect(thing.respond_to?(:updated_at)).to be false
66
64
  end
67
65
  end
68
66
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: rmodel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.2.2.pre.dev
4
+ version: 0.3.0.pre.dev
5
5
  platform: ruby
6
6
  authors:
7
7
  - Alexei
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-10-27 00:00:00.000000000 Z
11
+ date: 2015-10-28 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: mongo
@@ -52,6 +52,20 @@ dependencies:
52
52
  - - ">="
53
53
  - !ruby/object:Gem::Version
54
54
  version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: sequel
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :runtime
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
55
69
  - !ruby/object:Gem::Dependency
56
70
  name: bundler
57
71
  requirement: !ruby/object:Gem::Requirement
@@ -94,6 +108,20 @@ dependencies:
94
108
  - - ">="
95
109
  - !ruby/object:Gem::Version
96
110
  version: '0'
111
+ - !ruby/object:Gem::Dependency
112
+ name: sqlite3
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
97
125
  description: Rmodel is an ORM library, which tends to follow the SOLID principles.
98
126
  email:
99
127
  - alexei.lexx@gmail.com
@@ -109,6 +137,7 @@ files:
109
137
  - README.md
110
138
  - Rakefile
111
139
  - examples/advanced_creation_of_repository.rb
140
+ - examples/sql_repository.rb
112
141
  - examples/timestamps.rb
113
142
  - examples/user.rb
114
143
  - lib/rmodel.rb
@@ -121,17 +150,26 @@ files:
121
150
  - lib/rmodel/mongo/repository_ext/queryable.rb
122
151
  - lib/rmodel/mongo/setup.rb
123
152
  - lib/rmodel/mongo/simple_factory.rb
153
+ - lib/rmodel/sequel/repository.rb
154
+ - lib/rmodel/sequel/repository_ext/query.rb
155
+ - lib/rmodel/sequel/repository_ext/queryable.rb
156
+ - lib/rmodel/sequel/setup.rb
157
+ - lib/rmodel/sequel/simple_factory.rb
124
158
  - lib/rmodel/setup.rb
125
159
  - lib/rmodel/version.rb
126
160
  - rmodel.gemspec
127
- - spec/rmodel/mongo/repository_crud_spec.rb
128
161
  - spec/rmodel/mongo/repository_ext/queryable_spec.rb
129
- - spec/rmodel/mongo/repository_ext/sugarable_spec.rb
130
- - spec/rmodel/mongo/repository_ext/timestampable_spec.rb
131
162
  - spec/rmodel/mongo/repository_initialize_spec.rb
163
+ - spec/rmodel/mongo/repository_spec.rb
132
164
  - spec/rmodel/mongo/simple_factory_spec.rb
165
+ - spec/rmodel/sequel/repository_ext/queryable_spec.rb
166
+ - spec/rmodel/sequel/repository_initialize_spec.rb
167
+ - spec/rmodel/sequel/repository_spec.rb
168
+ - spec/rmodel/sequel/simple_factory_spec.rb
133
169
  - spec/rmodel/setup_spec.rb
134
170
  - spec/shared/clean_moped.rb
171
+ - spec/shared/clean_sequel.rb
172
+ - spec/shared/repository_ext/crud.rb
135
173
  - spec/shared/repository_ext/sugarable.rb
136
174
  - spec/shared/repository_ext/timestampable.rb
137
175
  - spec/spec_helper.rb
@@ -160,14 +198,18 @@ signing_key:
160
198
  specification_version: 4
161
199
  summary: Rmodel is an ORM library, which tends to follow the SOLID principles.
162
200
  test_files:
163
- - spec/rmodel/mongo/repository_crud_spec.rb
164
201
  - spec/rmodel/mongo/repository_ext/queryable_spec.rb
165
- - spec/rmodel/mongo/repository_ext/sugarable_spec.rb
166
- - spec/rmodel/mongo/repository_ext/timestampable_spec.rb
167
202
  - spec/rmodel/mongo/repository_initialize_spec.rb
203
+ - spec/rmodel/mongo/repository_spec.rb
168
204
  - spec/rmodel/mongo/simple_factory_spec.rb
205
+ - spec/rmodel/sequel/repository_ext/queryable_spec.rb
206
+ - spec/rmodel/sequel/repository_initialize_spec.rb
207
+ - spec/rmodel/sequel/repository_spec.rb
208
+ - spec/rmodel/sequel/simple_factory_spec.rb
169
209
  - spec/rmodel/setup_spec.rb
170
210
  - spec/shared/clean_moped.rb
211
+ - spec/shared/clean_sequel.rb
212
+ - spec/shared/repository_ext/crud.rb
171
213
  - spec/shared/repository_ext/sugarable.rb
172
214
  - spec/shared/repository_ext/timestampable.rb
173
215
  - spec/spec_helper.rb
@@ -1,90 +0,0 @@
1
- RSpec.describe Rmodel::Mongo::Repository do
2
- include_context 'clean Mongo database'
3
-
4
- before do
5
- stub_const('User', Struct.new(:id, :name, :email))
6
- stub_const('UserRepository', Class.new(Rmodel::Mongo::Repository))
7
- end
8
-
9
- let(:factory) { Rmodel::Mongo::SimpleFactory.new(User, :name, :email) }
10
- subject(:repo) { UserRepository.new(mongo_session, :users, factory) }
11
-
12
- describe '#find' do
13
- context 'when an existent id is given' do
14
- before do
15
- mongo_session[:users].insert_one(_id: 1, name: 'John', email: 'john@example.com')
16
- end
17
-
18
- it 'returns the instance of correct type' do
19
- expect(repo.find(1)).to be_an_instance_of User
20
- end
21
- end
22
-
23
- context 'when a non-existent id is given' do
24
- it 'returns nil' do
25
- expect(repo.find(1)).to be_nil
26
- end
27
- end
28
- end
29
-
30
- describe '#insert' do
31
- context 'when the id is not provided' do
32
- let(:user) { User.new(nil, 'John', 'john@example.com') }
33
-
34
- it 'sets the id before insert' do
35
- repo.insert(user)
36
- expect(user.id).not_to be_nil
37
- end
38
-
39
- it 'persists the object' do
40
- repo.insert(user)
41
- found = mongo_session[:users].find(name: 'John', email: 'john@example.com').count
42
- expect(found).to eq 1
43
- end
44
- end
45
-
46
- context 'when the id is provided' do
47
- let(:user) { User.new(1, 'John', 'john@example.com') }
48
-
49
- it 'uses the existent id' do
50
- repo.insert(user)
51
- expect(user.id).to eq 1
52
- end
53
- end
54
-
55
- context 'when the given id already exists' do
56
- let(:user) { User.new(nil, 'John', 'john@example.com') }
57
- before { repo.insert(user) }
58
-
59
- it 'raises the error' do
60
- expect { repo.insert(user) }.to raise_error Mongo::Error::OperationFailure
61
- end
62
- end
63
- end
64
-
65
- describe '#update' do
66
- let(:user) { User.new(nil, 'John', 'john@example.com') }
67
-
68
- before do
69
- repo.insert(user)
70
- user.name = 'John Smith'
71
- end
72
-
73
- it 'updates the record' do
74
- repo.update(user)
75
- found = mongo_session[:users].find(name: 'John Smith').count
76
- expect(found).to eq 1
77
- end
78
- end
79
-
80
- describe '#remove' do
81
- let(:user) { User.new(nil, 'John', 'john@example.com') }
82
- before { repo.insert(user) }
83
-
84
- it 'removes the record' do
85
- repo.remove(user)
86
- found = mongo_session[:users].find(name: 'John').count
87
- expect(found).to eq 0
88
- end
89
- end
90
- end
@@ -1,14 +0,0 @@
1
- RSpec.describe Rmodel::Mongo::Repository do
2
- include_examples 'sugarable repository' do
3
- include_context 'clean Mongo database'
4
-
5
- before do
6
- stub_const('ThingRepository', Class.new(Rmodel::Mongo::Repository))
7
- end
8
-
9
- subject do
10
- factory = Rmodel::Mongo::SimpleFactory.new(Thing, :name)
11
- ThingRepository.new(mongo_session, :things, factory)
12
- end
13
- end
14
- end
@@ -1,19 +0,0 @@
1
- RSpec.describe Rmodel::Mongo::Repository do
2
- include_examples 'timestampable repository' do
3
- include_context 'clean Mongo database'
4
-
5
- before do
6
- stub_const('ThingRepository', Class.new(Rmodel::Mongo::Repository))
7
- end
8
-
9
- let(:repo_w_timestamps) do
10
- factory = Rmodel::Mongo::SimpleFactory.new(Thing, :name, :created_at, :updated_at)
11
- ThingRepository.new(mongo_session, :things, factory)
12
- end
13
-
14
- let(:repo_wo_timestamps) do
15
- factory = Rmodel::Mongo::SimpleFactory.new(Thing, :name)
16
- ThingRepository.new(mongo_session, :things, factory)
17
- end
18
- end
19
- end