Class: Sequel::Model
- Inherits:
-
Object
- Object
- Sequel::Model
- Extended by:
- Associations
- Includes:
- Validation
- Defined in:
- lib/sequel_model.rb,
lib/sequel_model.rb,
lib/sequel_model/base.rb,
lib/sequel_model/hooks.rb,
lib/sequel_model/record.rb,
lib/sequel_model/schema.rb,
lib/sequel_model/caching.rb,
lib/sequel_model/plugins.rb,
lib/sequel_model/validations.rb
Overview
Sequel Models
Models in Sequel are based on the Active Record pattern described by Martin Fowler (www.martinfowler.com/eaaCatalog/activeRecord.html). A model class corresponds to a table or a dataset, and an instance of that class wraps a single record in the model’s underlying dataset.
Model classes are defined as regular Ruby classes:
DB = Sequel('sqlite:/blog.db')
class Post < Sequel::Model
set_dataset DB[:posts]
end
You can also use the shorthand form:
DB = Sequel('sqlite:/blog.db')
class Post < Sequel::Model
end
Model instances
Model instance are identified by a primary key. By default, Sequel assumes the primary key column to be :id. The Model#[] method can be used to fetch records by their primary key:
post = Post[123]
The Model#pk method is used to retrieve the record’s primary key value:
post.pk #=> 123
Sequel models allow you to use any column as a primary key, and even composite keys made from multiple columns:
class Post < Sequel::Model
set_primary_key [:category, :title]
end
post = Post['ruby', 'hello world']
post.pk #=> ['ruby', 'hello world']
You can also define a model class that does not have a primary key, but then you lose the ability to update records.
A model instance can also be fetched by specifying a condition:
post = Post[:title => 'hello world']
post = Post.find {:stamp < 10.days.ago}
Iterating over records
A model class lets you iterate over specific records by acting as a proxy to the underlying dataset. This means that you can use the entire Dataset API to create customized queries that return model instances, e.g.:
Post.filter(:category => 'ruby').each {|post| p post}
You can also manipulate the records in the dataset:
Post.filter {:stamp < 7.days.ago}.delete
Post.filter {:title =~ /ruby/}.update(:category => 'ruby')
Accessing record values
A model instances stores its values as a hash:
post.values #=> {:id => 123, :category => 'ruby', :title => 'hello world'}
You can read the record values as object attributes:
post.id #=> 123
post.title #=> 'hello world'
You can also change record values:
post.title = 'hey there'
post.save
Another way to change values by using the #set method:
post.set(:title => 'hey there')
Creating new records
New records can be created by calling Model.create:
post = Post.create(:title => 'hello world')
Another way is to construct a new instance and save it:
post = Post.new
post.title = 'hello world'
post.save
You can also supply a block to Model.new and Model.create:
post = Post.create {|p| p.title = 'hello world'}
post = Post.new do |p|
p.title = 'hello world'
p.save
end
Hooks
You can execute custom code when creating, updating, or deleting records by using hooks. The before_create and after_create hooks wrap record creation. The before_update and after_update wrap record updating. The before_save and after_save wrap record creation and updating. The before_destroy and after_destroy wrap destruction.
Hooks are defined by supplying a block:
class Post < Sequel::Model
after_create do
set(:created_at => Time.now)
end
after_destroy do
.update_post_count
end
end
Deleting records
You can delete individual records by calling #delete or #destroy. The only difference between the two methods is that #destroy invokes before_destroy and after_destroy hooks, while #delete does not:
post.delete #=> bypasses hooks
post.destroy #=> runs hooks
Records can also be deleted en-masse by invoking Model.delete and Model.destroy. As stated above, you can specify filters for the deleted records:
Post.filter(:category => 32).delete #=> bypasses hooks
Post.filter(:category => 32).destroy #=> runs hooks
Please note that if Model.destroy is called, each record is deleted separately, but Model.delete deletes all relevant records with a single SQL statement.
Associations
Sequel provides macros for the three most common types of associations: many_to_one, one_to_many and many_to_many (equivalent to ActiveRecord’s belongs_to, has_many and has_and_belongs_to_many).
Associations are defined in similar fashion to ActiveRecord:
class Post < Sequel::Model
belongs_to :author
end
class Author < Sequel::Model
has_many :posts
end
Another way to define an association in a Sequel model is as a regular instance method:
class Post < Sequel::Model
def ; Author[]; end
end
class Author < Sequel::Model
def posts; Post.filter(:author_id => pk); end
end
Caching model instances with memcached
Sequel models can be cached using memcached based on their primary keys. The use of memcached can significantly reduce database load by keeping model instances in memory. The set_cache method is used to specify caching:
require 'memcache'
CACHE = MemCache.new 'localhost:11211', :namespace => 'blog'
class Author < Sequel::Model
set_cache CACHE, :ttl => 3600
end
Author[333] # database hit
Author[333] # cache hit
Extending the underlying dataset
The obvious way to add table-wide logic is to define class methods to the model class definition. That way you can define subsets of the underlying dataset, change the ordering, or perform actions on multiple records:
class Post < Sequel::Model
def self.old_posts
filter {:stamp < 30.days.ago}
end
def self.clean_old_posts
old_posts.delete
end
end
You can also implement table-wide logic by defining methods on the dataset:
class Post < Sequel::Model
def dataset.old_posts
filter {:stamp < 30.days.ago}
end
def dataset.clean_old_posts
old_posts.delete
end
end
This is the recommended way of implementing table-wide operations, and allows you to have access to your model API from filtered datasets as well:
Post.filter(:category => 'ruby').clean_old_posts
Sequel models also provide a short hand notation for filters:
class Post < Sequel::Model
subset(:old_posts) {:stamp < 30.days.ago}
subset :invisible, :visible => false
end
Defining the underlying schema
Model classes can also be used as a place to define your table schema and control it. The schema DSL is exactly the same provided by Sequel::Schema::Generator:
class Post < Sequel::Model
set_schema do
primary_key :id
text :title
text :category
foreign_key :author_id, :table => :authors
end
end
You can then create the underlying table, drop it, or recreate it:
Post.table_exists?
Post.create_table
Post.drop_table
Post.create_table! # drops the table if it exists and then recreates it
Defined Under Namespace
Modules: Associations
Constant Summary collapse
- HOOKS =
[ :after_initialize, :before_create, :after_create, :before_update, :after_update, :before_save, :after_save, :before_destroy, :after_destroy ]
- HOOK_METHOD_STR =
Some fancy code generation here in order to define the hook class methods…
%Q{ def self.%s(method = nil, &block) unless block (raise SequelError, 'No hook method specified') unless method block = proc {send method} end add_hook(%s, &block) end }
- ATTR_RE =
/^([a-zA-Z_]\w*)(=)?$/.freeze
- EQUAL_SIGN =
'='.freeze
Instance Attribute Summary collapse
-
#changed_columns ⇒ Object
readonly
Returns the value of attribute changed_columns.
-
#values ⇒ Object
readonly
Returns the value of attribute values.
Class Method Summary collapse
-
.[](*args) ⇒ Object
TODO: doc.
-
.add_hook(hook, &block) ⇒ Object
:nodoc:.
-
.all ⇒ Object
Returns an array containing all of the models records.
- .cache_key_from_values(values) ⇒ Object
- .cache_store ⇒ Object
- .cache_ttl ⇒ Object
-
.columns ⇒ Object
Returns the columns in the result set in their original order.
-
.create(values = {}, &block) ⇒ Object
Creates new instance with values set to passed-in Hash ensuring that new? returns true.
-
.create_table ⇒ Object
Creates table.
-
.create_table! ⇒ Object
Like create_table but invokes drop_table when table_exists? is true.
- .create_with_params(params) ⇒ Object (also: create_with)
-
.database_opened(db) ⇒ Object
Called when a database is opened in order to automatically associate the first opened database with model classes.
-
.dataset ⇒ Object
Returns the dataset associated with the Model class.
-
.db ⇒ Object
Returns the database associated with the Model class.
-
.db=(db) ⇒ Object
Sets the database associated with the Model class.
-
.def_hook_method(m) ⇒ Object
:nodoc:.
-
.delete_all ⇒ Object
Deletes all records in the model’s table.
-
.destroy_all ⇒ Object
Like delete_all, but invokes before_destroy and after_destroy hooks if used.
-
.drop_table ⇒ Object
Drops table.
-
.fetch(*args) ⇒ Object
TODO: doc.
-
.find(*args, &block) ⇒ Object
Finds a single record according to the supplied filter, e.g.:.
-
.find_or_create(cond) ⇒ Object
Like find but invokes create with given conditions when record does not exists.
-
.has_hooks?(key) ⇒ Boolean
Returns true if the model class or any of its ancestors have defined hooks for the given hook key.
-
.hooks ⇒ Object
Returns the hooks hash for the model class.
-
.implicit_table_name ⇒ Object
Returns the implicit table name for the model class.
-
.is(plugin, *args) ⇒ Object
(also: is_a)
Loads a plugin for use with the model class, passing optional arguments to the plugin.
- .is_dataset_magic_method?(m) ⇒ Boolean
-
.join(*args) ⇒ Object
TODO: Comprehensive description goes here!.
-
.load(values) ⇒ Object
Initializes a model instance as an existing record.
-
.method_missing(m, *args, &block) ⇒ Object
:nodoc:.
-
.no_primary_key ⇒ Object
:nodoc:.
-
.plugin_gem(plugin) ⇒ Object
Returns the gem name for the given plugin.
-
.plugin_module(plugin) ⇒ Object
Returns the module for the specified plugin.
-
.primary_key ⇒ Object
Returns key for primary key.
-
.primary_key_hash(value) ⇒ Object
Returns primary key attribute hash.
-
.schema ⇒ Object
Returns table schema for direct descendant of Model.
-
.serialize(*columns) ⇒ Object
Serializes column with YAML or through marshalling.
- .set_cache(store, opts = {}) ⇒ Object
- .set_cache_ttl(ttl) ⇒ Object
-
.set_dataset(ds) ⇒ Object
Sets the dataset associated with the Model class.
-
.set_primary_key(*key) ⇒ Object
Sets primary key, regular and composite are possible.
-
.set_schema(name = nil, &block) ⇒ Object
Defines a table schema (see Schema::Generator for more information).
-
.subset(name, *args, &block) ⇒ Object
Defines a method that returns a filtered dataset.
-
.super_dataset ⇒ Object
def self.dataset @dataset ||= super_dataset || (!(n = name).empty? && db) || (raise Error, “No dataset associated with #self”) end.
-
.table_exists? ⇒ Boolean
Returns true if table exists, false otherwise.
-
.table_name ⇒ Object
Returns name of table.
Instance Method Summary collapse
-
#==(obj) ⇒ Object
Compares model instances by values.
-
#===(obj) ⇒ Object
Compares model instances by pkey.
-
#[](column) ⇒ Object
Returns value of attribute.
-
#[]=(column, value) ⇒ Object
Sets value of attribute and marks the column as changed.
-
#cache_key ⇒ Object
Returns a key unique to the underlying record for caching.
-
#columns ⇒ Object
Returns the columns associated with the object’s Model class.
-
#dataset ⇒ Object
Returns the dataset assoiated with the object’s Model class.
-
#db ⇒ Object
Returns the database assoiated with the object’s Model class.
-
#delete ⇒ Object
Deletes and returns self.
-
#destroy ⇒ Object
Like delete but runs hooks before and after delete.
-
#each(&block) ⇒ Object
Enumerates through all attributes.
-
#exists? ⇒ Boolean
Returns true when current instance exists, false otherwise.
-
#id ⇒ Object
Returns value for
:id
attribute. -
#initialize(values = nil, from_db = false, &block) ⇒ Model
constructor
Creates new instance with values set to passed-in Hash.
-
#inspect ⇒ Object
Returns a string representation of the model instance including the class name and values.
-
#keys ⇒ Object
Returns attribute names.
-
#method_missing(m, *args) ⇒ Object
:nodoc:.
-
#new? ⇒ Boolean
(also: #new_record?)
Returns true if the current instance represents a new record.
-
#pk ⇒ Object
Returns the primary key value identifying the model instance.
-
#pk_hash ⇒ Object
Returns a hash identifying the model instance.
-
#primary_key ⇒ Object
Returns primary key column(s) for object’s Model class.
-
#refresh ⇒ Object
(also: #reload)
Reloads values from database and returns self.
-
#save(*args) ⇒ Object
Creates or updates the associated record.
-
#save! ⇒ Object
Creates or updates the associated record.
-
#save_changes ⇒ Object
Saves only changed columns or does nothing if no columns are marked as chanaged.
-
#set(values) ⇒ Object
(also: #update)
Updates and saves values to database from the passed-in Hash.
-
#this ⇒ Object
Returns (naked) dataset bound to current instance.
-
#update_with_params(values) ⇒ Object
(also: #update_with)
Updates the instance with the supplied values with support for virtual attributes, ignoring any values for which no setter method is available.
Methods included from Associations
all_association_reflections, associate, association_reflection, associations, many_to_many, many_to_one, one_to_many, one_to_one
Constructor Details
#initialize(values = nil, from_db = false, &block) ⇒ Model
Creates new instance with values set to passed-in Hash.
This method guesses whether the record exists when new_record
is set to false.
190 191 192 193 194 195 196 197 198 199 200 201 202 203 204 205 206 207 208 209 210 211 212 213 |
# File 'lib/sequel_model/record.rb', line 190 def initialize(values = nil, from_db = false, &block) @changed_columns = [] unless from_db @values = {} if values values.each do |k, v| m = :"#{k}=" if respond_to?(m) send(m, v) values.delete(k) end end values.inject(@values) {|m, kv| m[kv[0].to_sym] = kv[1]; m} # @values.merge!(values) end else @values = values || {} end k = primary_key @new = !from_db block[self] if block after_initialize end |
Dynamic Method Handling
This class handles dynamic methods through the method_missing method
#method_missing(m, *args) ⇒ Object
:nodoc:
304 305 306 307 308 309 310 311 312 313 314 315 316 317 318 319 320 321 322 323 324 325 326 327 328 |
# File 'lib/sequel_model/record.rb', line 304 def method_missing(m, *args) #:nodoc: if m.to_s =~ ATTR_RE att = $1.to_sym write = $2 == EQUAL_SIGN # check whether the column is legal unless @values.has_key?(att) || columns.include?(att) raise Error, "Invalid column (#{att.inspect}) for #{self}" end # define the column accessor Thread.exclusive do if write model.class_def(m) {|v| self[att] = v} else model.class_def(m) {self[att]} end end # call the accessor respond_to?(m) ? send(m, *args) : super(m, *args) else super(m, *args) end end |
Instance Attribute Details
#changed_columns ⇒ Object (readonly)
Returns the value of attribute changed_columns.
4 5 6 |
# File 'lib/sequel_model/record.rb', line 4 def changed_columns @changed_columns end |
#values ⇒ Object (readonly)
Returns the value of attribute values.
3 4 5 |
# File 'lib/sequel_model/record.rb', line 3 def values @values end |
Class Method Details
.[](*args) ⇒ Object
TODO: doc
264 265 266 267 268 269 270 |
# File 'lib/sequel_model.rb', line 264 def self.[](*args) args = args.first if (args.size == 1) if args === true || args === false raise Error::InvalidFilter, "Did you mean to supply a hash?" end dataset[(Hash === args) ? args : primary_key_hash(args)] end |
.add_hook(hook, &block) ⇒ Object
:nodoc:
38 39 40 41 42 43 44 45 |
# File 'lib/sequel_model/hooks.rb', line 38 def self.add_hook(hook, &block) #:nodoc: chain = hooks[hook] chain << block define_method(hook) do return false if super == false chain.each {|h| break false if instance_eval(&h) == false} end end |
.all ⇒ Object
Returns an array containing all of the models records.
319 320 321 |
# File 'lib/sequel_model.rb', line 319 def self.all dataset.all end |
.cache_key_from_values(values) ⇒ Object
38 39 40 |
# File 'lib/sequel_model/caching.rb', line 38 def self.cache_key_from_values(values) "#{self}:#{values.join(',')}" end |
.cache_store ⇒ Object
30 31 32 |
# File 'lib/sequel_model/caching.rb', line 30 def self.cache_store @cache_store end |
.cache_ttl ⇒ Object
34 35 36 |
# File 'lib/sequel_model/caching.rb', line 34 def self.cache_ttl @cache_ttl ||= 3600 end |
.columns ⇒ Object
Returns the columns in the result set in their original order.
See Dataset#columns for more information.
55 56 57 58 |
# File 'lib/sequel_model/base.rb', line 55 def self.columns @columns ||= dataset.columns or raise Error, "Could not fetch columns for #{self}" end |
.create(values = {}, &block) ⇒ Object
Creates new instance with values set to passed-in Hash ensuring that new? returns true.
130 131 132 133 134 135 136 |
# File 'lib/sequel_model/record.rb', line 130 def self.create(values = {}, &block) db.transaction do obj = new(values, &block) obj.save obj end end |
.create_table ⇒ Object
Creates table.
33 34 35 |
# File 'lib/sequel_model/schema.rb', line 33 def self.create_table db.create_table_sql_list(table_name, *schema.create_info).each {|s| db << s} end |
.create_table! ⇒ Object
Like create_table but invokes drop_table when table_exists? is true.
43 44 45 46 |
# File 'lib/sequel_model/schema.rb', line 43 def self.create_table! drop_table if table_exists? create_table end |
.create_with_params(params) ⇒ Object Also known as: create_with
150 151 152 153 154 |
# File 'lib/sequel_model/record.rb', line 150 def create_with_params(params) record = new record.update_with_params(params) record end |
.database_opened(db) ⇒ Object
Called when a database is opened in order to automatically associate the first opened database with model classes.
19 20 21 |
# File 'lib/sequel_model/base.rb', line 19 def self.database_opened(db) @db = db if (self == Model) && !@db end |
.dataset ⇒ Object
Returns the dataset associated with the Model class.
29 30 31 32 33 34 35 36 37 38 39 40 |
# File 'lib/sequel_model/base.rb', line 29 def self.dataset unless @dataset if ds = super_dataset set_dataset(ds.clone) elsif !name.empty? set_dataset(db[implicit_table_name]) else raise Error, "No dataset associated with #{self}" end end @dataset end |
.db ⇒ Object
Returns the database associated with the Model class.
4 5 6 7 |
# File 'lib/sequel_model/base.rb', line 4 def self.db @db ||= (superclass != Object) && superclass.db or raise Error, "No database associated with #{self}" end |
.db=(db) ⇒ Object
Sets the database associated with the Model class.
10 11 12 13 14 15 |
# File 'lib/sequel_model/base.rb', line 10 def self.db=(db) @db = db if @dataset set_dataset(db[table_name]) end end |
.def_hook_method(m) ⇒ Object
:nodoc:
26 27 28 |
# File 'lib/sequel_model/hooks.rb', line 26 def self.def_hook_method(m) #:nodoc: instance_eval(HOOK_METHOD_STR % [m.to_s, m.inspect]) end |
.delete_all ⇒ Object
Deletes all records in the model’s table.
286 287 288 |
# File 'lib/sequel_model.rb', line 286 def self.delete_all dataset.delete end |
.destroy_all ⇒ Object
Like delete_all, but invokes before_destroy and after_destroy hooks if used.
291 292 293 |
# File 'lib/sequel_model.rb', line 291 def self.destroy_all dataset.destroy end |
.drop_table ⇒ Object
Drops table.
38 39 40 |
# File 'lib/sequel_model/schema.rb', line 38 def self.drop_table db.execute db.drop_table_sql(table_name) end |
.fetch(*args) ⇒ Object
TODO: doc
273 274 275 |
# File 'lib/sequel_model.rb', line 273 def self.fetch(*args) db.fetch(*args).set_model(self) end |
.find(*args, &block) ⇒ Object
Finds a single record according to the supplied filter, e.g.:
Ticket.find :author => 'Sharon' # => record
Ticket.find {:price == 17} # => Dataset
259 260 261 |
# File 'lib/sequel_model.rb', line 259 def self.find(*args, &block) dataset.filter(*args, &block).first end |
.find_or_create(cond) ⇒ Object
Like find but invokes create with given conditions when record does not exists.
279 280 281 |
# File 'lib/sequel_model.rb', line 279 def self.find_or_create(cond) find(cond) || create(cond) end |
.has_hooks?(key) ⇒ Boolean
Returns true if the model class or any of its ancestors have defined hooks for the given hook key. Notice that this method cannot detect hooks defined using overridden methods.
50 51 52 53 |
# File 'lib/sequel_model/hooks.rb', line 50 def self.has_hooks?(key) has = hooks[key] && !hooks[key].empty? has || ((self != Model) && superclass.has_hooks?(key)) end |
.hooks ⇒ Object
Returns the hooks hash for the model class.
34 35 36 |
# File 'lib/sequel_model/hooks.rb', line 34 def self.hooks #:nodoc: @hooks ||= Hash.new {|h, k| h[k] = []} end |
.implicit_table_name ⇒ Object
Returns the implicit table name for the model class.
24 25 26 |
# File 'lib/sequel_model/base.rb', line 24 def self.implicit_table_name name.demodulize.underscore.pluralize.to_sym end |
.is(plugin, *args) ⇒ Object Also known as: is_a
Loads a plugin for use with the model class, passing optional arguments to the plugin.
8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 |
# File 'lib/sequel_model/plugins.rb', line 8 def is(plugin, *args) m = plugin_module(plugin) if m.respond_to?(:apply) m.apply(self, *args) end if m.const_defined?("InstanceMethods") class_def(:"#{plugin}_opts") {args.first} include(m::InstanceMethods) end if m.const_defined?("ClassMethods") (:"#{plugin}_opts") {args.first} .send(:include, m::ClassMethods) end if m.const_defined?("DatasetMethods") unless @dataset raise Sequel::Error, "Plugin cannot be applied because the model class has no dataset" end dataset.(:"#{plugin}_opts") {args.first} dataset..send(:include, m::DatasetMethods) end end |
.is_dataset_magic_method?(m) ⇒ Boolean
295 296 297 298 299 300 301 |
# File 'lib/sequel_model.rb', line 295 def self.is_dataset_magic_method?(m) method_name = m.to_s Sequel::Dataset::MAGIC_METHODS.each_key do |r| return true if method_name =~ r end false end |
.join(*args) ⇒ Object
TODO: Comprehensive description goes here!
313 314 315 316 |
# File 'lib/sequel_model.rb', line 313 def self.join(*args) table_name = dataset.opts[:from].first dataset.join(*args).select(table_name.to_sym.ALL) end |
.load(values) ⇒ Object
Initializes a model instance as an existing record. This constructor is used by Sequel to initialize model instances when fetching records.
217 218 219 |
# File 'lib/sequel_model/record.rb', line 217 def self.load(values) new(values, true) end |
.method_missing(m, *args, &block) ⇒ Object
:nodoc:
303 304 305 306 307 308 309 310 |
# File 'lib/sequel_model.rb', line 303 def self.method_missing(m, *args, &block) #:nodoc: Thread.exclusive do if dataset.respond_to?(m) || is_dataset_magic_method?(m) instance_eval("def #{m}(*args, &block); dataset.#{m}(*args, &block); end") end end respond_to?(m) ? send(m, *args, &block) : super(m, *args) end |
.no_primary_key ⇒ Object
:nodoc:
119 120 121 122 123 124 125 126 |
# File 'lib/sequel_model/record.rb', line 119 def self.no_primary_key #:nodoc: (:primary_key) {nil} (:primary_key_hash) {|v| raise Error, "#{self} does not have a primary key"} class_def(:this) {raise Error, "No primary key is associated with this model"} class_def(:pk) {raise Error, "No primary key is associated with this model"} class_def(:pk_hash) {raise Error, "No primary key is associated with this model"} class_def(:cache_key) {raise Error, "No primary key is associated with this model"} end |
.plugin_gem(plugin) ⇒ Object
Returns the gem name for the given plugin.
42 43 44 |
# File 'lib/sequel_model/plugins.rb', line 42 def plugin_gem(plugin) "sequel_#{plugin}" end |
.plugin_module(plugin) ⇒ Object
Returns the module for the specified plugin. If the module is not defined, the corresponding plugin gem is automatically loaded.
33 34 35 36 37 38 39 |
# File 'lib/sequel_model/plugins.rb', line 33 def plugin_module(plugin) module_name = plugin.to_s.gsub(/(^|_)(.)/) {$2.upcase} if not Sequel::Plugins.const_defined?(module_name) require plugin_gem(plugin) end Sequel::Plugins.const_get(module_name) end |
.primary_key ⇒ Object
Returns key for primary key.
44 45 46 |
# File 'lib/sequel_model/record.rb', line 44 def self.primary_key :id end |
.primary_key_hash(value) ⇒ Object
Returns primary key attribute hash.
49 50 51 |
# File 'lib/sequel_model/record.rb', line 49 def self.primary_key_hash(value) {:id => value} end |
.schema ⇒ Object
Returns table schema for direct descendant of Model.
18 19 20 |
# File 'lib/sequel_model/schema.rb', line 18 def self.schema @schema || ((superclass != Model) && (superclass.schema)) end |
.serialize(*columns) ⇒ Object
Serializes column with YAML or through marshalling.
86 87 88 89 90 91 92 93 94 95 |
# File 'lib/sequel_model/base.rb', line 86 def self.serialize(*columns) format = columns.pop[:format] if Hash === columns.last format ||= :yaml @transform = columns.inject({}) do |m, c| m[c] = format m end @dataset.transform(@transform) if @dataset end |
.set_cache(store, opts = {}) ⇒ Object
3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 |
# File 'lib/sequel_model/caching.rb', line 3 def self.set_cache(store, opts = {}) @cache_store = store if (ttl = opts[:ttl]) set_cache_ttl(ttl) end (:[]) do |*args| if (args.size == 1) && (Hash === (h = args.first)) return dataset[h] end unless obj = @cache_store.get(cache_key_from_values(args)) obj = dataset[primary_key_hash((args.size == 1) ? args.first : args)] @cache_store.set(cache_key_from_values(args), obj, cache_ttl) end obj end class_def(:set) {|v| store.delete(cache_key); super} class_def(:save) {store.delete(cache_key); super} class_def(:delete) {store.delete(cache_key); super} end |
.set_cache_ttl(ttl) ⇒ Object
26 27 28 |
# File 'lib/sequel_model/caching.rb', line 26 def self.set_cache_ttl(ttl) @cache_ttl = ttl end |
.set_dataset(ds) ⇒ Object
Sets the dataset associated with the Model class.
61 62 63 64 65 66 |
# File 'lib/sequel_model/base.rb', line 61 def self.set_dataset(ds) @db = ds.db @dataset = ds @dataset.set_model(self) @dataset.transform(@transform) if @transform end |
.set_primary_key(*key) ⇒ Object
67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 |
# File 'lib/sequel_model/record.rb', line 67 def self.set_primary_key(*key) # if k is nil, we go to no_primary_key if key.empty? || (key.size == 1 && key.first == nil) return no_primary_key end # backwards compat key = (key.length == 1) ? key[0] : key.flatten # redefine primary_key (:primary_key) {key} unless key.is_a? Array # regular primary key class_def(:this) do @this ||= dataset.filter(key => @values[key]).limit(1).naked end class_def(:pk) do @pk ||= @values[key] end class_def(:pk_hash) do @pk ||= {key => @values[key]} end class_def(:cache_key) do pk = @values[key] || (raise Error, 'no primary key for this record') @cache_key ||= "#{self.class}:#{pk}" end (:primary_key_hash) do |v| {key => v} end else # composite key exp_list = key.map {|k| "#{k.inspect} => @values[#{k.inspect}]"} block = eval("proc {@this ||= self.class.dataset.filter(#{exp_list.join(',')}).limit(1).naked}") class_def(:this, &block) exp_list = key.map {|k| "@values[#{k.inspect}]"} block = eval("proc {@pk ||= [#{exp_list.join(',')}]}") class_def(:pk, &block) exp_list = key.map {|k| "#{k.inspect} => @values[#{k.inspect}]"} block = eval("proc {@this ||= {#{exp_list.join(',')}}}") class_def(:pk_hash, &block) exp_list = key.map {|k| '#{@values[%s]}' % k.inspect}.join(',') block = eval('proc {@cache_key ||= "#{self.class}:%s"}' % exp_list) class_def(:cache_key, &block) (:primary_key_hash) do |v| key.inject({}) {|m, i| m[i] = v.shift; m} end end end |
.set_schema(name = nil, &block) ⇒ Object
Defines a table schema (see Schema::Generator for more information).
This is only needed if you want to use the create_table or drop_table methods.
7 8 9 10 11 12 13 14 15 |
# File 'lib/sequel_model/schema.rb', line 7 def self.set_schema(name = nil, &block) if name set_dataset(db[name]) end @schema = Schema::Generator.new(db, &block) if @schema.primary_key_name set_primary_key @schema.primary_key_name end end |
.subset(name, *args, &block) ⇒ Object
Defines a method that returns a filtered dataset.
250 251 252 |
# File 'lib/sequel_model.rb', line 250 def self.subset(name, *args, &block) dataset.(name) {filter(*args, &block)} end |
.super_dataset ⇒ Object
def self.dataset
@dataset ||= super_dataset ||
(!(n = name).empty? && db[n.underscore.pluralize.to_sym]) ||
(raise Error, "No dataset associated with #{self}")
end
48 49 50 |
# File 'lib/sequel_model/base.rb', line 48 def self.super_dataset # :nodoc: superclass.dataset if (superclass != Sequel::Model) && superclass.respond_to?(:dataset) end |
.table_exists? ⇒ Boolean
Returns true if table exists, false otherwise.
28 29 30 |
# File 'lib/sequel_model/schema.rb', line 28 def self.table_exists? db.table_exists?(table_name) end |
.table_name ⇒ Object
Returns name of table.
23 24 25 |
# File 'lib/sequel_model/schema.rb', line 23 def self.table_name dataset.opts[:from].first end |
Instance Method Details
#==(obj) ⇒ Object
Compares model instances by values.
34 35 36 |
# File 'lib/sequel_model/record.rb', line 34 def ==(obj) (obj.class == model) && (obj.values == @values) end |
#===(obj) ⇒ Object
Compares model instances by pkey.
39 40 41 |
# File 'lib/sequel_model/record.rb', line 39 def ===(obj) (obj.class == model) && (obj.pk == pk) end |
#[](column) ⇒ Object
Returns value of attribute.
7 8 9 |
# File 'lib/sequel_model/record.rb', line 7 def [](column) @values[column] end |
#[]=(column, value) ⇒ Object
Sets value of attribute and marks the column as changed.
11 12 13 14 |
# File 'lib/sequel_model/record.rb', line 11 def []=(column, value) @changed_columns << column unless @changed_columns.include?(column) @values[column] = value end |
#cache_key ⇒ Object
Returns a key unique to the underlying record for caching
164 165 166 167 |
# File 'lib/sequel_model/record.rb', line 164 def cache_key pk = @values[:id] || (raise Error, 'no primary key for this record') @cache_key ||= "#{self.class}:#{pk}" end |
#columns ⇒ Object
Returns the columns associated with the object’s Model class.
81 82 83 |
# File 'lib/sequel_model/base.rb', line 81 def columns model.columns end |
#dataset ⇒ Object
Returns the dataset assoiated with the object’s Model class.
See Dataset for more information.
76 77 78 |
# File 'lib/sequel_model/base.rb', line 76 def dataset model.dataset end |
#db ⇒ Object
Returns the database assoiated with the object’s Model class.
69 70 71 |
# File 'lib/sequel_model/base.rb', line 69 def db @db ||= model.db end |
#delete ⇒ Object
Deletes and returns self.
296 297 298 299 |
# File 'lib/sequel_model/record.rb', line 296 def delete this.delete self end |
#destroy ⇒ Object
Like delete but runs hooks before and after delete.
287 288 289 290 291 292 293 |
# File 'lib/sequel_model/record.rb', line 287 def destroy db.transaction do before_destroy delete after_destroy end end |
#each(&block) ⇒ Object
Enumerates through all attributes.
Example:
Ticket.find(7).each { |k, v| puts "#{k} => #{v}" }
20 21 22 |
# File 'lib/sequel_model/record.rb', line 20 def each(&block) @values.each(&block) end |
#exists? ⇒ Boolean
Returns true when current instance exists, false otherwise.
228 229 230 |
# File 'lib/sequel_model/record.rb', line 228 def exists? this.count > 0 end |
#id ⇒ Object
Returns value for :id
attribute.
29 30 31 |
# File 'lib/sequel_model/record.rb', line 29 def id @values[:id] end |
#inspect ⇒ Object
Returns a string representation of the model instance including the class name and values.
245 246 247 |
# File 'lib/sequel_model.rb', line 245 def inspect "#<%s @values=%s>" % [self.class.name, @values.inspect] end |
#keys ⇒ Object
Returns attribute names.
24 25 26 |
# File 'lib/sequel_model/record.rb', line 24 def keys @values.keys end |
#new? ⇒ Boolean Also known as: new_record?
Returns true if the current instance represents a new record.
222 223 224 |
# File 'lib/sequel_model/record.rb', line 222 def new? @new end |
#pk ⇒ Object
Returns the primary key value identifying the model instance. If the model’s primary key is changed (using #set_primary_key or #no_primary_key) this method is redefined accordingly.
177 178 179 |
# File 'lib/sequel_model/record.rb', line 177 def pk @pk ||= @values[:id] end |
#pk_hash ⇒ Object
Returns a hash identifying the model instance. Stock implementation.
182 183 184 |
# File 'lib/sequel_model/record.rb', line 182 def pk_hash @pk_hash ||= {:id => @values[:id]} end |
#primary_key ⇒ Object
Returns primary key column(s) for object’s Model class.
170 171 172 |
# File 'lib/sequel_model/record.rb', line 170 def primary_key @primary_key ||= self.class.primary_key end |
#refresh ⇒ Object Also known as: reload
Reloads values from database and returns self.
280 281 282 283 |
# File 'lib/sequel_model/record.rb', line 280 def refresh @values = this.first || raise(Error, "Record not found") self end |
#save(*args) ⇒ Object
Creates or updates the associated record. This method can also accept a list of specific columns to update.
234 235 236 237 238 239 240 241 242 243 244 245 246 247 248 249 250 251 252 253 254 255 256 257 258 259 260 261 262 263 |
# File 'lib/sequel_model/record.rb', line 234 def save(*columns) before_save if @new before_create iid = model.dataset.insert(@values) # if we have a regular primary key and it's not set in @values, # we assume it's the last inserted id if (pk = primary_key) && !(Array === pk) && !@values[pk] @values[pk] = iid end if pk @this = nil # remove memoized this dataset refresh end @new = false after_create else before_update if columns.empty? this.update(@values) @changed_columns = [] else # update only the specified columns this.update(@values.reject {|k, v| !columns.include?(k)}) @changed_columns.reject! {|c| columns.include?(c)} end after_update end after_save self end |
#save! ⇒ Object
Creates or updates the associated record. This method can also accept a list of specific columns to update.
9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 |
# File 'lib/sequel_model/validations.rb', line 9 def save(*columns) before_save if @new before_create iid = model.dataset.insert(@values) # if we have a regular primary key and it's not set in @values, # we assume it's the last inserted id if (pk = primary_key) && !(Array === pk) && !@values[pk] @values[pk] = iid end if pk @this = nil # remove memoized this dataset refresh end @new = false after_create else before_update if columns.empty? this.update(@values) @changed_columns = [] else # update only the specified columns this.update(@values.reject {|k, v| !columns.include?(k)}) @changed_columns.reject! {|c| columns.include?(c)} end after_update end after_save self end |
#save_changes ⇒ Object
Saves only changed columns or does nothing if no columns are marked as chanaged.
267 268 269 |
# File 'lib/sequel_model/record.rb', line 267 def save_changes save(*@changed_columns) unless @changed_columns.empty? end |
#set(values) ⇒ Object Also known as: update
Updates and saves values to database from the passed-in Hash.
272 273 274 275 276 |
# File 'lib/sequel_model/record.rb', line 272 def set(values) v = values.inject({}) {|m, kv| m[kv[0].to_sym] = kv[1]; m} this.update(v) v.each {|k, v| @values[k] = v} end |
#this ⇒ Object
Returns (naked) dataset bound to current instance.
159 160 161 |
# File 'lib/sequel_model/record.rb', line 159 def this @this ||= self.class.dataset.filter(:id => @values[:id]).limit(1).naked end |
#update_with_params(values) ⇒ Object Also known as: update_with
Updates the instance with the supplied values with support for virtual attributes, ignoring any values for which no setter method is available.
140 141 142 143 144 145 146 |
# File 'lib/sequel_model/record.rb', line 140 def update_with_params(values) c = columns values.each do |k, v| m = :"#{k}=" send(m, v) if c.include?(k) || respond_to?(m) end save_changes end |