m4dbi 0.8.1 → 0.8.2
Sign up to get free protection for your applications and to get access to all the features.
- data/lib/m4dbi/model.rb +19 -6
- data/lib/m4dbi/version.rb +1 -1
- data/spec/collection.rb +84 -0
- data/spec/model.rb +22 -90
- metadata +63 -50
data/lib/m4dbi/model.rb
CHANGED
@@ -2,7 +2,7 @@ module M4DBI
|
|
2
2
|
class Model
|
3
3
|
#attr_reader :row
|
4
4
|
ancestral_trait_reader :dbh, :table
|
5
|
-
ancestral_trait_class_reader :dbh, :table, :pk, :columns, :st
|
5
|
+
ancestral_trait_class_reader :dbh, :table, :pk, :columns, :st, :callbacks
|
6
6
|
|
7
7
|
M4DBI_UNASSIGNED = '__m4dbi_unassigned__'
|
8
8
|
|
@@ -142,9 +142,15 @@ module M4DBI
|
|
142
142
|
end
|
143
143
|
if ! pk_hash.empty?
|
144
144
|
rec = self.one_where( pk_hash )
|
145
|
+
callbacks[:after_create].each do |block|
|
146
|
+
block.yield rec
|
147
|
+
end
|
145
148
|
else
|
146
149
|
begin
|
147
150
|
rec = last_record( dbh_ )
|
151
|
+
callbacks[:after_create].each do |block|
|
152
|
+
block.yield rec
|
153
|
+
end
|
148
154
|
rescue NoMethodError => e
|
149
155
|
# ignore
|
150
156
|
#puts "not implemented: #{e.message}"
|
@@ -221,6 +227,10 @@ module M4DBI
|
|
221
227
|
stm.execute( *params )
|
222
228
|
end
|
223
229
|
|
230
|
+
def self.after_create(&block)
|
231
|
+
callbacks[:after_create] << block
|
232
|
+
end
|
233
|
+
|
224
234
|
# Example:
|
225
235
|
# M4DBI::Model.one_to_many( Author, Post, :posts, :author, :author_id )
|
226
236
|
# her_posts = some_author.posts
|
@@ -413,11 +423,14 @@ module M4DBI
|
|
413
423
|
@models ||= Hash.new
|
414
424
|
@models[ model_key ] ||= Class.new( M4DBI::Model ) do |klass|
|
415
425
|
klass.trait( {
|
416
|
-
:dbh
|
417
|
-
:table
|
418
|
-
:pk
|
419
|
-
:columns
|
420
|
-
:st
|
426
|
+
:dbh => h,
|
427
|
+
:table => table,
|
428
|
+
:pk => pk_,
|
429
|
+
:columns => h.table_schema( table.to_sym ).columns,
|
430
|
+
:st => Hash.new, # prepared statements for all queries
|
431
|
+
:callbacks => {
|
432
|
+
after_create: []
|
433
|
+
}
|
421
434
|
} )
|
422
435
|
|
423
436
|
meta_def( 'pk_str'.to_sym ) do
|
data/lib/m4dbi/version.rb
CHANGED
data/spec/collection.rb
ADDED
@@ -0,0 +1,84 @@
|
|
1
|
+
require File.join(File.dirname(__FILE__), 'helper')
|
2
|
+
|
3
|
+
$dbh = connect_to_spec_database
|
4
|
+
reset_data
|
5
|
+
|
6
|
+
describe 'M4DBI::Collection' do
|
7
|
+
before do
|
8
|
+
@m_author = Class.new( M4DBI::Model( :authors ) )
|
9
|
+
@m_post = Class.new( M4DBI::Model( :posts ) )
|
10
|
+
|
11
|
+
M4DBI::Model.one_to_many(
|
12
|
+
@m_author, @m_post, :posts, :author, :author_id
|
13
|
+
)
|
14
|
+
end
|
15
|
+
|
16
|
+
it 'accepts additions' do
|
17
|
+
num_posts = @m_post.count
|
18
|
+
|
19
|
+
a = @m_author[ 1 ]
|
20
|
+
the_text = 'A new post.'
|
21
|
+
|
22
|
+
num_posts_of_author = a.posts.count
|
23
|
+
|
24
|
+
# Insert without auto-incrementing primary key specified
|
25
|
+
# Try at least as many times as there were records in the DB,
|
26
|
+
# because the sequence used for the IDs is independent of
|
27
|
+
# the actual ID values in the DB for some RDBMSes.
|
28
|
+
num_posts.times do
|
29
|
+
begin
|
30
|
+
a.posts << { :text => the_text }
|
31
|
+
break # Stop on success
|
32
|
+
rescue Exception => e
|
33
|
+
if e.message !~ /duplicate/
|
34
|
+
raise e
|
35
|
+
end
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
a.posts.count.should.equal num_posts_of_author + 1
|
40
|
+
|
41
|
+
p = a.posts.find { |p| p.text == the_text }
|
42
|
+
p.should.not.be.nil
|
43
|
+
p.author.should.equal a
|
44
|
+
|
45
|
+
a_ = @m_author[ 1 ]
|
46
|
+
a_.posts.find { |p| p.text == the_text }.should.not.be.nil
|
47
|
+
|
48
|
+
reset_data
|
49
|
+
end
|
50
|
+
|
51
|
+
it 'facilitates single record deletions' do
|
52
|
+
a = @m_author[ 1 ]
|
53
|
+
posts = a.posts
|
54
|
+
n = posts.size
|
55
|
+
p = posts[ 0 ]
|
56
|
+
|
57
|
+
posts.delete( p ).should.be.true
|
58
|
+
a.posts.size.should.equal( n - 1 )
|
59
|
+
posts.find { |p_| p_ == p }.should.be.nil
|
60
|
+
|
61
|
+
reset_data
|
62
|
+
end
|
63
|
+
|
64
|
+
it 'facilitates multi-record deletions' do
|
65
|
+
a = @m_author[ 1 ]
|
66
|
+
posts = a.posts
|
67
|
+
n = posts.size
|
68
|
+
posts.delete( :text => 'Third post.' ).should.equal 1
|
69
|
+
a.posts.size.should.equal( n - 1 )
|
70
|
+
posts.find { |p| p.text == 'Third post.' }.should.be.nil
|
71
|
+
posts.find { |p| p.text == 'First post.' }.should.not.be.nil
|
72
|
+
|
73
|
+
reset_data
|
74
|
+
end
|
75
|
+
|
76
|
+
it 'facilitates table-wide deletion' do
|
77
|
+
a = @m_author[ 1 ]
|
78
|
+
a.posts.should.not.be.empty
|
79
|
+
a.posts.clear.should.be > 0
|
80
|
+
a.posts.should.be.empty
|
81
|
+
|
82
|
+
reset_data
|
83
|
+
end
|
84
|
+
end
|
data/spec/model.rb
CHANGED
@@ -86,16 +86,16 @@ describe 'A M4DBI::Model subclass' do
|
|
86
86
|
end
|
87
87
|
|
88
88
|
it 'provides the database handle it is using' do
|
89
|
-
begin
|
90
|
-
@m_author.dbh.should.equal $dbh
|
89
|
+
# begin
|
90
|
+
# @m_author.dbh.should.equal $dbh
|
91
91
|
|
92
|
-
dbh = connect_to_spec_database( ENV[ 'M4DBI_DATABASE2' ] || 'm4dbi2' )
|
93
|
-
@m_author2 = Class.new( M4DBI::Model( :authors ) )
|
94
|
-
@m_author2.dbh.should.equal dbh
|
95
|
-
ensure
|
96
|
-
# Clean up handles for later specs
|
97
|
-
connect_to_spec_database
|
98
|
-
end
|
92
|
+
# dbh = connect_to_spec_database( ENV[ 'M4DBI_DATABASE2' ] || 'm4dbi2' )
|
93
|
+
# @m_author2 = Class.new( M4DBI::Model( :authors ) )
|
94
|
+
# @m_author2.dbh.should.equal dbh
|
95
|
+
# ensure
|
96
|
+
# # Clean up handles for later specs
|
97
|
+
# connect_to_spec_database
|
98
|
+
# end
|
99
99
|
end
|
100
100
|
|
101
101
|
it 'maintains distinction from models of the same name in different databases' do
|
@@ -670,6 +670,19 @@ describe 'A M4DBI::Model subclass' do
|
|
670
670
|
|
671
671
|
reset_data
|
672
672
|
end
|
673
|
+
|
674
|
+
it 'after creation, executes code provided in an after_create hook' do
|
675
|
+
class Author < M4DBI::Model( :authors )
|
676
|
+
after_create do |author|
|
677
|
+
$test = 2
|
678
|
+
author.name = 'different name'
|
679
|
+
end
|
680
|
+
end
|
681
|
+
$test.should.not.equal 2
|
682
|
+
a = Author.create(name: 'theauthor')
|
683
|
+
$test.should.equal 2
|
684
|
+
a.name.should.equal 'different name'
|
685
|
+
end
|
673
686
|
end
|
674
687
|
|
675
688
|
describe 'A created M4DBI::Model subclass instance' do
|
@@ -950,7 +963,6 @@ describe 'A found M4DBI::Model subclass instance' do
|
|
950
963
|
p.save!
|
951
964
|
end
|
952
965
|
end
|
953
|
-
|
954
966
|
end
|
955
967
|
|
956
968
|
describe 'M4DBI::Model (relationships)' do
|
@@ -1029,83 +1041,3 @@ describe 'M4DBI::Model (relationships)' do
|
|
1029
1041
|
@m_fan[ 5 ].authors_liked.should.be.empty
|
1030
1042
|
end
|
1031
1043
|
end
|
1032
|
-
|
1033
|
-
describe 'M4DBI::Collection' do
|
1034
|
-
before do
|
1035
|
-
@m_author = Class.new( M4DBI::Model( :authors ) )
|
1036
|
-
@m_post = Class.new( M4DBI::Model( :posts ) )
|
1037
|
-
|
1038
|
-
M4DBI::Model.one_to_many(
|
1039
|
-
@m_author, @m_post, :posts, :author, :author_id
|
1040
|
-
)
|
1041
|
-
end
|
1042
|
-
|
1043
|
-
it 'accepts additions' do
|
1044
|
-
num_posts = @m_post.count
|
1045
|
-
|
1046
|
-
a = @m_author[ 1 ]
|
1047
|
-
the_text = 'A new post.'
|
1048
|
-
|
1049
|
-
num_posts_of_author = a.posts.count
|
1050
|
-
|
1051
|
-
# Insert without auto-incrementing primary key specified
|
1052
|
-
# Try at least as many times as there were records in the DB,
|
1053
|
-
# because the sequence used for the IDs is independent of
|
1054
|
-
# the actual ID values in the DB for some RDBMSes.
|
1055
|
-
num_posts.times do
|
1056
|
-
begin
|
1057
|
-
a.posts << { :text => the_text }
|
1058
|
-
break # Stop on success
|
1059
|
-
rescue Exception => e
|
1060
|
-
if e.message !~ /duplicate/
|
1061
|
-
raise e
|
1062
|
-
end
|
1063
|
-
end
|
1064
|
-
end
|
1065
|
-
|
1066
|
-
a.posts.count.should.equal num_posts_of_author + 1
|
1067
|
-
|
1068
|
-
p = a.posts.find { |p| p.text == the_text }
|
1069
|
-
p.should.not.be.nil
|
1070
|
-
p.author.should.equal a
|
1071
|
-
|
1072
|
-
a_ = @m_author[ 1 ]
|
1073
|
-
a_.posts.find { |p| p.text == the_text }.should.not.be.nil
|
1074
|
-
|
1075
|
-
reset_data
|
1076
|
-
end
|
1077
|
-
|
1078
|
-
it 'facilitates single record deletions' do
|
1079
|
-
a = @m_author[ 1 ]
|
1080
|
-
posts = a.posts
|
1081
|
-
n = posts.size
|
1082
|
-
p = posts[ 0 ]
|
1083
|
-
|
1084
|
-
posts.delete( p ).should.be.true
|
1085
|
-
a.posts.size.should.equal( n - 1 )
|
1086
|
-
posts.find { |p_| p_ == p }.should.be.nil
|
1087
|
-
|
1088
|
-
reset_data
|
1089
|
-
end
|
1090
|
-
|
1091
|
-
it 'facilitates multi-record deletions' do
|
1092
|
-
a = @m_author[ 1 ]
|
1093
|
-
posts = a.posts
|
1094
|
-
n = posts.size
|
1095
|
-
posts.delete( :text => 'Third post.' ).should.equal 1
|
1096
|
-
a.posts.size.should.equal( n - 1 )
|
1097
|
-
posts.find { |p| p.text == 'Third post.' }.should.be.nil
|
1098
|
-
posts.find { |p| p.text == 'First post.' }.should.not.be.nil
|
1099
|
-
|
1100
|
-
reset_data
|
1101
|
-
end
|
1102
|
-
|
1103
|
-
it 'facilitates table-wide deletion' do
|
1104
|
-
a = @m_author[ 1 ]
|
1105
|
-
a.posts.should.not.be.empty
|
1106
|
-
a.posts.clear.should.be > 0
|
1107
|
-
a.posts.should.be.empty
|
1108
|
-
|
1109
|
-
reset_data
|
1110
|
-
end
|
1111
|
-
end
|
metadata
CHANGED
@@ -1,58 +1,61 @@
|
|
1
|
-
--- !ruby/object:Gem::Specification
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
2
|
name: m4dbi
|
3
|
-
version: !ruby/object:Gem::Version
|
4
|
-
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
hash: 59
|
5
5
|
prerelease:
|
6
|
+
segments:
|
7
|
+
- 0
|
8
|
+
- 8
|
9
|
+
- 2
|
10
|
+
version: 0.8.2
|
6
11
|
platform: ruby
|
7
|
-
authors:
|
12
|
+
authors:
|
8
13
|
- Pistos
|
9
14
|
autorequire:
|
10
15
|
bindir: bin
|
11
16
|
cert_chain: []
|
12
|
-
|
13
|
-
|
14
|
-
|
17
|
+
|
18
|
+
date: 2012-06-25 00:00:00 Z
|
19
|
+
dependencies:
|
20
|
+
- !ruby/object:Gem::Dependency
|
15
21
|
name: metaid
|
16
|
-
requirement: !ruby/object:Gem::Requirement
|
17
|
-
none: false
|
18
|
-
requirements:
|
19
|
-
- - ! '>='
|
20
|
-
- !ruby/object:Gem::Version
|
21
|
-
version: '0'
|
22
|
-
type: :runtime
|
23
22
|
prerelease: false
|
24
|
-
|
25
|
-
none: false
|
26
|
-
requirements:
|
27
|
-
- - ! '>='
|
28
|
-
- !ruby/object:Gem::Version
|
29
|
-
version: '0'
|
30
|
-
- !ruby/object:Gem::Dependency
|
31
|
-
name: rdbi
|
32
|
-
requirement: !ruby/object:Gem::Requirement
|
23
|
+
requirement: &id001 !ruby/object:Gem::Requirement
|
33
24
|
none: false
|
34
|
-
requirements:
|
35
|
-
- -
|
36
|
-
- !ruby/object:Gem::Version
|
37
|
-
|
25
|
+
requirements:
|
26
|
+
- - ">="
|
27
|
+
- !ruby/object:Gem::Version
|
28
|
+
hash: 3
|
29
|
+
segments:
|
30
|
+
- 0
|
31
|
+
version: "0"
|
38
32
|
type: :runtime
|
33
|
+
version_requirements: *id001
|
34
|
+
- !ruby/object:Gem::Dependency
|
35
|
+
name: rdbi
|
39
36
|
prerelease: false
|
40
|
-
|
37
|
+
requirement: &id002 !ruby/object:Gem::Requirement
|
41
38
|
none: false
|
42
|
-
requirements:
|
43
|
-
- -
|
44
|
-
- !ruby/object:Gem::Version
|
45
|
-
|
46
|
-
|
47
|
-
|
39
|
+
requirements:
|
40
|
+
- - ">="
|
41
|
+
- !ruby/object:Gem::Version
|
42
|
+
hash: 3
|
43
|
+
segments:
|
44
|
+
- 0
|
45
|
+
version: "0"
|
46
|
+
type: :runtime
|
47
|
+
version_requirements: *id002
|
48
|
+
description: M4DBI provides models, associations and some convenient extensions to RDBI.
|
48
49
|
email: m4dbi dot pistos at purepistos dot net
|
49
50
|
executables: []
|
51
|
+
|
50
52
|
extensions: []
|
51
|
-
|
53
|
+
|
54
|
+
extra_rdoc_files:
|
52
55
|
- README.rdoc
|
53
56
|
- CHANGELOG
|
54
57
|
- LICENCE
|
55
|
-
files:
|
58
|
+
files:
|
56
59
|
- README.rdoc
|
57
60
|
- CHANGELOG
|
58
61
|
- LICENCE
|
@@ -69,34 +72,44 @@ files:
|
|
69
72
|
- spec/database.rb
|
70
73
|
- spec/model.rb
|
71
74
|
- spec/helper.rb
|
75
|
+
- spec/collection.rb
|
72
76
|
- spec/hash.rb
|
73
77
|
homepage: https://github.com/Pistos/m4dbi
|
74
78
|
licenses: []
|
79
|
+
|
75
80
|
post_install_message:
|
76
81
|
rdoc_options: []
|
77
|
-
|
82
|
+
|
83
|
+
require_paths:
|
78
84
|
- lib
|
79
|
-
required_ruby_version: !ruby/object:Gem::Requirement
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
80
86
|
none: false
|
81
|
-
requirements:
|
82
|
-
- -
|
83
|
-
- !ruby/object:Gem::Version
|
84
|
-
|
85
|
-
|
87
|
+
requirements:
|
88
|
+
- - ">="
|
89
|
+
- !ruby/object:Gem::Version
|
90
|
+
hash: 3
|
91
|
+
segments:
|
92
|
+
- 0
|
93
|
+
version: "0"
|
94
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
86
95
|
none: false
|
87
|
-
requirements:
|
88
|
-
- -
|
89
|
-
- !ruby/object:Gem::Version
|
90
|
-
|
91
|
-
|
96
|
+
requirements:
|
97
|
+
- - ">="
|
98
|
+
- !ruby/object:Gem::Version
|
99
|
+
hash: 3
|
100
|
+
segments:
|
101
|
+
- 0
|
102
|
+
version: "0"
|
103
|
+
requirements:
|
92
104
|
- bacon (optional)
|
93
105
|
rubyforge_project:
|
94
|
-
rubygems_version: 1.8.
|
106
|
+
rubygems_version: 1.8.24
|
95
107
|
signing_key:
|
96
108
|
specification_version: 3
|
97
109
|
summary: Models (and More) for RDBI
|
98
|
-
test_files:
|
110
|
+
test_files:
|
99
111
|
- spec/database.rb
|
100
112
|
- spec/model.rb
|
101
113
|
- spec/helper.rb
|
114
|
+
- spec/collection.rb
|
102
115
|
- spec/hash.rb
|