composite_primary_keys 0.1.3
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.
- data/CHANGELOG +8 -0
- data/README +0 -0
- data/Rakefile +177 -0
- data/install.rb +30 -0
- data/lib/composite_primary_keys.rb +41 -0
- data/lib/composite_primary_keys/base.rb +219 -0
- data/lib/composite_primary_keys/fixtures.rb +600 -0
- data/lib/composite_primary_keys/version.rb +9 -0
- data/test/abstract_unit.rb +69 -0
- data/test/connections/native_mysql/connection.rb +13 -0
- data/test/dummy_test.rb +8 -0
- data/test/find_test.rb +88 -0
- data/test/fixtures/db_definitions/mysql.drop.sql +30 -0
- data/test/fixtures/db_definitions/mysql.sql +16 -0
- data/test/fixtures/db_definitions/mysql2.drop.sql +2 -0
- data/test/fixtures/db_definitions/mysql2.sql +5 -0
- data/test/fixtures/reference_code.rb +7 -0
- data/test/fixtures/reference_codes.yml +28 -0
- data/test/fixtures/reference_type.rb +7 -0
- data/test/fixtures/reference_types.yml +9 -0
- metadata +78 -0
data/CHANGELOG
ADDED
|
@@ -0,0 +1,8 @@
|
|
|
1
|
+
* 0.0.1 * Initial version
|
|
2
|
+
- set_primary_keys(*keys) is the activation class method to transform an ActiveRecord into
|
|
3
|
+
a composite primary key AR
|
|
4
|
+
- find(*ids) supports the passing of
|
|
5
|
+
- id sets: Foo.find(2,1),
|
|
6
|
+
- lists of id sets: Foo.find([2,1], [7,3], [8,12]),
|
|
7
|
+
- and even stringified versions of the above:
|
|
8
|
+
- Foo.find '2,1' or Foo.find '2,1;7,3'
|
data/README
ADDED
|
File without changes
|
data/Rakefile
ADDED
|
@@ -0,0 +1,177 @@
|
|
|
1
|
+
require 'rubygems'
|
|
2
|
+
require 'rake'
|
|
3
|
+
require 'rake/testtask'
|
|
4
|
+
require 'rake/rdoctask'
|
|
5
|
+
require 'rake/packagetask'
|
|
6
|
+
require 'rake/gempackagetask'
|
|
7
|
+
require 'rake/contrib/rubyforgepublisher'
|
|
8
|
+
require File.join(File.dirname(__FILE__), 'lib', 'composite_primary_keys', 'version')
|
|
9
|
+
|
|
10
|
+
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
|
11
|
+
PKG_NAME = 'composite_primary_keys'
|
|
12
|
+
PKG_VERSION = CompositePrimayKeys::VERSION::STRING + PKG_BUILD
|
|
13
|
+
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
|
14
|
+
|
|
15
|
+
RELEASE_NAME = "REL #{PKG_VERSION}"
|
|
16
|
+
|
|
17
|
+
RUBY_FORGE_PROJECT = "compositekeys"
|
|
18
|
+
RUBY_FORGE_USER = "nicwilliams"
|
|
19
|
+
|
|
20
|
+
PKG_FILES = FileList[
|
|
21
|
+
"lib/**/*", "test/**/*", "examples/**/*", "doc/**/*", "[A-Z]*", "install.rb", "Rakefile"
|
|
22
|
+
].exclude(/\bCVS\b|~$/)
|
|
23
|
+
|
|
24
|
+
|
|
25
|
+
desc "Default Task"
|
|
26
|
+
task :default => [ :test_mysql ] # UNTESTED =, :test_sqlite, :test_postgresql ]
|
|
27
|
+
|
|
28
|
+
# Run the unit tests
|
|
29
|
+
|
|
30
|
+
for adapter in %w( mysql ) # UNTESTED - postgresql sqlite sqlite3 firebird sqlserver sqlserver_odbc db2 oracle sybase openbase )
|
|
31
|
+
Rake::TestTask.new("test_#{adapter}") { |t|
|
|
32
|
+
t.libs << "test" << "test/connections/native_#{adapter}"
|
|
33
|
+
t.pattern = "test/*_test{,_#{adapter}}.rb"
|
|
34
|
+
t.verbose = true
|
|
35
|
+
}
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
SCHEMA_PATH = File.join(File.dirname(__FILE__), *%w(test fixtures db_definitions))
|
|
39
|
+
|
|
40
|
+
desc 'Build the MySQL test databases'
|
|
41
|
+
task :build_mysql_databases => [:drop_mysql_databases] do
|
|
42
|
+
puts File.join(SCHEMA_PATH, 'mysql.sql')
|
|
43
|
+
%x( mysqladmin -u root create activerecord_unittest )
|
|
44
|
+
#%x( mysqladmin -u root create activerecord_unittest2 )
|
|
45
|
+
%x( mysql -u root activerecord_unittest < #{File.join(SCHEMA_PATH, 'mysql.sql')} )
|
|
46
|
+
#%x( mysql -u root activerecord_unittest < #{File.join(SCHEMA_PATH, 'mysql2.sql')} )
|
|
47
|
+
end
|
|
48
|
+
|
|
49
|
+
desc 'Drop the MySQL test databases'
|
|
50
|
+
task :drop_mysql_databases do
|
|
51
|
+
%x( mysqladmin -u root -f drop activerecord_unittest )
|
|
52
|
+
#%x( mysqladmin -u root -f drop activerecord_unittest2 )
|
|
53
|
+
end
|
|
54
|
+
|
|
55
|
+
desc 'Rebuild the MySQL test databases'
|
|
56
|
+
task :rebuild_mysql_databases => [:drop_mysql_databases, :build_mysql_databases]
|
|
57
|
+
|
|
58
|
+
desc 'Build the PostgreSQL test databases'
|
|
59
|
+
task :build_postgresql_databases do
|
|
60
|
+
%x( createdb activerecord_unittest )
|
|
61
|
+
%x( createdb activerecord_unittest2 )
|
|
62
|
+
%x( psql activerecord_unittest -f #{File.join(SCHEMA_PATH, 'postgresql.sql')} )
|
|
63
|
+
%x( psql activerecord_unittest2 -f #{File.join(SCHEMA_PATH, 'postgresql2.sql')} )
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
desc 'Drop the PostgreSQL test databases'
|
|
67
|
+
task :drop_postgresql_databases do
|
|
68
|
+
%x( dropdb activerecord_unittest )
|
|
69
|
+
%x( dropdb activerecord_unittest2 )
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
desc 'Rebuild the PostgreSQL test databases'
|
|
73
|
+
task :rebuild_postgresql_databases => [:drop_postgresql_databases, :build_postgresql_databases]
|
|
74
|
+
|
|
75
|
+
# Generate the RDoc documentation
|
|
76
|
+
|
|
77
|
+
Rake::RDocTask.new { |rdoc|
|
|
78
|
+
rdoc.rdoc_dir = 'doc'
|
|
79
|
+
rdoc.title = "Composite Primary Keys -- Composite keys for Active Records/Rails"
|
|
80
|
+
rdoc.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
|
|
81
|
+
rdoc.template = "#{ENV['template']}.rb" if ENV['template']
|
|
82
|
+
rdoc.rdoc_files.include('README', 'CHANGELOG')
|
|
83
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
84
|
+
rdoc.rdoc_files.exclude('lib/active_record/vendor/*')
|
|
85
|
+
rdoc.rdoc_files.include('dev-utils/*.rb')
|
|
86
|
+
}
|
|
87
|
+
|
|
88
|
+
# Enhance rdoc task to copy referenced images also
|
|
89
|
+
task :rdoc do
|
|
90
|
+
FileUtils.mkdir_p "doc/files/examples/"
|
|
91
|
+
end
|
|
92
|
+
|
|
93
|
+
|
|
94
|
+
# Create compressed packages
|
|
95
|
+
|
|
96
|
+
dist_dirs = [ "lib", "test", "examples", "dev-utils" ]
|
|
97
|
+
|
|
98
|
+
spec = Gem::Specification.new do |s|
|
|
99
|
+
s.name = PKG_NAME
|
|
100
|
+
s.version = PKG_VERSION
|
|
101
|
+
s.summary = "Support for composite primary keys in ActiveRecords"
|
|
102
|
+
s.description = %q{ActiveRecords only support a single primary key, preventing their use on legacy databases where tables have primary keys over 2+ columns. This solution allows an ActiveRecord to be extended to support multiple keys using the class method set_primary_keys.}
|
|
103
|
+
|
|
104
|
+
s.files = [ "Rakefile", "install.rb", "README", "CHANGELOG" ]
|
|
105
|
+
dist_dirs.each do |dir|
|
|
106
|
+
s.files = s.files + Dir.glob( "#{dir}/**/*" ).delete_if { |item| item.include?( "\.svn" ) }
|
|
107
|
+
end
|
|
108
|
+
|
|
109
|
+
s.add_dependency('activerecord', '= 1.14.3' + PKG_BUILD)
|
|
110
|
+
|
|
111
|
+
s.require_path = 'lib'
|
|
112
|
+
s.autorequire = 'composite_primary_keys'
|
|
113
|
+
|
|
114
|
+
s.has_rdoc = true
|
|
115
|
+
s.extra_rdoc_files = %w( README )
|
|
116
|
+
s.rdoc_options.concat ['--main', 'README']
|
|
117
|
+
|
|
118
|
+
s.author = "Dr Nic Williams"
|
|
119
|
+
s.email = "drnicwilliams@gmail.com"
|
|
120
|
+
s.homepage = "http://composite_primary_keys.rubyforge.org"
|
|
121
|
+
s.rubyforge_project = "composite_primary_keys"
|
|
122
|
+
end
|
|
123
|
+
|
|
124
|
+
Rake::GemPackageTask.new(spec) do |p|
|
|
125
|
+
p.gem_spec = spec
|
|
126
|
+
p.need_tar = false
|
|
127
|
+
p.need_zip = false
|
|
128
|
+
end
|
|
129
|
+
|
|
130
|
+
task :lines do
|
|
131
|
+
lines, codelines, total_lines, total_codelines = 0, 0, 0, 0
|
|
132
|
+
|
|
133
|
+
for file_name in FileList["lib/composite_primary_keys/**/*.rb"]
|
|
134
|
+
next if file_name =~ /vendor/
|
|
135
|
+
f = File.open(file_name)
|
|
136
|
+
|
|
137
|
+
while line = f.gets
|
|
138
|
+
lines += 1
|
|
139
|
+
next if line =~ /^\s*$/
|
|
140
|
+
next if line =~ /^\s*#/
|
|
141
|
+
codelines += 1
|
|
142
|
+
end
|
|
143
|
+
puts "L: #{sprintf("%4d", lines)}, LOC #{sprintf("%4d", codelines)} | #{file_name}"
|
|
144
|
+
|
|
145
|
+
total_lines += lines
|
|
146
|
+
total_codelines += codelines
|
|
147
|
+
|
|
148
|
+
lines, codelines = 0, 0
|
|
149
|
+
end
|
|
150
|
+
|
|
151
|
+
puts "Total: Lines #{total_lines}, LOC #{total_codelines}"
|
|
152
|
+
end
|
|
153
|
+
|
|
154
|
+
|
|
155
|
+
# Publishing ------------------------------------------------------
|
|
156
|
+
|
|
157
|
+
desc "Publish the beta gem"
|
|
158
|
+
task :pgem => [:package] do
|
|
159
|
+
Rake::SshFilePublisher.new("drnicwilliams@gmail.com", "public_html/gems/gems", "pkg", "#{PKG_FILE_NAME}.gem").upload
|
|
160
|
+
`ssh drnicwilliams@gmail.com './gemupdate.sh'`
|
|
161
|
+
end
|
|
162
|
+
|
|
163
|
+
desc "Publish the API documentation"
|
|
164
|
+
task :pdoc => [:rdoc] do
|
|
165
|
+
Rake::SshDirPublisher.new("drnicwilliams@gmail.com", "public_html/ar", "doc").upload
|
|
166
|
+
end
|
|
167
|
+
|
|
168
|
+
desc "Publish the release files to RubyForge."
|
|
169
|
+
task :release => [ :package ] do
|
|
170
|
+
`ruby scripts/rubyforge login`
|
|
171
|
+
|
|
172
|
+
for ext in %w( gem tgz zip )
|
|
173
|
+
release_command = "ruby scripts/rubyforge add_release #{PKG_NAME} #{PKG_NAME} 'REL #{PKG_VERSION}' pkg/#{PKG_NAME}-#{PKG_VERSION}.#{ext}"
|
|
174
|
+
puts release_command
|
|
175
|
+
system(release_command)
|
|
176
|
+
end
|
|
177
|
+
end
|
data/install.rb
ADDED
|
@@ -0,0 +1,30 @@
|
|
|
1
|
+
require 'rbconfig'
|
|
2
|
+
require 'find'
|
|
3
|
+
require 'ftools'
|
|
4
|
+
|
|
5
|
+
include Config
|
|
6
|
+
|
|
7
|
+
# this was adapted from rdoc's install.rb by ways of Log4r
|
|
8
|
+
|
|
9
|
+
$sitedir = CONFIG["sitelibdir"]
|
|
10
|
+
unless $sitedir
|
|
11
|
+
version = CONFIG["MAJOR"] + "." + CONFIG["MINOR"]
|
|
12
|
+
$libdir = File.join(CONFIG["libdir"], "ruby", version)
|
|
13
|
+
$sitedir = $:.find {|x| x =~ /site_ruby/ }
|
|
14
|
+
if !$sitedir
|
|
15
|
+
$sitedir = File.join($libdir, "site_ruby")
|
|
16
|
+
elsif $sitedir !~ Regexp.quote(version)
|
|
17
|
+
$sitedir = File.join($sitedir, version)
|
|
18
|
+
end
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
# the acual gruntwork
|
|
22
|
+
Dir.chdir("lib")
|
|
23
|
+
|
|
24
|
+
Find.find("composite_primary_keys", "composite_primary_keys.rb") { |f|
|
|
25
|
+
if f[-3..-1] == ".rb"
|
|
26
|
+
File::install(f, File.join($sitedir, *f.split(/\//)), 0644, true)
|
|
27
|
+
else
|
|
28
|
+
File::makedirs(File.join($sitedir, *f.split(/\//)))
|
|
29
|
+
end
|
|
30
|
+
}
|
|
@@ -0,0 +1,41 @@
|
|
|
1
|
+
#--
|
|
2
|
+
# Copyright (c) 2006 Nic Williams
|
|
3
|
+
#
|
|
4
|
+
# Permission is hereby granted, free of charge, to any person obtaining
|
|
5
|
+
# a copy of this software and associated documentation files (the
|
|
6
|
+
# "Software"), to deal in the Software without restriction, including
|
|
7
|
+
# without limitation the rights to use, copy, modify, merge, publish,
|
|
8
|
+
# distribute, sublicense, and/or sell copies of the Software, and to
|
|
9
|
+
# permit persons to whom the Software is furnished to do so, subject to
|
|
10
|
+
# the following conditions:
|
|
11
|
+
#
|
|
12
|
+
# The above copyright notice and this permission notice shall be
|
|
13
|
+
# included in all copies or substantial portions of the Software.
|
|
14
|
+
#
|
|
15
|
+
# THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
|
16
|
+
# EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
|
17
|
+
# MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
|
18
|
+
# NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
|
19
|
+
# LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
|
20
|
+
# OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
|
21
|
+
# WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
|
22
|
+
#++
|
|
23
|
+
|
|
24
|
+
$:.unshift(File.dirname(__FILE__)) unless
|
|
25
|
+
$:.include?(File.dirname(__FILE__)) || $:.include?(File.expand_path(File.dirname(__FILE__)))
|
|
26
|
+
|
|
27
|
+
unless defined?(ActiveRecord)
|
|
28
|
+
begin
|
|
29
|
+
$:.unshift(File.dirname(__FILE__) + "/../../activerecord/lib")
|
|
30
|
+
require 'active_record'
|
|
31
|
+
rescue LoadError
|
|
32
|
+
require 'rubygems'
|
|
33
|
+
require_gem 'activerecord'
|
|
34
|
+
end
|
|
35
|
+
end
|
|
36
|
+
|
|
37
|
+
require 'composite_primary_keys/base'
|
|
38
|
+
|
|
39
|
+
ActiveRecord::Base.class_eval do
|
|
40
|
+
include CompositePrimayKeys::ActiveRecord::Base
|
|
41
|
+
end
|
|
@@ -0,0 +1,219 @@
|
|
|
1
|
+
module CompositePrimayKeys
|
|
2
|
+
module ActiveRecord #:nodoc:
|
|
3
|
+
module Base #:nodoc:
|
|
4
|
+
|
|
5
|
+
INVALID_FOR_COMPOSITE_KEYS = 'Not appropriate for composite primary keys'
|
|
6
|
+
ID_SEP = ','
|
|
7
|
+
|
|
8
|
+
def self.append_features(base)
|
|
9
|
+
super
|
|
10
|
+
base.extend(ClassMethods)
|
|
11
|
+
end
|
|
12
|
+
|
|
13
|
+
module ClassMethods
|
|
14
|
+
def set_primary_keys(*keys)
|
|
15
|
+
@@primary_keys = []
|
|
16
|
+
cattr_accessor :primary_keys
|
|
17
|
+
self.primary_keys = keys
|
|
18
|
+
|
|
19
|
+
class_eval <<-EOV
|
|
20
|
+
include CompositePrimayKeys::ActiveRecord::Base::InstanceMethods
|
|
21
|
+
extend CompositePrimayKeys::ActiveRecord::Base::CompositeClassMethods
|
|
22
|
+
EOV
|
|
23
|
+
end
|
|
24
|
+
end
|
|
25
|
+
|
|
26
|
+
module InstanceMethods
|
|
27
|
+
|
|
28
|
+
# A model instance's primary keys is always available as model.ids
|
|
29
|
+
# whether you name it the default 'id' or set it to something else.
|
|
30
|
+
def id
|
|
31
|
+
attr_names = self.class.primary_keys
|
|
32
|
+
attr_names.map {|attr_name| read_attribute(attr_name)}
|
|
33
|
+
end
|
|
34
|
+
alias_method :ids, :id
|
|
35
|
+
|
|
36
|
+
#id_to_s([1,2]) -> "1,2"
|
|
37
|
+
#id_to_s([1,2], '-') -> "1-2"
|
|
38
|
+
def id_to_s(ids, id_sep = CompositePrimayKeys::ActiveRecord::Base::ID_SEP)
|
|
39
|
+
ids.map{|id| self.class.sanitize(id)}.join("#{id_sep}")
|
|
40
|
+
end
|
|
41
|
+
|
|
42
|
+
# Enables Active Record objects to be used as URL parameters in Action Pack automatically.
|
|
43
|
+
def to_param
|
|
44
|
+
id_to_s(ids)
|
|
45
|
+
end
|
|
46
|
+
|
|
47
|
+
def id_before_type_cast #:nodoc:
|
|
48
|
+
# TODO
|
|
49
|
+
read_attribute_before_type_cast(self.class.primary_key)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def quoted_id #:nodoc:
|
|
53
|
+
# TODO
|
|
54
|
+
quote(id, column_for_attribute(self.class.primary_key))
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# Sets the primary ID.
|
|
58
|
+
def id=(value)
|
|
59
|
+
ids = id.split(value) if value.is_a?(String)
|
|
60
|
+
unless ids.is_a?(Array) and ids.length == self.class.primary_keys.length
|
|
61
|
+
raise "#{self.class}.id= requires #{self.class.primary_keys.length} ids"
|
|
62
|
+
end
|
|
63
|
+
ids.each {|id| write_attribute(self.class.primary_key , id)}
|
|
64
|
+
end
|
|
65
|
+
|
|
66
|
+
# Define an attribute reader method. Cope with nil column.
|
|
67
|
+
def define_read_method(symbol, attr_name, column)
|
|
68
|
+
cast_code = column.type_cast_code('v') if column
|
|
69
|
+
access_code = cast_code ? "(v=@attributes['#{attr_name}']) && #{cast_code}" : "@attributes['#{attr_name}']"
|
|
70
|
+
|
|
71
|
+
unless self.class.primary_keys.include? attr_name.to_sym
|
|
72
|
+
access_code = access_code.insert(0, "raise NoMethodError, 'missing attribute: #{attr_name}', caller unless @attributes.has_key?('#{attr_name}'); ")
|
|
73
|
+
self.class.read_methods << attr_name
|
|
74
|
+
end
|
|
75
|
+
|
|
76
|
+
evaluate_read_method attr_name, "def #{symbol}; #{access_code}; end"
|
|
77
|
+
end
|
|
78
|
+
|
|
79
|
+
def method_missing(method_id, *args, &block)
|
|
80
|
+
method_name = method_id.to_s
|
|
81
|
+
if @attributes.include?(method_name) or
|
|
82
|
+
(md = /\?$/.match(method_name) and
|
|
83
|
+
@attributes.include?(method_name = md.pre_match))
|
|
84
|
+
define_read_methods if self.class.read_methods.empty? && self.class.generate_read_methods
|
|
85
|
+
md ? query_attribute(method_name) : read_attribute(method_name)
|
|
86
|
+
elsif self.class.primary_keys.include? method_name.to_sym
|
|
87
|
+
get_attr(method_name.to_sym)
|
|
88
|
+
elsif md = /(=|_before_type_cast)$/.match(method_name)
|
|
89
|
+
attribute_name, method_type = md.pre_match, md.to_s
|
|
90
|
+
if @attributes.include?(attribute_name)
|
|
91
|
+
case method_type
|
|
92
|
+
when '='
|
|
93
|
+
write_attribute(attribute_name, args.first)
|
|
94
|
+
when '_before_type_cast'
|
|
95
|
+
read_attribute_before_type_cast(attribute_name)
|
|
96
|
+
end
|
|
97
|
+
else
|
|
98
|
+
super
|
|
99
|
+
end
|
|
100
|
+
else
|
|
101
|
+
super
|
|
102
|
+
end
|
|
103
|
+
end
|
|
104
|
+
end
|
|
105
|
+
|
|
106
|
+
module CompositeClassMethods
|
|
107
|
+
|
|
108
|
+
def primary_keys_to_s(sep = CompositePrimayKeys::ActiveRecord::Base::ID_SEP)
|
|
109
|
+
primary_keys.map(&:to_s).join(sep)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
#ids_to_s([[1,2],[7,3]]) -> "(1,2),(7,3)"
|
|
113
|
+
#ids_to_s([[1,2],[7,3]], ',', ';', '', '') -> "1,2;7,3"
|
|
114
|
+
def ids_to_s(ids, id_sep = CompositePrimayKeys::ActiveRecord::Base::ID_SEP, list_sep = ',', left_bracket = '(', right_bracket = ')')
|
|
115
|
+
"#{left_bracket}#{ids.map{|id| sanitize(id)}.join('#{id_sep}')}#{right_bracket}"
|
|
116
|
+
end
|
|
117
|
+
|
|
118
|
+
# Returns true if the given +ids+ represents the primary keys of a record in the database, false otherwise.
|
|
119
|
+
# Example:
|
|
120
|
+
# Person.exists?(5,7)
|
|
121
|
+
def exists?(ids)
|
|
122
|
+
obj = find(ids) rescue false
|
|
123
|
+
!obj.nil? and obj.is_a?(self)
|
|
124
|
+
end
|
|
125
|
+
|
|
126
|
+
# Deletes the record with the given +ids+ without instantiating an object first, e.g. delete(1,2)
|
|
127
|
+
# If an array of ids is provided (e.g. delete([1,2], [3,4]), all of them
|
|
128
|
+
# are deleted.
|
|
129
|
+
def delete(*ids)
|
|
130
|
+
delete_all([ "(#{primary_keys_to_s}) IN (#{ids_to_s(ids)})" ])
|
|
131
|
+
end
|
|
132
|
+
|
|
133
|
+
# Destroys the record with the given +ids+ by instantiating the object and calling #destroy (all the callbacks are the triggered).
|
|
134
|
+
# If an array of ids is provided, all of them are destroyed.
|
|
135
|
+
def destroy(*ids)
|
|
136
|
+
ids.first.is_a?(Array) ? ids.each { |id_set| destroy(id_set) } : find(ids).destroy
|
|
137
|
+
end
|
|
138
|
+
|
|
139
|
+
# Alias for the composite primary_keys accessor method
|
|
140
|
+
def primary_key
|
|
141
|
+
raise CompositePrimayKeys::ActiveRecord::Base::INVALID_FOR_COMPOSITE_KEYS
|
|
142
|
+
# primary_keys
|
|
143
|
+
# Initially invalidate the method to find places where its used
|
|
144
|
+
end
|
|
145
|
+
|
|
146
|
+
# Returns an array of column objects for the table associated with this class.
|
|
147
|
+
# Each column that matches to one of the primary keys has its
|
|
148
|
+
# primary attribute set to true
|
|
149
|
+
def columns
|
|
150
|
+
unless @columns
|
|
151
|
+
@columns = connection.columns(table_name, "#{name} Columns")
|
|
152
|
+
@columns.each {|column| column.primary = primary_keys.include?(column.name.to_sym)}
|
|
153
|
+
end
|
|
154
|
+
@columns
|
|
155
|
+
end
|
|
156
|
+
|
|
157
|
+
## DEACTIVATED METHODS ##
|
|
158
|
+
public
|
|
159
|
+
# Lazy-set the sequence name to the connection's default. This method
|
|
160
|
+
# is only ever called once since set_sequence_name overrides it.
|
|
161
|
+
def sequence_name #:nodoc:
|
|
162
|
+
raise CompositePrimayKeys::ActiveRecord::Base::INVALID_FOR_COMPOSITE_KEYS
|
|
163
|
+
end
|
|
164
|
+
|
|
165
|
+
def reset_sequence_name #:nodoc:
|
|
166
|
+
raise CompositePrimayKeys::ActiveRecord::Base::INVALID_FOR_COMPOSITE_KEYS
|
|
167
|
+
end
|
|
168
|
+
|
|
169
|
+
def set_primary_key(value = nil, &block)
|
|
170
|
+
raise CompositePrimayKeys::ActiveRecord::Base::INVALID_FOR_COMPOSITE_KEYS
|
|
171
|
+
end
|
|
172
|
+
|
|
173
|
+
private
|
|
174
|
+
def find_one(id, options)
|
|
175
|
+
raise CompositePrimayKeys::ActiveRecord::Base::INVALID_FOR_COMPOSITE_KEYS
|
|
176
|
+
end
|
|
177
|
+
|
|
178
|
+
def find_some(ids, options)
|
|
179
|
+
raise CompositePrimayKeys::ActiveRecord::Base::INVALID_FOR_COMPOSITE_KEYS
|
|
180
|
+
end
|
|
181
|
+
|
|
182
|
+
def find_from_ids(ids, options)
|
|
183
|
+
conditions = " AND (#{sanitize_sql(options[:conditions])})" if options[:conditions]
|
|
184
|
+
# if ids is just a flat list, then its size must = primary_key.length (one id per primary key, in order)
|
|
185
|
+
# if ids is list of lists, then each inner list must follow rule above
|
|
186
|
+
#if ids.first.is_a?(String) - find '2,1' -> find_from_ids ['2,1']
|
|
187
|
+
ids = ids[0].split(';').map {|id_set| id_set.split ','} if ids.first.is_a? String
|
|
188
|
+
ids = [ids] if not ids.first.kind_of?(Array)
|
|
189
|
+
|
|
190
|
+
ids.each do |id_set|
|
|
191
|
+
unless id_set.is_a?(Array)
|
|
192
|
+
raise "Ids must be in an Array, instead received: #{id_set.inspect}"
|
|
193
|
+
end
|
|
194
|
+
unless id_set.length == primary_keys.length
|
|
195
|
+
raise "Incorrect number of primary keys for #{class_name}: #{primary_keys.inspect}"
|
|
196
|
+
end
|
|
197
|
+
end
|
|
198
|
+
|
|
199
|
+
# Let keys = [:a, :b]
|
|
200
|
+
# If ids = [[10, 50], [11, 51]], then :conditions =>
|
|
201
|
+
# "(#{table_name}.a, #{table_name}.b) IN ((10, 50), (11, 51))"
|
|
202
|
+
|
|
203
|
+
keys_sql = primary_keys.map {|key| "#{table_name}.#{key.to_s}"}.join(',')
|
|
204
|
+
ids_sql = ids.map {|id_set| id_set.map {|id| sanitize(id)}.join(',')}.join('),(')
|
|
205
|
+
options.update :conditions => "(#{keys_sql}) IN ((#{ids_sql}))"
|
|
206
|
+
|
|
207
|
+
result = find_every(options)
|
|
208
|
+
|
|
209
|
+
if result.size == ids.size
|
|
210
|
+
ids.size == 1 ? result[0] : result
|
|
211
|
+
else
|
|
212
|
+
raise RecordNotFound, "Couldn't find all #{name.pluralize} with IDs (#{ids_list})#{conditions}"
|
|
213
|
+
end
|
|
214
|
+
end
|
|
215
|
+
|
|
216
|
+
end
|
|
217
|
+
end
|
|
218
|
+
end
|
|
219
|
+
end
|