sequel 0.2.0.2 → 0.2.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.
@@ -0,0 +1,84 @@
1
+ module Sequel
2
+ class Model
3
+ # returns the database associated with the model class
4
+ def self.db
5
+ @db ||= ((superclass != Object) && (superclass.db)) || \
6
+ raise(SequelError, "No database associated with #{self}.")
7
+ end
8
+
9
+ # sets the database associated with the model class
10
+ def self.db=(db); @db = db; end
11
+
12
+ # called when a database is opened in order to automatically associate the
13
+ # first opened database with model classes.
14
+ def self.database_opened(db)
15
+ @db = db if self == Model && !@db
16
+ end
17
+
18
+ # returns the dataset associated with the model class.
19
+ def self.dataset
20
+ @dataset || super_dataset || raise(SequelError, "No dataset associated with #{self}.")
21
+ end
22
+
23
+ def self.super_dataset
24
+ if superclass && superclass.respond_to?(:dataset) && ds = superclass.dataset
25
+ ds
26
+ end
27
+ end
28
+
29
+ def self.columns
30
+ @columns ||= @dataset.columns || raise(SequelError, "Could not fetch columns for #{self}")
31
+ end
32
+
33
+ # Sets the dataset associated with the model class.
34
+ def self.set_dataset(ds)
35
+ @db = ds.db
36
+ @dataset = ds
37
+ @dataset.set_model(self)
38
+ @dataset.transform(@transform) if @transform
39
+ end
40
+
41
+ def model
42
+ @model ||= self.class
43
+ end
44
+
45
+ # Returns the dataset assoiated with the object's model class.
46
+ def db
47
+ @db ||= model.db
48
+ end
49
+
50
+ # Returns the dataset assoiated with the object's model class.
51
+ def dataset
52
+ @dataset ||= model.dataset
53
+ end
54
+
55
+ def columns
56
+ @columns ||= model.columns
57
+ end
58
+
59
+ SERIALIZE_FORMATS = {
60
+ :yaml => [proc {|v| YAML.load v if v}, proc {|v| v.to_yaml}],
61
+ :marshal => [proc {|v| Marshal.load(v) if v}, proc {|v| Marshal.dump(v)}]
62
+ }
63
+
64
+ def self.serialize(*columns)
65
+ format = columns.pop[:format] if Hash === columns.last
66
+ filters = SERIALIZE_FORMATS[format || :yaml]
67
+ # add error handling here
68
+
69
+ @transform = columns.inject({}) do |m, c|
70
+ m[c] = filters
71
+ m
72
+ end
73
+ end
74
+ end
75
+
76
+ def self.Model(source)
77
+ @models ||= {}
78
+ @models[source] ||= Class.new(Sequel::Model) do
79
+ meta_def(:inherited) do |c|
80
+ c.set_dataset(source.is_a?(Dataset) ? source : c.db[source])
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,40 @@
1
+ module Sequel
2
+ class Model
3
+ def self.get_hooks(key)
4
+ @hooks ||= {}
5
+ @hooks[key] ||= []
6
+ end
7
+
8
+ def self.has_hooks?(key)
9
+ !get_hooks(key).empty?
10
+ end
11
+
12
+ def run_hooks(key)
13
+ self.class.get_hooks(key).each {|h| instance_eval(&h)}
14
+ end
15
+
16
+ def self.before_save(&block)
17
+ get_hooks(:before_save).unshift(block)
18
+ end
19
+
20
+ def self.before_create(&block)
21
+ get_hooks(:before_create).unshift(block)
22
+ end
23
+
24
+ def self.before_destroy(&block)
25
+ get_hooks(:before_destroy).unshift(block)
26
+ end
27
+
28
+ def self.after_save(&block)
29
+ get_hooks(:after_save) << block
30
+ end
31
+
32
+ def self.after_create(&block)
33
+ get_hooks(:after_create) << block
34
+ end
35
+
36
+ def self.after_destroy(&block)
37
+ get_hooks(:after_destroy) << block
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,154 @@
1
+ module Sequel
2
+ class Model
3
+ attr_reader :values
4
+
5
+ def self.primary_key; :id; end
6
+ def self.primary_key_hash(v); {:id => v}; end
7
+
8
+ def self.set_primary_key(key)
9
+ # if k is nil, we go to no_primary_key
10
+ return no_primary_key unless key
11
+
12
+ # redefine primary_key
13
+ meta_def(:primary_key) {key}
14
+
15
+ if key.is_a?(Array) # composite key
16
+ class_def(:this) do
17
+ @this ||= self.class.dataset.filter( \
18
+ @values.reject {|k, v| !key.include?(k)} \
19
+ ).naked
20
+ end
21
+ meta_def(:primary_key_hash) do |v|
22
+ key.inject({}) {|m, i| m[i] = v.shift; m}
23
+ end
24
+ else # regular key
25
+ class_def(:this) do
26
+ @this ||= self.class.dataset.filter(key => @values[key]).naked
27
+ end
28
+ meta_def(:primary_key_hash) do |v|
29
+ {key => v}
30
+ end
31
+ end
32
+ end
33
+
34
+ def self.no_primary_key
35
+ meta_def(:primary_key) {nil}
36
+ meta_def(:primary_key_hash) {|v| raise SequelError, "#{self} does not have a primary key"}
37
+ class_def(:this) {raise SequelError, "No primary key is associated with this model"}
38
+ end
39
+
40
+ def self.create(values = {})
41
+ db.transaction do
42
+ obj = new(values, true)
43
+ obj.save
44
+ obj
45
+ end
46
+ end
47
+
48
+ def this
49
+ @this ||= self.class.dataset.filter(:id => @values[:id]).naked
50
+ end
51
+
52
+ # instance method
53
+ def primary_key
54
+ @primary_key ||= self.class.primary_key
55
+ end
56
+
57
+ def pkey
58
+ @pkey ||= @values[primary_key]
59
+ end
60
+
61
+ def initialize(values = {}, new = false)
62
+ @values = values
63
+ @new = new
64
+ if !new # determine if it's a new record
65
+ pk = primary_key
66
+ @new = (pk == nil) || (!(Array === pk) && !@values[pk])
67
+ end
68
+ end
69
+
70
+ def new?
71
+ @new
72
+ end
73
+
74
+ def exists?
75
+ this.count > 0
76
+ end
77
+
78
+ def save
79
+ run_hooks(:before_save)
80
+ if @new
81
+ run_hooks(:before_create)
82
+ iid = model.dataset.insert(@values)
83
+ # if we have a regular primary key and it's not set in @values,
84
+ # we assume it's the last inserted id
85
+ if (pk = primary_key) && !(Array === pk) && !@values[pk]
86
+ @values[pk] = iid
87
+ end
88
+ if pk
89
+ @this = nil # remove memoized this dataset
90
+ refresh
91
+ end
92
+ run_hooks(:after_create)
93
+ else
94
+ run_hooks(:before_update)
95
+ this.update(@values)
96
+ run_hooks(:after_update)
97
+ end
98
+ run_hooks(:after_save)
99
+ @new = false
100
+ self
101
+ end
102
+
103
+ def set(values)
104
+ this.update(values)
105
+ @values.merge!(values)
106
+ end
107
+
108
+ def refresh
109
+ @values = this.first || raise(SequelError, "Record not found")
110
+ self
111
+ end
112
+
113
+ def destroy
114
+ db.transaction do
115
+ run_hooks(:before_destroy)
116
+ delete
117
+ run_hooks(:after_destroy)
118
+ end
119
+ end
120
+
121
+ def delete
122
+ this.delete
123
+ self
124
+ end
125
+
126
+ ATTR_RE = /^([a-zA-Z_]\w*)(=)?$/.freeze
127
+
128
+ def method_missing(m, *args)
129
+ if m.to_s =~ ATTR_RE
130
+ att = $1.to_sym
131
+ write = $2 == '='
132
+
133
+ # check wether the column is legal
134
+ unless columns.include?(att)
135
+ raise SequelError, "Invalid column (#{att.inspect}) for #{self}"
136
+ end
137
+
138
+ # define the column accessor
139
+ Thread.exclusive do
140
+ if write
141
+ model.class_def(m) {|v| @values[att] = v}
142
+ else
143
+ model.class_def(m) {@values[att]}
144
+ end
145
+ end
146
+
147
+ # call the accessor
148
+ respond_to?(m) ? send(m, *args) : super(m, *args)
149
+ else
150
+ super(m, *args)
151
+ end
152
+ end
153
+ end
154
+ end
@@ -0,0 +1,26 @@
1
+ module Sequel
2
+ class Model
3
+ ONE_TO_ONE_PROC = "proc {i = @values[:%s]; %s[i] if i}".freeze
4
+ ID_POSTFIX = "_id".freeze
5
+ FROM_DATASET = "db[%s]".freeze
6
+
7
+ def self.one_to_one(name, opts)
8
+ klass = opts[:class] ? opts[:class] : (FROM_DATASET % name.inspect)
9
+ key = opts[:key] || (name.to_s + ID_POSTFIX)
10
+ define_method name, &eval(ONE_TO_ONE_PROC % [key, klass])
11
+ end
12
+
13
+ ONE_TO_MANY_PROC = "proc {%s.filter(:%s => pkey)}".freeze
14
+ ONE_TO_MANY_ORDER_PROC = "proc {%s.filter(:%s => pkey).order(%s)}".freeze
15
+ def self.one_to_many(name, opts)
16
+ klass = opts[:class] ? opts[:class] :
17
+ (FROM_DATASET % (opts[:table] || name.inspect))
18
+ key = opts[:on]
19
+ order = opts[:order]
20
+ define_method name, &eval(
21
+ (order ? ONE_TO_MANY_ORDER_PROC : ONE_TO_MANY_PROC) %
22
+ [klass, key, order.inspect]
23
+ )
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,36 @@
1
+ module Sequel
2
+ class Model
3
+ def self.set_schema(name = nil, &block)
4
+ name ? set_dataset(db[name]) : name = table_name
5
+ @schema = Schema::Generator.new(db, name, &block)
6
+ if @schema.primary_key_name
7
+ set_primary_key @schema.primary_key_name
8
+ end
9
+ end
10
+
11
+ def self.schema
12
+ @schema || ((superclass != Model) && (superclass.schema))
13
+ end
14
+
15
+ def self.table_name
16
+ dataset.opts[:from].first
17
+ end
18
+
19
+ def self.table_exists?
20
+ db.table_exists?(table_name)
21
+ end
22
+
23
+ def self.create_table
24
+ db.create_table_sql_list(*schema.create_info).each {|s| db << s}
25
+ end
26
+
27
+ def self.drop_table
28
+ db.execute db.drop_table_sql(table_name)
29
+ end
30
+
31
+ def self.recreate_table
32
+ drop_table if table_exists?
33
+ create_table
34
+ end
35
+ end
36
+ end
data/lib/sequel/mysql.rb CHANGED
@@ -69,10 +69,10 @@ module Sequel
69
69
  conn = Mysql.real_connect(@opts[:host], @opts[:user], @opts[:password],
70
70
  @opts[:database], @opts[:port])
71
71
  conn.query_with_result = false
72
- if @opts[:charset]
73
- conn.query("set character_set_connection = '#{@opts[:charset]}';")
74
- conn.query("set character_set_client = '#{@opts[:charset]}';")
75
- conn.query("set character_set_results = '#{@opts[:charset]}';")
72
+ if encoding = @opts[:encoding] || @opts[:charset]
73
+ conn.query("set character_set_connection = '#{encoding}';")
74
+ conn.query("set character_set_client = '#{encoding}';")
75
+ conn.query("set character_set_results = '#{encoding}';")
76
76
  end
77
77
  conn.reconnect = true
78
78
  conn
@@ -95,7 +95,7 @@ module Sequel
95
95
  end
96
96
  end
97
97
 
98
- def query(sql)
98
+ def execute_select(sql)
99
99
  @logger.info(sql) if @logger
100
100
  @pool.hold do |conn|
101
101
  conn.query(sql)
@@ -176,7 +176,9 @@ module Sequel
176
176
  def match_expr(l, r)
177
177
  case r
178
178
  when Regexp:
179
- "(#{literal(l)} REGEXP #{literal(r.source)})"
179
+ r.casefold? ? \
180
+ "(#{literal(l)} REGEXP #{literal(r.source)})" :
181
+ "(#{literal(l)} REGEXP BINARY #{literal(r.source)})"
180
182
  else
181
183
  super
182
184
  end
@@ -213,7 +215,7 @@ module Sequel
213
215
 
214
216
  def fetch_rows(sql)
215
217
  @db.synchronize do
216
- r = @db.query(sql)
218
+ r = @db.execute_select(sql)
217
219
  begin
218
220
  @columns = r.columns
219
221
  r.each_hash {|row| yield row}
@@ -141,7 +141,7 @@ module Sequel
141
141
  set_adapter_scheme :postgres
142
142
 
143
143
  def connect
144
- PGconn.connect(
144
+ conn = PGconn.connect(
145
145
  @opts[:host] || 'localhost',
146
146
  @opts[:port] || 5432,
147
147
  '', '',
@@ -149,6 +149,10 @@ module Sequel
149
149
  @opts[:user],
150
150
  @opts[:password]
151
151
  )
152
+ if encoding = @opts[:encoding] || @opts[:charset]
153
+ conn.set_client_encoding(encoding)
154
+ end
155
+ conn
152
156
  end
153
157
 
154
158
  def dataset(opts = nil)
@@ -282,9 +286,6 @@ module Sequel
282
286
  end
283
287
  end
284
288
 
285
- LIKE = '(%s ~ %s)'.freeze
286
- LIKE_CI = '%s ~* %s'.freeze
287
-
288
289
  def match_expr(l, r)
289
290
  case r
290
291
  when Regexp:
@@ -6,6 +6,7 @@ module Sequel
6
6
  @table_name = table_name
7
7
  @columns = []
8
8
  @indexes = []
9
+ @primary_key = nil
9
10
  instance_eval(&block)
10
11
  end
11
12
 
data/lib/sequel/sqlite.rb CHANGED
@@ -47,7 +47,7 @@ module Sequel
47
47
  @pool.hold {|conn| conn.get_first_value(sql)}
48
48
  end
49
49
 
50
- def query(sql, &block)
50
+ def execute_select(sql, &block)
51
51
  @logger.info(sql) if @logger
52
52
  @pool.hold {|conn| conn.query(sql, &block)}
53
53
  end
@@ -105,7 +105,7 @@ module Sequel
105
105
  end
106
106
 
107
107
  def fetch_rows(sql, &block)
108
- @db.query(sql) do |result|
108
+ @db.execute_select(sql) do |result|
109
109
  @columns = result.columns.map {|c| c.to_sym}
110
110
  column_count = @columns.size
111
111
  result.each do |values|