sequel 1.3 → 1.4.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,127 @@
1
+ === 1.4.0 (2008-04-08)
2
+
3
+ * Don't mark a column as changed unless the new value is different from the current value (tamas.denes, jeremyevans) (#203).
4
+
5
+ * Switch gem name from "sequel_model" to just "sequel", which required large version bump (jeremyevans).
6
+
7
+ * Add :select option to many_to_many associations, default to selecting only the associated model table and not the join table (jeremyevans) (#208).
8
+
9
+ * Add :reciprocal one_to_many association option, for setting corresponding many_to_one instance variable (jeremyevans).
10
+
11
+ * Add eager loading implementation (jeremyevans).
12
+
13
+ * Change *_to_many associations so that the all associations are considered :cache=>true (jeremyevans).
14
+
15
+ * Fix associations with block arguments and :cache=>true (jeremyevans).
16
+
17
+ === 0.5.0.2 (2008-03-12)
18
+
19
+ * More fixes for Model.associate to accept strings and symbols as class references.
20
+
21
+ === 0.5.0.1 (2008-03-09)
22
+
23
+ * Fixed Model.associate to accept class and class name in :class option.
24
+
25
+ === 0.5 (2008-03-08)
26
+
27
+ * Merged new associations branch into trunk.
28
+
29
+ * Rewrote RDoc for associations.
30
+
31
+ * Added has_and_belongs_to_many alias for many_to_many.
32
+
33
+ * Added support for optional dataset block.
34
+
35
+ * Added :order option to order association datasets.
36
+
37
+ * Added :cache option to return and cache array of objects for association.
38
+
39
+ * Changed one_to_many, many_to_many associations to return dataset by default.
40
+
41
+ * Added has_many, belongs_to aliases.
42
+
43
+ * Refactored associations code.
44
+
45
+ * Added deprecations for old-style relations.
46
+
47
+ * Completed specs for new associations code.
48
+
49
+ * New associations code by Jeremy Evans (replaces relations code.)
50
+
51
+ === 0.4.2 (2008-02-29)
52
+
53
+ * Fixed one_to_many implicit key to work correctly for namespaced classes (#167).
54
+
55
+ * Fixed Model.db= to affect the underlying dataset (#183).
56
+
57
+ * Fixed Model.implicit_table_name to disregard namespaces.
58
+
59
+ === 0.4.1 (2008-02-10)
60
+
61
+ * Implemented Model#inspect (#151).
62
+
63
+ * Changed Model#method_missing to short-circuit and bypass checking #columns if the values hash already contains the relevant column (#150).
64
+
65
+ * Updated to reflect changes in sequel_core (Dataset#clone_merge renamed to Dataset#clone).
66
+
67
+ === 0.4 (2008-02-05)
68
+
69
+ * Fixed Model#set to work with string keys (#143).
70
+
71
+ * Fixed Model.create to correctly initialize instances marked as new (#135).
72
+
73
+ * Fixed Model#initialize to convert string keys into symbol keys. This also fixes problem with validating objects initialized with string keys (#136).
74
+
75
+ === 0.3.3 (2008-01-25)
76
+
77
+ * Finalized support for virtual attributes.
78
+
79
+ === 0.3.2.1 (2008-01-24)
80
+
81
+ * Fixed Model.dataset to correctly set the dataset if using implicit naming or inheriting the superclass dataset (thanks celldee).
82
+
83
+ === 0.3.2 (2008-01-24)
84
+
85
+ * Added Model#update_with_params method with support for virtual attributes and auto-filtering of unrelated parameters, and changed Model.create_with_params to support virtual attributes (#128).
86
+
87
+ * Cleaned up gem spec (#132).
88
+
89
+ * Removed validations code. Now relying on validations in assistance gem.
90
+
91
+ === 0.3.1 (2008-01-21)
92
+
93
+ * Changed Model.dataset to use inflector to pluralize the class name into the table name. Works in similar fashion to table names in AR or DM.
94
+
95
+ === 0.3 (2008-01-18)
96
+
97
+ * Implemented Validatable::Errors class.
98
+
99
+ * Added Model#reload as alias to Model#refresh.
100
+
101
+ * Changed Model.create to accept a block (#126).
102
+
103
+ * Rewrote validations.
104
+
105
+ * Fixed Model#initialize to accept nil values (#115).
106
+
107
+ === 0.2 (2008-01-02)
108
+
109
+ * Removed deprecated Model.recreate_table method.
110
+
111
+ * Removed deprecated :class and :on options from one_to_many macro.
112
+
113
+ * Removed deprecated :class option from one_to_one macro.
114
+
115
+ * Removed deprecated Model#pkey method.
116
+
117
+ * Changed dependency to sequel_core.
118
+
119
+ * Removed examples from sequel core.
120
+
121
+ * Additional specs. We're now at 100% coverage.
122
+
123
+ * Refactored hooks code. Hooks are now inheritable, and can be defined by supplying a block or a method name, or by overriding the hook instance method. Hook chains can now be broken by returning false (#111, #112).
124
+
125
+ === 0.1 (2007-12-30)
126
+
127
+ * Moved model code from sequel into separate model sub-project.
data/COPYING CHANGED
@@ -1,4 +1,5 @@
1
1
  Copyright (c) 2007-2008 Sharon Rosner
2
+ Copyright (c) 2008 Jeremy Evans
2
3
 
3
4
  Permission is hereby granted, free of charge, to any person obtaining a copy
4
5
  of this software and associated documentation files (the "Software"), to
data/README CHANGED
@@ -7,18 +7,19 @@ Sequel makes it easy to deal with multiple records without having to break your
7
7
  == Resources
8
8
 
9
9
  * {Project page}[http://code.google.com/p/ruby-sequel/]
10
- * {Source code}[http://ruby-sequel.googlecode.com/svn/]
10
+ * {Source code}[http://github.com/jeremyevans/sequel]
11
11
  * {Bug tracking}[http://code.google.com/p/ruby-sequel/issues/list]
12
12
  * {Google group}[http://groups.google.com/group/sequel-talk]
13
13
  * {RubyForge page}[http://rubyforge.org/projects/sequel/]
14
+ * {API RDoc}[http://sequel.rubyforge.org]
14
15
 
15
16
  To check out the source code:
16
17
 
17
- svn co http://ruby-sequel.googlecode.com/svn/trunk
18
+ git clone git://github.com/jeremyevans/sequel.git
18
19
 
19
20
  === Contact
20
21
 
21
- If you have any comments or suggestions please send an email to ciconia at gmail.com and I'll get back to you.
22
+ If you have any comments or suggestions please post to the Google group.
22
23
 
23
24
  == Installation
24
25
 
@@ -208,7 +209,7 @@ Or calculate a sum:
208
209
 
209
210
  You can also specify descending order
210
211
 
211
- posts.order(:stamp.DESC)
212
+ posts.order(:stamp.desc)
212
213
 
213
214
  === Deleting Records
214
215
 
data/Rakefile CHANGED
@@ -9,17 +9,30 @@ include FileUtils
9
9
  # Configuration
10
10
  ##############################################################################
11
11
  NAME = "sequel"
12
- VERS = "1.3"
12
+ VERS = "1.4.0"
13
13
  CLEAN.include ["**/.*.sw?", "pkg/*", ".config", "doc/*", "coverage/*"]
14
14
  RDOC_OPTS = [
15
15
  "--quiet",
16
- "--title", "Sequel: Database access for Ruby",
16
+ "--title", "Sequel: The Database Toolkit for Ruby",
17
17
  "--opname", "index.html",
18
18
  "--line-numbers",
19
19
  "--main", "README",
20
20
  "--inline-source"
21
21
  ]
22
22
 
23
+ ##############################################################################
24
+ # RDoc
25
+ ##############################################################################
26
+ task :doc => [:rdoc]
27
+
28
+ Rake::RDocTask.new do |rdoc|
29
+ rdoc.rdoc_dir = "doc/rdoc"
30
+ rdoc.options += RDOC_OPTS
31
+ rdoc.main = "README"
32
+ rdoc.title = "Sequel: The Database Toolkit for Ruby"
33
+ rdoc.rdoc_files.add ["README", "COPYING", "lib/sequel_model.rb", "lib/**/*.rb"]
34
+ end
35
+
23
36
  ##############################################################################
24
37
  # Gem packaging
25
38
  ##############################################################################
@@ -27,34 +40,34 @@ desc "Packages up Sequel."
27
40
  task :default => [:package]
28
41
  task :package => [:clean]
29
42
 
30
- RDOC_OPTS = [
31
- "--quiet",
32
- "--title", "Sequel Model: The Database Toolkit for Ruby",
33
- "--opname", "index.html",
34
- "--line-numbers",
35
- "--main", "README",
36
- "--inline-source"
37
- ]
38
-
39
43
  spec = Gem::Specification.new do |s|
40
44
  s.name = NAME
41
45
  s.rubyforge_project = 'sequel'
42
46
  s.version = VERS
43
47
  s.platform = Gem::Platform::RUBY
44
- s.summary = "Database access for Ruby"
48
+ s.has_rdoc = true
49
+ s.extra_rdoc_files = ["README", "CHANGELOG", "COPYING"]
50
+ s.rdoc_options += RDOC_OPTS +
51
+ ["--exclude", "^(examples|extras)\/", "--exclude", "lib/sequel_model.rb"]
52
+ s.summary = "The Database Toolkit for Ruby: Model Classes"
45
53
  s.description = s.summary
46
- s.author = "Sharon Rosner"
47
- s.email = "ciconia@gmail.com"
54
+ s.author = "Jeremy Evans"
55
+ s.email = "code@jeremyevans.net"
48
56
  s.homepage = "http://sequel.rubyforge.org"
49
57
  s.required_ruby_version = ">= 1.8.4"
50
58
 
51
- s.has_rdoc = true
52
- s.extra_rdoc_files = ["README", "COPYING"]
59
+ case RUBY_PLATFORM
60
+ when /java/
61
+ s.platform = "jruby"
62
+ else
63
+ s.platform = Gem::Platform::RUBY
64
+ end
53
65
 
54
- s.add_dependency("sequel_core")
55
- s.add_dependency("sequel_model")
66
+ s.add_dependency("assistance", '>= 0.1.2')
67
+ s.add_dependency("sequel_core", '= 1.4.0')
56
68
 
57
- s.files = %w(Rakefile) + Dir.glob("{lib}/**/*")
69
+ s.files = %w(COPYING README Rakefile) + Dir.glob("{doc,spec,lib}/**/*")
70
+
58
71
  s.require_path = "lib"
59
72
  end
60
73
 
@@ -80,16 +93,56 @@ task :uninstall => [:clean] do
80
93
  sh %{sudo gem uninstall #{NAME}}
81
94
  end
82
95
 
83
- task :tag do
84
- cwd = FileUtils.pwd
85
- sh %{cd ../.. && svn copy #{cwd} tags/#{NAME}-#{VERS} && svn commit -m "#{NAME}-#{VERS} tag." tags}
86
- end
87
-
88
96
  ##############################################################################
89
- # gem release
97
+ # gem and rdoc release
90
98
  ##############################################################################
91
99
  task :release => [:package] do
92
100
  sh %{rubyforge login}
93
101
  sh %{rubyforge add_release sequel #{NAME} #{VERS} pkg/#{NAME}-#{VERS}.tgz}
94
102
  sh %{rubyforge add_file sequel #{NAME} #{VERS} pkg/#{NAME}-#{VERS}.gem}
95
103
  end
104
+
105
+ ##############################################################################
106
+ # specs
107
+ ##############################################################################
108
+ require "spec/rake/spectask"
109
+ sqcdir = File.join(File.dirname(File.dirname(__FILE__)), 'sequel_core', 'lib')
110
+ fixRUBYLIB = Proc.new{ENV['RUBYLIB'] ? (ENV['RUBYLIB'] += ":#{sqcdir}") : (ENV['RUBYLIB'] = sqcdir)}
111
+
112
+ desc "Run specs with coverage"
113
+ Spec::Rake::SpecTask.new("spec") do |t|
114
+ fixRUBYLIB.call
115
+ t.spec_files = FileList["spec/**/*_spec.rb"]
116
+ t.spec_opts = File.read("spec/spec.opts").split("\n")
117
+ t.rcov_opts = File.read("spec/rcov.opts").split("\n")
118
+ t.rcov = true
119
+ end
120
+
121
+ desc "Run specs without coverage"
122
+ Spec::Rake::SpecTask.new("spec_no_cov") do |t|
123
+ fixRUBYLIB.call
124
+ t.spec_files = FileList["spec/**/*_spec.rb"]
125
+ t.spec_opts = File.read("spec/spec.opts").split("\n")
126
+ end
127
+
128
+ desc "check documentation coverage"
129
+ task :dcov do
130
+ sh "find lib -name '*.rb' | xargs dcov"
131
+ end
132
+
133
+ ##############################################################################
134
+ # Statistics
135
+ ##############################################################################
136
+
137
+ STATS_DIRECTORIES = [
138
+ %w(Code lib/),
139
+ %w(Spec spec/)
140
+ ].collect { |name, dir| [ name, "./#{dir}" ] }.select { |name, dir| File.directory?(dir) }
141
+
142
+ desc "Report code statistics (KLOCs, etc) from the application"
143
+ task :stats do
144
+ require "extra/stats"
145
+ verbose = true
146
+ CodeStatistics.new(*STATS_DIRECTORIES).to_s
147
+ end
148
+
@@ -1,2 +1 @@
1
- require 'sequel_core'
2
- require 'sequel_model'
1
+ require 'sequel_model'
@@ -0,0 +1,324 @@
1
+ require 'sequel_core'
2
+
3
+ module Sequel
4
+ class Model
5
+ alias_method :model, :class
6
+ end
7
+ end
8
+
9
+ files = %w[
10
+ base hooks record schema associations
11
+ caching plugins validations eager_loading
12
+ ]
13
+ dir = File.join(File.dirname(__FILE__), "sequel_model")
14
+ files.each {|f| require(File.join(dir, f))}
15
+
16
+ module Sequel
17
+ # == Sequel Models
18
+ #
19
+ # Models in Sequel are based on the Active Record pattern described by Martin Fowler (http://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.
20
+ #
21
+ # Model classes are defined as regular Ruby classes:
22
+ #
23
+ # DB = Sequel('sqlite:/blog.db')
24
+ # class Post < Sequel::Model
25
+ # set_dataset DB[:posts]
26
+ # end
27
+ #
28
+ # You can also use the shorthand form:
29
+ #
30
+ # DB = Sequel('sqlite:/blog.db')
31
+ # class Post < Sequel::Model
32
+ # end
33
+ #
34
+ # === Model instances
35
+ #
36
+ # 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:
37
+ #
38
+ # post = Post[123]
39
+ #
40
+ # The Model#pk method is used to retrieve the record's primary key value:
41
+ #
42
+ # post.pk #=> 123
43
+ #
44
+ # Sequel models allow you to use any column as a primary key, and even composite keys made from multiple columns:
45
+ #
46
+ # class Post < Sequel::Model
47
+ # set_primary_key [:category, :title]
48
+ # end
49
+ #
50
+ # post = Post['ruby', 'hello world']
51
+ # post.pk #=> ['ruby', 'hello world']
52
+ #
53
+ # You can also define a model class that does not have a primary key, but then you lose the ability to update records.
54
+ #
55
+ # A model instance can also be fetched by specifying a condition:
56
+ #
57
+ # post = Post[:title => 'hello world']
58
+ # post = Post.find {:stamp < 10.days.ago}
59
+ #
60
+ # === Iterating over records
61
+ #
62
+ # 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.:
63
+ #
64
+ # Post.filter(:category => 'ruby').each {|post| p post}
65
+ #
66
+ # You can also manipulate the records in the dataset:
67
+ #
68
+ # Post.filter {:stamp < 7.days.ago}.delete
69
+ # Post.filter {:title =~ /ruby/}.update(:category => 'ruby')
70
+ #
71
+ # === Accessing record values
72
+ #
73
+ # A model instances stores its values as a hash:
74
+ #
75
+ # post.values #=> {:id => 123, :category => 'ruby', :title => 'hello world'}
76
+ #
77
+ # You can read the record values as object attributes:
78
+ #
79
+ # post.id #=> 123
80
+ # post.title #=> 'hello world'
81
+ #
82
+ # You can also change record values:
83
+ #
84
+ # post.title = 'hey there'
85
+ # post.save
86
+ #
87
+ # Another way to change values by using the #set method:
88
+ #
89
+ # post.set(:title => 'hey there')
90
+ #
91
+ # === Creating new records
92
+ #
93
+ # New records can be created by calling Model.create:
94
+ #
95
+ # post = Post.create(:title => 'hello world')
96
+ #
97
+ # Another way is to construct a new instance and save it:
98
+ #
99
+ # post = Post.new
100
+ # post.title = 'hello world'
101
+ # post.save
102
+ #
103
+ # You can also supply a block to Model.new and Model.create:
104
+ #
105
+ # post = Post.create {|p| p.title = 'hello world'}
106
+ #
107
+ # post = Post.new do |p|
108
+ # p.title = 'hello world'
109
+ # p.save
110
+ # end
111
+ #
112
+ # === Hooks
113
+ #
114
+ # 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.
115
+ #
116
+ # Hooks are defined by supplying a block:
117
+ #
118
+ # class Post < Sequel::Model
119
+ # after_create do
120
+ # set(:created_at => Time.now)
121
+ # end
122
+ #
123
+ # after_destroy do
124
+ # author.update_post_count
125
+ # end
126
+ # end
127
+ #
128
+ # === Deleting records
129
+ #
130
+ # 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:
131
+ #
132
+ # post.delete #=> bypasses hooks
133
+ # post.destroy #=> runs hooks
134
+ #
135
+ # 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:
136
+ #
137
+ # Post.filter(:category => 32).delete #=> bypasses hooks
138
+ # Post.filter(:category => 32).destroy #=> runs hooks
139
+ #
140
+ # Please note that if Model.destroy is called, each record is deleted
141
+ # separately, but Model.delete deletes all relevant records with a single
142
+ # SQL statement.
143
+ #
144
+ # === Associations
145
+ #
146
+ # Sequel provides macros for the three most common types of associations:
147
+ # many_to_one, one_to_many and many_to_many (equivalent to ActiveRecord's
148
+ # belongs_to, has_many and has_and_belongs_to_many).
149
+ #
150
+ # Associations are defined in similar fashion to ActiveRecord:
151
+ #
152
+ # class Post < Sequel::Model
153
+ # belongs_to :author
154
+ # end
155
+ #
156
+ # class Author < Sequel::Model
157
+ # has_many :posts
158
+ # end
159
+ #
160
+ # Another way to define an association in a Sequel model is as a regular
161
+ # instance method:
162
+ #
163
+ # class Post < Sequel::Model
164
+ # def author; Author[author_id]; end
165
+ # end
166
+ #
167
+ # class Author < Sequel::Model
168
+ # def posts; Post.filter(:author_id => pk); end
169
+ # end
170
+ #
171
+ # === Caching model instances with memcached
172
+ #
173
+ # 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:
174
+ #
175
+ # require 'memcache'
176
+ # CACHE = MemCache.new 'localhost:11211', :namespace => 'blog'
177
+ #
178
+ # class Author < Sequel::Model
179
+ # set_cache CACHE, :ttl => 3600
180
+ # end
181
+ #
182
+ # Author[333] # database hit
183
+ # Author[333] # cache hit
184
+ #
185
+ # === Extending the underlying dataset
186
+ #
187
+ # 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:
188
+ #
189
+ # class Post < Sequel::Model
190
+ # def self.old_posts
191
+ # filter {:stamp < 30.days.ago}
192
+ # end
193
+ #
194
+ # def self.clean_old_posts
195
+ # old_posts.delete
196
+ # end
197
+ # end
198
+ #
199
+ # You can also implement table-wide logic by defining methods on the dataset:
200
+ #
201
+ # class Post < Sequel::Model
202
+ # def dataset.old_posts
203
+ # filter {:stamp < 30.days.ago}
204
+ # end
205
+ #
206
+ # def dataset.clean_old_posts
207
+ # old_posts.delete
208
+ # end
209
+ # end
210
+ #
211
+ # 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:
212
+ #
213
+ # Post.filter(:category => 'ruby').clean_old_posts
214
+ #
215
+ # Sequel models also provide a short hand notation for filters:
216
+ #
217
+ # class Post < Sequel::Model
218
+ # subset(:old_posts) {:stamp < 30.days.ago}
219
+ # subset :invisible, :visible => false
220
+ # end
221
+ #
222
+ # === Defining the underlying schema
223
+ #
224
+ # 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:
225
+ #
226
+ # class Post < Sequel::Model
227
+ # set_schema do
228
+ # primary_key :id
229
+ # text :title
230
+ # text :category
231
+ # foreign_key :author_id, :table => :authors
232
+ # end
233
+ # end
234
+ #
235
+ # You can then create the underlying table, drop it, or recreate it:
236
+ #
237
+ # Post.table_exists?
238
+ # Post.create_table
239
+ # Post.drop_table
240
+ # Post.create_table! # drops the table if it exists and then recreates it
241
+ #
242
+ class Model
243
+ extend Associations
244
+ # Returns a string representation of the model instance including
245
+ # the class name and values.
246
+ def inspect
247
+ "#<%s @values=%s>" % [self.class.name, @values.inspect]
248
+ end
249
+
250
+ # Defines a method that returns a filtered dataset.
251
+ def self.subset(name, *args, &block)
252
+ dataset.meta_def(name) {filter(*args, &block)}
253
+ end
254
+
255
+ # Finds a single record according to the supplied filter, e.g.:
256
+ #
257
+ # Ticket.find :author => 'Sharon' # => record
258
+ # Ticket.find {:price == 17} # => Dataset
259
+ #
260
+ def self.find(*args, &block)
261
+ dataset.filter(*args, &block).first
262
+ end
263
+
264
+ # TODO: doc
265
+ def self.[](*args)
266
+ args = args.first if (args.size == 1)
267
+ if args === true || args === false
268
+ raise Error::InvalidFilter, "Did you mean to supply a hash?"
269
+ end
270
+ dataset[(Hash === args) ? args : primary_key_hash(args)]
271
+ end
272
+
273
+ # TODO: doc
274
+ def self.fetch(*args)
275
+ db.fetch(*args).set_model(self)
276
+ end
277
+
278
+ # Like find but invokes create with given conditions when record does not
279
+ # exists.
280
+ def self.find_or_create(cond)
281
+ find(cond) || create(cond)
282
+ end
283
+
284
+ ############################################################################
285
+
286
+ # Deletes all records in the model's table.
287
+ def self.delete_all
288
+ dataset.delete
289
+ end
290
+
291
+ # Like delete_all, but invokes before_destroy and after_destroy hooks if used.
292
+ def self.destroy_all
293
+ dataset.destroy
294
+ end
295
+
296
+ def self.is_dataset_magic_method?(m)
297
+ method_name = m.to_s
298
+ Sequel::Dataset::MAGIC_METHODS.each_key do |r|
299
+ return true if method_name =~ r
300
+ end
301
+ false
302
+ end
303
+
304
+ def self.method_missing(m, *args, &block) #:nodoc:
305
+ Thread.exclusive do
306
+ if dataset.respond_to?(m) || is_dataset_magic_method?(m)
307
+ instance_eval("def #{m}(*args, &block); dataset.#{m}(*args, &block); end")
308
+ end
309
+ end
310
+ respond_to?(m) ? send(m, *args, &block) : super(m, *args)
311
+ end
312
+
313
+ # TODO: Comprehensive description goes here!
314
+ def self.join(*args)
315
+ table_name = dataset.opts[:from].first
316
+ dataset.join(*args).select(table_name.to_sym.ALL)
317
+ end
318
+
319
+ # Returns an array containing all of the models records.
320
+ def self.all
321
+ dataset.all
322
+ end
323
+ end
324
+ end