sequel 0.3.0.1 → 0.3.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -28,7 +28,9 @@ module Sequel
28
28
  # <i>You can even set it to nil!</i>
29
29
  def self.set_primary_key(*key)
30
30
  # if k is nil, we go to no_primary_key
31
- return no_primary_key unless key
31
+ if key.empty? || (key.size == 1 && key.first == nil)
32
+ return no_primary_key
33
+ end
32
34
 
33
35
  # backwards compat
34
36
  key = (key.length == 1) ? key[0] : key.flatten
@@ -40,6 +42,12 @@ module Sequel
40
42
  class_def(:this) do
41
43
  @this ||= dataset.filter(key => @values[key]).limit(1).naked
42
44
  end
45
+ class_def(:pk) do
46
+ @pk ||= @values[key]
47
+ end
48
+ class_def(:pk_hash) do
49
+ @pk ||= {key => @values[key]}
50
+ end
43
51
  class_def(:cache_key) do
44
52
  pk = @values[key] || (raise SequelError, 'no primary key for this record')
45
53
  @cache_key ||= "#{self.class}:#{pk}"
@@ -52,6 +60,14 @@ module Sequel
52
60
  block = eval("proc {@this ||= self.class.dataset.filter(#{exp_list.join(',')}).limit(1).naked}")
53
61
  class_def(:this, &block)
54
62
 
63
+ exp_list = key.map {|k| "@values[#{k.inspect}]"}
64
+ block = eval("proc {@pk ||= [#{exp_list.join(',')}]}")
65
+ class_def(:pk, &block)
66
+
67
+ exp_list = key.map {|k| "#{k.inspect} => @values[#{k.inspect}]"}
68
+ block = eval("proc {@this ||= {#{exp_list.join(',')}}}")
69
+ class_def(:pk_hash, &block)
70
+
55
71
  exp_list = key.map {|k| '#{@values[%s]}' % k.inspect}.join(',')
56
72
  block = eval('proc {@cache_key ||= "#{self.class}:%s"}' % exp_list)
57
73
  class_def(:cache_key, &block)
@@ -65,7 +81,9 @@ module Sequel
65
81
  def self.no_primary_key #:nodoc:
66
82
  meta_def(:primary_key) {nil}
67
83
  meta_def(:primary_key_hash) {|v| raise SequelError, "#{self} does not have a primary key"}
68
- class_def(:this) {raise SequelError, "No primary key is associated with this model"}
84
+ class_def(:this) {raise SequelError, "No primary key is associated with this model"}
85
+ class_def(:pk) {raise SequelError, "No primary key is associated with this model"}
86
+ class_def(:pk_hash) {raise SequelError, "No primary key is associated with this model"}
69
87
  class_def(:cache_key) {raise SequelError, "No primary key is associated with this model"}
70
88
  end
71
89
 
@@ -97,7 +115,18 @@ module Sequel
97
115
 
98
116
  # Returns value for primary key.
99
117
  def pkey
100
- @pkey ||= @values[primary_key]
118
+ warn "Model#pkey is deprecated. Please use Model#pk instead."
119
+ @pkey ||= @values[self.class.primary_key]
120
+ end
121
+
122
+ # Returns the primary key value identifying the model instance. Stock implementation.
123
+ def pk
124
+ @pk ||= @values[:id]
125
+ end
126
+
127
+ # Returns a hash identifying the model instance. Stock implementation.
128
+ def pk_hash
129
+ @pk_hash ||= {:id => @values[:id]}
101
130
  end
102
131
 
103
132
  # Creates new instance with values set to passed-in Hash.
@@ -109,11 +138,11 @@ module Sequel
109
138
 
110
139
  @new = new_record
111
140
  unless @new # determine if it's a new record
112
- pk = primary_key
141
+ k = self.class.primary_key
113
142
  # if there's no primary key for the model class, or
114
143
  # @values doesn't contain a primary key value, then
115
144
  # we regard this instance as new.
116
- @new = (pk == nil) || (!(Array === pk) && !@values[pk])
145
+ @new = (k == nil) || (!(Array === k) && !@values[k])
117
146
  end
118
147
  end
119
148
 
@@ -187,8 +216,14 @@ module Sequel
187
216
  att = $1.to_sym
188
217
  write = $2 == '='
189
218
 
190
- # check wether the column is legal
219
+ # check whether the column is legal
191
220
  unless columns.include?(att)
221
+ # if read accessor and a value exists for the column, we return it
222
+ if !write && @values.has_key?(att)
223
+ return @values[att]
224
+ end
225
+
226
+ # otherwise, raise an error
192
227
  raise SequelError, "Invalid column (#{att.inspect}) for #{self}"
193
228
  end
194
229
 
@@ -1,31 +1,107 @@
1
- # TODO: refactoring...
2
1
  module Sequel
3
2
  class Model
4
-
5
- ONE_TO_ONE_PROC = "proc {i = @values[:%s]; %s[i] if i}".freeze
6
- ID_POSTFIX = "_id".freeze
7
- FROM_DATASET = "db[%s]".freeze
3
+ ID_POSTFIX = '_id'.freeze
8
4
 
9
- # Comprehensive description goes here!
5
+ # Creates a 1-1 relationship by defining an association method, e.g.:
6
+ #
7
+ # class Session < Sequel::Model(:sessions)
8
+ # end
9
+ #
10
+ # class Node < Sequel::Model(:nodes)
11
+ # one_to_one :producer, :from => Session
12
+ # # which is equivalent to
13
+ # def producer
14
+ # Session[producer_id] if producer_id
15
+ # end
16
+ # end
17
+ #
18
+ # You can also set the foreign key explicitly by including a :key option:
19
+ #
20
+ # one_to_one :producer, :from => Session, :key => :producer_id
21
+ #
22
+ # The one_to_one macro also creates a setter, which accepts nil, a hash or
23
+ # a model instance, e.g.:
24
+ #
25
+ # p = Producer[1234]
26
+ # node = Node[:path => '/']
27
+ # node.producer = p
28
+ # node.producer_id #=> 1234
29
+ #
10
30
  def self.one_to_one(name, opts)
11
- klass = opts[:class] ? opts[:class] : (FROM_DATASET % name.inspect)
12
- key = opts[:key] || (name.to_s + ID_POSTFIX)
13
- define_method name, &eval(ONE_TO_ONE_PROC % [key, klass])
31
+ # deprecation
32
+ if opts[:class]
33
+ warn "The :class option has been deprecated. Please use :from instead."
34
+ opts[:from] = opts[:class]
35
+ end
36
+
37
+ from = opts[:from]
38
+ from || (raise SequelError, "No association source defined (use :from option)")
39
+ key = opts[:key] || (name.to_s + ID_POSTFIX).to_sym
40
+
41
+ setter_name = "#{name}=".to_sym
42
+
43
+ case from
44
+ when Symbol
45
+ class_def(name) {(k = @values[key]) ? db[from][:id => k] : nil}
46
+ when Sequel::Dataset
47
+ class_def(name) {(k = @values[key]) ? from[:id => k] : nil}
48
+ else
49
+ class_def(name) {(k = @values[key]) ? from[k] : nil}
50
+ end
51
+ class_def(setter_name) do |v|
52
+ case v
53
+ when nil
54
+ set(key => nil)
55
+ when Sequel::Model
56
+ set(key => v.pk)
57
+ when Hash
58
+ set(key => v[:id])
59
+ end
60
+ end
61
+
62
+ # define_method name, &eval(ONE_TO_ONE_PROC % [key, from])
14
63
  end
15
64
 
16
- ONE_TO_MANY_PROC = "proc {%s.filter(:%s => pkey)}".freeze
17
- ONE_TO_MANY_ORDER_PROC = "proc {%s.filter(:%s => pkey).order(%s)}".freeze
18
-
19
- # Comprehensive description goes here!
65
+ # Creates a 1-N relationship by defining an association method, e.g.:
66
+ #
67
+ # class Book < Sequel::Model(:books)
68
+ # end
69
+ #
70
+ # class Author < Sequel::Model(:authors)
71
+ # one_to_many :books, :from => Book
72
+ # # which is equivalent to
73
+ # def books
74
+ # Book.filter(:author_id => id)
75
+ # end
76
+ # end
77
+ #
78
+ # You can also set the foreign key explicitly by including a :key option:
79
+ #
80
+ # one_to_many :books, :from => Book, :key => :author_id
81
+ #
20
82
  def self.one_to_many(name, opts)
21
- klass = opts[:class] ? opts[:class] :
22
- (FROM_DATASET % (opts[:table] || name.inspect))
23
- key = opts[:on]
24
- order = opts[:order]
25
- define_method name, &eval(
26
- (order ? ONE_TO_MANY_ORDER_PROC : ONE_TO_MANY_PROC) %
27
- [klass, key, order.inspect]
28
- )
83
+ # deprecation
84
+ if opts[:class]
85
+ warn "The :class option has been deprecated. Please use :from instead."
86
+ opts[:from] = opts[:class]
87
+ end
88
+ # deprecation
89
+ if opts[:on]
90
+ warn "The :on option has been deprecated. Please use :key instead."
91
+ opts[:key] = opts[:on]
92
+ end
93
+
94
+
95
+ from = opts[:from]
96
+ from || (raise SequelError, "No association source defined (use :from option)")
97
+ key = opts[:key] || (self.to_s + ID_POSTFIX).to_sym
98
+
99
+ case from
100
+ when Symbol
101
+ class_def(name) {db[from].filter(key => pk)}
102
+ else
103
+ class_def(name) {from.filter(key => pk)}
104
+ end
29
105
  end
30
106
  end
31
107
  end
data/lib/sequel/mysql.rb CHANGED
@@ -197,6 +197,7 @@ module Sequel
197
197
  def literal(v)
198
198
  case v
199
199
  when LiteralString: quoted_field_name(v)
200
+ when String: "'#{v.gsub(/'|\\/, '\&\&')}'"
200
201
  when true: TRUE
201
202
  when false: FALSE
202
203
  else
@@ -0,0 +1,109 @@
1
+ if !Object.const_defined?('Sequel')
2
+ require File.join(File.dirname(__FILE__), '../sequel')
3
+ end
4
+
5
+ require 'oci8'
6
+
7
+ module Sequel
8
+ module Oracle
9
+ class Database < Sequel::Database
10
+ set_adapter_scheme :oracle
11
+
12
+ # AUTO_INCREMENT = 'IDENTITY(1,1)'.freeze
13
+ #
14
+ # def auto_increment_sql
15
+ # AUTO_INCREMENT
16
+ # end
17
+ #
18
+ def connect
19
+ if @opts[:database]
20
+ dbname = @opts[:host] ? \
21
+ "//#{@opts[:host]}/#{@opts[:database]}" : @opts[:database]
22
+ else
23
+ dbname = @opts[:host]
24
+ end
25
+ conn = OCI8.new(@opts[:user], @opts[:password], dbname, @opts[:privilege])
26
+ conn.autocommit = true
27
+ conn.non_blocking = true
28
+ conn
29
+ end
30
+
31
+ def disconnect
32
+ @pool.disconnect {|c| c.logoff}
33
+ end
34
+
35
+ def dataset(opts = nil)
36
+ Oracle::Dataset.new(self, opts)
37
+ end
38
+
39
+ def execute(sql)
40
+ @logger.info(sql) if @logger
41
+ @pool.hold {|conn| conn.Execute(sql)}
42
+ end
43
+
44
+ alias_method :do, :execute
45
+ end
46
+
47
+ class Dataset < Sequel::Dataset
48
+ def literal(v)
49
+ case v
50
+ when Time: literal(v.iso8601)
51
+ else
52
+ super
53
+ end
54
+ end
55
+
56
+ def fetch_rows(sql, &block)
57
+ @db.synchronize do
58
+ cursor = @db.execute sql
59
+ begin
60
+ @columns = cursor.get_col_names.map {|c| c.to_sym}
61
+ while r = cursor.fetch
62
+ row = {}
63
+ r.each_with_index {|v, i| row[columns[i]] = v}
64
+ yield row
65
+ end
66
+ ensure
67
+ cursor.close
68
+ end
69
+ end
70
+ self
71
+ end
72
+
73
+ def hash_row(row)
74
+ @columns.inject({}) do |m, c|
75
+ m[c] = row.shift
76
+ m
77
+ end
78
+ end
79
+
80
+ def array_tuples_fetch_rows(sql, &block)
81
+ @db.synchronize do
82
+ cursor = @db.execute sql
83
+ begin
84
+ @columns = cursor.get_col_names.map {|c| c.to_sym}
85
+ while r = cursor.fetch
86
+ r.keys = columns
87
+ yield r
88
+ end
89
+ ensure
90
+ cursor.close
91
+ end
92
+ end
93
+ self
94
+ end
95
+
96
+ def insert(*values)
97
+ @db.do insert_sql(*values)
98
+ end
99
+
100
+ def update(values, opts = nil)
101
+ @db.do update_sql(values, opts)
102
+ end
103
+
104
+ def delete(opts = nil)
105
+ @db.do delete_sql(opts)
106
+ end
107
+ end
108
+ end
109
+ end
@@ -137,6 +137,13 @@ module Sequel
137
137
  1114 => :to_time
138
138
  }
139
139
 
140
+ if PGconn.respond_to?(:translate_results=)
141
+ PGconn.translate_results = true
142
+ AUTO_TRANSLATE = true
143
+ else
144
+ AUTO_TRANSLATE = false
145
+ end
146
+
140
147
  class Database < Sequel::Database
141
148
  set_adapter_scheme :postgres
142
149
 
@@ -433,7 +440,7 @@ module Sequel
433
440
  next if used_fields.include?(field)
434
441
  used_fields << field
435
442
 
436
- if translator = translators[idx]
443
+ if !AUTO_TRANSLATE and translator = translators[idx]
437
444
  kvs << ":\"#{field}\" => ((t = r[#{idx}]) ? t.#{translator} : nil)"
438
445
  else
439
446
  kvs << ":\"#{field}\" => r[#{idx}]"
@@ -475,7 +482,7 @@ module Sequel
475
482
  def array_tuples_compile_converter(fields, translators)
476
483
  tr = []
477
484
  fields.each_with_index do |field, idx|
478
- if t = translators[idx]
485
+ if !AUTO_TRANSLATE and t = translators[idx]
479
486
  tr << "if (v = r[#{idx}]); r[#{idx}] = v.#{t}; end"
480
487
  end
481
488
  end
@@ -40,7 +40,10 @@ module Sequel
40
40
  sql << NOT_NULL if column[:null] == false
41
41
  sql << " DEFAULT #{literal(column[:default])}" if column.include?(:default)
42
42
  sql << PRIMARY_KEY if column[:primary_key]
43
- sql << " REFERENCES #{column[:table]}" if column[:table]
43
+ if column[:table]
44
+ sql << " REFERENCES #{column[:table]}"
45
+ sql << "(#{column[:key]})" if column[:key]
46
+ end
44
47
  sql << " ON DELETE #{on_delete_clause(column[:on_delete])}" if column[:on_delete]
45
48
  sql << " #{auto_increment_sql}" if column[:auto_increment]
46
49
  sql
@@ -134,6 +134,13 @@ context "A MySQL dataset" do
134
134
  @d.filter(:name => /bc/).count.should == 2
135
135
  @d.filter(:name => /^bc/).count.should == 1
136
136
  end
137
+
138
+ specify "should correctly literalize strings with comment backslashes in them" do
139
+ @d.delete
140
+ proc {@d << {:name => ':\\'}}.should_not raise_error
141
+
142
+ @d.first[:name].should == ':\\'
143
+ end
137
144
  end
138
145
 
139
146
  context "A MySQL dataset in array tuples mode" do
@@ -264,9 +264,9 @@ context "An SQLite dataset in array tuples mode" do
264
264
  @d << {:name => 'abc', :value => 4.56}
265
265
  @d << {:name => 'def', :value => 7.89}
266
266
  @d.select(:name, :value).to_a.sort_by {|h| h[:value]}.should == [
267
- Array.from_hash({:name => 'abc', :value => 1.23}),
268
- Array.from_hash({:name => 'abc', :value => 4.56}),
269
- Array.from_hash({:name => 'def', :value => 7.89})
267
+ ['abc', 1.23],
268
+ ['abc', 4.56],
269
+ ['def', 7.89]
270
270
  ]
271
271
  end
272
272
  end
@@ -28,7 +28,7 @@ context "An array with symbol keys" do
28
28
  @a.keys.should == [:a, :b, :c, :d]
29
29
  end
30
30
 
31
- specify "should provide key acess using strings" do
31
+ specify "should provide key access using strings" do
32
32
  @a['a'].should == 1
33
33
  @a['A'].should be_nil
34
34
 
@@ -280,7 +280,7 @@ context "An array with string keys" do
280
280
  @a.keys.should == ['a', 'b', 'c', :d]
281
281
  end
282
282
 
283
- specify "should provide key acess using strings" do
283
+ specify "should provide key access using strings" do
284
284
  @a['a'].should == 1
285
285
  @a['A'].should be_nil
286
286
 
@@ -205,7 +205,7 @@ context "A connection pool with a max size of 5" do
205
205
  @pool = Sequel::ConnectionPool.new(5) {@invoked_count += 1}
206
206
  end
207
207
 
208
- specify "should let five threads simulatneously access separate connections" do
208
+ specify "should let five threads simultaneously access separate connections" do
209
209
  cc = {}
210
210
  threads = []
211
211
  stop = nil