epiphy 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/EXAMPLE.md +173 -0
- data/lib/epiphy/adapter/rethinkdb.rb +60 -24
- data/lib/epiphy/entity/timestamp.rb +14 -0
- data/lib/epiphy/model.rb +8 -0
- data/lib/epiphy/repository.rb +67 -30
- data/lib/epiphy/repository/helper.rb +28 -0
- data/lib/epiphy/scheme.rb +19 -0
- data/lib/epiphy/version.rb +1 -1
- metadata +5 -3
- data/lib/epiphy/adapter/helper.rb +0 -114
- data/lib/epiphy/adapter/model.rb +0 -99
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 346e101f2179b4b096e7d9e38ef7c08879598e99
|
4
|
+
data.tar.gz: fc54f4cf44e3a1e25e79d10f5ecd2e85bd0bff0c
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: a8823aac0ef0a54099141567fcc3155d3289ca71d08b39b506f14ed668bb073f78d99a622d3090a804997f69370ec36841d7c256b7ff98c507fc057c3e4e100e
|
7
|
+
data.tar.gz: e6fd39cb0525b115d1fb6bfb35d094ef3f1661902f4db8bfbb5924eef21f3b74281ed71db385a19ce00bcac225ad8398ba302f8e5d49d200d1a4baa1b842bc74
|
data/EXAMPLE.md
ADDED
@@ -0,0 +1,173 @@
|
|
1
|
+
# Epiphy
|
2
|
+
|
3
|
+
This is a guide that helps you to getting started with [**Epiphy**](https://github.com/kureikain/epiphy). This file is inspired by Lotus::Model
|
4
|
+
|
5
|
+
## Gems
|
6
|
+
|
7
|
+
First of all, we need to setup a `Gemfile`.
|
8
|
+
|
9
|
+
```ruby
|
10
|
+
source 'https://rubygems.org'
|
11
|
+
|
12
|
+
gem 'epiphy'
|
13
|
+
```
|
14
|
+
|
15
|
+
Then we can fetch the dependencies with `bundle install`.
|
16
|
+
|
17
|
+
## Setup
|
18
|
+
|
19
|
+
We need to feed `Epiphy` with an adapter. Adapter is a lighweight wrap
|
20
|
+
contains a RethinkDB connection and its run option.
|
21
|
+
|
22
|
+
```
|
23
|
+
# Default connect to localhost, 28015, no auth key, use database 'test'
|
24
|
+
# by default
|
25
|
+
connection = Epiphy::Connection.create
|
26
|
+
adapter = Epiphy::Adapter::RethinkDB.new connection, 'test'
|
27
|
+
RethinkDB::Repository.configure do |r|
|
28
|
+
r.adapter = adapter
|
29
|
+
end
|
30
|
+
|
31
|
+
# Or merge them all
|
32
|
+
RethinkDB::Repository.configure do |r|
|
33
|
+
r.adapter = Epiphy::Adapter::RethinkDB.new(Epiphy::Connection.create)
|
34
|
+
end
|
35
|
+
|
36
|
+
# Or connect to a different host, use database `cms`
|
37
|
+
RethinkDB::Repository.configure do |r|
|
38
|
+
r.adapter = Epiphy::Adapter::RethinkDB.new Epiphy::Connection.create({:host => '192.168.1.2'}, 'cms')
|
39
|
+
end
|
40
|
+
|
41
|
+
```
|
42
|
+
|
43
|
+
## Entities
|
44
|
+
|
45
|
+
We have two entities in our application: `Author` and `Article`.
|
46
|
+
`Author` is a `Struct`, Epiphy can persist it.
|
47
|
+
`Article` has a small API concerning its publishing process.
|
48
|
+
|
49
|
+
```ruby
|
50
|
+
Author = Struct.new(:id, :name) do
|
51
|
+
def initialize(attributes = {})
|
52
|
+
@id, @name = attributes.values_at(:id, :name)
|
53
|
+
end
|
54
|
+
end
|
55
|
+
|
56
|
+
class Article
|
57
|
+
include Epiphy::Entity
|
58
|
+
self.attributes = :author_id, :title, :comments_count, :published # id is implicit
|
59
|
+
|
60
|
+
def published?
|
61
|
+
!!published
|
62
|
+
end
|
63
|
+
|
64
|
+
def publish!
|
65
|
+
@published = true
|
66
|
+
end
|
67
|
+
end
|
68
|
+
```
|
69
|
+
|
70
|
+
## Repositories
|
71
|
+
|
72
|
+
In order to persist and query the entities above, we define two corresponding repositories:
|
73
|
+
|
74
|
+
```ruby
|
75
|
+
class AuthorRepository
|
76
|
+
include Epiphy::Repository
|
77
|
+
end
|
78
|
+
|
79
|
+
class ArticleRepository
|
80
|
+
include Epiphy::Repository
|
81
|
+
|
82
|
+
def self.most_recent_by_author(author, limit = 8)
|
83
|
+
query do
|
84
|
+
where(author_id: author.id).
|
85
|
+
desc(:id).
|
86
|
+
limit(limit)
|
87
|
+
end
|
88
|
+
end
|
89
|
+
|
90
|
+
def self.most_recent_published_by_author(author, limit = 8)
|
91
|
+
most_recent_by_author(author, limit).published
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.published
|
95
|
+
query do
|
96
|
+
where(published: true)
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
def self.drafts
|
101
|
+
exclude published
|
102
|
+
end
|
103
|
+
|
104
|
+
def self.rank
|
105
|
+
published.desc(:comments_count)
|
106
|
+
end
|
107
|
+
|
108
|
+
def self.best_article_ever
|
109
|
+
rank.limit(1).first
|
110
|
+
end
|
111
|
+
|
112
|
+
def self.comments_average
|
113
|
+
query.average(:comments_count)
|
114
|
+
end
|
115
|
+
end
|
116
|
+
```
|
117
|
+
|
118
|
+
## Persist
|
119
|
+
|
120
|
+
Let's instantiate and persist some objects for our example:
|
121
|
+
|
122
|
+
```ruby
|
123
|
+
author = Author.new(name: 'Luca')
|
124
|
+
AuthorRepository.create(author)
|
125
|
+
|
126
|
+
articles = [
|
127
|
+
Article.new(title: 'Announcing Epiphy', author_id: author.id, comments_count: 123, published: true),
|
128
|
+
Article.new(title: 'Introducing Epiphy::Router', author_id: author.id, comments_count: 63, published: true),
|
129
|
+
Article.new(title: 'Introducing Epiphy::Controller', author_id: author.id, comments_count: 82, published: true),
|
130
|
+
Article.new(title: 'Introducing Epiphy', author_id: author.id)
|
131
|
+
]
|
132
|
+
|
133
|
+
articles.each do |article|
|
134
|
+
ArticleRepository.create(article)
|
135
|
+
end
|
136
|
+
```
|
137
|
+
|
138
|
+
## Query
|
139
|
+
|
140
|
+
We can use repositories to query the database and return the entities we're looking for:
|
141
|
+
|
142
|
+
```ruby
|
143
|
+
ArticleRepository.first # => return the first article
|
144
|
+
ArticleRepository.last # => return the last article
|
145
|
+
|
146
|
+
ArticleRepository.published # => return all the published articles
|
147
|
+
ArticleRepository.drafts # => return all the drafts
|
148
|
+
|
149
|
+
ArticleRepository.rank # => all the published articles, sorted by popularity
|
150
|
+
|
151
|
+
ArticleRepository.best_article_ever # => the most commented article
|
152
|
+
|
153
|
+
ArticleRepository.comments_average # => calculates the average of comments across all the published articles.
|
154
|
+
|
155
|
+
ArticleRepository.most_recent_by_author(author) # => most recent articles by an author (drafts and published).
|
156
|
+
ArticleRepository.most_recent_published_by_author(author) # => most recent published articles by an author
|
157
|
+
```
|
158
|
+
|
159
|
+
## Business logic
|
160
|
+
|
161
|
+
As we've seen above, `Article` implements an API for publishing.
|
162
|
+
We're gonna use that logic to alter the state of an article (from draft to published) and then we use the repository to persist this new state.
|
163
|
+
|
164
|
+
```ruby
|
165
|
+
article = ArticleRepository.drafts.first
|
166
|
+
|
167
|
+
article.published? # => false
|
168
|
+
article.publish!
|
169
|
+
|
170
|
+
article.published? # => true
|
171
|
+
|
172
|
+
ArticleRepository.update(article)
|
173
|
+
```
|
@@ -1,5 +1,5 @@
|
|
1
1
|
require 'epiphy/adapter/error'
|
2
|
-
|
2
|
+
|
3
3
|
module Epiphy
|
4
4
|
module Adapter
|
5
5
|
class Rethinkdb
|
@@ -102,6 +102,7 @@ module Epiphy
|
|
102
102
|
raise ArgumentError, 'Missing query block' unless block_given?
|
103
103
|
if block_given?
|
104
104
|
rql = get_table(table, database)
|
105
|
+
@current_rql = rql
|
105
106
|
rql = yield(rql, r)
|
106
107
|
end
|
107
108
|
rql.run(@connection)
|
@@ -125,9 +126,15 @@ module Epiphy
|
|
125
126
|
end
|
126
127
|
|
127
128
|
# Insert a document.
|
129
|
+
#
|
130
|
+
# When the ID is already existed, we simply return the ID if insert
|
131
|
+
# succesful. Or, the generated ID will be returned.
|
132
|
+
#
|
128
133
|
# @param collection [Symbol the target collection
|
129
134
|
# @param entity [#id, #id=] the entity to create
|
130
135
|
# @return [Object] the entity
|
136
|
+
#
|
137
|
+
# @raise
|
131
138
|
#
|
132
139
|
# @api private
|
133
140
|
# @since 0.0.1
|
@@ -136,12 +143,17 @@ module Epiphy
|
|
136
143
|
result = query table: collection do |r|
|
137
144
|
r.insert(entity)
|
138
145
|
end
|
139
|
-
rescue
|
140
|
-
|
146
|
+
rescue RethinkDB::RqlRuntimeError => e
|
147
|
+
raise e
|
141
148
|
end
|
142
149
|
|
143
150
|
if result["inserted"]==1
|
144
|
-
result["generated_keys"].
|
151
|
+
return entity["id"] if result["generated_keys"].nil?
|
152
|
+
result["generated_keys"].first
|
153
|
+
else
|
154
|
+
if result['first_error'].include? 'Duplicate primary key'
|
155
|
+
raise Epiphy::Model::EntityExisted, 'Duplicate primary key'
|
156
|
+
end
|
145
157
|
end
|
146
158
|
end
|
147
159
|
|
@@ -179,8 +191,31 @@ module Epiphy
|
|
179
191
|
query table: collection do |r|
|
180
192
|
r
|
181
193
|
end
|
182
|
-
rescue
|
183
|
-
|
194
|
+
rescue RethinkDB::RqlRuntimeError => e
|
195
|
+
raise Epiphy::Model::RuntimeError, e.message
|
196
|
+
rescue Exception =>e
|
197
|
+
raise Epiphy::Model::RuntimeError, e.message
|
198
|
+
end
|
199
|
+
end
|
200
|
+
|
201
|
+
# Count entity in table
|
202
|
+
#
|
203
|
+
# @param collection [Symbol] the target collection (it must be mapped).
|
204
|
+
#
|
205
|
+
# @return [Integer] How many record?
|
206
|
+
#
|
207
|
+
# @api private
|
208
|
+
# @since 0.2.0
|
209
|
+
def count(collection)
|
210
|
+
# TODO consider to make this lazy (aka remove #all)
|
211
|
+
begin
|
212
|
+
query table: collection do |r|
|
213
|
+
r.count
|
214
|
+
end
|
215
|
+
rescue RethinkDB::RqlRuntimeError => e
|
216
|
+
raise Epiphy::Model::RuntimeError, e.message
|
217
|
+
rescue Exception =>e
|
218
|
+
raise Epiphy::Model::RuntimeError, e.message
|
184
219
|
end
|
185
220
|
end
|
186
221
|
|
@@ -243,8 +278,8 @@ module Epiphy
|
|
243
278
|
return result['deleted']
|
244
279
|
end
|
245
280
|
return false
|
246
|
-
rescue
|
247
|
-
|
281
|
+
rescue RethinkDB::RqlRuntimeError => e
|
282
|
+
raise Epiphy::Model::RuntimeError, e.message
|
248
283
|
end
|
249
284
|
end
|
250
285
|
|
@@ -256,10 +291,14 @@ module Epiphy
|
|
256
291
|
#
|
257
292
|
# @api private
|
258
293
|
# @since 0.1.0
|
259
|
-
def first(collection)
|
260
|
-
|
261
|
-
query
|
262
|
-
|
294
|
+
def first(collection, order_by: nil)
|
295
|
+
begin
|
296
|
+
query table: collection do |q,r|
|
297
|
+
q.order_by(r.asc(order_by)).nth(0)
|
298
|
+
end
|
299
|
+
rescue RethinkDB::RqlRuntimeError => e
|
300
|
+
return nil
|
301
|
+
end
|
263
302
|
end
|
264
303
|
|
265
304
|
# Returns the last record in the given collection.
|
@@ -270,21 +309,18 @@ module Epiphy
|
|
270
309
|
#
|
271
310
|
# @api private
|
272
311
|
# @since 0.1.0
|
273
|
-
def last(collection)
|
274
|
-
|
275
|
-
query
|
276
|
-
|
312
|
+
def last(collection, order_by: nil)
|
313
|
+
begin
|
314
|
+
query table: collection do |q, r|
|
315
|
+
q.order_by(r.desc(order_by)).nth(0)
|
316
|
+
end
|
317
|
+
rescue RethinkDB::RqlRuntimeError => e
|
318
|
+
return nil
|
319
|
+
end
|
320
|
+
|
277
321
|
end
|
278
322
|
|
279
323
|
private
|
280
|
-
def _collection(name)
|
281
|
-
raise NotImplementedError
|
282
|
-
end
|
283
|
-
|
284
|
-
def _mapped_collection(name)
|
285
|
-
@mapper.collection(name)
|
286
|
-
end
|
287
|
-
|
288
324
|
def _find(collection, id)
|
289
325
|
identity = _identity(collection)
|
290
326
|
query(collection).where(identity => _id(collection, identity, id))
|
data/lib/epiphy/model.rb
CHANGED
@@ -9,6 +9,10 @@ module Epiphy
|
|
9
9
|
#
|
10
10
|
# @since 0.1.0
|
11
11
|
module Model
|
12
|
+
|
13
|
+
class RuntimeError < RethinkDB::RqlRuntimeError
|
14
|
+
|
15
|
+
end
|
12
16
|
# Error for not found entity
|
13
17
|
#
|
14
18
|
# @since 0.1.0
|
@@ -33,5 +37,9 @@ module Epiphy
|
|
33
37
|
# @see epiphy::Repository.update
|
34
38
|
class NonPersistedEntityError < ::StandardError
|
35
39
|
end
|
40
|
+
|
41
|
+
class EntityExisted < ::StandardError
|
42
|
+
|
43
|
+
end
|
36
44
|
end
|
37
45
|
end
|
data/lib/epiphy/repository.rb
CHANGED
@@ -1,6 +1,7 @@
|
|
1
1
|
require 'lotus/utils/class_attribute'
|
2
2
|
require 'epiphy/repository/configuration'
|
3
3
|
require 'epiphy/repository/cursor'
|
4
|
+
require 'epiphy/repository/helper'
|
4
5
|
|
5
6
|
module Epiphy
|
6
7
|
# Mediates between the entities and the persistence layer, by offering an API
|
@@ -215,6 +216,7 @@ module Epiphy
|
|
215
216
|
end
|
216
217
|
|
217
218
|
module ClassMethods
|
219
|
+
include Epiphy::Repository::Helper
|
218
220
|
# Assigns an adapter.
|
219
221
|
#
|
220
222
|
# Epiphy::Repository is shipped with an adapters:
|
@@ -289,7 +291,8 @@ module Epiphy
|
|
289
291
|
# Creates a record in the database for the given entity.
|
290
292
|
# It assigns the `id` attribute, in case of success.
|
291
293
|
#
|
292
|
-
# If already persisted (`id` present) it
|
294
|
+
# If already persisted (`id` present), it will try to insert use that id
|
295
|
+
# and will raise an error if the `id` is already exist
|
293
296
|
#
|
294
297
|
# @param entity [#id,#id=] the entity to create
|
295
298
|
#
|
@@ -314,10 +317,16 @@ module Epiphy
|
|
314
317
|
#
|
315
318
|
# ArticleRepository.create(article) # no-op
|
316
319
|
def create(entity)
|
317
|
-
unless entity.id
|
320
|
+
#unless entity.id
|
321
|
+
begin
|
318
322
|
result = @adapter.create(collection, to_document(entity))
|
319
323
|
entity.id = result
|
324
|
+
rescue Epiphy::Model::EntityExisted => e
|
325
|
+
raise e
|
326
|
+
rescue RethinkDB::RqlRuntimeError => e
|
327
|
+
raise Epiphy::Model::RuntimeError, e.message
|
320
328
|
end
|
329
|
+
#end
|
321
330
|
end
|
322
331
|
|
323
332
|
# Updates a record in the database corresponding to the given entity.
|
@@ -504,8 +513,13 @@ module Epiphy
|
|
504
513
|
# end
|
505
514
|
#
|
506
515
|
# ArticleRepository.first # => nil
|
507
|
-
def first
|
508
|
-
@adapter.first(collection)
|
516
|
+
def first(order_by=:id)
|
517
|
+
result = @adapter.first(collection, order_by: order_by)
|
518
|
+
if result
|
519
|
+
to_entity result
|
520
|
+
else
|
521
|
+
result
|
522
|
+
end
|
509
523
|
end
|
510
524
|
|
511
525
|
# Returns the last entity in the database.
|
@@ -533,8 +547,12 @@ module Epiphy
|
|
533
547
|
# end
|
534
548
|
#
|
535
549
|
# ArticleRepository.last # => nil
|
536
|
-
def last
|
537
|
-
@adapter.last(collection)
|
550
|
+
def last(order_by=:id)
|
551
|
+
if result = @adapter.last(collection, order_by: order_by)
|
552
|
+
to_entity result
|
553
|
+
else
|
554
|
+
nil
|
555
|
+
end
|
538
556
|
end
|
539
557
|
|
540
558
|
# Deletes all the records from the current collection.
|
@@ -553,22 +571,16 @@ module Epiphy
|
|
553
571
|
# ArticleRepository.clear # deletes all the records
|
554
572
|
def clear
|
555
573
|
@adapter.clear(collection)
|
556
|
-
end
|
574
|
+
end
|
557
575
|
|
558
|
-
#
|
576
|
+
# Count the entity in this collection
|
559
577
|
#
|
560
|
-
|
561
|
-
|
562
|
-
|
563
|
-
|
564
|
-
|
565
|
-
|
566
|
-
# Drop a collection storage in database
|
567
|
-
#
|
568
|
-
def drop_collection
|
569
|
-
query do |r|
|
570
|
-
r.table_drop(self.collection)
|
571
|
-
end
|
578
|
+
# @param void
|
579
|
+
# @return Interget
|
580
|
+
# @api public
|
581
|
+
# @since 0.2.0
|
582
|
+
def count
|
583
|
+
@adapter.count(collection)
|
572
584
|
end
|
573
585
|
|
574
586
|
private
|
@@ -585,27 +597,34 @@ module Epiphy
|
|
585
597
|
# The returned query SHOULD refer to the entire collection by default.
|
586
598
|
#
|
587
599
|
# Queries can be reused and combined together. See the example below.
|
588
|
-
# IMPORTANT: This feature works only with the Sql adapter.
|
589
600
|
#
|
590
601
|
# A repository is storage independent.
|
591
602
|
# All the queries are delegated to the current adapter, which is
|
592
603
|
# responsible to implement a querying API.
|
593
604
|
#
|
594
|
-
# Epiphy::Model is shipped with
|
605
|
+
# Epiphy::Model is shipped with adapter:
|
595
606
|
#
|
596
|
-
# *
|
597
|
-
# * MemoryAdapter, which yields a Epiphy::Model::Adapters::Memory::Query
|
607
|
+
# * RethinkDB: which yields a RethinkDB::ReQL class.
|
598
608
|
#
|
599
|
-
#
|
600
|
-
#
|
609
|
+
# By default, all return items will be convert into its entity. The
|
610
|
+
# behavious can change by alter `to_entity` parameter
|
601
611
|
#
|
612
|
+
# @param to_entity [Boolean][Optional] to convert the result back to a
|
613
|
+
# entity class or not.
|
614
|
+
#
|
615
|
+
# @param blk [Proc] a block of code that is executed in the context of a
|
616
|
+
# query.
|
617
|
+
# The block will be passed two parameters. First parameter is the `reql`
|
618
|
+
# which is building. the second parameter is th `r` top name space of
|
619
|
+
# RethinkDB. By doing this, Repository doesn't have to include
|
620
|
+
# RethinkDB::Shortcuts
|
621
|
+
#
|
602
622
|
# @return a query, the type depends on the current adapter
|
603
623
|
#
|
604
624
|
# @api public
|
605
625
|
# @since 0.1.0
|
606
626
|
#
|
607
|
-
# @see Epiphy::
|
608
|
-
# @see Epiphy::Model::Adapters::Memory::Query
|
627
|
+
# @see Epiphy::Adapters::Rethinkdb
|
609
628
|
#
|
610
629
|
# @example
|
611
630
|
# require 'epiphy/model'
|
@@ -646,9 +665,27 @@ module Epiphy
|
|
646
665
|
# query.average(:comments_count)
|
647
666
|
# end
|
648
667
|
# end
|
649
|
-
def query(&blk)
|
650
|
-
@adapter.query(collection,
|
668
|
+
def query(to_entity: true, &blk)
|
669
|
+
result = @adapter.query(table: collection, &blk)
|
670
|
+
require 'pp'
|
671
|
+
if result.is_a? RethinkDB::Cursor
|
672
|
+
return Epiphy::Repository::Cursor.new result do |item|
|
673
|
+
to_entity(item)
|
674
|
+
end
|
675
|
+
end
|
676
|
+
|
677
|
+
if result.is_a? Array
|
678
|
+
result.map! do |item|
|
679
|
+
to_entity(item)
|
680
|
+
end
|
681
|
+
end
|
682
|
+
|
683
|
+
if result.is_a? Hash
|
684
|
+
return to_entity(result)
|
685
|
+
end
|
686
|
+
result
|
651
687
|
end
|
688
|
+
|
652
689
|
|
653
690
|
# Negates the filtering conditions of a given query with the logical
|
654
691
|
# opposite operator.
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module Epiphy
|
2
|
+
module Repository
|
3
|
+
module Helper
|
4
|
+
|
5
|
+
# Find the first record matching the
|
6
|
+
#
|
7
|
+
# @param Keyword argument [field_name: value]
|
8
|
+
# @return Entity [Object]
|
9
|
+
# @raise Epiphy::Model::EntityNotFound if not found
|
10
|
+
#
|
11
|
+
# @api public
|
12
|
+
# @since 0.2.0
|
13
|
+
def find_by(**option)
|
14
|
+
begin
|
15
|
+
query do |r|
|
16
|
+
r.filter(option).nth(0)
|
17
|
+
end
|
18
|
+
rescue RethinkDB::RqlRuntimeError => e
|
19
|
+
#raise RethinkDB::RqlRuntimeError
|
20
|
+
raise Epiphy::Model::EntityNotFound if e.message.include?("Index out of bounds")
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
#private :where, :desc#, :exlude
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module Epiphy
|
2
|
+
module Schema
|
3
|
+
# Create a collection storage in database.
|
4
|
+
#
|
5
|
+
def create_collection
|
6
|
+
query do |r|
|
7
|
+
r.table_create(self.collection)
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
# Drop a collection storage in database
|
12
|
+
#
|
13
|
+
def drop_collection
|
14
|
+
query do |r|
|
15
|
+
r.table_drop(self.collection)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
data/lib/epiphy/version.rb
CHANGED
metadata
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: epiphy
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0
|
4
|
+
version: 0.1.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Vinh
|
@@ -59,18 +59,20 @@ executables: []
|
|
59
59
|
extensions: []
|
60
60
|
extra_rdoc_files: []
|
61
61
|
files:
|
62
|
+
- EXAMPLE.md
|
62
63
|
- README.md
|
63
64
|
- lib/epiphy.rb
|
64
65
|
- lib/epiphy/adapter/error.rb
|
65
|
-
- lib/epiphy/adapter/helper.rb
|
66
|
-
- lib/epiphy/adapter/model.rb
|
67
66
|
- lib/epiphy/adapter/rethinkdb.rb
|
68
67
|
- lib/epiphy/connection.rb
|
69
68
|
- lib/epiphy/entity.rb
|
69
|
+
- lib/epiphy/entity/timestamp.rb
|
70
70
|
- lib/epiphy/model.rb
|
71
71
|
- lib/epiphy/repository.rb
|
72
72
|
- lib/epiphy/repository/configuration.rb
|
73
73
|
- lib/epiphy/repository/cursor.rb
|
74
|
+
- lib/epiphy/repository/helper.rb
|
75
|
+
- lib/epiphy/scheme.rb
|
74
76
|
- lib/epiphy/version.rb
|
75
77
|
homepage: http://rubygems.org/gems/hola
|
76
78
|
licenses:
|
@@ -1,114 +0,0 @@
|
|
1
|
-
module Epiphy
|
2
|
-
module Adapter
|
3
|
-
class Rethinkdb
|
4
|
-
# RethinkDB method related. Should be in its helper
|
5
|
-
def insert_object
|
6
|
-
get_table.insert(
|
7
|
-
safe_params.merge(
|
8
|
-
id: params[:id],
|
9
|
-
created_at: Time.now,
|
10
|
-
updated_at: Time.now
|
11
|
-
).merge(parents)
|
12
|
-
).run(@connection)
|
13
|
-
:created
|
14
|
-
end
|
15
|
-
|
16
|
-
def update_object
|
17
|
-
get_table.update(
|
18
|
-
safe_params.merge(
|
19
|
-
id: params[:id],
|
20
|
-
updated_at: Time.now
|
21
|
-
)
|
22
|
-
).run(@connection)
|
23
|
-
:ok
|
24
|
-
end
|
25
|
-
|
26
|
-
def replace_object
|
27
|
-
get_table.replace(
|
28
|
-
safe_params.merge(
|
29
|
-
id: params[:id],
|
30
|
-
created_at: Time.now,
|
31
|
-
updated_at: Time.now
|
32
|
-
).merge(parents)
|
33
|
-
).run(@connection)
|
34
|
-
:ok
|
35
|
-
end
|
36
|
-
|
37
|
-
def delete_object
|
38
|
-
get_table.get(params[:id]).delete(
|
39
|
-
:durability => "hard", :return_vals => false
|
40
|
-
).run(@connection)
|
41
|
-
:no_content
|
42
|
-
end
|
43
|
-
|
44
|
-
def sort(qry)
|
45
|
-
ordering = params[:sort].split(",").map do |attr|
|
46
|
-
if attr[0] == "-"
|
47
|
-
r.desc(attr[1..-1].to_sym)
|
48
|
-
else
|
49
|
-
r.asc(attr.to_sym)
|
50
|
-
end
|
51
|
-
end
|
52
|
-
|
53
|
-
qry.order_by(*ordering)
|
54
|
-
end
|
55
|
-
|
56
|
-
def select(qry)
|
57
|
-
qry = qry.get_all(*params[:ids].split(",")) if params[:ids]
|
58
|
-
qry
|
59
|
-
end
|
60
|
-
|
61
|
-
def parents
|
62
|
-
params.select {|k,v| k.match(/\A[a-z0-9_]+_id\z/i) }.compact
|
63
|
-
end
|
64
|
-
|
65
|
-
def filter(qry)
|
66
|
-
parents.empty? ? qry : qry.filter(parents)
|
67
|
-
end
|
68
|
-
|
69
|
-
def attrs
|
70
|
-
[ :id ]
|
71
|
-
end
|
72
|
-
|
73
|
-
def get_range(qry)
|
74
|
-
begin
|
75
|
-
rhdr = request.headers[:HTTP_RANGE].split("=")
|
76
|
-
|
77
|
-
if rhdr[0] == collection
|
78
|
-
qry = qry[Range.new(*rhdr[1].split("-").map(&:to_i))]
|
79
|
-
end
|
80
|
-
rescue Exception => e
|
81
|
-
puts e.message
|
82
|
-
raise Exception.new(:bad_request)
|
83
|
-
end
|
84
|
-
qry
|
85
|
-
end
|
86
|
-
|
87
|
-
def get_records
|
88
|
-
qry = get_table
|
89
|
-
qry = sort(qry) if params[:sort]
|
90
|
-
|
91
|
-
fields = if params[:fields]
|
92
|
-
params[:fields].split(",").map {|f| f.to_sym }.select do |field|
|
93
|
-
attrs.include? field
|
94
|
-
end
|
95
|
-
else
|
96
|
-
attrs
|
97
|
-
end
|
98
|
-
|
99
|
-
qry = filter(select(qry)).pluck(fields)
|
100
|
-
qry = get_range(qry) if request.headers[:HTTP_RANGE]
|
101
|
-
|
102
|
-
qry.run(@connection).map do |record|
|
103
|
-
record.merge(href: some_url(record["id"]))
|
104
|
-
end
|
105
|
-
end
|
106
|
-
|
107
|
-
def get_object
|
108
|
-
get_table.filter(parents.merge({id: params[:id]})).pluck(attrs).run(@connection).first
|
109
|
-
end
|
110
|
-
|
111
|
-
end
|
112
|
-
end
|
113
|
-
end
|
114
|
-
|
data/lib/epiphy/adapter/model.rb
DELETED
@@ -1,99 +0,0 @@
|
|
1
|
-
require 'errors'
|
2
|
-
|
3
|
-
class Rethink
|
4
|
-
include RethinkDB::Shortcuts
|
5
|
-
include RethinkdbHelper
|
6
|
-
include Errors
|
7
|
-
|
8
|
-
def initialize
|
9
|
-
create_connect_if_need
|
10
|
-
@r = @@r.table(self.class.name.downcase)
|
11
|
-
end
|
12
|
-
|
13
|
-
def all
|
14
|
-
self
|
15
|
-
end
|
16
|
-
|
17
|
-
# Get a single element by primary key
|
18
|
-
def single(id)
|
19
|
-
@r = @r.get(id)
|
20
|
-
self
|
21
|
-
end
|
22
|
-
|
23
|
-
# Return raw RethinkDB object
|
24
|
-
def raw_query
|
25
|
-
@r
|
26
|
-
end
|
27
|
-
|
28
|
-
def reset
|
29
|
-
@r = @@r.table self.class.name.downcase
|
30
|
-
end
|
31
|
-
|
32
|
-
# Run a RQL
|
33
|
-
def get(r=nil)
|
34
|
-
if r.nil?
|
35
|
-
r = @r
|
36
|
-
end
|
37
|
-
r.run(@@rdb_connection)
|
38
|
-
end
|
39
|
-
|
40
|
-
[:update, "get_all", :limit, :order_by, :slice, :count, :filter].each do |name|
|
41
|
-
define_method(name) do |*arg, &block|
|
42
|
-
@r = @r.send(name, *arg, &block)
|
43
|
-
self
|
44
|
-
end
|
45
|
-
end
|
46
|
-
|
47
|
-
# Command work ona single object
|
48
|
-
#{:destroy => :delete}.each do |public_name, name|
|
49
|
-
#define_method(public_name) do |*arg, &block|
|
50
|
-
#r = @@r.table self.class.name.downcase
|
51
|
-
#r.get(*arg)
|
52
|
-
#r.send(name, *arg, &block)
|
53
|
-
#r.run @@rdb_connection
|
54
|
-
#end
|
55
|
-
#end
|
56
|
-
|
57
|
-
def destroy(id)
|
58
|
-
r = @@r.table self.class.name.downcase
|
59
|
-
r.get(id)
|
60
|
-
.delete()
|
61
|
-
.run(@@rdb_connection)
|
62
|
-
end
|
63
|
-
|
64
|
-
# Validation
|
65
|
-
def self.validate(args)
|
66
|
-
if args.has_key? :errors
|
67
|
-
raise ValidationError.new('Bad request', args[:errors])
|
68
|
-
end
|
69
|
-
|
70
|
-
args
|
71
|
-
end
|
72
|
-
|
73
|
-
def self.validate_present(args, *keys)
|
74
|
-
for k in keys
|
75
|
-
unless args.has_key?(k) and args[k].present?
|
76
|
-
args[:errors] ||= {}
|
77
|
-
args[:errors][k] ||= []
|
78
|
-
args[:errors][k] << "can't be blank."
|
79
|
-
end
|
80
|
-
end
|
81
|
-
args
|
82
|
-
end
|
83
|
-
|
84
|
-
def self.validate_email(args)
|
85
|
-
if args.has_key? :email
|
86
|
-
args[:email].downcase!
|
87
|
-
|
88
|
-
unless args[:email].present? and
|
89
|
-
args[:email].match EMAIL_REGEX
|
90
|
-
|
91
|
-
args[:errors] ||= {}
|
92
|
-
args[:errors][:email] ||= []
|
93
|
-
args[:errors][:email] << "is invalid."
|
94
|
-
end
|
95
|
-
end
|
96
|
-
args
|
97
|
-
end
|
98
|
-
end
|
99
|
-
|