sequel 0.1.6 → 0.1.7
Sign up to get free protection for your applications and to get access to all the features.
- data/CHANGELOG +22 -0
- data/Rakefile +1 -1
- data/lib/sequel.rb +6 -0
- data/lib/sequel/core_ext.rb +32 -0
- data/lib/sequel/dataset.rb +55 -44
- data/lib/sequel/model.rb +7 -12
- data/lib/sequel/postgres.rb +65 -7
- data/lib/sequel/sqlite.rb +3 -9
- metadata +2 -2
data/CHANGELOG
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
*0.1.7*
|
2
|
+
|
3
|
+
* Removed db.synchronize wrapping calls in sqlite adapter.
|
4
|
+
|
5
|
+
* Implemented Model.join method to restrict returned columns to the model table (thanks Pedro Gutierrez).
|
6
|
+
|
7
|
+
* Implemented Dataset#paginate method.
|
8
|
+
|
9
|
+
* Fixed after_destroy hook.
|
10
|
+
|
11
|
+
* Improved Dataset#first and #last to accept a filter hash.
|
12
|
+
|
13
|
+
* Added Dataset#[]= method.
|
14
|
+
|
15
|
+
* Added Sequel() convenience method.
|
16
|
+
|
17
|
+
* Fixed Dataset#first to include a LIMIT clause for a single record.
|
18
|
+
|
19
|
+
* Small fix to Postgres driver to return a primary_key value for the inserted record if it is specified in the insertion values (thanks Florian Aßmann and Pedro Gutierrez).
|
20
|
+
|
21
|
+
* Fixed Symbol#DESC to support qualified notation (thanks Pedro Gutierrez).
|
22
|
+
|
1
23
|
*0.1.6*
|
2
24
|
|
3
25
|
* Fixed Model#method_missing to raise for an invalid attribute.
|
data/Rakefile
CHANGED
@@ -6,7 +6,7 @@ require 'fileutils'
|
|
6
6
|
include FileUtils
|
7
7
|
|
8
8
|
NAME = "sequel"
|
9
|
-
VERS = "0.1.
|
9
|
+
VERS = "0.1.7"
|
10
10
|
CLEAN.include ['**/.*.sw?', 'pkg/*', '.config', 'doc/*', 'coverage/*']
|
11
11
|
RDOC_OPTS = ['--quiet', '--title', "Sequel: Concise ORM for Ruby",
|
12
12
|
"--opname", "index.html",
|
data/lib/sequel.rb
CHANGED
data/lib/sequel/core_ext.rb
CHANGED
@@ -39,3 +39,35 @@ class String
|
|
39
39
|
Sequel::ExpressionString.new(self)
|
40
40
|
end
|
41
41
|
end
|
42
|
+
|
43
|
+
# Symbol extensions
|
44
|
+
class Symbol
|
45
|
+
def DESC
|
46
|
+
"#{to_field_name} DESC"
|
47
|
+
end
|
48
|
+
|
49
|
+
def AS(target)
|
50
|
+
"#{to_field_name} AS #{target}"
|
51
|
+
end
|
52
|
+
|
53
|
+
AS_REGEXP = /(.*)___(.*)/.freeze
|
54
|
+
DOUBLE_UNDERSCORE = '__'.freeze
|
55
|
+
PERIOD = '.'.freeze
|
56
|
+
|
57
|
+
def to_field_name
|
58
|
+
s = to_s
|
59
|
+
if s =~ AS_REGEXP
|
60
|
+
s = "#{$1} AS #{$2}"
|
61
|
+
end
|
62
|
+
s.split(DOUBLE_UNDERSCORE).join(PERIOD)
|
63
|
+
end
|
64
|
+
|
65
|
+
def ALL
|
66
|
+
"#{to_s}.*"
|
67
|
+
end
|
68
|
+
|
69
|
+
def MIN; "min(#{to_field_name})"; end
|
70
|
+
def MAX; "max(#{to_field_name})"; end
|
71
|
+
def SUM; "sum(#{to_field_name})"; end
|
72
|
+
def AVG; "avg(#{to_field_name})"; end
|
73
|
+
end
|
data/lib/sequel/dataset.rb
CHANGED
@@ -294,12 +294,17 @@ module Sequel
|
|
294
294
|
clause = (@opts[:group] ? :having : :where)
|
295
295
|
cond = cond.first if cond.size == 1
|
296
296
|
parenthesize = !(cond.is_a?(Hash) || cond.is_a?(Array))
|
297
|
+
filter = cond.is_a?(Hash) && cond
|
297
298
|
if @opts[clause]
|
299
|
+
if filter && cond.is_a?(Hash)
|
300
|
+
filter
|
301
|
+
end
|
302
|
+
filter =
|
298
303
|
l = expression_list(@opts[clause])
|
299
304
|
r = expression_list(block || cond, parenthesize)
|
300
305
|
dup_merge(clause => "#{l} AND #{r}")
|
301
306
|
else
|
302
|
-
dup_merge(clause => expression_list(block || cond))
|
307
|
+
dup_merge(:filter => cond, clause => expression_list(block || cond))
|
303
308
|
end
|
304
309
|
end
|
305
310
|
|
@@ -606,6 +611,30 @@ module Sequel
|
|
606
611
|
end
|
607
612
|
alias size count
|
608
613
|
|
614
|
+
# returns a paginated dataset. The resulting dataset also provides the
|
615
|
+
# total number of pages (Dataset#page_count) and the current page number
|
616
|
+
# (Dataset#current_page), as well as Dataset#prev_page and Dataset#next_page
|
617
|
+
# for implementing pagination controls.
|
618
|
+
def paginate(page_no, page_size)
|
619
|
+
total_pages = (count / page_size.to_f).ceil
|
620
|
+
paginated = limit(page_size, (page_no - 1) * page_size)
|
621
|
+
paginated.current_page = page_no
|
622
|
+
paginated.page_count = total_pages
|
623
|
+
paginated
|
624
|
+
end
|
625
|
+
|
626
|
+
attr_accessor :page_count, :current_page
|
627
|
+
|
628
|
+
# Returns the previous page number or nil if the current page is the first
|
629
|
+
def prev_page
|
630
|
+
current_page > 1 ? (current_page - 1) : nil
|
631
|
+
end
|
632
|
+
|
633
|
+
# Returns the next page number or nil if the current page is the last page
|
634
|
+
def next_page
|
635
|
+
current_page < page_count ? (current_page + 1) : nil
|
636
|
+
end
|
637
|
+
|
609
638
|
# Returns a table reference for use in the FROM clause. If the dataset has
|
610
639
|
# only a :from option refering to a single table, only the table name is
|
611
640
|
# returned. Otherwise a subquery is returned.
|
@@ -660,35 +689,47 @@ module Sequel
|
|
660
689
|
|
661
690
|
# Returns the first record in the dataset. If the num argument is specified,
|
662
691
|
# an array is returned with the first <i>num</i> records.
|
663
|
-
def first(
|
664
|
-
|
665
|
-
|
692
|
+
def first(*args)
|
693
|
+
args = args.empty? ? 1 : (args.size == 1) ? args.first : args
|
694
|
+
case args
|
695
|
+
when 1: single_record(:limit => 1)
|
696
|
+
when Fixnum: limit(args).all
|
666
697
|
else
|
667
|
-
|
698
|
+
filter(args).single_record(:limit => 1)
|
668
699
|
end
|
669
700
|
end
|
670
701
|
|
671
702
|
# Returns the first record matching the condition.
|
672
703
|
def [](*conditions)
|
673
|
-
|
704
|
+
first(*conditions)
|
705
|
+
end
|
706
|
+
|
707
|
+
def []=(conditions, values)
|
708
|
+
filter(conditions).update(values)
|
674
709
|
end
|
675
710
|
|
676
711
|
# Returns the last records in the dataset by inverting the order. If no
|
677
712
|
# order is given, an exception is raised. If num is not given, the last
|
678
713
|
# record is returned. Otherwise an array is returned with the last
|
679
714
|
# <i>num</i> records.
|
680
|
-
def last(
|
715
|
+
def last(*args)
|
681
716
|
raise SequelError, 'No order specified' unless
|
682
717
|
@opts[:order] || (opts && opts[:order])
|
683
718
|
|
684
|
-
|
685
|
-
|
686
|
-
|
687
|
-
|
688
|
-
|
689
|
-
|
719
|
+
args = args.empty? ? 1 : (args.size == 1) ? args.first : args
|
720
|
+
|
721
|
+
case args
|
722
|
+
when Fixnum:
|
723
|
+
l = {:limit => args}
|
724
|
+
opts = {:order => invert_order(@opts[:order])}. \
|
725
|
+
merge(opts ? opts.merge(l) : l)
|
726
|
+
if args == 1
|
727
|
+
single_record(opts)
|
728
|
+
else
|
729
|
+
dup_merge(opts).all
|
730
|
+
end
|
690
731
|
else
|
691
|
-
|
732
|
+
filter(args).last(1)
|
692
733
|
end
|
693
734
|
end
|
694
735
|
|
@@ -709,33 +750,3 @@ module Sequel
|
|
709
750
|
end
|
710
751
|
end
|
711
752
|
|
712
|
-
class Symbol
|
713
|
-
def DESC
|
714
|
-
"#{to_s} DESC"
|
715
|
-
end
|
716
|
-
|
717
|
-
def AS(target)
|
718
|
-
"#{to_field_name} AS #{target}"
|
719
|
-
end
|
720
|
-
|
721
|
-
def MIN; "min(#{to_field_name})"; end
|
722
|
-
def MAX; "max(#{to_field_name})"; end
|
723
|
-
def SUM; "sum(#{to_field_name})"; end
|
724
|
-
def AVG; "avg(#{to_field_name})"; end
|
725
|
-
|
726
|
-
AS_REGEXP = /(.*)___(.*)/.freeze
|
727
|
-
DOUBLE_UNDERSCORE = '__'.freeze
|
728
|
-
PERIOD = '.'.freeze
|
729
|
-
|
730
|
-
def to_field_name
|
731
|
-
s = to_s
|
732
|
-
if s =~ AS_REGEXP
|
733
|
-
s = "#{$1} AS #{$2}"
|
734
|
-
end
|
735
|
-
s.split(DOUBLE_UNDERSCORE).join(PERIOD)
|
736
|
-
end
|
737
|
-
|
738
|
-
def ALL
|
739
|
-
"#{to_s}.*"
|
740
|
-
end
|
741
|
-
end
|
data/lib/sequel/model.rb
CHANGED
@@ -154,7 +154,7 @@ module Sequel
|
|
154
154
|
end
|
155
155
|
|
156
156
|
def self.after_destroy(&block)
|
157
|
-
get_hooks(:after_destroy)
|
157
|
+
get_hooks(:after_destroy) << block
|
158
158
|
end
|
159
159
|
|
160
160
|
def self.find(cond)
|
@@ -194,17 +194,6 @@ module Sequel
|
|
194
194
|
self
|
195
195
|
end
|
196
196
|
|
197
|
-
# def self.each(&block); dataset.each(&block); end
|
198
|
-
# def self.all; dataset.all; end
|
199
|
-
# def self.filter(*arg, &block); dataset.filter(*arg, &block); end
|
200
|
-
# def self.exclude(*arg, &block); dataset.exclude(*arg, &block); end
|
201
|
-
# def self.order(*arg); dataset.order(*arg); end
|
202
|
-
# def self.first(*arg); dataset.first(*arg); end
|
203
|
-
# def self.count; dataset.count; end
|
204
|
-
# def self.map(*arg, &block); dataset.map(*arg, &block); end
|
205
|
-
# def self.hash_column(column); dataset.hash_column(primary_key, column); end
|
206
|
-
# def self.join(*args); dataset.join(*args); end
|
207
|
-
# def self.lock(mode, &block); dataset.lock(mode, &block); end
|
208
197
|
def self.destroy_all
|
209
198
|
has_hooks?(:before_destroy) ? dataset.destroy : dataset.delete
|
210
199
|
end
|
@@ -222,6 +211,7 @@ module Sequel
|
|
222
211
|
db.transaction do
|
223
212
|
run_hooks(:before_destroy)
|
224
213
|
delete
|
214
|
+
run_hooks(:after_destroy)
|
225
215
|
end
|
226
216
|
end
|
227
217
|
|
@@ -252,6 +242,11 @@ module Sequel
|
|
252
242
|
respond_to?(m) ? send(m, *args, &block) : super(m, *args)
|
253
243
|
end
|
254
244
|
|
245
|
+
def self.join(*args)
|
246
|
+
table_name = dataset.opts[:from].first
|
247
|
+
dataset.join(*args).select(table_name.to_sym.ALL)
|
248
|
+
end
|
249
|
+
|
255
250
|
def db; self.class.db; end
|
256
251
|
|
257
252
|
def [](field); @values[field]; end
|
data/lib/sequel/postgres.rb
CHANGED
@@ -41,9 +41,17 @@ class PGconn
|
|
41
41
|
|
42
42
|
def last_insert_id(table)
|
43
43
|
@table_sequences ||= {}
|
44
|
-
|
45
|
-
|
46
|
-
|
44
|
+
if !@table_sequences.include?(table)
|
45
|
+
pkey_and_seq = pkey_and_sequence(table)
|
46
|
+
if pkey_and_seq
|
47
|
+
@table_sequences[table] = pkey_and_seq[1]
|
48
|
+
end
|
49
|
+
end
|
50
|
+
if seq = @table_sequences[table]
|
51
|
+
r = async_query(SELECT_CURRVAL % seq)
|
52
|
+
return r[0][0].to_i unless r.nil? || r.empty?
|
53
|
+
end
|
54
|
+
nil # primary key sequence not found
|
47
55
|
end
|
48
56
|
|
49
57
|
# Shamelessly appropriated from ActiveRecord's Postgresql adapter.
|
@@ -74,13 +82,31 @@ class PGconn
|
|
74
82
|
AND cons.contype = 'p'
|
75
83
|
AND def.adsrc ~* 'nextval'
|
76
84
|
end_sql
|
85
|
+
|
86
|
+
SELECT_PK = <<-end_sql
|
87
|
+
SELECT pg_attribute.attname
|
88
|
+
FROM pg_class, pg_attribute, pg_index
|
89
|
+
WHERE pg_class.oid = pg_attribute.attrelid AND
|
90
|
+
pg_class.oid = pg_index.indrelid AND
|
91
|
+
pg_index.indkey[0] = pg_attribute.attnum AND
|
92
|
+
pg_index.indisprimary = 't' AND
|
93
|
+
pg_class.relname = '%s'
|
94
|
+
end_sql
|
77
95
|
|
78
96
|
def pkey_and_sequence(table)
|
79
97
|
r = async_query(SELECT_PK_AND_SERIAL_SEQUENCE % table)
|
80
98
|
return [r[0].first, r[0].last] unless r.nil? or r.empty?
|
81
99
|
|
82
100
|
r = async_query(SELECT_PK_AND_CUSTOM_SEQUENCE % table)
|
83
|
-
return [r.first, r.last] unless r.nil? or r.empty?
|
101
|
+
return [r[0].first, r[0].last] unless r.nil? or r.empty?
|
102
|
+
rescue
|
103
|
+
nil
|
104
|
+
end
|
105
|
+
|
106
|
+
def primary_key(table)
|
107
|
+
r = async_query(SELECT_PK % table)
|
108
|
+
pkey = r[0].first unless r.nil? or r.empty?
|
109
|
+
return pkey.to_sym if pkey
|
84
110
|
rescue
|
85
111
|
nil
|
86
112
|
end
|
@@ -164,11 +190,42 @@ module Sequel
|
|
164
190
|
raise e
|
165
191
|
end
|
166
192
|
|
167
|
-
def
|
193
|
+
def primary_key_for_table(conn, table)
|
194
|
+
@primary_keys ||= {}
|
195
|
+
@primary_keys[table] ||= conn.primary_key(table)
|
196
|
+
end
|
197
|
+
|
198
|
+
RE_CURRVAL_ERROR = /currval of sequence "(.*)" is not yet defined in this session/.freeze
|
199
|
+
|
200
|
+
def insert_result(conn, table, values)
|
201
|
+
begin
|
202
|
+
result = conn.last_insert_id(table)
|
203
|
+
return result if result
|
204
|
+
rescue PGError => e
|
205
|
+
# An error could occur if the inserted values include a primary key
|
206
|
+
# value, while the primary key is serial.
|
207
|
+
if e.message =~ RE_CURRVAL_ERROR
|
208
|
+
raise SequelError, "Could not return primary key value for the inserted record. Are you specifying a primary key value for a serial primary key?"
|
209
|
+
else
|
210
|
+
raise e
|
211
|
+
end
|
212
|
+
end
|
213
|
+
|
214
|
+
case values
|
215
|
+
when Hash:
|
216
|
+
values[primary_key_for_table(conn, table)]
|
217
|
+
when Array:
|
218
|
+
values.first
|
219
|
+
else
|
220
|
+
nil
|
221
|
+
end
|
222
|
+
end
|
223
|
+
|
224
|
+
def execute_insert(sql, table, values)
|
168
225
|
@logger.info(sql) if @logger
|
169
226
|
@pool.hold do |conn|
|
170
227
|
conn.execute(sql).clear
|
171
|
-
conn
|
228
|
+
insert_result(conn, table, values)
|
172
229
|
end
|
173
230
|
rescue => e
|
174
231
|
@logger.error(e.message) if @logger
|
@@ -314,7 +371,8 @@ module Sequel
|
|
314
371
|
end
|
315
372
|
|
316
373
|
def insert(*values)
|
317
|
-
@db.execute_insert(insert_sql(*values), @opts[:from]
|
374
|
+
@db.execute_insert(insert_sql(*values), @opts[:from],
|
375
|
+
values.size == 1 ? values.first : values)
|
318
376
|
end
|
319
377
|
|
320
378
|
def update(values, opts = nil)
|
data/lib/sequel/sqlite.rb
CHANGED
@@ -75,22 +75,16 @@ module Sequel
|
|
75
75
|
end
|
76
76
|
|
77
77
|
def insert(*values)
|
78
|
-
@db.
|
79
|
-
@db.execute_insert insert_sql(*values)
|
80
|
-
end
|
78
|
+
@db.execute_insert insert_sql(*values)
|
81
79
|
end
|
82
80
|
|
83
81
|
def update(values, opts = nil)
|
84
|
-
@db.
|
85
|
-
@db.execute update_sql(values, opts)
|
86
|
-
end
|
82
|
+
@db.execute update_sql(values, opts)
|
87
83
|
self
|
88
84
|
end
|
89
85
|
|
90
86
|
def delete(opts = nil)
|
91
|
-
@db.
|
92
|
-
@db.execute delete_sql(opts)
|
93
|
-
end
|
87
|
+
@db.execute delete_sql(opts)
|
94
88
|
self
|
95
89
|
end
|
96
90
|
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.9.4
|
|
3
3
|
specification_version: 1
|
4
4
|
name: sequel
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.1.
|
7
|
-
date: 2007-06-
|
6
|
+
version: 0.1.7
|
7
|
+
date: 2007-06-30 00:00:00 +03:00
|
8
8
|
summary: Concise ORM for Ruby.
|
9
9
|
require_paths:
|
10
10
|
- lib
|