shoden 0.3.0 → 0.4.0
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.travis.yml +14 -0
- data/lib/shoden.rb +156 -54
- data/shoden.gemspec +1 -1
- data/test/shoden_test.rb +41 -25
- metadata +3 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 28a3009afa5b70167a8c9128a356002e11224695
|
4
|
+
data.tar.gz: 77efc3fd3a51f5d98ad27591657e96e56f7b9628
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 0ae31056c6817127730fdebeddd82ecf8cbff424c9dbedef97dd1674825a421c4b9725670ddaf5094828703fdb0214de600011b718aa5bdbfa4bb0c7dc837853
|
7
|
+
data.tar.gz: 72d6ca3fa679e24c8201ba4231228f56f6a59c6b4f399b8ec70b9566b9eda835c00750759402ceb126d684b87095d48a28d0c811422b06c7c97b6b06f9bb2e74
|
data/.travis.yml
ADDED
data/lib/shoden.rb
CHANGED
@@ -1,4 +1,5 @@
|
|
1
1
|
require 'sequel'
|
2
|
+
require 'set'
|
2
3
|
|
3
4
|
Sequel.extension :pg_hstore, :pg_hstore_ops
|
4
5
|
|
@@ -10,10 +11,45 @@ module Shoden
|
|
10
11
|
|
11
12
|
Proxy = Struct.new(:klass, :parent) do
|
12
13
|
def create(args = {})
|
13
|
-
key
|
14
|
-
|
15
|
-
|
16
|
-
|
14
|
+
klass.create(args.merge(key => parent.id))
|
15
|
+
end
|
16
|
+
|
17
|
+
def all
|
18
|
+
klass.filter(parent_filter)
|
19
|
+
end
|
20
|
+
|
21
|
+
def count
|
22
|
+
klass.count
|
23
|
+
end
|
24
|
+
|
25
|
+
def any?
|
26
|
+
count > 0
|
27
|
+
end
|
28
|
+
|
29
|
+
def first
|
30
|
+
filter = { order: "id ASC LIMIT 1" }.merge!(parent_filter)
|
31
|
+
klass.filter(filter).first
|
32
|
+
end
|
33
|
+
|
34
|
+
def last
|
35
|
+
filter = { order: "id DESC LIMIT 1" }.merge!(parent_filter)
|
36
|
+
klass.filter(filter).first
|
37
|
+
end
|
38
|
+
|
39
|
+
def [](id)
|
40
|
+
filter = { id: id }.merge!(parent_filter)
|
41
|
+
|
42
|
+
klass.filter(filter).first
|
43
|
+
end
|
44
|
+
|
45
|
+
private
|
46
|
+
|
47
|
+
def parent_filter
|
48
|
+
{ key => parent.id }
|
49
|
+
end
|
50
|
+
|
51
|
+
def key
|
52
|
+
"#{parent.class.to_reference}_id".freeze
|
17
53
|
end
|
18
54
|
end
|
19
55
|
|
@@ -25,14 +61,28 @@ module Shoden
|
|
25
61
|
@_url ||= ENV['DATABASE_URL']
|
26
62
|
end
|
27
63
|
|
28
|
-
def self.
|
29
|
-
@
|
64
|
+
def self.models
|
65
|
+
@_models ||= Set.new
|
30
66
|
end
|
31
67
|
|
32
|
-
def self.
|
33
|
-
|
34
|
-
|
68
|
+
def self.connection
|
69
|
+
loggers = []
|
70
|
+
|
71
|
+
if ENV["DEBUG"]
|
72
|
+
require 'logger'
|
73
|
+
loggers << Logger.new($stdout)
|
35
74
|
end
|
75
|
+
|
76
|
+
@_connection ||= Sequel.connect(url, loggers: loggers)
|
77
|
+
end
|
78
|
+
|
79
|
+
def self.setup
|
80
|
+
connection.execute("CREATE EXTENSION IF NOT EXISTS hstore")
|
81
|
+
models.each { |m| m.setup }
|
82
|
+
end
|
83
|
+
|
84
|
+
def self.destroy_tables
|
85
|
+
models.each { |m| m.destroy_table }
|
36
86
|
end
|
37
87
|
|
38
88
|
class Model
|
@@ -43,12 +93,12 @@ module Shoden
|
|
43
93
|
end
|
44
94
|
|
45
95
|
def id
|
46
|
-
|
96
|
+
return nil if !defined?(@_id)
|
47
97
|
@_id.to_i
|
48
98
|
end
|
49
99
|
|
50
100
|
def destroy
|
51
|
-
lookup(id).delete
|
101
|
+
self.class.lookup(id).delete
|
52
102
|
end
|
53
103
|
|
54
104
|
def update(attrs = {})
|
@@ -61,32 +111,47 @@ module Shoden
|
|
61
111
|
end
|
62
112
|
|
63
113
|
def save
|
64
|
-
|
65
|
-
table.where(id: @_id).update data: sanitized_attrs
|
66
|
-
else
|
67
|
-
begin
|
68
|
-
@_id = table.insert data: sanitized_attrs
|
69
|
-
rescue Sequel::UniqueConstraintViolation
|
70
|
-
raise UniqueIndexViolation
|
71
|
-
end
|
72
|
-
end
|
73
|
-
|
74
|
-
self.class.indices.each { |i| create_index(i) }
|
75
|
-
self.class.uniques.each { |i| create_index(i, :unique) }
|
76
|
-
|
114
|
+
self.class.save(self)
|
77
115
|
self
|
78
116
|
end
|
79
117
|
|
80
118
|
def load!
|
81
|
-
ret = lookup(@_id)
|
119
|
+
ret = self.class.lookup(@_id)
|
120
|
+
return nil if ret.nil?
|
82
121
|
update(ret.to_a.first[:data])
|
83
122
|
self
|
84
123
|
end
|
85
124
|
|
125
|
+
def self.inherited(model)
|
126
|
+
Shoden.models.add(model)
|
127
|
+
end
|
128
|
+
|
129
|
+
def self.save(record)
|
130
|
+
if record.id
|
131
|
+
table.where(id: record.id).update(data: record.attributes)
|
132
|
+
else
|
133
|
+
begin
|
134
|
+
id = table.insert(data: record.attributes)
|
135
|
+
record.instance_variable_set(:@_id, id)
|
136
|
+
rescue Sequel::UniqueConstraintViolation
|
137
|
+
raise UniqueIndexViolation
|
138
|
+
end
|
139
|
+
end
|
140
|
+
end
|
141
|
+
|
86
142
|
def self.all
|
87
143
|
collect
|
88
144
|
end
|
89
145
|
|
146
|
+
def self.count
|
147
|
+
size = 0
|
148
|
+
Shoden.connection.fetch("SELECT COUNT(*) FROM \"#{table_name}\"") do |r|
|
149
|
+
size = r[:count]
|
150
|
+
end
|
151
|
+
|
152
|
+
size
|
153
|
+
end
|
154
|
+
|
90
155
|
def self.first
|
91
156
|
collect("ORDER BY id ASC LIMIT 1").first
|
92
157
|
end
|
@@ -152,15 +217,57 @@ module Shoden
|
|
152
217
|
end
|
153
218
|
end
|
154
219
|
|
220
|
+
def self.filter(conditions = {})
|
221
|
+
query = []
|
222
|
+
id = conditions.delete(:id)
|
223
|
+
order = conditions.delete(:order)
|
224
|
+
|
225
|
+
if id && !conditions.any?
|
226
|
+
rows = table.where(id: id)
|
227
|
+
else
|
228
|
+
conditions.each { |k,v| query << "data->'#{k}' = '#{v}'" }
|
229
|
+
seek_conditions = query.join(" AND ")
|
230
|
+
|
231
|
+
where = "WHERE (#{seek_conditions})"
|
232
|
+
|
233
|
+
where += " AND id = '#{id}'" if id
|
234
|
+
order_condition = "ORDER BY #{order}" if order
|
235
|
+
|
236
|
+
sql = "#{base_query} #{where} #{order_condition}"
|
237
|
+
|
238
|
+
rows = Shoden.connection.fetch(sql) || []
|
239
|
+
end
|
240
|
+
|
241
|
+
rows.lazy.map do |row|
|
242
|
+
attrs = row[:data].merge({ id: row[:id] })
|
243
|
+
|
244
|
+
new(attrs)
|
245
|
+
end
|
246
|
+
end
|
247
|
+
|
248
|
+
def attributes
|
249
|
+
sanitized = @attributes.map do |k, _|
|
250
|
+
val = send(k)
|
251
|
+
return if val.nil?
|
252
|
+
[k, val.to_s]
|
253
|
+
end.compact
|
254
|
+
|
255
|
+
Sequel::Postgres::HStore.new(sanitized)
|
256
|
+
end
|
257
|
+
|
155
258
|
private
|
156
259
|
|
260
|
+
def self.base_query
|
261
|
+
"SELECT * FROM \"#{table_name}\""
|
262
|
+
end
|
263
|
+
|
157
264
|
def self.collect(condition = '')
|
158
|
-
|
265
|
+
records = []
|
159
266
|
Shoden.connection.fetch("SELECT * FROM \"#{table_name}\" #{condition}") do |r|
|
160
267
|
attrs = r[:data].merge(id: r[:id])
|
161
|
-
|
268
|
+
records << new(attrs)
|
162
269
|
end
|
163
|
-
|
270
|
+
records
|
164
271
|
end
|
165
272
|
|
166
273
|
def self.table_name
|
@@ -174,53 +281,48 @@ module Shoden
|
|
174
281
|
downcase.to_sym
|
175
282
|
end
|
176
283
|
|
177
|
-
def create_index(name, type = '')
|
284
|
+
def self.create_index(name, type = '')
|
178
285
|
conn.execute <<EOS
|
179
|
-
CREATE #{type.upcase} INDEX index_#{self.
|
286
|
+
CREATE #{type.upcase} INDEX index_#{self.name}_#{name}
|
180
287
|
ON "#{table_name}" (( data -> '#{name}'))
|
181
288
|
WHERE ( data ? '#{name}' );
|
182
289
|
EOS
|
290
|
+
rescue
|
183
291
|
end
|
184
292
|
|
185
|
-
def
|
186
|
-
sanitized = @attributes.map do |k, _|
|
187
|
-
val = send(k)
|
188
|
-
return if val.nil?
|
189
|
-
[k, val.to_s]
|
190
|
-
end.compact
|
191
|
-
|
192
|
-
Sequel::Postgres::HStore.new(sanitized)
|
193
|
-
end
|
194
|
-
|
195
|
-
def lookup(id)
|
196
|
-
raise NotFound if !conn.tables.include?(table_name.to_sym)
|
197
|
-
|
293
|
+
def self.lookup(id)
|
198
294
|
row = table.where(id: id)
|
199
|
-
|
295
|
+
return nil if !row.any?
|
200
296
|
|
201
297
|
row
|
202
298
|
end
|
203
299
|
|
204
|
-
def setup
|
205
|
-
|
206
|
-
Shoden.connection.create_table? table_name do
|
300
|
+
def self.setup
|
301
|
+
conn.create_table? table_name do
|
207
302
|
primary_key :id
|
208
303
|
hstore :data
|
209
304
|
end
|
305
|
+
|
306
|
+
indices.each { |i| create_index(i) }
|
307
|
+
uniques.each { |i| create_index(i, :unique) }
|
308
|
+
end
|
309
|
+
|
310
|
+
def self.destroy_all
|
311
|
+
conn.execute("DELETE FROM \"#{table_name}\"")
|
312
|
+
rescue Sequel::DatabaseError
|
210
313
|
end
|
211
314
|
|
212
|
-
def
|
213
|
-
|
315
|
+
def self.destroy_table
|
316
|
+
conn.drop_table(table_name)
|
317
|
+
rescue Sequel::DatabaseError
|
214
318
|
end
|
215
319
|
|
216
|
-
def table
|
320
|
+
def self.table
|
217
321
|
conn[table_name]
|
218
322
|
end
|
219
323
|
|
220
|
-
def conn
|
221
|
-
|
222
|
-
@created ||= setup
|
223
|
-
c
|
324
|
+
def self.conn
|
325
|
+
Shoden.connection
|
224
326
|
end
|
225
327
|
end
|
226
328
|
end
|
data/shoden.gemspec
CHANGED
data/test/shoden_test.rb
CHANGED
@@ -3,12 +3,35 @@ require 'shoden'
|
|
3
3
|
|
4
4
|
Model = Class.new(Shoden::Model)
|
5
5
|
|
6
|
+
class Person < Shoden::Model
|
7
|
+
attribute :email
|
8
|
+
attribute :origin
|
9
|
+
|
10
|
+
index :origin
|
11
|
+
unique :email
|
12
|
+
end
|
13
|
+
|
14
|
+
class Tree < Shoden::Model
|
15
|
+
attribute :name
|
16
|
+
collection :sprouts, :Sprout
|
17
|
+
end
|
18
|
+
|
19
|
+
class Sprout < Shoden::Model
|
20
|
+
attribute :leaves
|
21
|
+
reference :tree, :Tree
|
22
|
+
end
|
23
|
+
|
6
24
|
class User < Shoden::Model
|
7
25
|
attribute :name
|
8
26
|
end
|
9
27
|
|
28
|
+
class A < Shoden::Model
|
29
|
+
attribute :n, ->(x) { x.to_i }
|
30
|
+
end
|
31
|
+
|
10
32
|
setup do
|
11
|
-
Shoden.
|
33
|
+
Shoden.destroy_tables
|
34
|
+
Shoden.setup
|
12
35
|
end
|
13
36
|
|
14
37
|
test 'model' do
|
@@ -37,17 +60,15 @@ test 'update' do
|
|
37
60
|
assert_equal user.name, 'Cyril'
|
38
61
|
end
|
39
62
|
|
40
|
-
test '
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
end
|
63
|
+
test 'filtering' do
|
64
|
+
person = { email: 'elcuervo@elcuervo.net' }
|
65
|
+
Person.create(person)
|
66
|
+
p = Person.filter(person).first
|
45
67
|
|
46
|
-
|
47
|
-
|
48
|
-
reference :tree, :Tree
|
49
|
-
end
|
68
|
+
assert p.email == 'elcuervo@elcuervo.net'
|
69
|
+
end
|
50
70
|
|
71
|
+
test 'relations' do
|
51
72
|
tree = Tree.create(name: 'asd')
|
52
73
|
|
53
74
|
assert tree.id
|
@@ -56,7 +77,14 @@ test 'relations' do
|
|
56
77
|
sprout = tree.sprouts.create(leaves: 4)
|
57
78
|
|
58
79
|
assert sprout.is_a?(Sprout)
|
80
|
+
assert tree.sprouts.all.each.is_a?(Enumerator)
|
81
|
+
|
82
|
+
assert tree.sprouts[sprout.id].id == sprout.id
|
83
|
+
|
84
|
+
assert_equal tree.sprouts.count, 1
|
59
85
|
assert_equal sprout.tree.id, tree.id
|
86
|
+
assert tree.sprouts.first != nil
|
87
|
+
assert tree.sprouts.last != nil
|
60
88
|
end
|
61
89
|
|
62
90
|
test 'deletion' do
|
@@ -65,14 +93,10 @@ test 'deletion' do
|
|
65
93
|
|
66
94
|
user.destroy
|
67
95
|
|
68
|
-
|
96
|
+
assert_equal User[id], nil
|
69
97
|
end
|
70
98
|
|
71
99
|
test 'casting' do
|
72
|
-
class A < Shoden::Model
|
73
|
-
attribute :n, ->(x) { x.to_i }
|
74
|
-
end
|
75
|
-
|
76
100
|
a = A.create(n: 1)
|
77
101
|
a_prime = A[a.id]
|
78
102
|
|
@@ -80,14 +104,6 @@ test 'casting' do
|
|
80
104
|
end
|
81
105
|
|
82
106
|
test 'indices' do
|
83
|
-
class Person < Shoden::Model
|
84
|
-
attribute :email
|
85
|
-
attribute :origin
|
86
|
-
|
87
|
-
index :origin
|
88
|
-
unique :email
|
89
|
-
end
|
90
|
-
|
91
107
|
person = Person.create(email: 'elcuervo@elcuervo.net', origin: 'The internerd')
|
92
108
|
|
93
109
|
assert person.id
|
@@ -98,9 +114,9 @@ test 'indices' do
|
|
98
114
|
end
|
99
115
|
|
100
116
|
test 'basic querying' do
|
117
|
+
User.destroy_all
|
101
118
|
5.times { User.create }
|
102
119
|
|
103
120
|
assert_equal User.all.size, 5
|
104
|
-
|
105
|
-
assert_equal User.last.id, 5
|
121
|
+
|
106
122
|
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: shoden
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.
|
4
|
+
version: 0.4.0
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- elcuervo
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2014-
|
11
|
+
date: 2014-11-15 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: sequel
|
@@ -46,6 +46,7 @@ extensions: []
|
|
46
46
|
extra_rdoc_files: []
|
47
47
|
files:
|
48
48
|
- .gems
|
49
|
+
- .travis.yml
|
49
50
|
- HUGS
|
50
51
|
- LICENSE
|
51
52
|
- README.md
|
@@ -80,4 +81,3 @@ specification_version: 4
|
|
80
81
|
summary: Object hash mapper for postgres
|
81
82
|
test_files:
|
82
83
|
- test/shoden_test.rb
|
83
|
-
has_rdoc:
|