shoden 0.4.0 → 1.0

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
- SHA1:
3
- metadata.gz: 28a3009afa5b70167a8c9128a356002e11224695
4
- data.tar.gz: 77efc3fd3a51f5d98ad27591657e96e56f7b9628
2
+ SHA256:
3
+ metadata.gz: d83a4046d2ee009631a461d017eb91587d878950b5bd2ccdd71caff7d1cbff18
4
+ data.tar.gz: 16d8fd4fd98795d99727440786ef56e7afc5a25b772753831c21dade14cb28c2
5
5
  SHA512:
6
- metadata.gz: 0ae31056c6817127730fdebeddd82ecf8cbff424c9dbedef97dd1674825a421c4b9725670ddaf5094828703fdb0214de600011b718aa5bdbfa4bb0c7dc837853
7
- data.tar.gz: 72d6ca3fa679e24c8201ba4231228f56f6a59c6b4f399b8ec70b9566b9eda835c00750759402ceb126d684b87095d48a28d0c811422b06c7c97b6b06f9bb2e74
6
+ metadata.gz: 1790591d549b5d230962ad6b72d718857e536a040f32c7c2f6a7ea04db6ae52dbe42575904d0ea2210e09f23d6c9076114440f2ba888ba487e0a1ba570256f63
7
+ data.tar.gz: 21bd29c0a876cbce995a9a833e03c453c179db0bae6e51a209995196dbac156633fd91120618759b25016dfa2b8c63dca68a5c9b25b93792b66edb22bcfa8cfb
@@ -0,0 +1 @@
1
+ coverage
@@ -1,10 +1,14 @@
1
1
  language: ruby
2
2
 
3
3
  rvm:
4
- - "1.9.3"
5
- - "2.0.0"
4
+ - 2.4
5
+ - 2.5
6
6
 
7
- services: postgresql
7
+ services:
8
+ - postgresql
9
+
10
+ addons:
11
+ postgresql: "9.6"
8
12
 
9
13
  before_script:
10
14
  - psql -c 'create database shoden_test;' -U postgres
data/Gemfile ADDED
@@ -0,0 +1,5 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem "rake"
@@ -0,0 +1,33 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ shoden (1.0)
5
+ pg (~> 1.1)
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ clap (1.0.0)
11
+ cutest (1.2.3)
12
+ clap
13
+ docile (1.3.1)
14
+ json (2.2.0)
15
+ pg (1.1.4)
16
+ rake (12.3.2)
17
+ simplecov (0.16.1)
18
+ docile (~> 1.1)
19
+ json (>= 1.8, < 3)
20
+ simplecov-html (~> 0.10.0)
21
+ simplecov-html (0.10.2)
22
+
23
+ PLATFORMS
24
+ ruby
25
+
26
+ DEPENDENCIES
27
+ cutest (~> 1.2)
28
+ rake
29
+ shoden!
30
+ simplecov (~> 0.16)
31
+
32
+ BUNDLED WITH
33
+ 1.17.2
data/README.md CHANGED
@@ -1,10 +1,9 @@
1
- # Shôden - [![Gem Version](https://badge.fury.io/rb/shoden.svg)](http://badge.fury.io/rb/shoden)
1
+ # Shôden - [![Build Status](https://travis-ci.org/elcuervo/shoden.svg)](https://travis-ci.org/elcuervo/shoden)
2
2
 
3
- ![Elephant god](http://www.redprintdna.com/wp-content/uploads/2011/09/L-Elephant-Against-Sky.jpg)
3
+ ![Elephant god](https://images.unsplash.com/photo-1523190301657-195ef118bb36?ixlib=rb-1.2.1&ixid=eyJhcHBfaWQiOjEyMDd9&auto=format&fit=crop&w=1500&q=80)
4
4
 
5
5
  Shôden is a persistance library on top of Postgres.
6
- It is basically an [Ohm](https://github.com/soveran/ohm) clone but using
7
- Postgres as a main database.
6
+ Uses JSONB as an storage abstraction so no need for migrations.
8
7
 
9
8
  ## Installation
10
9
 
@@ -12,6 +11,20 @@ Postgres as a main database.
12
11
  gem install shoden
13
12
  ```
14
13
 
14
+ ## Connect
15
+
16
+ Shoden connects by default using a `DATABASE_URL` env variable.
17
+ But you can change the connection string by calling `Shoden.url=`
18
+
19
+ ## Setup
20
+
21
+ Shoden needs a setup method to create the proper tables.
22
+ You should do that after connecting
23
+
24
+ ```ruby
25
+ Shoden.setup
26
+ ```
27
+
15
28
  ## Models
16
29
 
17
30
  ```ruby
@@ -47,6 +60,17 @@ class Post < Shoden::Model
47
60
  end
48
61
  ```
49
62
 
63
+ ## Attributes
64
+
65
+ Shoden attributes offer you a way to type cast the values, or to perform changes
66
+ in the data itself.
67
+
68
+ ```ruby
69
+ class Shout < Shoden::Model
70
+ attribute :what, ->(x) { x.uppcase }
71
+ end
72
+ ```
73
+
50
74
  ## Indexing
51
75
 
52
76
  ```ruby
@@ -58,3 +82,17 @@ class User < Shoden::Model
58
82
  unique :email
59
83
  end
60
84
  ```
85
+
86
+ ## Querying
87
+
88
+ You can query models or relations using the `filter` method.
89
+
90
+ ```ruby
91
+ User.filter(email: "elcuervo@elcuervo.net")
92
+ User.first
93
+ User.last
94
+ User.count
95
+ ```
96
+
97
+ You can go through the entire set using: `User.all` which will give you a
98
+ `Enumerator::Lazy`
data/Rakefile CHANGED
@@ -5,4 +5,4 @@ Rake::TestTask.new("test") do |t|
5
5
  t.pattern = "test/**/*_test.rb"
6
6
  end
7
7
 
8
- task :default => [:test]
8
+ task default: [:test]
@@ -1,7 +1,6 @@
1
- require 'sequel'
2
- require 'set'
3
-
4
- Sequel.extension :pg_hstore, :pg_hstore_ops
1
+ require "pg"
2
+ require "set"
3
+ require "shoden/model"
5
4
 
6
5
  module Shoden
7
6
  Error = Class.new(StandardError)
@@ -9,56 +8,12 @@ module Shoden
9
8
  NotFound = Class.new(Error)
10
9
  UniqueIndexViolation = Class.new(Error)
11
10
 
12
- Proxy = Struct.new(:klass, :parent) do
13
- def create(args = {})
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
53
- end
54
- end
55
-
56
11
  def self.url=(url)
57
12
  @_url = url
58
13
  end
59
14
 
60
15
  def self.url
61
- @_url ||= ENV['DATABASE_URL']
16
+ @_url ||= ENV["DATABASE_URL"]
62
17
  end
63
18
 
64
19
  def self.models
@@ -66,263 +21,17 @@ module Shoden
66
21
  end
67
22
 
68
23
  def self.connection
69
- loggers = []
70
-
71
- if ENV["DEBUG"]
72
- require 'logger'
73
- loggers << Logger.new($stdout)
24
+ @_connection ||= begin
25
+ uri = URI.parse(url)
26
+ PG.connect(uri.hostname, uri.port, nil, nil, uri.path[1..-1], uri.user, uri.password)
74
27
  end
75
-
76
- @_connection ||= Sequel.connect(url, loggers: loggers)
77
28
  end
78
29
 
79
30
  def self.setup
80
- connection.execute("CREATE EXTENSION IF NOT EXISTS hstore")
81
- models.each { |m| m.setup }
31
+ models.each(&:setup)
82
32
  end
83
33
 
84
34
  def self.destroy_tables
85
- models.each { |m| m.destroy_table }
86
- end
87
-
88
- class Model
89
- def initialize(attrs = {})
90
- @_id = attrs.delete(:id) if attrs[:id]
91
- @attributes = {}
92
- update(attrs)
93
- end
94
-
95
- def id
96
- return nil if !defined?(@_id)
97
- @_id.to_i
98
- end
99
-
100
- def destroy
101
- self.class.lookup(id).delete
102
- end
103
-
104
- def update(attrs = {})
105
- attrs.each { |name, value| send(:"#{name}=", value) }
106
- end
107
-
108
- def update_attributes(attrs = {})
109
- update(attrs)
110
- save
111
- end
112
-
113
- def save
114
- self.class.save(self)
115
- self
116
- end
117
-
118
- def load!
119
- ret = self.class.lookup(@_id)
120
- return nil if ret.nil?
121
- update(ret.to_a.first[:data])
122
- self
123
- end
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
-
142
- def self.all
143
- collect
144
- end
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
-
155
- def self.first
156
- collect("ORDER BY id ASC LIMIT 1").first
157
- end
158
-
159
- def self.last
160
- collect("ORDER BY id DESC LIMIT 1").first
161
- end
162
-
163
- def self.create(attrs = {})
164
- new(attrs).save
165
- end
166
-
167
- def self.attributes
168
- @attributes ||= []
169
- end
170
-
171
- def self.indices
172
- @indices ||= []
173
- end
174
-
175
- def self.uniques
176
- @uniques ||= []
177
- end
178
-
179
- def self.[](id)
180
- new(id: id).load!
181
- end
182
-
183
- def self.index(name)
184
- indices << name if !indices.include?(name)
185
- end
186
-
187
- def self.unique(name)
188
- uniques << name if !uniques.include?(name)
189
- end
190
-
191
- def self.attribute(name, caster = ->(x) { x })
192
- attributes << name if !attributes.include?(name)
193
-
194
- define_method(name) { caster[@attributes[name]] }
195
- define_method(:"#{name}=") { |value| @attributes[name] = value }
196
- end
197
-
198
- def self.collection(name, model)
199
- define_method(name) do
200
- klass = Kernel.const_get(model)
201
- Proxy.new(klass, self)
202
- end
203
- end
204
-
205
- def self.reference(name, model)
206
- reader = :"#{name}_id"
207
- writer = :"#{name}_id="
208
-
209
- attributes << name if !attributes.include?(name)
210
-
211
- define_method(reader) { @attributes[reader] }
212
- define_method(writer) { |value| @attributes[reader] = value }
213
-
214
- define_method(name) do
215
- klass = Kernel.const_get("Shoden::#{model}")
216
- klass[send(reader)]
217
- end
218
- end
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
-
258
- private
259
-
260
- def self.base_query
261
- "SELECT * FROM \"#{table_name}\""
262
- end
263
-
264
- def self.collect(condition = '')
265
- records = []
266
- Shoden.connection.fetch("SELECT * FROM \"#{table_name}\" #{condition}") do |r|
267
- attrs = r[:data].merge(id: r[:id])
268
- records << new(attrs)
269
- end
270
- records
271
- end
272
-
273
- def self.table_name
274
- :"Shoden::#{self.name}"
275
- end
276
-
277
- def self.to_reference
278
- name.to_s.
279
- match(/^(?:.*::)*(.*)$/)[1].
280
- gsub(/([a-z\d])([A-Z])/, '\1_\2').
281
- downcase.to_sym
282
- end
283
-
284
- def self.create_index(name, type = '')
285
- conn.execute <<EOS
286
- CREATE #{type.upcase} INDEX index_#{self.name}_#{name}
287
- ON "#{table_name}" (( data -> '#{name}'))
288
- WHERE ( data ? '#{name}' );
289
- EOS
290
- rescue
291
- end
292
-
293
- def self.lookup(id)
294
- row = table.where(id: id)
295
- return nil if !row.any?
296
-
297
- row
298
- end
299
-
300
- def self.setup
301
- conn.create_table? table_name do
302
- primary_key :id
303
- hstore :data
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
313
- end
314
-
315
- def self.destroy_table
316
- conn.drop_table(table_name)
317
- rescue Sequel::DatabaseError
318
- end
319
-
320
- def self.table
321
- conn[table_name]
322
- end
323
-
324
- def self.conn
325
- Shoden.connection
326
- end
35
+ models.each(&:destroy_table)
327
36
  end
328
37
  end
@@ -0,0 +1,259 @@
1
+ require "json"
2
+ require "shoden/proxy"
3
+
4
+ module Shoden
5
+ class Model
6
+ attr_reader :attributes
7
+
8
+ def initialize(attrs = {})
9
+ @_id = attrs.delete(:id) if attrs[:id]
10
+ @attributes = {}
11
+ update(attrs)
12
+ end
13
+
14
+ def id
15
+ return nil unless defined?(@_id)
16
+ @_id.to_i
17
+ end
18
+
19
+ def destroy
20
+ query = "DELETE FROM \"#{self.class.table_name}\" WHERE id = $1 RETURNING id"
21
+ ret = Shoden.connection.exec_params(query, [id])
22
+ ret.first["id"]
23
+ end
24
+
25
+ def update(attrs = {})
26
+ attrs.each { |name, value| send(:"#{name}=", value) }
27
+ end
28
+
29
+ def update_attributes(attrs = {})
30
+ update(attrs)
31
+ save
32
+ end
33
+
34
+ def save
35
+ self.class.save(self)
36
+ self
37
+ end
38
+
39
+ def load!
40
+ ret = self.class.lookup(@_id)
41
+ return nil if ret.nil?
42
+ data = self.class.from_json(ret.first["data"])
43
+ update(data)
44
+
45
+ self
46
+ end
47
+
48
+ def self.inherited(model)
49
+ Shoden.models.add(model)
50
+ end
51
+
52
+ def self.save(record)
53
+ if record.id
54
+ query = "UPDATE \"#{table_name}\" SET data = $1 WHERE id = $2"
55
+ Shoden.connection.exec_params(query, [record.attributes, record.id])
56
+ else
57
+ begin
58
+ query = "INSERT INTO \"#{table_name}\" (data) VALUES ($1) RETURNING id"
59
+ res = Shoden.connection.exec_params(query, [record.attributes.to_json])
60
+ record.instance_variable_set(:@_id, res.first["id"])
61
+ rescue PG::UniqueViolation
62
+ raise Shoden::UniqueIndexViolation
63
+ end
64
+ end
65
+ end
66
+
67
+ def self.all
68
+ collect
69
+ end
70
+
71
+ def self.count
72
+ query = "SELECT COUNT(*) FROM \"#{table_name}\""
73
+ Shoden.connection.exec(query).first["count"].to_i
74
+ end
75
+
76
+ def self.first
77
+ collect(order: "id ASC", limit: 1).first
78
+ end
79
+
80
+ def self.last
81
+ collect(order: "id DESC", limit: 1).first
82
+ end
83
+
84
+ def self.create(attrs = {})
85
+ new(attrs).save
86
+ end
87
+
88
+ def self.attributes
89
+ @attributes ||= []
90
+ end
91
+
92
+ def self.indices
93
+ @indices ||= []
94
+ end
95
+
96
+ def self.uniques
97
+ @uniques ||= []
98
+ end
99
+
100
+ def self.[](id)
101
+ new(id: id).load!
102
+ end
103
+
104
+ def self.index(name)
105
+ indices << name unless indices.include?(name)
106
+ end
107
+
108
+ def self.unique(name)
109
+ uniques << name unless uniques.include?(name)
110
+ end
111
+
112
+ def self.attribute(name, caster = ->(x) { x })
113
+ attributes << name unless attributes.include?(name)
114
+
115
+ define_method(name) { caster[@attributes[name]] }
116
+ define_method(:"#{name}=") { |value| @attributes[name] = value }
117
+ end
118
+
119
+ def self.collection(name, model)
120
+ define_method(name) do
121
+ klass = Kernel.const_get(model)
122
+ Shoden::Proxy.new(klass, self)
123
+ end
124
+ end
125
+
126
+ def self.reference(name, model)
127
+ reader = :"#{name}_id"
128
+ writer = :"#{name}_id="
129
+
130
+ attributes << name unless attributes.include?(name)
131
+
132
+ define_method(reader) { @attributes[reader] }
133
+ define_method(writer) { |value| @attributes[reader] = value }
134
+
135
+ define_method(name) do
136
+ klass = Kernel.const_get("Shoden::#{model}")
137
+ klass[send(reader)]
138
+ end
139
+ end
140
+
141
+ def self.filter(conditions = {})
142
+ rows = query(conditions: conditions)
143
+
144
+ rows.lazy.map do |row|
145
+ data = from_json(row["data"])
146
+ data[:id] = row["id"].to_i
147
+
148
+ new(data)
149
+ end
150
+ end
151
+
152
+ def self.query(fields: "*", conditions: {})
153
+ id = conditions.delete(:id)
154
+ order = conditions.delete(:order)
155
+
156
+ if id && conditions.none?
157
+ sql = "#{base_query(fields)} WHERE id = $1"
158
+ Shoden.connection.exec_params(sql, [id]) || []
159
+ else
160
+ count = conditions.count
161
+ where = count.times.map { |i| "data->>$#{2 * i + 1} = $#{2 * i + 2}" }
162
+ params = conditions.flatten
163
+
164
+ if id
165
+ where << "id = $#{count * 2 + 1}"
166
+ params << id
167
+ end
168
+
169
+ where = where.join(" AND ")
170
+ order_condition = "ORDER BY #{order}" if order
171
+ sql = "#{base_query(fields)} WHERE #{where} #{order_condition}"
172
+
173
+ Shoden.connection.exec_params(sql, params) || []
174
+ end
175
+ end
176
+
177
+ private
178
+
179
+ def self.from_json(string)
180
+ JSON.parse(string, symbolize_names: true)
181
+ end
182
+
183
+ def self.base_query(fields = "*")
184
+ "SELECT #{fields} FROM \"#{table_name}\""
185
+ end
186
+
187
+ def self.collect(order: :id, limit: 0)
188
+ query = base_query("*")
189
+
190
+ params = [].tap do |item|
191
+ item << order
192
+ query << " ORDER BY $1"
193
+
194
+ if limit > 0
195
+ item << limit
196
+ query << " LIMIT $2 "
197
+ end
198
+ end
199
+
200
+ [].tap do |records|
201
+ Shoden.connection.exec_params(query, params).each do |row|
202
+ data = from_json(row["data"])
203
+ data[:id] = row["id"].to_i
204
+
205
+ records << new(data)
206
+ end
207
+ end
208
+ end
209
+
210
+ def self.table_name
211
+ :"Shoden::#{name}"
212
+ end
213
+
214
+ def self.to_reference
215
+ name.to_s
216
+ .match(/^(?:.*::)*(.*)$/)[1]
217
+ .gsub(/([a-z\d])([A-Z])/, '\1_\2')
218
+ .downcase.to_sym
219
+ end
220
+
221
+ def self.create_index(name, type = "")
222
+ query = <<~EOS
223
+ CREATE #{type.upcase} INDEX index_#{self.name}_#{name}
224
+ ON "#{table_name}" (( data ->> '#{name}'))
225
+ WHERE ( data ? '#{name}' );
226
+ EOS
227
+
228
+ Shoden.connection.exec(query)
229
+ end
230
+
231
+ def self.lookup(id)
232
+ query = "SELECT * FROM \"#{table_name}\" WHERE id = $1"
233
+ row = Shoden.connection.exec_params(query, [id])
234
+ return nil if row.none?
235
+
236
+ row
237
+ end
238
+
239
+ def self.setup
240
+ Shoden.connection.exec <<~EOS
241
+ CREATE TABLE IF NOT EXISTS \"#{table_name}\" (
242
+ id SERIAL NOT NULL PRIMARY KEY,
243
+ data JSONB
244
+ )
245
+ EOS
246
+
247
+ indices.each { |i| create_index(i) }
248
+ uniques.each { |i| create_index(i, :unique) }
249
+ end
250
+
251
+ def self.destroy_all
252
+ Shoden.connection.exec("DELETE FROM \"#{table_name}\"")
253
+ end
254
+
255
+ def self.destroy_table
256
+ Shoden.connection.exec("DROP TABLE IF EXISTS \"#{table_name}\"")
257
+ end
258
+ end
259
+ end
@@ -0,0 +1,46 @@
1
+ module Shoden
2
+ Proxy = Struct.new(:klass, :parent) do
3
+ def create(args = {})
4
+ klass.create(args.merge(key => parent.id))
5
+ end
6
+
7
+ def all
8
+ klass.filter(parent_filter)
9
+ end
10
+
11
+ def count
12
+ row = klass.query(fields: "COUNT(id)", conditions: parent_filter)
13
+ row.first["count"].to_i
14
+ end
15
+
16
+ def any?
17
+ count > 0
18
+ end
19
+
20
+ def first
21
+ filter = { order: "id ASC LIMIT 1" }.merge!(parent_filter)
22
+ klass.filter(filter).first
23
+ end
24
+
25
+ def last
26
+ filter = { order: "id DESC LIMIT 1" }.merge!(parent_filter)
27
+ klass.filter(filter).first
28
+ end
29
+
30
+ def [](id)
31
+ filter = { id: id }.merge!(parent_filter)
32
+
33
+ klass.filter(filter).first
34
+ end
35
+
36
+ private
37
+
38
+ def parent_filter
39
+ { key => parent.id }
40
+ end
41
+
42
+ def key
43
+ "#{parent.class.to_reference}_id".freeze
44
+ end
45
+ end
46
+ end
@@ -1,15 +1,17 @@
1
1
  Gem::Specification.new do |s|
2
2
  s.name = "shoden"
3
- s.version = "0.4.0"
3
+ s.version = "1.0"
4
4
  s.summary = "Object hash mapper for postgres"
5
5
  s.description = "Slim postgres models"
6
6
  s.authors = ["elcuervo"]
7
- s.licenses = ["MIT", "HUGWARE"]
7
+ s.licenses = %w[MIT HUGWARE]
8
8
  s.email = ["yo@brunoaguirre.com"]
9
9
  s.homepage = "http://github.com/elcuervo/shoden"
10
10
  s.files = `git ls-files`.split("\n")
11
11
  s.test_files = `git ls-files test`.split("\n")
12
12
 
13
- s.add_dependency("sequel", "~> 4.11.0")
14
- s.add_development_dependency("cutest", "~> 1.2.1")
13
+ s.add_dependency("pg", "~> 1.1")
14
+
15
+ s.add_development_dependency("cutest", "~> 1.2")
16
+ s.add_development_dependency("simplecov", "~> 0.16")
15
17
  end
@@ -1,5 +1,9 @@
1
- require 'cutest'
2
- require 'shoden'
1
+ require "cutest"
2
+ require "simplecov"
3
+
4
+ SimpleCov.start
5
+
6
+ require "shoden"
3
7
 
4
8
  Model = Class.new(Shoden::Model)
5
9
 
@@ -12,8 +16,8 @@ class Person < Shoden::Model
12
16
  end
13
17
 
14
18
  class Tree < Shoden::Model
15
- attribute :name
16
- collection :sprouts, :Sprout
19
+ attribute :name
20
+ collection :sprouts, :Sprout
17
21
  end
18
22
 
19
23
  class Sprout < Shoden::Model
@@ -34,61 +38,70 @@ setup do
34
38
  Shoden.setup
35
39
  end
36
40
 
37
- test 'model' do
41
+ test "url setup" do
42
+ Shoden.url = ENV["DATABASE_URL"]
43
+ end
44
+
45
+ test "model" do
38
46
  model = Model.create
39
- assert_equal model.id.class, Fixnum
47
+
48
+ assert Model.count
49
+ assert_equal model.id.class, Integer
40
50
  end
41
51
 
42
- test 'attributes' do
43
- user = User.create name: 'Michel'
44
- assert_equal user.name, 'Michel'
52
+ test "attributes" do
53
+ user = User.create name: "Michel"
54
+ assert_equal user.name, "Michel"
45
55
  end
46
56
 
47
- test 'update' do
48
- user = User.create name: 'Cyril'
57
+ test "update" do
58
+ user = User.create name: "Cyril"
49
59
  id = user.id
50
60
 
51
- assert_equal user.name, 'Cyril'
61
+ assert_equal user.name, "Cyril"
52
62
 
53
- user.name = 'cyx'
63
+ user.name = "cyx"
54
64
  user.save
55
65
 
56
- assert_equal user.name, 'cyx'
66
+ assert_equal user.name, "cyx"
57
67
  assert_equal user.id, id
58
68
 
59
- user.update_attributes(name: 'Cyril')
60
- assert_equal user.name, 'Cyril'
69
+ user.update_attributes(name: "Cyril")
70
+ assert_equal user.name, "Cyril"
61
71
  end
62
72
 
63
- test 'filtering' do
64
- person = { email: 'elcuervo@elcuervo.net' }
73
+ test "filtering" do
74
+ person = { email: "edgar@poe.com" }
65
75
  Person.create(person)
66
76
  p = Person.filter(person).first
67
77
 
68
- assert p.email == 'elcuervo@elcuervo.net'
78
+ assert p.email == "edgar@poe.com"
69
79
  end
70
80
 
71
- test 'relations' do
72
- tree = Tree.create(name: 'asd')
81
+ test "relations" do
82
+ tree = Tree.create(name: "asd")
83
+ tree2 = Tree.create(name: "qwe")
73
84
 
74
85
  assert tree.id
75
- assert_equal tree.name, 'asd'
86
+ assert_equal tree.name, "asd"
76
87
 
77
88
  sprout = tree.sprouts.create(leaves: 4)
89
+ tree2.sprouts.create(leaves: 5)
78
90
 
79
91
  assert sprout.is_a?(Sprout)
80
92
  assert tree.sprouts.all.each.is_a?(Enumerator)
81
93
 
82
94
  assert tree.sprouts[sprout.id].id == sprout.id
83
95
 
96
+ assert tree.sprouts.any?
84
97
  assert_equal tree.sprouts.count, 1
85
98
  assert_equal sprout.tree.id, tree.id
86
- assert tree.sprouts.first != nil
87
- assert tree.sprouts.last != nil
99
+ assert !tree.sprouts.first.nil?
100
+ assert !tree.sprouts.last.nil?
88
101
  end
89
102
 
90
- test 'deletion' do
91
- user = User.create(name: 'Damian')
103
+ test "deletion" do
104
+ user = User.create(name: "Damian")
92
105
  id = user.id
93
106
 
94
107
  user.destroy
@@ -96,27 +109,28 @@ test 'deletion' do
96
109
  assert_equal User[id], nil
97
110
  end
98
111
 
99
- test 'casting' do
112
+ test "casting" do
100
113
  a = A.create(n: 1)
101
114
  a_prime = A[a.id]
102
115
 
103
116
  assert_equal a_prime.n, 1
104
117
  end
105
118
 
106
- test 'indices' do
107
- person = Person.create(email: 'elcuervo@elcuervo.net', origin: 'The internerd')
119
+ test "indices" do
120
+ person = Person.create(email: "edgar@poe.com", origin: "The internerd")
108
121
 
109
122
  assert person.id
110
123
 
111
124
  assert_raise Shoden::UniqueIndexViolation do
112
- Person.create(email: 'elcuervo@elcuervo.net', origin: 'Montevideo City')
125
+ Person.create(email: "edgar@poe.com", origin: "Montevideo City")
113
126
  end
114
127
  end
115
128
 
116
- test 'basic querying' do
129
+ test "basic querying" do
117
130
  User.destroy_all
118
131
  5.times { User.create }
119
132
 
133
+ assert User.query(conditions: { id: 1 })
134
+ assert User.first != User.last
120
135
  assert_equal User.all.size, 5
121
-
122
136
  end
metadata CHANGED
@@ -1,43 +1,57 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: shoden
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.4.0
4
+ version: '1.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-15 00:00:00.000000000 Z
11
+ date: 2019-09-04 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
- name: sequel
14
+ name: pg
15
15
  requirement: !ruby/object:Gem::Requirement
16
16
  requirements:
17
- - - ~>
17
+ - - "~>"
18
18
  - !ruby/object:Gem::Version
19
- version: 4.11.0
19
+ version: '1.1'
20
20
  type: :runtime
21
21
  prerelease: false
22
22
  version_requirements: !ruby/object:Gem::Requirement
23
23
  requirements:
24
- - - ~>
24
+ - - "~>"
25
25
  - !ruby/object:Gem::Version
26
- version: 4.11.0
26
+ version: '1.1'
27
27
  - !ruby/object:Gem::Dependency
28
28
  name: cutest
29
29
  requirement: !ruby/object:Gem::Requirement
30
30
  requirements:
31
- - - ~>
31
+ - - "~>"
32
32
  - !ruby/object:Gem::Version
33
- version: 1.2.1
33
+ version: '1.2'
34
34
  type: :development
35
35
  prerelease: false
36
36
  version_requirements: !ruby/object:Gem::Requirement
37
37
  requirements:
38
- - - ~>
38
+ - - "~>"
39
39
  - !ruby/object:Gem::Version
40
- version: 1.2.1
40
+ version: '1.2'
41
+ - !ruby/object:Gem::Dependency
42
+ name: simplecov
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '0.16'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '0.16'
41
55
  description: Slim postgres models
42
56
  email:
43
57
  - yo@brunoaguirre.com
@@ -45,13 +59,18 @@ executables: []
45
59
  extensions: []
46
60
  extra_rdoc_files: []
47
61
  files:
48
- - .gems
49
- - .travis.yml
62
+ - ".gems"
63
+ - ".gitignore"
64
+ - ".travis.yml"
65
+ - Gemfile
66
+ - Gemfile.lock
50
67
  - HUGS
51
68
  - LICENSE
52
69
  - README.md
53
70
  - Rakefile
54
71
  - lib/shoden.rb
72
+ - lib/shoden/model.rb
73
+ - lib/shoden/proxy.rb
55
74
  - shoden.gemspec
56
75
  - test/shoden_test.rb
57
76
  homepage: http://github.com/elcuervo/shoden
@@ -65,17 +84,16 @@ require_paths:
65
84
  - lib
66
85
  required_ruby_version: !ruby/object:Gem::Requirement
67
86
  requirements:
68
- - - '>='
87
+ - - ">="
69
88
  - !ruby/object:Gem::Version
70
89
  version: '0'
71
90
  required_rubygems_version: !ruby/object:Gem::Requirement
72
91
  requirements:
73
- - - '>='
92
+ - - ">="
74
93
  - !ruby/object:Gem::Version
75
94
  version: '0'
76
95
  requirements: []
77
- rubyforge_project:
78
- rubygems_version: 2.0.14
96
+ rubygems_version: 3.0.3
79
97
  signing_key:
80
98
  specification_version: 4
81
99
  summary: Object hash mapper for postgres