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

Sign up to get free protection for your applications and to get access to all the features.
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