dr_nic_magic_models 0.2.5 → 0.7.0
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/Rakefile +3 -3
- data/install.rb +1 -1
- data/lib/base.rb +45 -14
- data/lib/{dr_nic.rb → dr_nic_magic_models.rb} +2 -2
- data/lib/dr_nic_magic_models/associations.rb +12 -0
- data/lib/dr_nic_magic_models/schema.rb +40 -0
- data/lib/dr_nic_magic_models/validations.rb +26 -0
- data/lib/dr_nic_magic_models/version.rb +9 -0
- data/lib/module.rb +4 -2
- data/lib/new_base.rb +105 -0
- data/test/abstract_unit.rb +1 -1
- data/test/dummy_test.rb +3 -3
- data/test/env_test.rb +3 -4
- data/test/fixtures/db_definitions/mysql.sql +7 -9
- data/test/fixtures/fun_users.yml +7 -0
- data/test/fixtures/group_memberships.yml +4 -0
- data/test/fixtures/group_tag.yml +9 -0
- data/test/fixtures/groups.yml +4 -0
- data/test/invisible_model_access_test.rb +37 -0
- data/test/invisible_model_assoc_test.rb +42 -0
- data/test/invisible_model_classes_test.rb +6 -7
- data/website/index.html +238 -3
- data/website/index.txt +201 -1
- metadata +16 -13
- data/lib/dr_nic/magic_models/schema.rb +0 -45
- data/lib/dr_nic/magic_models/validations.rb +0 -28
- data/lib/dr_nic/magic_models/version.rb +0 -11
- data/test/fixtures/reference_code.rb +0 -7
- data/test/fixtures/reference_codes.yml +0 -28
- data/test/fixtures/reference_type.rb +0 -7
- data/test/fixtures/reference_types.yml +0 -9
data/Rakefile
CHANGED
@@ -5,11 +5,11 @@ require 'rake/rdoctask'
|
|
5
5
|
require 'rake/packagetask'
|
6
6
|
require 'rake/gempackagetask'
|
7
7
|
require 'rake/contrib/rubyforgepublisher'
|
8
|
-
require File.join(File.dirname(__FILE__), 'lib', '
|
8
|
+
require File.join(File.dirname(__FILE__), 'lib', 'dr_nic_magic_models', 'version')
|
9
9
|
|
10
10
|
PKG_BUILD = ENV['PKG_BUILD'] ? '.' + ENV['PKG_BUILD'] : ''
|
11
11
|
PKG_NAME = 'dr_nic_magic_models'
|
12
|
-
PKG_VERSION =
|
12
|
+
PKG_VERSION = DrNicMagicModels::VERSION::STRING + PKG_BUILD
|
13
13
|
PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
|
14
14
|
|
15
15
|
RELEASE_NAME = "REL #{PKG_VERSION}"
|
@@ -102,7 +102,7 @@ spec = Gem::Specification.new do |s|
|
|
102
102
|
s.add_dependency('activerecord', '= 1.14.3' + PKG_BUILD)
|
103
103
|
|
104
104
|
s.require_path = 'lib'
|
105
|
-
s.autorequire = '
|
105
|
+
s.autorequire = 'dr_nic_magic_models'
|
106
106
|
|
107
107
|
s.has_rdoc = true
|
108
108
|
s.extra_rdoc_files = %w( README )
|
data/install.rb
CHANGED
@@ -21,7 +21,7 @@ end
|
|
21
21
|
# the acual gruntwork
|
22
22
|
Dir.chdir("lib")
|
23
23
|
|
24
|
-
Find.find("
|
24
|
+
Find.find("dr_nic_magic_models", "dr_nic_magic_models.rb") { |f|
|
25
25
|
if f[-3..-1] == ".rb"
|
26
26
|
File::install(f, File.join($sitedir, *f.split(/\//)), 0644, true)
|
27
27
|
else
|
data/lib/base.rb
CHANGED
@@ -6,7 +6,7 @@ module ActiveRecord
|
|
6
6
|
def new(*args)
|
7
7
|
obj = old_new(*args)
|
8
8
|
unless self.methods.include? 'generate_validations'
|
9
|
-
self.send(:include,
|
9
|
+
self.send(:include, DrNicMagicModels::Validations)
|
10
10
|
self.generate_validations
|
11
11
|
end
|
12
12
|
obj
|
@@ -15,26 +15,38 @@ module ActiveRecord
|
|
15
15
|
def allocate
|
16
16
|
obj = old_allocate
|
17
17
|
unless self.methods.include? 'generate_validations'
|
18
|
-
self.send(:include,
|
18
|
+
self.send(:include, DrNicMagicModels::Validations)
|
19
19
|
self.generate_validations
|
20
20
|
end
|
21
21
|
obj
|
22
22
|
end
|
23
|
+
|
24
|
+
# Returns the AssociationReflection object for the named +aggregation+ (use the symbol). Example:
|
25
|
+
# Account.reflect_on_association(:owner) # returns the owner AssociationReflection
|
26
|
+
# Invoice.reflect_on_association(:line_items).macro # returns :has_many
|
27
|
+
def reflect_on_association(association)
|
28
|
+
unless reflections[association]
|
29
|
+
# See if an assocation can be generated
|
30
|
+
self.new.send(association) rescue nil
|
31
|
+
end
|
32
|
+
reflections[association].is_a?(ActiveRecord::Reflection::AssociationReflection) ? reflections[association] : nil
|
33
|
+
end
|
23
34
|
end
|
24
35
|
|
25
|
-
alias :normal_method_missing :method_missing
|
26
36
|
class_eval do
|
37
|
+
alias :normal_method_missing :method_missing
|
27
38
|
def method_missing(method, *args, &block)
|
28
39
|
if unknown_method? method
|
29
|
-
result =
|
30
|
-
result =
|
40
|
+
result = find_belongs_to method, *args, &block
|
41
|
+
result = find_has_some method, *args, &block if not result
|
42
|
+
result = find_has_some_indirect method, *args, &block if not result
|
31
43
|
return result if result
|
32
44
|
end
|
33
|
-
|
45
|
+
add_known_unknown method
|
34
46
|
normal_method_missing(method, *args, &block)
|
35
47
|
end
|
36
48
|
|
37
|
-
def
|
49
|
+
def add_known_unknown(method)
|
38
50
|
@known_unknowns ||= {}
|
39
51
|
@known_unknowns[method] = true
|
40
52
|
end
|
@@ -43,6 +55,16 @@ module ActiveRecord
|
|
43
55
|
@known_unknowns.nil? or @known_unknowns.include? method
|
44
56
|
end
|
45
57
|
|
58
|
+
def find_belongs_to(method, *args, &block)
|
59
|
+
foreign_key = self.class.columns.select {|column| column.name == method.to_s.foreign_key}.first
|
60
|
+
return add_belongs_to(method, *args, &block) if foreign_key
|
61
|
+
end
|
62
|
+
|
63
|
+
def add_belongs_to(method, *args, &block)
|
64
|
+
self.class.send 'belongs_to', method
|
65
|
+
self.send(method, *args, &block)
|
66
|
+
end
|
67
|
+
|
46
68
|
def find_has_some(method, *args, &block)
|
47
69
|
klass = Module.const_get method.to_s.downcase.singularize.camelize rescue nil
|
48
70
|
foreign_key = klass.columns.select {|column| column.name == self.class.name.foreign_key}.first if klass
|
@@ -54,17 +76,26 @@ module ActiveRecord
|
|
54
76
|
association = _method.singularize == _method ? 'has_one' : 'has_many'
|
55
77
|
self.class.send association, method
|
56
78
|
self.send(method, *args, &block)
|
57
|
-
|
79
|
+
end
|
58
80
|
|
59
|
-
def
|
60
|
-
|
61
|
-
|
81
|
+
def find_has_some_indirect(method, *args, &block)
|
82
|
+
klass = Module.const_get method.to_s.downcase.singularize.camelize rescue return
|
83
|
+
join_table = nil
|
84
|
+
self.connection.tables.each do |table|
|
85
|
+
unless [self.class.table_name, klass.table_name].include? table
|
86
|
+
columns = self.connection.columns(table).map(&:name)
|
87
|
+
join_table = table if columns.include?(self.class.to_s.foreign_key) and columns.include?(klass.to_s.foreign_key)
|
88
|
+
end
|
89
|
+
break if join_table
|
90
|
+
end
|
91
|
+
return add_has_some_through(join_table, method, *args, &block) if join_table
|
62
92
|
end
|
63
|
-
|
64
|
-
def
|
65
|
-
self.class.send '
|
93
|
+
|
94
|
+
def add_has_some_through(join_table, method, *args, &block)
|
95
|
+
self.class.send 'has_many', method, :through => join_table.to_sym
|
66
96
|
self.send(method, *args, &block)
|
67
97
|
end
|
98
|
+
|
68
99
|
end
|
69
100
|
end
|
70
101
|
end
|
@@ -0,0 +1,40 @@
|
|
1
|
+
module DrNicMagicModels
|
2
|
+
class Schema
|
3
|
+
class << self
|
4
|
+
def models
|
5
|
+
@@models ||= nil
|
6
|
+
load_schema if @@models.blank?
|
7
|
+
@@models
|
8
|
+
end
|
9
|
+
|
10
|
+
def reset
|
11
|
+
@@models = nil
|
12
|
+
end
|
13
|
+
|
14
|
+
def load_schema
|
15
|
+
if conn = ActiveRecord::Base.connection
|
16
|
+
@@models = DrNicMagicModels::ModelHash.new
|
17
|
+
@@models.merge! conn.tables.inject({}) {|model_list,table_name| model_list[ActiveRecord::Base.class_name(table_name)] = table_name; model_list}
|
18
|
+
end
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
class ModelHash < Hash
|
24
|
+
def unenquire(class_id)
|
25
|
+
@enquired ||= {}
|
26
|
+
@enquired[class_id = class_id.to_s] = false
|
27
|
+
end
|
28
|
+
|
29
|
+
def enquired?(class_id)
|
30
|
+
@enquired ||= {}
|
31
|
+
@enquired[class_id.to_s]
|
32
|
+
end
|
33
|
+
|
34
|
+
def [](class_id)
|
35
|
+
enquired?(class_id = class_id.to_s)
|
36
|
+
@enquired[class_id] = true
|
37
|
+
super(class_id)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
@@ -0,0 +1,26 @@
|
|
1
|
+
module DrNicMagicModels
|
2
|
+
module Validations
|
3
|
+
def self.append_features(base)
|
4
|
+
super
|
5
|
+
base.extend(ClassMethods)
|
6
|
+
base.generate_validations
|
7
|
+
|
8
|
+
# Currently only invoked on generated classes
|
9
|
+
# How to include and invoke on all ARs? - hook into class loading??
|
10
|
+
end
|
11
|
+
|
12
|
+
module ClassMethods
|
13
|
+
def generate_validations
|
14
|
+
unless @@generated_validations ||= false
|
15
|
+
@@generated_validations = true
|
16
|
+
column_names = self.columns.select {|column| !column.null and !column.primary}.map {|column| column.name.to_sym}
|
17
|
+
add_validation :validates_presence_of, column_names
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def add_validation(validation, column_names)
|
22
|
+
self.send validation, *column_names
|
23
|
+
end
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
data/lib/module.rb
CHANGED
@@ -6,11 +6,13 @@ class Module
|
|
6
6
|
return normal_const_missing(class_id)
|
7
7
|
rescue
|
8
8
|
end
|
9
|
-
unless table_name =
|
10
|
-
raise NameError.new("uninitialized constant #{class_id}") if
|
9
|
+
unless table_name = DrNicMagicModels::Schema.models[class_id]
|
10
|
+
raise NameError.new("uninitialized constant #{class_id}") if DrNicMagicModels::Schema.models.enquired? class_id
|
11
11
|
end
|
12
12
|
klass_code = lambda {klass_code}
|
13
13
|
klass = Class.new ActiveRecord::Base, &klass_code
|
14
14
|
const_set class_id, klass
|
15
|
+
klass.set_table_name table_name
|
16
|
+
klass
|
15
17
|
end
|
16
18
|
end
|
data/lib/new_base.rb
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
# Only needs to be an Include module for the generated classes, not for all ARs
|
2
|
+
module ActiveRecord
|
3
|
+
class Base
|
4
|
+
class << self
|
5
|
+
alias old_new new
|
6
|
+
def new(*args)
|
7
|
+
obj = old_new(*args)
|
8
|
+
unless self.methods.include? 'generate_validations'
|
9
|
+
self.send(:include, DrNicMagicModels::Validations)
|
10
|
+
self.generate_validations
|
11
|
+
end
|
12
|
+
obj
|
13
|
+
end
|
14
|
+
alias old_allocate allocate
|
15
|
+
def allocate
|
16
|
+
obj = old_allocate
|
17
|
+
unless self.methods.include? 'generate_validations'
|
18
|
+
self.send(:include, DrNicMagicModels::Validations)
|
19
|
+
self.generate_validations
|
20
|
+
end
|
21
|
+
obj
|
22
|
+
end
|
23
|
+
|
24
|
+
cattr_accessor :known_unknowns
|
25
|
+
|
26
|
+
# Returns the AssociationReflection object for the named +aggregation+ (use the symbol). Example:
|
27
|
+
# Account.reflect_on_association(:owner) # returns the owner AssociationReflection
|
28
|
+
# Invoice.reflect_on_association(:line_items).macro # returns :has_many
|
29
|
+
def reflect_on_association(association)
|
30
|
+
puts "Known association? #{association.to_sym.inspect} -> #{reflections[association]}"
|
31
|
+
reflections[association].is_a?(AssociationReflection) ? reflections[association] : nil
|
32
|
+
end
|
33
|
+
|
34
|
+
def assess_unknown_method(instance, method, *args, &block)
|
35
|
+
result = find_belongs_to instance, method, *args, &block
|
36
|
+
result = find_has_some instance, method, *args, &block if not result
|
37
|
+
result = find_has_some_indirect instance, method, *args, &block if not result
|
38
|
+
result
|
39
|
+
end
|
40
|
+
|
41
|
+
def add_known_unknown(method)
|
42
|
+
known_unknowns ||= {}
|
43
|
+
known_unknowns[method] = true
|
44
|
+
end
|
45
|
+
|
46
|
+
def unknown_method?(method)
|
47
|
+
known_unknowns.nil? or known_unknowns.include? method
|
48
|
+
end
|
49
|
+
|
50
|
+
def find_belongs_to(instance, method, *args, &block)
|
51
|
+
foreign_key = self.columns.select {|column| column.name == method.to_s.foreign_key}.first
|
52
|
+
return add_belongs_to(instance, method, *args, &block) if foreign_key
|
53
|
+
end
|
54
|
+
|
55
|
+
def add_belongs_to(instance, method, *args, &block)
|
56
|
+
self.send 'belongs_to', method
|
57
|
+
instance.send(method, *args, &block)
|
58
|
+
end
|
59
|
+
|
60
|
+
def find_has_some(instance, method, *args, &block)
|
61
|
+
klass = Module.const_get method.to_s.downcase.singularize.camelize rescue nil
|
62
|
+
foreign_key = klass.columns.select {|column| column.name == self.name.foreign_key}.first if klass
|
63
|
+
return add_has_some(instance, method, *args, &block) if foreign_key
|
64
|
+
end
|
65
|
+
|
66
|
+
def add_has_some(instance, method, *args, &block)
|
67
|
+
_method = method.to_s
|
68
|
+
association = _method.singularize == _method ? 'has_one' : 'has_many'
|
69
|
+
self.send association, method
|
70
|
+
instance.send(instance, method, *args, &block)
|
71
|
+
end
|
72
|
+
|
73
|
+
def find_has_some_indirect(instance, method, *args, &block)
|
74
|
+
klass = Module.const_get method.to_s.downcase.singularize.camelize rescue return
|
75
|
+
join_table = nil
|
76
|
+
self.connection.tables.each do |table|
|
77
|
+
unless [self.table_name, klass.table_name].include? table
|
78
|
+
columns = self.connection.columns(table).map(&:name)
|
79
|
+
join_table = table if columns.include?(self.to_s.foreign_key) and columns.include?(klass.to_s.foreign_key)
|
80
|
+
end
|
81
|
+
break if join_table
|
82
|
+
end
|
83
|
+
return add_has_some_through(instance, join_table, method, *args, &block) if join_table
|
84
|
+
end
|
85
|
+
|
86
|
+
def add_has_some_through(instance, join_table, method, *args, &block)
|
87
|
+
puts "self.class.send 'has_many', #{method}, :through => #{join_table.inspect}"
|
88
|
+
self.send 'has_many', method, :through => join_table.to_sym
|
89
|
+
instance.send(method, *args, &block)
|
90
|
+
end
|
91
|
+
end
|
92
|
+
|
93
|
+
class_eval do
|
94
|
+
alias :normal_method_missing :method_missing
|
95
|
+
def method_missing(method, *args, &block)
|
96
|
+
if unknown_method? method
|
97
|
+
self.class.assess_unknown_method(self, method, *args, &block)
|
98
|
+
end
|
99
|
+
add_known_unknown method
|
100
|
+
normal_method_missing(method, *args, &block)
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
data/test/abstract_unit.rb
CHANGED
@@ -6,7 +6,7 @@ require 'active_record/fixtures'
|
|
6
6
|
require 'active_support/binding_of_caller'
|
7
7
|
require 'active_support/breakpoint'
|
8
8
|
require 'connection'
|
9
|
-
require '
|
9
|
+
require 'dr_nic_magic_models'
|
10
10
|
|
11
11
|
QUOTED_TYPE = ActiveRecord::Base.connection.quote_column_name('type') unless Object.const_defined?(:QUOTED_TYPE)
|
12
12
|
|
data/test/dummy_test.rb
CHANGED
data/test/env_test.rb
CHANGED
@@ -3,9 +3,8 @@ require 'abstract_unit'
|
|
3
3
|
class EnvTest < Test::Unit::TestCase
|
4
4
|
|
5
5
|
def test_modules
|
6
|
-
assert_not_nil
|
7
|
-
assert_not_nil
|
8
|
-
assert_not_nil
|
9
|
-
assert_not_nil DrNic::MagicModels::Schema
|
6
|
+
assert_not_nil DrNicMagicModels
|
7
|
+
assert_not_nil DrNicMagicModels::Validations
|
8
|
+
assert_not_nil DrNicMagicModels::Schema
|
10
9
|
end
|
11
10
|
end
|
@@ -1,4 +1,4 @@
|
|
1
|
-
CREATE TABLE `
|
1
|
+
CREATE TABLE `fun_users` (
|
2
2
|
`id` int(11) NOT NULL auto_increment,
|
3
3
|
`firstname` varchar(50) NOT NULL,
|
4
4
|
`lastname` varchar(50) NOT NULL,
|
@@ -9,24 +9,22 @@ CREATE TABLE `users` (
|
|
9
9
|
|
10
10
|
CREATE TABLE `groups` (
|
11
11
|
`id` int(11) NOT NULL auto_increment,
|
12
|
-
`name`
|
12
|
+
`name` varchar(50) NOT NULL,
|
13
13
|
`description` varchar(50) default NULL,
|
14
14
|
PRIMARY KEY (`id`)
|
15
15
|
) TYPE=InnoDB;
|
16
16
|
|
17
|
-
CREATE TABLE `
|
17
|
+
CREATE TABLE `group_memberships` (
|
18
18
|
`id` int(11) NOT NULL auto_increment,
|
19
|
-
`
|
19
|
+
`fun_user_id` int(11) NOT NULL,
|
20
20
|
`group_id` int(11) NOT NULL,
|
21
21
|
PRIMARY KEY (`id`)
|
22
22
|
) TYPE=InnoDB;
|
23
23
|
|
24
|
-
CREATE TABLE `
|
24
|
+
CREATE TABLE `group_tag` (
|
25
25
|
`id` int(11) NOT NULL auto_increment,
|
26
|
-
`
|
27
|
-
`
|
28
|
-
`login` varchar(50) NOT NULL,
|
29
|
-
`email` varchar(50) NULL,
|
26
|
+
`name` varchar(50) NOT NULL,
|
27
|
+
`group_id` int(11) NOT NULL,
|
30
28
|
PRIMARY KEY (`id`)
|
31
29
|
) TYPE=InnoDB;
|
32
30
|
|
@@ -0,0 +1,37 @@
|
|
1
|
+
require 'abstract_unit'
|
2
|
+
|
3
|
+
class InvisibleModelAccessTest < Test::Unit::TestCase
|
4
|
+
fixtures :fun_users, :groups, :group_memberships, :group_tag
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@classes = [FunUser, Group, GroupMembership, GroupTag]
|
8
|
+
@group = groups(:first)
|
9
|
+
end
|
10
|
+
|
11
|
+
def test_find
|
12
|
+
@classes.each do |klass|
|
13
|
+
assert_not_nil obj = klass.find(1)
|
14
|
+
assert_equal klass, obj.class
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
18
|
+
def test_create
|
19
|
+
assert group = Group.create(:name => 'New Group')
|
20
|
+
assert_equal Group, group.class
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_update
|
24
|
+
assert @group.update_attributes(:name => 'Group 1')
|
25
|
+
assert_equal Group, @group.class
|
26
|
+
end
|
27
|
+
|
28
|
+
def test_delete
|
29
|
+
assert @group.destroy
|
30
|
+
end
|
31
|
+
|
32
|
+
def test_validations
|
33
|
+
group = Group.new
|
34
|
+
assert !group.valid?
|
35
|
+
assert_not_nil group.errors[:name]
|
36
|
+
end
|
37
|
+
end
|
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'abstract_unit'
|
2
|
+
|
3
|
+
class InvisibleModelAssocTest < Test::Unit::TestCase
|
4
|
+
fixtures :fun_users, :groups, :group_memberships
|
5
|
+
|
6
|
+
def setup
|
7
|
+
@group = groups(:first)
|
8
|
+
@user = fun_users(:first)
|
9
|
+
@membership = group_memberships(:first_first)
|
10
|
+
end
|
11
|
+
|
12
|
+
def test_has_some
|
13
|
+
assert_equal @membership, @user.group_membership
|
14
|
+
assert_equal [@membership], @user.group_memberships
|
15
|
+
assert_equal @membership, @group.group_membership
|
16
|
+
assert_equal [@membership], @group.group_memberships
|
17
|
+
end
|
18
|
+
|
19
|
+
def test_belongs_to
|
20
|
+
assert_equal @user, @membership.fun_user
|
21
|
+
assert_equal @group, @membership.group
|
22
|
+
GroupTag.find(:all, :conditions => ['group_id = ?', @group.id]).each do |group_tag|
|
23
|
+
assert_equal @group, group_tag.group
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def test_indirect
|
28
|
+
assert_equal [@user], @group.fun_users
|
29
|
+
assert_equal [@group], @user.groups
|
30
|
+
end
|
31
|
+
|
32
|
+
# TODO - Support these
|
33
|
+
#def test_more_indirect
|
34
|
+
# group_tags = GroupTag.find(:all, :conditions => ['group_id = ?', @group.id])
|
35
|
+
# assert_equal group_tags, @user.group_tags
|
36
|
+
#end
|
37
|
+
|
38
|
+
#def test_more_indirect_via_belongs_to
|
39
|
+
# @group_tag = group_tag(:first)
|
40
|
+
# assert_equal [@user], @group_tag.fun_users
|
41
|
+
#end
|
42
|
+
end
|
@@ -4,17 +4,16 @@ class InvisibleModelClassesTest < Test::Unit::TestCase
|
|
4
4
|
|
5
5
|
def test_available
|
6
6
|
assert_not_nil Group
|
7
|
-
assert_not_nil
|
8
|
-
assert_not_nil
|
9
|
-
assert_not_nil
|
7
|
+
assert_not_nil FunUser
|
8
|
+
assert_not_nil GroupMembership
|
9
|
+
assert_not_nil GroupTag, "Could not find GroupTag with singularized table name 'GroupTag'"
|
10
10
|
end
|
11
11
|
|
12
12
|
def test_table_names
|
13
13
|
assert_equal 'groups', Group.table_name
|
14
|
-
assert_equal '
|
15
|
-
assert_equal '
|
16
|
-
assert_equal '
|
14
|
+
assert_equal 'fun_users', FunUser.table_name
|
15
|
+
assert_equal 'group_memberships', GroupMembership.table_name
|
16
|
+
assert_equal 'group_tag', GroupTag.table_name
|
17
17
|
end
|
18
18
|
|
19
|
-
|
20
19
|
end
|
data/website/index.html
CHANGED
@@ -33,11 +33,246 @@
|
|
33
33
|
<h1>Dr Nic’s Magic Models</h1>
|
34
34
|
<div id="version" class="clickable" onclick='document.location = "http://rubyforge.org/projects/magicmodels"; return false'>
|
35
35
|
Get Version
|
36
|
-
<a href="http://rubyforge.org/projects/magicmodels" class="numbers">0.
|
36
|
+
<a href="http://rubyforge.org/projects/magicmodels" class="numbers">0.7.0</a>
|
37
37
|
</div>
|
38
|
-
<
|
38
|
+
<blockquote>
|
39
|
+
<strong>conjure</strong> 1. To create events that appear to be magical<br />
|
40
|
+
- <a href="http://www.dcopperfield.com/">David Copperfield</a> website
|
41
|
+
</blockquote>
|
42
|
+
If you’ve used Ruby on Rails you’ll have written at least one model class like this:
|
43
|
+
|
44
|
+
<p><pre class="syntax">
|
45
|
+
<span class="keyword">class </span><span class="class">Person</span> <span class="punct"><</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Base</span>
|
46
|
+
<span class="ident">has_many</span> <span class="symbol">:memberships</span>
|
47
|
+
<span class="ident">has_many</span> <span class="symbol">:groups</span><span class="punct">,</span> <span class="symbol">:through</span> <span class="punct">=></span> <span class="symbol">:memberships</span>
|
48
|
+
<span class="ident">belongs_to</span> <span class="symbol">:family</span>
|
49
|
+
<span class="ident">validates_presence_of</span> <span class="symbol">:firstname</span><span class="punct">,</span> <span class="symbol">:lastname</span><span class="punct">,</span> <span class="symbol">:email</span>
|
50
|
+
<span class="keyword">end</span>
|
51
|
+
</pre></p>
|
52
|
+
|
53
|
+
|
54
|
+
<p>A few minutes later you’ll have wondered to yourself,</p>
|
55
|
+
|
56
|
+
|
57
|
+
<blockquote>
|
58
|
+
Why do I have write my own <code>has_many</code>, <code>belongs_to</code>, and <code>validates_presence_of</code>
|
59
|
+
commands if all the data is in the database schema?
|
60
|
+
</blockquote>
|
61
|
+
|
62
|
+
<p>Now, for the very first time, your classes can look like this:</p>
|
63
|
+
|
64
|
+
|
65
|
+
<p><pre class="syntax">
|
66
|
+
<span class="keyword">class </span><span class="class">Person</span> <span class="punct"><</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Base</span>
|
67
|
+
<span class="keyword">end</span>
|
68
|
+
</pre></p>
|
69
|
+
|
70
|
+
|
71
|
+
<p>or, if you are lazy…</p>
|
72
|
+
|
73
|
+
|
74
|
+
<p><pre class="syntax">
|
75
|
+
<span class="keyword">class </span><span class="class">Person</span> <span class="punct"><</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Base</span><span class="punct">;</span> <span class="keyword">end</span>
|
76
|
+
</pre></p>
|
77
|
+
|
78
|
+
|
79
|
+
<p>or, if you read right to the end of this page, this…</p>
|
80
|
+
|
81
|
+
|
82
|
+
<p><pre class="syntax">
|
83
|
+
|
84
|
+
</pre></p>
|
85
|
+
|
86
|
+
|
87
|
+
<p>Magic and mystery abound. All for you. Impress your friends, amaze your mother.</p>
|
88
|
+
|
89
|
+
|
90
|
+
<p><span class="caps">NOTE</span>: The gratuitous use of <strong>Dr Nic’s</strong> in the name should only enhance the mystical magicery,
|
91
|
+
for magic needs a magician; and I love magic. I always wanted to create my own magic trick.
|
92
|
+
So I shall be the magician for the sake of magic itself. I look a bit like Harry Potter
|
93
|
+
if Harry were 31 and better dressed.</p>
|
94
|
+
|
95
|
+
|
96
|
+
<h2>Installation</h2>
|
97
|
+
|
98
|
+
|
99
|
+
To install the Dr Nic’s Magic Models gem you can run the following command to
|
100
|
+
fetch the gem remotely from RubyForge:
|
101
|
+
<pre>
|
102
|
+
gem install dr_nic_magic_models
|
103
|
+
</pre>
|
104
|
+
|
105
|
+
<p>or <a href="http://rubyforge.org/projects/magicmodels">download the gem manually</a> and
|
106
|
+
run the above command in the download directory.</p>
|
107
|
+
|
108
|
+
|
109
|
+
<p>Now you need to <code>require</code> the gem into your Ruby/Rails app. Insert the following
|
110
|
+
line into your script (use <code>config/environment.rb</code> for your Rails apps):</p>
|
111
|
+
|
112
|
+
|
113
|
+
<pre>
|
114
|
+
require 'dr_nic_magic_models'
|
115
|
+
</pre>
|
116
|
+
|
117
|
+
<p>Your application is now blessed with magical mystery.</p>
|
118
|
+
|
119
|
+
|
120
|
+
<h2>David Copperfield eat your Ruby-crusted heart out</h2>
|
121
|
+
|
122
|
+
|
123
|
+
<p>Let’s demonstrate the magical mystery in all its full-stage glory. Create a Ruby on Rails app:</p>
|
124
|
+
|
125
|
+
|
126
|
+
<p><pre class="syntax">
|
127
|
+
<span class="ident">rails</span> <span class="ident">magic_show</span>
|
128
|
+
<span class="ident">cd</span> <span class="ident">magic_show</span>
|
129
|
+
<span class="ident">ruby</span> <span class="ident">script</span><span class="punct">/</span><span class="ident">generate</span> <span class="ident">model</span> <span class="constant">Person</span>
|
130
|
+
<span class="ident">ruby</span> <span class="ident">script</span><span class="punct">/</span><span class="ident">generate</span> <span class="ident">model</span> <span class="constant">Group</span>
|
131
|
+
<span class="ident">ruby</span> <span class="ident">script</span><span class="punct">/</span><span class="ident">generate</span> <span class="ident">model</span> <span class="constant">Membership</span>
|
132
|
+
</pre></p>
|
133
|
+
|
134
|
+
|
135
|
+
<p>Update the migration <code>001_create_people.rb</code> with:
|
136
|
+
<pre class="syntax">
|
137
|
+
<span class="keyword">class </span><span class="class">CreatePeople</span> <span class="punct"><</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Migration</span>
|
138
|
+
<span class="keyword">def </span><span class="method">self.up</span>
|
139
|
+
<span class="ident">create_table</span> <span class="symbol">:people</span> <span class="keyword">do</span> <span class="punct">|</span><span class="ident">t</span><span class="punct">|</span>
|
140
|
+
<span class="ident">t</span><span class="punct">.</span><span class="ident">column</span> <span class="symbol">:firstname</span><span class="punct">,</span> <span class="symbol">:string</span><span class="punct">,</span> <span class="symbol">:null</span> <span class="punct">=></span> <span class="constant">false</span>
|
141
|
+
<span class="ident">t</span><span class="punct">.</span><span class="ident">column</span> <span class="symbol">:lastname</span><span class="punct">,</span> <span class="symbol">:string</span><span class="punct">,</span> <span class="symbol">:null</span> <span class="punct">=></span> <span class="constant">false</span>
|
142
|
+
<span class="ident">t</span><span class="punct">.</span><span class="ident">column</span> <span class="symbol">:email</span><span class="punct">,</span> <span class="symbol">:string</span><span class="punct">,</span> <span class="symbol">:null</span> <span class="punct">=></span> <span class="constant">false</span>
|
143
|
+
<span class="keyword">end</span>
|
144
|
+
<span class="keyword">end</span>
|
145
|
+
|
146
|
+
<span class="keyword">def </span><span class="method">self.down</span>
|
147
|
+
<span class="ident">drop_table</span> <span class="symbol">:people</span>
|
148
|
+
<span class="keyword">end</span>
|
149
|
+
<span class="keyword">end</span>
|
150
|
+
</pre></p>
|
151
|
+
|
152
|
+
|
153
|
+
<p>Similarly, update the <code>def self.up</code> method of <code>002_create_groups.rb</code>
|
154
|
+
with:
|
155
|
+
<pre class="syntax">
|
156
|
+
<span class="ident">create_table</span> <span class="symbol">:groups</span> <span class="keyword">do</span> <span class="punct">|</span><span class="ident">t</span><span class="punct">|</span>
|
157
|
+
<span class="ident">t</span><span class="punct">.</span><span class="ident">column</span> <span class="symbol">:name</span><span class="punct">,</span> <span class="symbol">:string</span><span class="punct">,</span> <span class="symbol">:null</span> <span class="punct">=></span> <span class="constant">false</span>
|
158
|
+
<span class="ident">t</span><span class="punct">.</span><span class="ident">column</span> <span class="symbol">:description</span><span class="punct">,</span> <span class="symbol">:datetime</span>
|
159
|
+
<span class="keyword">end</span>
|
160
|
+
</pre></p>
|
161
|
+
|
162
|
+
|
163
|
+
<p>and <code>003_create_memberships.rb</code> with:
|
164
|
+
<pre class="syntax">
|
165
|
+
<span class="ident">create_table</span> <span class="symbol">:memberships</span> <span class="keyword">do</span> <span class="punct">|</span><span class="ident">t</span><span class="punct">|</span>
|
166
|
+
<span class="ident">t</span><span class="punct">.</span><span class="ident">column</span> <span class="symbol">:person_id</span><span class="punct">,</span> <span class="symbol">:integer</span><span class="punct">,</span> <span class="symbol">:null</span> <span class="punct">=></span> <span class="constant">false</span>
|
167
|
+
<span class="ident">t</span><span class="punct">.</span><span class="ident">column</span> <span class="symbol">:group_id</span><span class="punct">,</span> <span class="symbol">:integer</span><span class="punct">,</span> <span class="symbol">:null</span> <span class="punct">=></span> <span class="constant">false</span>
|
168
|
+
<span class="keyword">end</span>
|
169
|
+
</pre></p>
|
170
|
+
|
171
|
+
|
172
|
+
Now create your database. For MySql:
|
173
|
+
<pre>
|
174
|
+
mysqladmin -u root create magic_show_development
|
175
|
+
mysqladmin -u root create magic_show_test
|
176
|
+
</pre>
|
177
|
+
|
178
|
+
And run your migrations to create the three tables:
|
179
|
+
<pre>
|
180
|
+
rake migrate
|
181
|
+
</pre>
|
182
|
+
|
183
|
+
<h2>And now for some <a href="http://en.wikipedia.org/wiki/List_of_conjuring_terms">woofle dust</a> ...</h2>
|
184
|
+
|
185
|
+
|
186
|
+
<p>At the end of <code>config/environment.rb</code> add the following line:</p>
|
187
|
+
|
188
|
+
|
189
|
+
<pre>
|
190
|
+
require 'dr_nic_magic_models'
|
191
|
+
</pre>
|
192
|
+
|
193
|
+
<p>Now, let’s do a magic trick. First, let’s check our model classes (<code>app/models/person.rb</code> etc):</p>
|
194
|
+
|
195
|
+
|
196
|
+
<p><pre class="syntax">
|
197
|
+
<span class="keyword">class </span><span class="class">Person</span> <span class="punct"><</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Base</span>
|
198
|
+
<span class="keyword">end</span>
|
199
|
+
<span class="keyword">class </span><span class="class">Group</span> <span class="punct"><</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Base</span>
|
200
|
+
<span class="keyword">end</span>
|
201
|
+
<span class="keyword">class </span><span class="class">Membership</span> <span class="punct"><</span> <span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Base</span>
|
202
|
+
<span class="keyword">end</span>
|
203
|
+
</pre></p>
|
204
|
+
|
205
|
+
|
206
|
+
<p>Nothing suspicious here. We have no validations and no associations. Just some plain old model classes.</p>
|
207
|
+
|
208
|
+
|
209
|
+
<p>For this trick, we’ll need an ordinary console session. Any old one lying around the house will do.</p>
|
210
|
+
|
211
|
+
|
212
|
+
<pre>
|
213
|
+
ruby script/console
|
214
|
+
</pre>
|
215
|
+
|
216
|
+
<p>Now a normal model class is valid until you explicitly add <code>validates_xxx</code> commands.
|
217
|
+
With Dr Nic’s Magic Models:</p>
|
218
|
+
|
219
|
+
|
220
|
+
<p><pre class="syntax">
|
221
|
+
<span class="punct">>></span> <span class="ident">person</span> <span class="punct">=</span> <span class="constant">Person</span><span class="punct">.</span><span class="ident">new</span>
|
222
|
+
<span class="punct">=></span> <span class="comment">#<Person:0x393e0f8 @attributes={"lastname"=>"", "firstname"=>"", "email"=>""}, @new_record=true></span>
|
223
|
+
<span class="punct">>></span> <span class="ident">person</span><span class="punct">.</span><span class="ident">valid?</span>
|
224
|
+
<span class="punct">=></span> <span class="constant">false</span>
|
225
|
+
<span class="punct">>></span> <span class="ident">person</span><span class="punct">.</span><span class="ident">errors</span>
|
226
|
+
<span class="punct">=></span> <span class="punct"><</span><span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Errors</span><span class="punct">:</span><span class="number">0x3938b18</span> <span class="attribute">@errors</span><span class="punct">={"</span><span class="string">firstname</span><span class="punct">"=>["</span><span class="string">can't be blank</span><span class="punct">"],</span>
|
227
|
+
<span class="punct">"</span><span class="string">lastname</span><span class="punct">"=>["</span><span class="string">can't be blank</span><span class="punct">"],</span> <span class="punct">"</span><span class="string">email</span><span class="punct">"=>["</span><span class="string">can't be blank</span><span class="punct">"]},</span>
|
228
|
+
<span class="attribute">@base</span><span class="punct">=<</span><span class="constant">Person</span><span class="punct">:</span><span class="number">0x393e0f8</span> <span class="attribute">@errors</span><span class="punct">=<</span><span class="constant">ActiveRecord</span><span class="punct">::</span><span class="constant">Errors</span><span class="punct">:</span><span class="number">0x3938b18</span> <span class="punct">...>,</span>
|
229
|
+
<span class="attribute">@attributes</span><span class="punct">={"</span><span class="string">lastname</span><span class="punct">"=>"</span><span class="string"></span><span class="punct">",</span> <span class="punct">"</span><span class="string">firstname</span><span class="punct">"=>"</span><span class="string"></span><span class="punct">",</span> <span class="punct">"</span><span class="string">email</span><span class="punct">"=>"</span><span class="string"></span><span class="punct">"},</span> <span class="attribute">@new_record</span><span class="punct">=</span><span class="constant">true</span><span class="punct">>></span>
|
230
|
+
</pre></p>
|
231
|
+
|
232
|
+
|
233
|
+
<p><strong>Kapoow!</strong> Instant validation!</p>
|
234
|
+
|
235
|
+
|
236
|
+
<p>Because you specified the three columns as <code>:null => false</code>,
|
237
|
+
your ActiveRecord models will now automatically generated <code>validates_presence_of</code>
|
238
|
+
for each non-null field.</p>
|
239
|
+
|
240
|
+
|
241
|
+
<p>Ok, we’re just warming up.</p>
|
242
|
+
|
243
|
+
|
244
|
+
<p>Your models normally require association commands (<code>has_many</code>, <code>belongs_to</code>, etc, as
|
245
|
+
demonstrated above) to have the brilliantly simple support that Rails/ActiveRecords are known for.</p>
|
246
|
+
|
247
|
+
|
248
|
+
<p>Let’s just watch what Dr Nic’s Magic Models can do without any effort at all…</p>
|
249
|
+
|
250
|
+
|
251
|
+
<p><pre class="syntax">
|
252
|
+
<span class="punct">>></span> <span class="ident">person</span> <span class="punct">=</span> <span class="constant">Person</span><span class="punct">.</span><span class="ident">create</span><span class="punct">(</span><span class="symbol">:firstname</span> <span class="punct">=></span> <span class="punct">"</span><span class="string">Nic</span><span class="punct">",</span> <span class="symbol">:lastname</span> <span class="punct">=></span> <span class="punct">"</span><span class="string">Williams</span><span class="punct">",</span> <span class="symbol">:email</span> <span class="punct">=></span> <span class="punct">"</span><span class="string">drnicwilliams@gmail.com</span><span class="punct">")</span>
|
253
|
+
<span class="punct">>></span> <span class="ident">group</span> <span class="punct">=</span> <span class="constant">Group</span><span class="punct">.</span><span class="ident">create</span><span class="punct">(</span><span class="symbol">:name</span> <span class="punct">=></span> <span class="punct">"</span><span class="string">Magic Models Forum</span><span class="punct">",</span> <span class="symbol">:description</span> <span class="punct">=></span> <span class="punct">"</span><span class="string">http://groups.google.com/magicmodels</span><span class="punct">")</span>
|
254
|
+
<span class="punct">>></span> <span class="ident">membership</span> <span class="punct">=</span> <span class="constant">Membership</span><span class="punct">.</span><span class="ident">create</span><span class="punct">(</span><span class="symbol">:person_id</span> <span class="punct">=></span> <span class="ident">person</span><span class="punct">,</span> <span class="symbol">:group_id</span> <span class="punct">=></span> <span class="ident">group</span><span class="punct">)</span>
|
255
|
+
<span class="punct">>></span> <span class="ident">person</span><span class="punct">.</span><span class="ident">memberships</span><span class="punct">.</span><span class="ident">length</span>
|
256
|
+
<span class="punct">=></span> <span class="number">1</span>
|
257
|
+
<span class="punct">>></span> <span class="ident">membership</span><span class="punct">.</span><span class="ident">person</span>
|
258
|
+
<span class="punct">=></span> <span class="punct"><</span><span class="constant">Person</span><span class="punct">:</span><span class="number">0x38898e8</span> <span class="attribute">@attributes</span><span class="punct">={"</span><span class="string">lastname</span><span class="punct">"=>"</span><span class="string">Williams</span><span class="punct">",</span> <span class="punct">"</span><span class="string">firstname</span><span class="punct">"=>"</span><span class="string">Nic</span><span class="punct">",</span>
|
259
|
+
<span class="punct">"</span><span class="string">id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">",</span> <span class="punct">"</span><span class="string">email</span><span class="punct">"=>"</span><span class="string">drnicwilliams@gmail.com</span><span class="punct">"}></span>
|
260
|
+
<span class="punct">>></span> <span class="ident">group</span><span class="punct">.</span><span class="ident">memberships</span>
|
261
|
+
<span class="punct">=></span> <span class="punct">[<</span><span class="constant">Membership</span><span class="punct">:</span><span class="number">0x3c8cd70</span> <span class="attribute">@attributes</span><span class="punct">={"</span><span class="string">group_id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">",</span> <span class="punct">"</span><span class="string">id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">",</span> <span class="punct">"</span><span class="string">person_id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">"}>]</span>
|
262
|
+
</pre></p>
|
263
|
+
|
264
|
+
|
265
|
+
<p>The final association trick is a ripper. Automatic generation of <code>has_many :through</code> associations…</p>
|
266
|
+
|
267
|
+
|
268
|
+
<p><pre class="syntax">
|
269
|
+
<span class="punct">>></span> <span class="ident">person</span><span class="punct">.</span><span class="ident">groups</span>
|
270
|
+
<span class="punct">=></span> <span class="punct">[<</span><span class="constant">Group</span><span class="punct">:</span><span class="number">0x39047e0</span> <span class="attribute">@attributes</span><span class="punct">={"</span><span class="string">name</span><span class="punct">"=>"</span><span class="string">Magic Models Forum</span><span class="punct">",</span> <span class="punct">"</span><span class="string">id</span><span class="punct">"=>"</span><span class="string">1</span><span class="punct">",</span> <span class="punct">"</span><span class="string">description</span><span class="punct">"=></span><span class="constant">nil</span><span class="punct">}>]</span>
|
271
|
+
<span class="punct">>></span> <span class="ident">group</span><span class="punct">.</span><span class="ident">people</span>
|
272
|
+
<span class="punct">=></span>
|
273
|
+
</pre></p>
|
39
274
|
<p class="coda">
|
40
|
-
<a href="mailto:drnicwilliams@gmail.com">Dr Nic</a>,
|
275
|
+
<a href="mailto:drnicwilliams@gmail.com">Dr Nic</a>, 7th August 2006<br>
|
41
276
|
Theme extended from <a href="http://rb2js.rubyforge.org/">Paul Battley</a>
|
42
277
|
</p>
|
43
278
|
</div>
|
data/website/index.txt
CHANGED
@@ -1,4 +1,204 @@
|
|
1
1
|
h1. Dr Nic's Magic Models
|
2
2
|
|
3
|
-
|
3
|
+
<blockquote>
|
4
|
+
*conjure* 1. To create events that appear to be magical<br />
|
5
|
+
- "David Copperfield":http://www.dcopperfield.com/ website
|
6
|
+
</blockquote>
|
7
|
+
If you've used Ruby on Rails you'll have written at least one model class like this:
|
4
8
|
|
9
|
+
<pre syntax='ruby'>
|
10
|
+
class Person < ActiveRecord::Base
|
11
|
+
has_many :memberships
|
12
|
+
has_many :groups, :through => :memberships
|
13
|
+
belongs_to :family
|
14
|
+
validates_presence_of :firstname, :lastname, :email
|
15
|
+
end
|
16
|
+
</pre>
|
17
|
+
|
18
|
+
A few minutes later you'll have wondered to yourself,
|
19
|
+
|
20
|
+
<blockquote>
|
21
|
+
Why do I have write my own <code>has_many</code>, <code>belongs_to</code>, and <code>validates_presence_of</code>
|
22
|
+
commands if all the data is in the database schema?
|
23
|
+
</blockquote>
|
24
|
+
|
25
|
+
Now, for the very first time, your classes can look like this:
|
26
|
+
|
27
|
+
<pre syntax='ruby'>
|
28
|
+
class Person < ActiveRecord::Base
|
29
|
+
end
|
30
|
+
</pre>
|
31
|
+
|
32
|
+
or, if you are lazy...
|
33
|
+
|
34
|
+
<pre syntax='ruby'>
|
35
|
+
class Person < ActiveRecord::Base; end
|
36
|
+
</pre>
|
37
|
+
|
38
|
+
or, if you read right to the end of this page, this...
|
39
|
+
|
40
|
+
<pre syntax='ruby'>
|
41
|
+
|
42
|
+
</pre>
|
43
|
+
|
44
|
+
Magic and mystery abound. All for you. Impress your friends, amaze your mother.
|
45
|
+
|
46
|
+
NOTE: The gratuitous use of *Dr Nic's* in the name should only enhance the mystical magicery,
|
47
|
+
for magic needs a magician; and I love magic. I always wanted to create my own magic trick.
|
48
|
+
So I shall be the magician for the sake of magic itself. I look a bit like Harry Potter
|
49
|
+
if Harry were 31 and better dressed.
|
50
|
+
|
51
|
+
h2. Installation
|
52
|
+
|
53
|
+
To install the Dr Nic's Magic Models gem you can run the following command to
|
54
|
+
fetch the gem remotely from RubyForge:
|
55
|
+
<pre>
|
56
|
+
gem install dr_nic_magic_models
|
57
|
+
</pre>
|
58
|
+
|
59
|
+
or "download the gem manually":http://rubyforge.org/projects/magicmodels and
|
60
|
+
run the above command in the download directory.
|
61
|
+
|
62
|
+
Now you need to <code>require</code> the gem into your Ruby/Rails app. Insert the following
|
63
|
+
line into your script (use <code>config/environment.rb</code> for your Rails apps):
|
64
|
+
|
65
|
+
<pre>
|
66
|
+
require 'dr_nic_magic_models'
|
67
|
+
</pre>
|
68
|
+
|
69
|
+
Your application is now blessed with magical mystery.
|
70
|
+
|
71
|
+
h2. David Copperfield eat your Ruby-crusted heart out
|
72
|
+
|
73
|
+
Let's demonstrate the magical mystery in all its full-stage glory. Create a Ruby on Rails app:
|
74
|
+
|
75
|
+
<pre syntax="ruby">
|
76
|
+
rails magic_show
|
77
|
+
cd magic_show
|
78
|
+
ruby script/generate model Person
|
79
|
+
ruby script/generate model Group
|
80
|
+
ruby script/generate model Membership
|
81
|
+
</pre>
|
82
|
+
|
83
|
+
Update the migration <code>001_create_people.rb</code> with:
|
84
|
+
<pre syntax="ruby">
|
85
|
+
class CreatePeople < ActiveRecord::Migration
|
86
|
+
def self.up
|
87
|
+
create_table :people do |t|
|
88
|
+
t.column :firstname, :string, :null => false
|
89
|
+
t.column :lastname, :string, :null => false
|
90
|
+
t.column :email, :string, :null => false
|
91
|
+
end
|
92
|
+
end
|
93
|
+
|
94
|
+
def self.down
|
95
|
+
drop_table :people
|
96
|
+
end
|
97
|
+
end
|
98
|
+
</pre>
|
99
|
+
|
100
|
+
Similarly, update the <code>def self.up</code> method of <code>002_create_groups.rb</code>
|
101
|
+
with:
|
102
|
+
<pre syntax="ruby">
|
103
|
+
create_table :groups do |t|
|
104
|
+
t.column :name, :string, :null => false
|
105
|
+
t.column :description, :datetime
|
106
|
+
end
|
107
|
+
</pre>
|
108
|
+
|
109
|
+
and <code>003_create_memberships.rb</code> with:
|
110
|
+
<pre syntax="ruby">
|
111
|
+
create_table :memberships do |t|
|
112
|
+
t.column :person_id, :integer, :null => false
|
113
|
+
t.column :group_id, :integer, :null => false
|
114
|
+
end
|
115
|
+
</pre>
|
116
|
+
|
117
|
+
Now create your database. For MySql:
|
118
|
+
<pre>
|
119
|
+
mysqladmin -u root create magic_show_development
|
120
|
+
mysqladmin -u root create magic_show_test
|
121
|
+
</pre>
|
122
|
+
|
123
|
+
And run your migrations to create the three tables:
|
124
|
+
<pre>
|
125
|
+
rake migrate
|
126
|
+
</pre>
|
127
|
+
|
128
|
+
h2. And now for some "woofle dust":http://en.wikipedia.org/wiki/List_of_conjuring_terms ...
|
129
|
+
|
130
|
+
At the end of <code>config/environment.rb</code> add the following line:
|
131
|
+
|
132
|
+
<pre>
|
133
|
+
require 'dr_nic_magic_models'
|
134
|
+
</pre>
|
135
|
+
|
136
|
+
Now, let's do a magic trick. First, let's check our model classes (<code>app/models/person.rb</code> etc):
|
137
|
+
|
138
|
+
<pre syntax="ruby">
|
139
|
+
class Person < ActiveRecord::Base
|
140
|
+
end
|
141
|
+
class Group < ActiveRecord::Base
|
142
|
+
end
|
143
|
+
class Membership < ActiveRecord::Base
|
144
|
+
end
|
145
|
+
</pre>
|
146
|
+
|
147
|
+
Nothing suspicious here. We have no validations and no associations. Just some plain old model classes.
|
148
|
+
|
149
|
+
For this trick, we'll need an ordinary console session. Any old one lying around the house will do.
|
150
|
+
|
151
|
+
<pre>
|
152
|
+
ruby script/console
|
153
|
+
</pre>
|
154
|
+
|
155
|
+
Now a normal model class is valid until you explicitly add <code>validates_xxx</code> commands.
|
156
|
+
With Dr Nic's Magic Models:
|
157
|
+
|
158
|
+
<pre syntax="ruby">
|
159
|
+
>> person = Person.new
|
160
|
+
=> #<Person:0x393e0f8 @attributes={"lastname"=>"", "firstname"=>"", "email"=>""}, @new_record=true>
|
161
|
+
>> person.valid?
|
162
|
+
=> false
|
163
|
+
>> person.errors
|
164
|
+
=> <ActiveRecord::Errors:0x3938b18 @errors={"firstname"=>["can't be blank"],
|
165
|
+
"lastname"=>["can't be blank"], "email"=>["can't be blank"]},
|
166
|
+
@base=<Person:0x393e0f8 @errors=<ActiveRecord::Errors:0x3938b18 ...>,
|
167
|
+
@attributes={"lastname"=>"", "firstname"=>"", "email"=>""}, @new_record=true>>
|
168
|
+
</pre>
|
169
|
+
|
170
|
+
*Kapoow!* Instant validation!
|
171
|
+
|
172
|
+
Because you specified the three columns as <code>:null => false</code>,
|
173
|
+
your ActiveRecord models will now automatically generated <code>validates_presence_of</code>
|
174
|
+
for each non-null field.
|
175
|
+
|
176
|
+
Ok, we're just warming up.
|
177
|
+
|
178
|
+
Your models normally require association commands (<code>has_many</code>, <code>belongs_to</code>, etc, as
|
179
|
+
demonstrated above) to have the brilliantly simple support that Rails/ActiveRecords are known for.
|
180
|
+
|
181
|
+
Let's just watch what Dr Nic's Magic Models can do without any effort at all...
|
182
|
+
|
183
|
+
<pre syntax="ruby">
|
184
|
+
>> person = Person.create(:firstname => "Nic", :lastname => "Williams", :email => "drnicwilliams@gmail.com")
|
185
|
+
>> group = Group.create(:name => "Magic Models Forum", :description => "http://groups.google.com/magicmodels")
|
186
|
+
>> membership = Membership.create(:person_id => person, :group_id => group)
|
187
|
+
>> person.memberships.length
|
188
|
+
=> 1
|
189
|
+
>> membership.person
|
190
|
+
=> <Person:0x38898e8 @attributes={"lastname"=>"Williams", "firstname"=>"Nic",
|
191
|
+
"id"=>"1", "email"=>"drnicwilliams@gmail.com"}>
|
192
|
+
>> group.memberships
|
193
|
+
=> [<Membership:0x3c8cd70 @attributes={"group_id"=>"1", "id"=>"1", "person_id"=>"1"}>]
|
194
|
+
</pre>
|
195
|
+
|
196
|
+
|
197
|
+
The final association trick is a ripper. Automatic generation of <code>has_many :through</code> associations...
|
198
|
+
|
199
|
+
<pre syntax="ruby">
|
200
|
+
>> person.groups
|
201
|
+
=> [<Group:0x39047e0 @attributes={"name"=>"Magic Models Forum", "id"=>"1", "description"=>nil}>]
|
202
|
+
>> group.people
|
203
|
+
=>
|
204
|
+
</pre>
|
metadata
CHANGED
@@ -3,8 +3,8 @@ rubygems_version: 0.8.11
|
|
3
3
|
specification_version: 1
|
4
4
|
name: dr_nic_magic_models
|
5
5
|
version: !ruby/object:Gem::Version
|
6
|
-
version: 0.
|
7
|
-
date: 2006-07
|
6
|
+
version: 0.7.0
|
7
|
+
date: 2006-08-07 00:00:00 +02:00
|
8
8
|
summary: Invisible validations, assocations and Active Record models themselves!
|
9
9
|
require_paths:
|
10
10
|
- lib
|
@@ -12,7 +12,7 @@ email: drnicwilliams@gmail.com
|
|
12
12
|
homepage: http://magicmodels.rubyforge.org
|
13
13
|
rubyforge_project: magicmodels
|
14
14
|
description: Associations and validations are automagically available to your ActiveRecord models. Model classes themselves are automagically generated if you haven't defined them already!
|
15
|
-
autorequire:
|
15
|
+
autorequire: dr_nic_magic_models
|
16
16
|
default_executable:
|
17
17
|
bindir: bin
|
18
18
|
has_rdoc: true
|
@@ -32,27 +32,30 @@ files:
|
|
32
32
|
- install.rb
|
33
33
|
- README
|
34
34
|
- CHANGELOG
|
35
|
-
- lib/dr_nic
|
36
|
-
- lib/dr_nic.rb
|
37
35
|
- lib/module.rb
|
36
|
+
- lib/dr_nic_magic_models
|
37
|
+
- lib/dr_nic_magic_models.rb
|
38
|
+
- lib/new_base.rb
|
38
39
|
- lib/base.rb
|
39
|
-
- lib/
|
40
|
-
- lib/
|
41
|
-
- lib/
|
42
|
-
- lib/
|
40
|
+
- lib/dr_nic_magic_models/associations.rb
|
41
|
+
- lib/dr_nic_magic_models/schema.rb
|
42
|
+
- lib/dr_nic_magic_models/validations.rb
|
43
|
+
- lib/dr_nic_magic_models/version.rb
|
43
44
|
- test/connections
|
44
45
|
- test/fixtures
|
45
46
|
- test/abstract_unit.rb
|
46
47
|
- test/dummy_test.rb
|
47
48
|
- test/invisible_model_classes_test.rb
|
48
49
|
- test/env_test.rb
|
50
|
+
- test/invisible_model_access_test.rb
|
51
|
+
- test/invisible_model_assoc_test.rb
|
49
52
|
- test/connections/native_mysql
|
50
53
|
- test/connections/native_mysql/connection.rb
|
51
54
|
- test/fixtures/db_definitions
|
52
|
-
- test/fixtures/
|
53
|
-
- test/fixtures/
|
54
|
-
- test/fixtures/
|
55
|
-
- test/fixtures/
|
55
|
+
- test/fixtures/groups.yml
|
56
|
+
- test/fixtures/group_tag.yml
|
57
|
+
- test/fixtures/group_memberships.yml
|
58
|
+
- test/fixtures/fun_users.yml
|
56
59
|
- test/fixtures/db_definitions/mysql.sql
|
57
60
|
- test/fixtures/db_definitions/mysql.drop.sql
|
58
61
|
- website/index.txt
|
@@ -1,45 +0,0 @@
|
|
1
|
-
module DrNic
|
2
|
-
module MagicModels
|
3
|
-
class Schema
|
4
|
-
class << self
|
5
|
-
def models
|
6
|
-
@@models ||= nil
|
7
|
-
load_schema if @@models.blank?
|
8
|
-
@@models
|
9
|
-
end
|
10
|
-
|
11
|
-
def reset
|
12
|
-
@@models = nil
|
13
|
-
end
|
14
|
-
|
15
|
-
def load_schema
|
16
|
-
if conn = ActiveRecord::Base.connection
|
17
|
-
@@models = DrNic::MagicModels::ModelHash.new
|
18
|
-
conn.tables.inspect
|
19
|
-
@@models.merge! conn.tables.inject({}) {|model_list,table_name| model_list[ActiveRecord::Base.class_name(table_name)] = table_name; model_list}
|
20
|
-
end
|
21
|
-
end
|
22
|
-
end
|
23
|
-
end
|
24
|
-
|
25
|
-
class ModelHash < Hash
|
26
|
-
def unenquire(class_id)
|
27
|
-
@enquired ||= {}
|
28
|
-
@enquired[class_id = class_id.to_s] = false
|
29
|
-
end
|
30
|
-
|
31
|
-
def enquired?(class_id)
|
32
|
-
@enquired ||= {}
|
33
|
-
result = @enquired[class_id.to_s]
|
34
|
-
#@enquired[class_id = class_id.to_s] = true
|
35
|
-
result
|
36
|
-
end
|
37
|
-
|
38
|
-
def [](class_id)
|
39
|
-
enquired?(class_id = class_id.to_s)
|
40
|
-
@enquired[class_id] = true
|
41
|
-
super(class_id)
|
42
|
-
end
|
43
|
-
end
|
44
|
-
end
|
45
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
module DrNic
|
2
|
-
module MagicModels
|
3
|
-
module Validations
|
4
|
-
def self.append_features(base)
|
5
|
-
super
|
6
|
-
base.extend(ClassMethods)
|
7
|
-
base.generate_validations
|
8
|
-
|
9
|
-
# Currently only invoked on generated classes
|
10
|
-
# How to include and invoke on all ARs? - hook into class loading??
|
11
|
-
end
|
12
|
-
|
13
|
-
module ClassMethods
|
14
|
-
def generate_validations
|
15
|
-
unless @@generated_validations ||= false
|
16
|
-
@@generated_validations = true
|
17
|
-
column_names = self.columns.select {|column| !column.null and !column.primary}.map {|column| column.name.to_sym}
|
18
|
-
add_validation :validates_presence_of, column_names
|
19
|
-
end
|
20
|
-
end
|
21
|
-
|
22
|
-
def add_validation(validation, column_names)
|
23
|
-
self.send validation, *column_names
|
24
|
-
end
|
25
|
-
end
|
26
|
-
end
|
27
|
-
end
|
28
|
-
end
|
@@ -1,28 +0,0 @@
|
|
1
|
-
name_prefix_mr:
|
2
|
-
reference_type_id: 1
|
3
|
-
reference_code: 1
|
4
|
-
code_label: MR
|
5
|
-
abbreviation: Mr
|
6
|
-
name_prefix_mrs:
|
7
|
-
reference_type_id: 1
|
8
|
-
reference_code: 2
|
9
|
-
code_label: MRS
|
10
|
-
abbreviation: Mrs
|
11
|
-
name_prefix_ms:
|
12
|
-
reference_type_id: 1
|
13
|
-
reference_code: 3
|
14
|
-
code_label: MS
|
15
|
-
abbreviation: Ms
|
16
|
-
|
17
|
-
gender_male:
|
18
|
-
reference_type_id: 2
|
19
|
-
reference_code: 1
|
20
|
-
code_label: MALE
|
21
|
-
abbreviation: Male
|
22
|
-
gender_female:
|
23
|
-
reference_type_id: 2
|
24
|
-
reference_code: 2
|
25
|
-
code_label: FEMALE
|
26
|
-
abbreviation: Female
|
27
|
-
|
28
|
-
|