polymorpheus 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/LICENSE.txt +22 -0
- data/README.md +0 -0
- data/Rakefile +39 -0
- data/lib/polymorpheus/adapter.rb +23 -0
- data/lib/polymorpheus/interface.rb +97 -0
- data/lib/polymorpheus/mysql_adapter.rb +127 -0
- data/lib/polymorpheus/railtie.rb +20 -0
- data/lib/polymorpheus/schema_statements.rb +17 -0
- data/lib/polymorpheus/version.rb +3 -0
- data/lib/polymorpheus.rb +12 -0
- data/polymorpheus.gemspec +18 -0
- data/spec/interface_spec.rb +75 -0
- data/spec/mysql2_adapter_spec.rb +54 -0
- data/spec/spec_helper.rb +21 -0
- data/spec/sql_logger.rb +17 -0
- metadata +73 -0
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
The MIT LICENSE
|
2
|
+
|
3
|
+
Copyright (c) 2011 Wegowise Inc.
|
4
|
+
|
5
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
6
|
+
a copy of this software and associated documentation files (the
|
7
|
+
"Software"), to deal in the Software without restriction, including
|
8
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
9
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
10
|
+
permit persons to whom the Software is furnished to do so, subject to
|
11
|
+
the following conditions:
|
12
|
+
|
13
|
+
The above copyright notice and this permission notice shall be
|
14
|
+
included in all copies or substantial portions of the Software.
|
15
|
+
|
16
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
17
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
18
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
19
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
20
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
21
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
22
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
File without changes
|
data/Rakefile
ADDED
@@ -0,0 +1,39 @@
|
|
1
|
+
require 'rake'
|
2
|
+
require 'fileutils'
|
3
|
+
|
4
|
+
def gemspec_name
|
5
|
+
@gemspec_name ||= Dir['*.gemspec'][0]
|
6
|
+
end
|
7
|
+
|
8
|
+
def gemspec
|
9
|
+
@gemspec ||= eval(File.read(gemspec_name), binding, gemspec_name)
|
10
|
+
end
|
11
|
+
|
12
|
+
desc "Build the gem"
|
13
|
+
task :gem=>:gemspec do
|
14
|
+
sh "gem build #{gemspec_name}"
|
15
|
+
FileUtils.mkdir_p 'pkg'
|
16
|
+
FileUtils.mv "#{gemspec.name}-#{gemspec.version}.gem", 'pkg'
|
17
|
+
end
|
18
|
+
|
19
|
+
desc "Install the gem locally"
|
20
|
+
task :install => :gem do
|
21
|
+
sh %{gem install pkg/#{gemspec.name}-#{gemspec.version}}
|
22
|
+
end
|
23
|
+
|
24
|
+
desc "Generate the gemspec"
|
25
|
+
task :generate do
|
26
|
+
puts gemspec.to_ruby
|
27
|
+
end
|
28
|
+
|
29
|
+
desc "Validate the gemspec"
|
30
|
+
task :gemspec do
|
31
|
+
gemspec.validate
|
32
|
+
end
|
33
|
+
|
34
|
+
desc 'Run tests'
|
35
|
+
task :test do |t|
|
36
|
+
sh 'rspec spec'
|
37
|
+
end
|
38
|
+
|
39
|
+
task :default => :test
|
@@ -0,0 +1,23 @@
|
|
1
|
+
module Polymorpheus
|
2
|
+
class Adapter
|
3
|
+
|
4
|
+
class << self
|
5
|
+
@@registered_adapters = {}
|
6
|
+
|
7
|
+
def register(adapter_name, file_name)
|
8
|
+
@@registered_adapters[adapter_name] = file_name
|
9
|
+
end
|
10
|
+
|
11
|
+
def load!
|
12
|
+
if file = @@registered_adapters[configured_adapter]
|
13
|
+
require file
|
14
|
+
end
|
15
|
+
end
|
16
|
+
|
17
|
+
def configured_adapter
|
18
|
+
ActiveRecord::Base.connection_pool.spec.config[:adapter]
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
end
|
23
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module Polymorpheus
|
2
|
+
module Interface
|
3
|
+
class PolymorphicError < ::StandardError; end
|
4
|
+
|
5
|
+
class InvalidTypeError < PolymorphicError
|
6
|
+
def initialize(*accepted_classes)
|
7
|
+
error = "Invalid type."
|
8
|
+
error += " Must be one of {#{accepted_classes.join(', ')}}"
|
9
|
+
super(error)
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
class AmbiguousTypeError < PolymorphicError
|
14
|
+
def initialize
|
15
|
+
super("Ambiguous polymorphic interface or object type")
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def self.included(base)
|
20
|
+
base.extend(ClassMethods)
|
21
|
+
end
|
22
|
+
|
23
|
+
module ClassMethods
|
24
|
+
|
25
|
+
def belongs_to_polymorphic(*args)
|
26
|
+
options = args.extract_options!
|
27
|
+
polymorphic_api = options[:as]
|
28
|
+
associations = args.collect(&:to_s).collect(&:downcase)
|
29
|
+
association_keys = associations.collect{|association| "#{association}_id"}
|
30
|
+
|
31
|
+
# Set belongs_to assocaitions
|
32
|
+
associations.each do |associated_model|
|
33
|
+
belongs_to associated_model.to_sym
|
34
|
+
end
|
35
|
+
|
36
|
+
# Class constant defining the keys
|
37
|
+
const_set "#{polymorphic_api}_keys".upcase, association_keys
|
38
|
+
|
39
|
+
# Helper methods and constants
|
40
|
+
define_method "#{polymorphic_api}_types" do
|
41
|
+
associations
|
42
|
+
end
|
43
|
+
|
44
|
+
define_method "#{polymorphic_api}_active_key" do
|
45
|
+
keys = association_keys.select { |key| self.send(key).present? }
|
46
|
+
keys.first if keys.length == 1
|
47
|
+
end
|
48
|
+
|
49
|
+
define_method "#{polymorphic_api}_query_condition" do
|
50
|
+
fk = self.send("#{polymorphic_api}_active_key")
|
51
|
+
{ fk.to_s => self.send(fk) } if fk
|
52
|
+
end
|
53
|
+
|
54
|
+
# Getter method
|
55
|
+
define_method polymorphic_api do
|
56
|
+
if key = self.send("#{polymorphic_api}_active_key")
|
57
|
+
self.send key.gsub(/_id$/,'')
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
# Setter method
|
62
|
+
define_method "#{polymorphic_api}=" do |polymorphic_obj|
|
63
|
+
possible_associations = [ polymorphic_obj.class.name.underscore,
|
64
|
+
polymorphic_obj.class.superclass.name.underscore ]
|
65
|
+
match = associations & possible_associations
|
66
|
+
if match.blank?
|
67
|
+
raise Polymorpheus::Interface::PolymorphicError, associations
|
68
|
+
elsif match.length > 1
|
69
|
+
raise Polymorpheus::Interface::AmbiguousTypeError
|
70
|
+
else
|
71
|
+
self.send("#{match[0]}_id=", polymorphic_obj.id)
|
72
|
+
(associations - match).each do |association_to_reset|
|
73
|
+
self.send("#{association_to_reset}_id=", nil)
|
74
|
+
end
|
75
|
+
end
|
76
|
+
end
|
77
|
+
|
78
|
+
# Private method called as part of validation
|
79
|
+
# Validate that there is exactly one associated object
|
80
|
+
define_method "polymorphic_#{polymorphic_api}_relationship_is_valid" do
|
81
|
+
if !self.send(polymorphic_api)
|
82
|
+
self.errors.add(:base,
|
83
|
+
"You must specify exactly one of the following: {#{associations.join(', ')}}")
|
84
|
+
end
|
85
|
+
end
|
86
|
+
private "polymorphic_#{polymorphic_api}_relationship_is_valid"
|
87
|
+
|
88
|
+
end
|
89
|
+
|
90
|
+
def validates_polymorph(polymorphic_api)
|
91
|
+
validate "polymorphic_#{polymorphic_api}_relationship_is_valid"
|
92
|
+
end
|
93
|
+
|
94
|
+
end
|
95
|
+
|
96
|
+
end
|
97
|
+
end
|
@@ -0,0 +1,127 @@
|
|
1
|
+
module Polymorpheus
|
2
|
+
module ConnectionAdapters
|
3
|
+
module MysqlAdapter
|
4
|
+
|
5
|
+
INSERT = 'INSERT'
|
6
|
+
UPDATE = 'UPDATE'
|
7
|
+
|
8
|
+
# Suppose I have a table named "pets" with columns dog_id and kitty_id, and I want them to
|
9
|
+
# have a polymorphic database constraint such that dog_id references the "id" column of the
|
10
|
+
# dogs table, and kitty_id references the "name" column of the "cats" table. then my inputs
|
11
|
+
# to this method would be:
|
12
|
+
# table: 'pets'
|
13
|
+
# columns: { 'dog_id' => 'dogs.id', 'kitty_id' => 'cats.name' }
|
14
|
+
#
|
15
|
+
# UNIQUENESS CONSTRAINTS:
|
16
|
+
#
|
17
|
+
# Suppose the pets table also has a 'person_id' column, and we want to impose a uniqueness
|
18
|
+
# constraint such that a given cat or dog can only be associated with the person one time
|
19
|
+
# We can specify this in the options as follows:
|
20
|
+
# options: :unique => 'person_id'
|
21
|
+
|
22
|
+
def add_polymorphic_constraints(table, columns, options = {})
|
23
|
+
poly_drop_triggers(table)
|
24
|
+
poly_create_triggers(table, columns.keys)
|
25
|
+
options.symbolize_keys!
|
26
|
+
index_suffix = options[:index_suffix]
|
27
|
+
if options[:unique].present?
|
28
|
+
poly_create_indexes(table, columns.keys, Array(options[:unique]), index_suffix)
|
29
|
+
end
|
30
|
+
columns.each do |(col, reference)|
|
31
|
+
ref_table, ref_col = reference.to_s.split('.')
|
32
|
+
add_foreign_key table, ref_table, :column => col, :primary_key => (ref_col || 'id')
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
def remove_polymorphic_constraints(table, columns, options = {})
|
37
|
+
poly_drop_triggers(table)
|
38
|
+
index_suffix = options[:index_suffix]
|
39
|
+
columns.each do |(col, reference)|
|
40
|
+
ref_table, ref_col = reference.to_s.split('.')
|
41
|
+
remove_foreign_key table, ref_table
|
42
|
+
end
|
43
|
+
if options[:unique].present?
|
44
|
+
poly_remove_indexes(table, columns.keys, Array(options[:unique]), index_suffix)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
|
49
|
+
##########################################################################
|
50
|
+
private
|
51
|
+
|
52
|
+
def poly_trigger_name(table, action)
|
53
|
+
"#{table}_unique_polyfk_on_#{action}"
|
54
|
+
end
|
55
|
+
|
56
|
+
def poly_drop_trigger(table, action)
|
57
|
+
execute %{DROP TRIGGER IF EXISTS #{poly_trigger_name(table, action)}}
|
58
|
+
end
|
59
|
+
|
60
|
+
def poly_create_trigger(table, action, columns)
|
61
|
+
sql = "CREATE TRIGGER #{poly_trigger_name(table, action)} BEFORE #{action} ON #{table}\n" +
|
62
|
+
"FOR EACH ROW\n" +
|
63
|
+
"BEGIN\n"
|
64
|
+
colchecks = columns.collect { |col| "IF(NEW.#{col} IS NULL, 0, 1)" }.
|
65
|
+
join(' + ')
|
66
|
+
sql += "IF(#{colchecks}) <> 1 THEN\n" +
|
67
|
+
"SET NEW = 'Error';\n" +
|
68
|
+
"END IF;\n" +
|
69
|
+
"END"
|
70
|
+
|
71
|
+
execute sql
|
72
|
+
end
|
73
|
+
|
74
|
+
def poly_drop_triggers(table)
|
75
|
+
poly_drop_trigger(table, 'INSERT')
|
76
|
+
poly_drop_trigger(table, 'UPDATE')
|
77
|
+
end
|
78
|
+
|
79
|
+
def poly_create_triggers(table, columns)
|
80
|
+
poly_create_trigger(table, 'INSERT', columns)
|
81
|
+
poly_create_trigger(table, 'UPDATE', columns)
|
82
|
+
end
|
83
|
+
|
84
|
+
def poly_create_index(table, column, unique_cols, index_suffix)
|
85
|
+
unique_cols = unique_cols.collect(&:to_s)
|
86
|
+
name = poly_index_name(table, column, unique_cols, index_suffix)
|
87
|
+
execute %{
|
88
|
+
CREATE UNIQUE INDEX #{name} ON #{table} (#{column},#{unique_cols.join(',')})
|
89
|
+
}
|
90
|
+
end
|
91
|
+
|
92
|
+
def poly_remove_index(table, column, unique_cols, index_suffix)
|
93
|
+
unique_cols = unique_cols.collect(&:to_s)
|
94
|
+
name = poly_index_name(table, column, unique_cols, index_suffix)
|
95
|
+
execute %{ DROP INDEX #{name} ON #{table} }
|
96
|
+
end
|
97
|
+
|
98
|
+
def poly_index_name(table, column, unique_cols, index_suffix)
|
99
|
+
index_suffix ||= unique_cols.join('_and_')
|
100
|
+
"index_#{table}_on_#{column}_and_#{index_suffix}"
|
101
|
+
end
|
102
|
+
|
103
|
+
def poly_create_indexes(table, columns, unique_cols, index_suffix)
|
104
|
+
columns.each do |column|
|
105
|
+
poly_create_index(table, column, unique_cols, index_suffix)
|
106
|
+
end
|
107
|
+
end
|
108
|
+
|
109
|
+
def poly_remove_indexes(table, columns, unique_cols, index_suffix)
|
110
|
+
columns.each do |column|
|
111
|
+
poly_remove_index(table, column, unique_cols, index_suffix)
|
112
|
+
end
|
113
|
+
end
|
114
|
+
|
115
|
+
end
|
116
|
+
end
|
117
|
+
end
|
118
|
+
|
119
|
+
[:MysqlAdapter, :Mysql2Adapter].each do |adapter|
|
120
|
+
begin
|
121
|
+
ActiveRecord::ConnectionAdapters.const_get(adapter).class_eval do
|
122
|
+
include Polymorpheus::ConnectionAdapters::MysqlAdapter
|
123
|
+
end
|
124
|
+
rescue
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# Thanks to matthuhiggins/foreigner gem for the template used here
|
2
|
+
module Polymorpheus
|
3
|
+
class Railtie < Rails::Railtie
|
4
|
+
|
5
|
+
initializer 'polymorpheus.load_adapter' do
|
6
|
+
ActiveSupport.on_load :active_record do
|
7
|
+
|
8
|
+
ActiveRecord::Base.send :include, Polymorpheus::Interface
|
9
|
+
|
10
|
+
ActiveRecord::ConnectionAdapters.module_eval do
|
11
|
+
include Polymorpheus::ConnectionAdapters::SchemaStatements
|
12
|
+
end
|
13
|
+
|
14
|
+
Polymorpheus::Adapter.load!
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
module Polymorpheus
|
2
|
+
module ConnectionAdapters
|
3
|
+
module SchemaStatements
|
4
|
+
def self.included(base)
|
5
|
+
base::AbstractAdapter.class_eval do
|
6
|
+
include Polymorpheus::ConnectionAdapters::AbstractAdapter
|
7
|
+
end
|
8
|
+
end
|
9
|
+
end
|
10
|
+
|
11
|
+
module AbstractAdapter
|
12
|
+
def add_polymorphic_constraints(table, columns, options = {})
|
13
|
+
end
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
data/lib/polymorpheus.rb
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
module Polymorpheus
|
2
|
+
autoload :Adapter, 'polymorpheus/adapter'
|
3
|
+
autoload :Interface, 'polymorpheus/interface'
|
4
|
+
|
5
|
+
module ConnectionAdapters
|
6
|
+
autoload :SchemaStatements, 'polymorpheus/schema_statements'
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
Polymorpheus::Adapter.register 'mysql2', 'polymorpheus/mysql_adapter'
|
11
|
+
|
12
|
+
require 'polymorpheus/railtie' if defined?(Rails)
|
@@ -0,0 +1,18 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
require 'rubygems' unless defined? Gem
|
3
|
+
require File.dirname(__FILE__) + "/lib/polymorpheus/version"
|
4
|
+
|
5
|
+
Gem::Specification.new do |s|
|
6
|
+
s.name = "polymorpheus"
|
7
|
+
s.version = Polymorpheus::VERSION
|
8
|
+
s.authors = ["Barun Singh"]
|
9
|
+
s.email = "bsingh@wegowise.com"
|
10
|
+
s.homepage = "http://github.com/wegowise/polymorpheus"
|
11
|
+
s.summary = "Provides a database-friendly method for polymorphic relationships"
|
12
|
+
s.description = "Provides a database-friendly method for polymorphic relationships"
|
13
|
+
s.required_rubygems_version = ">= 1.3.6"
|
14
|
+
s.files = Dir.glob(%w[{lib,spec}/**/*.rb [A-Z]*.{txt,rdoc,md} *.gemspec]) + %w{Rakefile}
|
15
|
+
s.extra_rdoc_files = ["README.md", "LICENSE.txt"]
|
16
|
+
s.license = 'MIT'
|
17
|
+
s.add_dependency('foreigner')
|
18
|
+
end
|
@@ -0,0 +1,75 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'polymorpheus'
|
3
|
+
require 'spec_helper'
|
4
|
+
|
5
|
+
# this is normally done via a Railtie in non-testing situations
|
6
|
+
ActiveRecord::Base.send :include, Polymorpheus::Interface
|
7
|
+
|
8
|
+
class Shoe < ActiveRecord::Base
|
9
|
+
belongs_to_polymorphic :man, :woman, :as => :wearer
|
10
|
+
validates_polymorph :wearer
|
11
|
+
end
|
12
|
+
|
13
|
+
class Man < ActiveRecord::Base
|
14
|
+
end
|
15
|
+
|
16
|
+
class Woman < ActiveRecord::Base
|
17
|
+
end
|
18
|
+
|
19
|
+
describe Shoe do
|
20
|
+
|
21
|
+
let(:shoe) { Shoe.new(attributes) }
|
22
|
+
let(:man) { Man.create! }
|
23
|
+
let(:woman) { Woman.create! }
|
24
|
+
|
25
|
+
describe "class level constant" do
|
26
|
+
specify { Shoe::WEARER_KEYS.should == ["man_id", "woman_id"] }
|
27
|
+
end
|
28
|
+
|
29
|
+
describe "helper methods" do
|
30
|
+
specify { Shoe.new.wearer_types.should == ["man", "woman"] }
|
31
|
+
end
|
32
|
+
|
33
|
+
it "make the dynamically defined validation method private" do
|
34
|
+
Shoe.private_instance_methods.
|
35
|
+
include?(:polymorphic_wearer_relationship_is_valid).should be_true
|
36
|
+
end
|
37
|
+
|
38
|
+
shared_examples_for "invalid polymorphic relationship" do
|
39
|
+
specify { shoe.wearer.should == nil }
|
40
|
+
specify { shoe.wearer_active_key.should == nil }
|
41
|
+
specify { shoe.wearer_query_condition.should == nil }
|
42
|
+
it "validates appropriately" do
|
43
|
+
shoe.valid?.should be_false
|
44
|
+
shoe.errors[:base].should ==
|
45
|
+
["You must specify exactly one of the following: {man, woman}"]
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
context "when there is no relationship defined" do
|
50
|
+
let(:attributes) { {} }
|
51
|
+
it_should_behave_like "invalid polymorphic relationship"
|
52
|
+
end
|
53
|
+
|
54
|
+
context "when there are multiple relationships defined" do
|
55
|
+
let(:attributes) { { man_id: man.id, woman_id: woman.id } }
|
56
|
+
it_should_behave_like "invalid polymorphic relationship"
|
57
|
+
end
|
58
|
+
|
59
|
+
context "when there is exactly one relationship defined" do
|
60
|
+
shared_examples_for "valid polymorphic relationship" do
|
61
|
+
specify { shoe.wearer.should == man }
|
62
|
+
specify { shoe.wearer_active_key.should == 'man_id' }
|
63
|
+
specify { shoe.wearer_query_condition.should == { 'man_id' => man.id } }
|
64
|
+
end
|
65
|
+
context "and we have specified it via the id value" do
|
66
|
+
let(:attributes) { { man_id: man.id } }
|
67
|
+
it_should_behave_like "valid polymorphic relationship"
|
68
|
+
end
|
69
|
+
context "and we have specified it via the id value" do
|
70
|
+
let(:attributes) { { man: man } }
|
71
|
+
it_should_behave_like "valid polymorphic relationship"
|
72
|
+
end
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
@@ -0,0 +1,54 @@
|
|
1
|
+
require 'active_record'
|
2
|
+
require 'spec_helper'
|
3
|
+
require 'sql_logger'
|
4
|
+
require 'foreigner'
|
5
|
+
require 'foreigner/connection_adapters/mysql2_adapter'
|
6
|
+
require 'polymorpheus'
|
7
|
+
|
8
|
+
Polymorpheus::Adapter.load!
|
9
|
+
|
10
|
+
describe "Polymorpheus" do
|
11
|
+
class << ActiveRecord::Base.connection
|
12
|
+
include Polymorpheus::SqlLogger
|
13
|
+
end
|
14
|
+
|
15
|
+
let(:connection) { ActiveRecord::Base.connection }
|
16
|
+
let(:sql) { connection.sql_statements }
|
17
|
+
|
18
|
+
describe "add_polymorphic_constraints" do
|
19
|
+
before do
|
20
|
+
connection.add_polymorphic_constraints 'pets',
|
21
|
+
{ 'dog_id' => 'dogs.id', 'kitty_id' => 'cats.name' }
|
22
|
+
end
|
23
|
+
|
24
|
+
it "executes the correct sql statements" do
|
25
|
+
clean_sql(sql.join("\n")).should == clean_sql(%{
|
26
|
+
DROP TRIGGER IF EXISTS pets_unique_polyfk_on_INSERT
|
27
|
+
DROP TRIGGER IF EXISTS pets_unique_polyfk_on_UPDATE
|
28
|
+
CREATE TRIGGER pets_unique_polyfk_on_INSERT BEFORE INSERT ON pets
|
29
|
+
FOR EACH ROW
|
30
|
+
BEGIN
|
31
|
+
IF(IF(NEW.dog_id IS NULL, 0, 1) + IF(NEW.kitty_id IS NULL, 0, 1)) <> 1 THEN
|
32
|
+
SET NEW = 'Error';
|
33
|
+
END IF;
|
34
|
+
END
|
35
|
+
CREATE TRIGGER pets_unique_polyfk_on_UPDATE BEFORE UPDATE ON pets
|
36
|
+
FOR EACH ROW
|
37
|
+
BEGIN
|
38
|
+
IF(IF(NEW.dog_id IS NULL, 0, 1) + IF(NEW.kitty_id IS NULL, 0, 1)) <> 1 THEN
|
39
|
+
SET NEW = 'Error';
|
40
|
+
END IF;
|
41
|
+
END
|
42
|
+
|
43
|
+
ALTER TABLE `pets` ADD CONSTRAINT `pets_dog_id_fk` FOREIGN KEY (`dog_id`) REFERENCES `dogs`(id)
|
44
|
+
ALTER TABLE `pets` ADD CONSTRAINT `pets_kitty_id_fk` FOREIGN KEY (`kitty_id`) REFERENCES `cats`(name)
|
45
|
+
})
|
46
|
+
end
|
47
|
+
|
48
|
+
end
|
49
|
+
|
50
|
+
def clean_sql(sql_string)
|
51
|
+
sql_string.gsub(/^\n\s*/,'').gsub(/\s*\n\s*$/,'').gsub(/\n\s*/,"\n")
|
52
|
+
end
|
53
|
+
|
54
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1,21 @@
|
|
1
|
+
ActiveRecord::Base.establish_connection({
|
2
|
+
adapter: 'mysql2',
|
3
|
+
username: 'root',
|
4
|
+
password: '',
|
5
|
+
host: 'localhost',
|
6
|
+
database: 'polymorphicTest'
|
7
|
+
})
|
8
|
+
|
9
|
+
ActiveRecord::Base.connection.tables.each do |table|
|
10
|
+
ActiveRecord::Base.connection.drop_table table
|
11
|
+
end
|
12
|
+
|
13
|
+
ActiveRecord::Schema.define do
|
14
|
+
create_table :shoes do |t|
|
15
|
+
t.integer :man_id
|
16
|
+
t.integer :woman_id
|
17
|
+
end
|
18
|
+
create_table :men
|
19
|
+
create_table :women
|
20
|
+
end
|
21
|
+
|
data/spec/sql_logger.rb
ADDED
metadata
ADDED
@@ -0,0 +1,73 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: polymorpheus
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: '0.1'
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Barun Singh
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2011-12-13 00:00:00.000000000Z
|
13
|
+
dependencies:
|
14
|
+
- !ruby/object:Gem::Dependency
|
15
|
+
name: foreigner
|
16
|
+
requirement: &70170816060540 !ruby/object:Gem::Requirement
|
17
|
+
none: false
|
18
|
+
requirements:
|
19
|
+
- - ! '>='
|
20
|
+
- !ruby/object:Gem::Version
|
21
|
+
version: '0'
|
22
|
+
type: :runtime
|
23
|
+
prerelease: false
|
24
|
+
version_requirements: *70170816060540
|
25
|
+
description: Provides a database-friendly method for polymorphic relationships
|
26
|
+
email: bsingh@wegowise.com
|
27
|
+
executables: []
|
28
|
+
extensions: []
|
29
|
+
extra_rdoc_files:
|
30
|
+
- README.md
|
31
|
+
- LICENSE.txt
|
32
|
+
files:
|
33
|
+
- lib/polymorpheus/adapter.rb
|
34
|
+
- lib/polymorpheus/interface.rb
|
35
|
+
- lib/polymorpheus/mysql_adapter.rb
|
36
|
+
- lib/polymorpheus/railtie.rb
|
37
|
+
- lib/polymorpheus/schema_statements.rb
|
38
|
+
- lib/polymorpheus/version.rb
|
39
|
+
- lib/polymorpheus.rb
|
40
|
+
- spec/interface_spec.rb
|
41
|
+
- spec/mysql2_adapter_spec.rb
|
42
|
+
- spec/spec_helper.rb
|
43
|
+
- spec/sql_logger.rb
|
44
|
+
- LICENSE.txt
|
45
|
+
- README.md
|
46
|
+
- polymorpheus.gemspec
|
47
|
+
- Rakefile
|
48
|
+
homepage: http://github.com/wegowise/polymorpheus
|
49
|
+
licenses:
|
50
|
+
- MIT
|
51
|
+
post_install_message:
|
52
|
+
rdoc_options: []
|
53
|
+
require_paths:
|
54
|
+
- lib
|
55
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
56
|
+
none: false
|
57
|
+
requirements:
|
58
|
+
- - ! '>='
|
59
|
+
- !ruby/object:Gem::Version
|
60
|
+
version: '0'
|
61
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
62
|
+
none: false
|
63
|
+
requirements:
|
64
|
+
- - ! '>='
|
65
|
+
- !ruby/object:Gem::Version
|
66
|
+
version: 1.3.6
|
67
|
+
requirements: []
|
68
|
+
rubyforge_project:
|
69
|
+
rubygems_version: 1.8.6
|
70
|
+
signing_key:
|
71
|
+
specification_version: 3
|
72
|
+
summary: Provides a database-friendly method for polymorphic relationships
|
73
|
+
test_files: []
|