sequel_model 0.5.0.2 → 3.8.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile DELETED
@@ -1,152 +0,0 @@
1
- require "rake"
2
- require "rake/clean"
3
- require "rake/gempackagetask"
4
- require "rake/rdoctask"
5
- require "fileutils"
6
- include FileUtils
7
-
8
- ##############################################################################
9
- # Configuration
10
- ##############################################################################
11
- NAME = "sequel_model"
12
- VERS = "0.5.0.2"
13
- CLEAN.include ["**/.*.sw?", "pkg/*", ".config", "doc/*", "coverage/*"]
14
- RDOC_OPTS = [
15
- "--quiet",
16
- "--title", "Sequel: The Database Toolkit for Ruby",
17
- "--opname", "index.html",
18
- "--line-numbers",
19
- "--main", "README",
20
- "--inline-source"
21
- ]
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
-
36
- ##############################################################################
37
- # Gem packaging
38
- ##############################################################################
39
- desc "Packages up Sequel."
40
- task :default => [:package]
41
- task :package => [:clean]
42
-
43
- spec = Gem::Specification.new do |s|
44
- s.name = NAME
45
- s.rubyforge_project = 'sequel'
46
- s.version = VERS
47
- s.platform = Gem::Platform::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 = "Model classes for Sequel"
53
- s.description = s.summary
54
- s.author = "Sharon Rosner"
55
- s.email = "ciconia@gmail.com"
56
- s.homepage = "http://sequel.rubyforge.org"
57
- s.required_ruby_version = ">= 1.8.4"
58
-
59
- case RUBY_PLATFORM
60
- when /java/
61
- s.platform = "jruby"
62
- else
63
- s.platform = Gem::Platform::RUBY
64
- end
65
-
66
- s.add_dependency("assistance", '>= 0.1.2')
67
- s.add_dependency("sequel_core", '>= 1.0')
68
-
69
- s.files = %w(COPYING README Rakefile) + Dir.glob("{doc,spec,lib}/**/*")
70
-
71
- s.require_path = "lib"
72
- end
73
-
74
- Rake::GemPackageTask.new(spec) do |p|
75
- p.need_tar = true
76
- p.gem_spec = spec
77
- end
78
-
79
- ##############################################################################
80
- # installation & removal
81
- ##############################################################################
82
- task :install do
83
- sh %{rake package}
84
- sh %{sudo gem install pkg/#{NAME}-#{VERS}}
85
- end
86
-
87
- task :install_no_docs do
88
- sh %{rake package}
89
- sh %{sudo gem install pkg/#{NAME}-#{VERS} --no-rdoc --no-ri}
90
- end
91
-
92
- task :uninstall => [:clean] do
93
- sh %{sudo gem uninstall #{NAME}}
94
- end
95
-
96
- task :tag do
97
- cwd = FileUtils.pwd
98
- sh %{rm -rf doc/*}
99
- sh %{rm -rf pkg/*}
100
- sh %{rm -rf coverage/*}
101
- sh %{cd ../.. && svn copy #{cwd} tags/#{NAME}-#{VERS} && svn commit -m "#{NAME}-#{VERS} tag." tags}
102
- end
103
-
104
- ##############################################################################
105
- # gem and rdoc release
106
- ##############################################################################
107
- task :release => [:package] do
108
- sh %{rubyforge login}
109
- sh %{rubyforge add_release sequel #{NAME} #{VERS} pkg/#{NAME}-#{VERS}.tgz}
110
- sh %{rubyforge add_file sequel #{NAME} #{VERS} pkg/#{NAME}-#{VERS}.gem}
111
- end
112
-
113
- ##############################################################################
114
- # specs
115
- ##############################################################################
116
- require "spec/rake/spectask"
117
-
118
- desc "Run specs with coverage"
119
- Spec::Rake::SpecTask.new("spec") do |t|
120
- t.spec_files = FileList["spec/**/*_spec.rb"]
121
- t.spec_opts = File.read("spec/spec.opts").split("\n")
122
- t.rcov_opts = File.read("spec/rcov.opts").split("\n")
123
- t.rcov = true
124
- end
125
-
126
- desc "Run specs without coverage"
127
- Spec::Rake::SpecTask.new("spec_no_cov") do |t|
128
- t.spec_files = FileList["spec/**/*_spec.rb"]
129
- t.spec_opts = File.read("spec/spec.opts").split("\n")
130
- end
131
-
132
- desc "check documentation coverage"
133
- task :dcov do
134
- sh "find lib -name '*.rb' | xargs dcov"
135
- end
136
-
137
- ##############################################################################
138
- # Statistics
139
- ##############################################################################
140
-
141
- STATS_DIRECTORIES = [
142
- %w(Code lib/),
143
- %w(Spec spec/)
144
- ].collect { |name, dir| [ name, "./#{dir}" ] }.select { |name, dir| File.directory?(dir) }
145
-
146
- desc "Report code statistics (KLOCs, etc) from the application"
147
- task :stats do
148
- require "extra/stats"
149
- verbose = true
150
- CodeStatistics.new(*STATS_DIRECTORIES).to_s
151
- end
152
-
@@ -1,323 +0,0 @@
1
- module Sequel
2
- class Model
3
- alias_method :model, :class
4
- end
5
- end
6
-
7
- # TODO: add relationships when complete:
8
- files = %w[
9
- base hooks record schema associations
10
- caching plugins validations
11
- ]
12
- dir = File.join(File.dirname(__FILE__), "sequel_model")
13
- files.each {|f| require(File.join(dir, f))}
14
-
15
- module Sequel
16
- # == Sequel Models
17
- #
18
- # 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.
19
- #
20
- # Model classes are defined as regular Ruby classes:
21
- #
22
- # DB = Sequel('sqlite:/blog.db')
23
- # class Post < Sequel::Model
24
- # set_dataset DB[:posts]
25
- # end
26
- #
27
- # You can also use the shorthand form:
28
- #
29
- # DB = Sequel('sqlite:/blog.db')
30
- # class Post < Sequel::Model
31
- # end
32
- #
33
- # === Model instances
34
- #
35
- # 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:
36
- #
37
- # post = Post[123]
38
- #
39
- # The Model#pk method is used to retrieve the record's primary key value:
40
- #
41
- # post.pk #=> 123
42
- #
43
- # Sequel models allow you to use any column as a primary key, and even composite keys made from multiple columns:
44
- #
45
- # class Post < Sequel::Model
46
- # set_primary_key [:category, :title]
47
- # end
48
- #
49
- # post = Post['ruby', 'hello world']
50
- # post.pk #=> ['ruby', 'hello world']
51
- #
52
- # You can also define a model class that does not have a primary key, but then you lose the ability to update records.
53
- #
54
- # A model instance can also be fetched by specifying a condition:
55
- #
56
- # post = Post[:title => 'hello world']
57
- # post = Post.find {:stamp < 10.days.ago}
58
- #
59
- # === Iterating over records
60
- #
61
- # 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.:
62
- #
63
- # Post.filter(:category => 'ruby').each {|post| p post}
64
- #
65
- # You can also manipulate the records in the dataset:
66
- #
67
- # Post.filter {:stamp < 7.days.ago}.delete
68
- # Post.filter {:title =~ /ruby/}.update(:category => 'ruby')
69
- #
70
- # === Accessing record values
71
- #
72
- # A model instances stores its values as a hash:
73
- #
74
- # post.values #=> {:id => 123, :category => 'ruby', :title => 'hello world'}
75
- #
76
- # You can read the record values as object attributes:
77
- #
78
- # post.id #=> 123
79
- # post.title #=> 'hello world'
80
- #
81
- # You can also change record values:
82
- #
83
- # post.title = 'hey there'
84
- # post.save
85
- #
86
- # Another way to change values by using the #set method:
87
- #
88
- # post.set(:title => 'hey there')
89
- #
90
- # === Creating new records
91
- #
92
- # New records can be created by calling Model.create:
93
- #
94
- # post = Post.create(:title => 'hello world')
95
- #
96
- # Another way is to construct a new instance and save it:
97
- #
98
- # post = Post.new
99
- # post.title = 'hello world'
100
- # post.save
101
- #
102
- # You can also supply a block to Model.new and Model.create:
103
- #
104
- # post = Post.create {|p| p.title = 'hello world'}
105
- #
106
- # post = Post.new do |p|
107
- # p.title = 'hello world'
108
- # p.save
109
- # end
110
- #
111
- # === Hooks
112
- #
113
- # 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.
114
- #
115
- # Hooks are defined by supplying a block:
116
- #
117
- # class Post < Sequel::Model
118
- # after_create do
119
- # set(:created_at => Time.now)
120
- # end
121
- #
122
- # after_destroy do
123
- # author.update_post_count
124
- # end
125
- # end
126
- #
127
- # === Deleting records
128
- #
129
- # 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:
130
- #
131
- # post.delete #=> bypasses hooks
132
- # post.destroy #=> runs hooks
133
- #
134
- # 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:
135
- #
136
- # Post.filter(:category => 32).delete #=> bypasses hooks
137
- # Post.filter(:category => 32).destroy #=> runs hooks
138
- #
139
- # Please note that if Model.destroy is called, each record is deleted
140
- # separately, but Model.delete deletes all relevant records with a single
141
- # SQL statement.
142
- #
143
- # === Associations
144
- #
145
- # Sequel provides macros for the three most common types of associations:
146
- # many_to_one, one_to_many and many_to_many (equivalent to ActiveRecord's
147
- # belongs_to, has_many and has_and_belongs_to_many).
148
- #
149
- # Associations are defined in similar fashion to ActiveRecord:
150
- #
151
- # class Post < Sequel::Model
152
- # belongs_to :author
153
- # end
154
- #
155
- # class Author < Sequel::Model
156
- # has_many :posts
157
- # end
158
- #
159
- # Another way to define an association in a Sequel model is as a regular
160
- # instance method:
161
- #
162
- # class Post < Sequel::Model
163
- # def author; Author[author_id]; end
164
- # end
165
- #
166
- # class Author < Sequel::Model
167
- # def posts; Post.filter(:author_id => pk); end
168
- # end
169
- #
170
- # === Caching model instances with memcached
171
- #
172
- # 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:
173
- #
174
- # require 'memcache'
175
- # CACHE = MemCache.new 'localhost:11211', :namespace => 'blog'
176
- #
177
- # class Author < Sequel::Model
178
- # set_cache CACHE, :ttl => 3600
179
- # end
180
- #
181
- # Author[333] # database hit
182
- # Author[333] # cache hit
183
- #
184
- # === Extending the underlying dataset
185
- #
186
- # 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:
187
- #
188
- # class Post < Sequel::Model
189
- # def self.old_posts
190
- # filter {:stamp < 30.days.ago}
191
- # end
192
- #
193
- # def self.clean_old_posts
194
- # old_posts.delete
195
- # end
196
- # end
197
- #
198
- # You can also implement table-wide logic by defining methods on the dataset:
199
- #
200
- # class Post < Sequel::Model
201
- # def dataset.old_posts
202
- # filter {:stamp < 30.days.ago}
203
- # end
204
- #
205
- # def dataset.clean_old_posts
206
- # old_posts.delete
207
- # end
208
- # end
209
- #
210
- # 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:
211
- #
212
- # Post.filter(:category => 'ruby').clean_old_posts
213
- #
214
- # Sequel models also provide a short hand notation for filters:
215
- #
216
- # class Post < Sequel::Model
217
- # subset(:old_posts) {:stamp < 30.days.ago}
218
- # subset :invisible, :visible => false
219
- # end
220
- #
221
- # === Defining the underlying schema
222
- #
223
- # 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:
224
- #
225
- # class Post < Sequel::Model
226
- # set_schema do
227
- # primary_key :id
228
- # text :title
229
- # text :category
230
- # foreign_key :author_id, :table => :authors
231
- # end
232
- # end
233
- #
234
- # You can then create the underlying table, drop it, or recreate it:
235
- #
236
- # Post.table_exists?
237
- # Post.create_table
238
- # Post.drop_table
239
- # Post.create_table! # drops the table if it exists and then recreates it
240
- #
241
- class Model
242
- extend Associations
243
- # Returns a string representation of the model instance including
244
- # the class name and values.
245
- def inspect
246
- "#<%s @values=%s>" % [self.class.name, @values.inspect]
247
- end
248
-
249
- # Defines a method that returns a filtered dataset.
250
- def self.subset(name, *args, &block)
251
- dataset.meta_def(name) {filter(*args, &block)}
252
- end
253
-
254
- # Finds a single record according to the supplied filter, e.g.:
255
- #
256
- # Ticket.find :author => 'Sharon' # => record
257
- # Ticket.find {:price == 17} # => Dataset
258
- #
259
- def self.find(*args, &block)
260
- dataset.filter(*args, &block).first
261
- end
262
-
263
- # TODO: doc
264
- def self.[](*args)
265
- args = args.first if (args.size == 1)
266
- if args === true || args === false
267
- raise Error::InvalidFilter, "Did you mean to supply a hash?"
268
- end
269
- dataset[(Hash === args) ? args : primary_key_hash(args)]
270
- end
271
-
272
- # TODO: doc
273
- def self.fetch(*args)
274
- db.fetch(*args).set_model(self)
275
- end
276
-
277
- # Like find but invokes create with given conditions when record does not
278
- # exists.
279
- def self.find_or_create(cond)
280
- find(cond) || create(cond)
281
- end
282
-
283
- ############################################################################
284
-
285
- # Deletes all records in the model's table.
286
- def self.delete_all
287
- dataset.delete
288
- end
289
-
290
- # Like delete_all, but invokes before_destroy and after_destroy hooks if used.
291
- def self.destroy_all
292
- dataset.destroy
293
- end
294
-
295
- def self.is_dataset_magic_method?(m)
296
- method_name = m.to_s
297
- Sequel::Dataset::MAGIC_METHODS.each_key do |r|
298
- return true if method_name =~ r
299
- end
300
- false
301
- end
302
-
303
- def self.method_missing(m, *args, &block) #:nodoc:
304
- Thread.exclusive do
305
- if dataset.respond_to?(m) || is_dataset_magic_method?(m)
306
- instance_eval("def #{m}(*args, &block); dataset.#{m}(*args, &block); end")
307
- end
308
- end
309
- respond_to?(m) ? send(m, *args, &block) : super(m, *args)
310
- end
311
-
312
- # TODO: Comprehensive description goes here!
313
- def self.join(*args)
314
- table_name = dataset.opts[:from].first
315
- dataset.join(*args).select(table_name.to_sym.ALL)
316
- end
317
-
318
- # Returns an array containing all of the models records.
319
- def self.all
320
- dataset.all
321
- end
322
- end
323
- end