thingtank 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/.document +5 -0
- data/Gemfile +15 -0
- data/LICENSE.txt +20 -0
- data/README.md +329 -0
- data/Rakefile +45 -0
- data/VERSION +1 -0
- data/examples/bear_julius.rb +50 -0
- data/examples/first_marriage.rb +81 -0
- data/examples/immortal_julius.rb +25 -0
- data/examples/marriage_improvement.rb +120 -0
- data/examples/second_marriage.rb +78 -0
- data/lib/couchrest/extensions/view.rb +17 -0
- data/lib/thingtank/callbacks.rb +32 -0
- data/lib/thingtank/dependencies.rb +94 -0
- data/lib/thingtank/fakebase.rb +66 -0
- data/lib/thingtank/force_update.rb +24 -0
- data/lib/thingtank/instance_methods.rb +45 -0
- data/lib/thingtank/role.rb +110 -0
- data/lib/thingtank/role_handling.rb +198 -0
- data/lib/thingtank/shared_methods.rb +63 -0
- data/lib/thingtank/shortcuts.rb +36 -0
- data/lib/thingtank/thingtank.rb +21 -0
- data/lib/thingtank.rb +27 -0
- data/test/examples/test_bear_julius.rb +15 -0
- data/test/examples/test_first_marriage.rb +20 -0
- data/test/examples/test_immortal_julius.rb +16 -0
- data/test/examples/test_marriage_improvement.rb +22 -0
- data/test/examples/test_second_marriage.rb +18 -0
- data/test/helper.rb +118 -0
- data/test/test_fakebase.rb +389 -0
- data/test/test_thingtank.rb +282 -0
- data/test/test_views.rb +80 -0
- metadata +172 -0
data/.document
ADDED
data/Gemfile
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
source "http://rubygems.org"
|
2
|
+
|
3
|
+
gem 'couchrest'
|
4
|
+
gem 'couchrest_model'
|
5
|
+
|
6
|
+
group :development do
|
7
|
+
#gem "minitest", ">= 0"
|
8
|
+
gem "yard", "~> 0.6.0"
|
9
|
+
gem "bundler", "~> 1.0.0"
|
10
|
+
gem "jeweler", "~> 1.6.4"
|
11
|
+
gem "rcov", ">= 0"
|
12
|
+
|
13
|
+
gem 'linecache19'
|
14
|
+
gem 'ruby-debug19'
|
15
|
+
end
|
data/LICENSE.txt
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
Copyright (c) 2012 Marc Rene Arns
|
2
|
+
|
3
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
4
|
+
a copy of this software and associated documentation files (the
|
5
|
+
"Software"), to deal in the Software without restriction, including
|
6
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
7
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
8
|
+
permit persons to whom the Software is furnished to do so, subject to
|
9
|
+
the following conditions:
|
10
|
+
|
11
|
+
The above copyright notice and this permission notice shall be
|
12
|
+
included in all copies or substantial portions of the Software.
|
13
|
+
|
14
|
+
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
15
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
16
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
17
|
+
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
18
|
+
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
19
|
+
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
20
|
+
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/README.md
ADDED
@@ -0,0 +1,329 @@
|
|
1
|
+
thingtank: play different roles
|
2
|
+
=====================
|
3
|
+
|
4
|
+
ThingTank is a library that uses couchrest and couchrest model create arbitrary
|
5
|
+
objects that may have different roles. The roles determine the
|
6
|
+
properties and they can be mixed and matched at will.
|
7
|
+
|
8
|
+
|
9
|
+
Installation
|
10
|
+
------------
|
11
|
+
_thingtank_ is only tested with ruby 1.9.2 and above.
|
12
|
+
|
13
|
+
Install it as a gem:
|
14
|
+
|
15
|
+
sudo gem install thingtank
|
16
|
+
|
17
|
+
or in rvm:
|
18
|
+
|
19
|
+
gem install thingtank
|
20
|
+
|
21
|
+
|
22
|
+
Examples:
|
23
|
+
--------
|
24
|
+
|
25
|
+
To simplify the class building we let them all inherit from a role class
|
26
|
+
|
27
|
+
class Born < ThingTank::Role
|
28
|
+
property :birth_date
|
29
|
+
property :birth_place
|
30
|
+
|
31
|
+
validates_presence_of :birth_date # make sure a date is given
|
32
|
+
end
|
33
|
+
|
34
|
+
|
35
|
+
Imagine a Caesar is born
|
36
|
+
|
37
|
+
class Born < ThingTank::Role
|
38
|
+
property :birth_date, :alias => :born_at
|
39
|
+
property :birth_place
|
40
|
+
|
41
|
+
validates_presence_of :birth_date # make sure a date is given
|
42
|
+
end
|
43
|
+
|
44
|
+
# just in case he might die....we might want to have a date and maybe even a place
|
45
|
+
class Dead < ThingTank::Role
|
46
|
+
property :date_of_death
|
47
|
+
property :place_of_death
|
48
|
+
|
49
|
+
validates_presence_of :date_of_death
|
50
|
+
end
|
51
|
+
|
52
|
+
class Person < ThingTank::Role
|
53
|
+
property :name
|
54
|
+
property :gender
|
55
|
+
|
56
|
+
validates_presence_of :name
|
57
|
+
end
|
58
|
+
|
59
|
+
|
60
|
+
julius = ThingTank.create { :gender => :m, :name => 'Gaius Iulius Caesar' }
|
61
|
+
julius["birth_date"] = "100 BC"
|
62
|
+
julius["birth_place"] = "Rome"
|
63
|
+
julius.is(Born)
|
64
|
+
|
65
|
+
julius.could_be? Person # => true
|
66
|
+
julius.is? Person # => false
|
67
|
+
julius.is Person
|
68
|
+
julius.is? Person # => true
|
69
|
+
|
70
|
+
julius.is(Born)["gender"] # => nil (gender is not a property of born)
|
71
|
+
julius.is(Born)["birth_date"] # => "100BC"
|
72
|
+
|
73
|
+
id = julius.id
|
74
|
+
|
75
|
+
later...
|
76
|
+
julius = ThingTank.get id
|
77
|
+
julius["birth_date"] # => "100BC"
|
78
|
+
julius.is? Person # => true
|
79
|
+
|
80
|
+
|
81
|
+
when he is adult, he wants to marry. now things are getting a bit more complicated:
|
82
|
+
|
83
|
+
# he needs a marriage and a women
|
84
|
+
class Married < ThingTank::Role
|
85
|
+
property :date # the date of the marriage
|
86
|
+
property :end # when the marriage ended
|
87
|
+
property :spouse # doc_id of the spouse
|
88
|
+
property :state # state of the marriage
|
89
|
+
|
90
|
+
validates_presence_of :date
|
91
|
+
validates_presence_of :spouse
|
92
|
+
validate :spouse_should_be_a_person
|
93
|
+
|
94
|
+
# ensure that 'spouse' is a doc_id of a Person
|
95
|
+
def spouse_should_be_a_person
|
96
|
+
Person.get(self["spouse"]).valid? # loads doc as role Person and validates
|
97
|
+
end
|
98
|
+
end
|
99
|
+
|
100
|
+
# we want easy access to the name of the spouse
|
101
|
+
class Spouse < ThingTank::Role
|
102
|
+
property :married # doc must have "married" property (should be of class married)
|
103
|
+
property :married_state
|
104
|
+
|
105
|
+
validate :spouse_should_be_married
|
106
|
+
|
107
|
+
def married
|
108
|
+
self["married"] # contains a Married role
|
109
|
+
end
|
110
|
+
|
111
|
+
def spouse_should_be_married
|
112
|
+
married["spouse"] == self["_id"]
|
113
|
+
end
|
114
|
+
|
115
|
+
def name
|
116
|
+
self["married_state"] == "married" ?
|
117
|
+
Person.get(married["spouse"]).name :
|
118
|
+
nil
|
119
|
+
end
|
120
|
+
|
121
|
+
def ex
|
122
|
+
self["married_state"] == "divorced" ?
|
123
|
+
Person.get(married["spouse"]).name :
|
124
|
+
nil
|
125
|
+
end
|
126
|
+
end
|
127
|
+
|
128
|
+
now we could easily get julius married
|
129
|
+
|
130
|
+
conny = ThingTank.create :gender => "f", :name => 'Cornelia', :roles => ['Person']
|
131
|
+
julius["married"] = {"date" => "84 BC", "spouse" => conny.id}
|
132
|
+
julius["married_state"] = "married"
|
133
|
+
|
134
|
+
julius.with("married").is(Married).valid? # => true # "married" is a property that plays the Married
|
135
|
+
|
136
|
+
julius.has(Spouse).valid? # #has is an alias of #is
|
137
|
+
|
138
|
+
julius.save
|
139
|
+
julius.reload
|
140
|
+
|
141
|
+
julius.has(Spouse).name # => 'Cornelia'
|
142
|
+
|
143
|
+
while that is nice, let see if we could make it more comfortable:
|
144
|
+
|
145
|
+
|
146
|
+
class Married
|
147
|
+
|
148
|
+
# marry a doc or hash
|
149
|
+
def marry(person)
|
150
|
+
person = ThingTank.new(person) if person.is_a?(Hash)
|
151
|
+
person.save # should have a doc_id
|
152
|
+
|
153
|
+
# assign the doc_id to spouse
|
154
|
+
self["spouse"] = person["_id"]
|
155
|
+
self["state"] = 'married'
|
156
|
+
_doc["married_state"] = 'married'
|
157
|
+
|
158
|
+
unless person["married"] && person.last_role(Married, "married").spouse == _doc["_id"]
|
159
|
+
person.add_role(Married, "married") do |m|
|
160
|
+
m.date = self["date"]
|
161
|
+
m.marry _doc
|
162
|
+
end
|
163
|
+
person.is(Spouse)
|
164
|
+
person.save
|
165
|
+
end
|
166
|
+
end
|
167
|
+
|
168
|
+
def divorce(date)
|
169
|
+
self["state"] = 'divorced'
|
170
|
+
self['end'] = date
|
171
|
+
_doc.save
|
172
|
+
spouse = Spouse.get(self["spouse"])
|
173
|
+
if spouse.married_state == "married"
|
174
|
+
spouse.divorce(date)
|
175
|
+
end
|
176
|
+
end
|
177
|
+
|
178
|
+
end
|
179
|
+
|
180
|
+
class Spouse
|
181
|
+
def married(&code)
|
182
|
+
_doc.last_role Married, "married", &code
|
183
|
+
end
|
184
|
+
|
185
|
+
def divorce(date)
|
186
|
+
self["married_state"] = 'divorced'
|
187
|
+
married { |m| m.divorce(date) }
|
188
|
+
end
|
189
|
+
end
|
190
|
+
|
191
|
+
class Person
|
192
|
+
def marry(date, person)
|
193
|
+
_doc.add_role Married, "married", do |m|
|
194
|
+
m.date = "84 BC"
|
195
|
+
m.marry person
|
196
|
+
end
|
197
|
+
end
|
198
|
+
end
|
199
|
+
|
200
|
+
it now becomes much less work and Cornelia also knows that she is married to Julius
|
201
|
+
|
202
|
+
julius.as(Person).marry "84 BC", :gender => "f", :name => 'Cornelia'
|
203
|
+
julius.save
|
204
|
+
julius.reload
|
205
|
+
|
206
|
+
julius.has(Spouse).name # => 'Cornelia'
|
207
|
+
julius.has(Spouse).married_state # => 'married'
|
208
|
+
|
209
|
+
conny_id = julius.last_role(Married,"married").spouse
|
210
|
+
conny = ThingTank.get conny_id
|
211
|
+
conny.has(Spouse).married_state # => 'married'
|
212
|
+
|
213
|
+
|
214
|
+
julius could even marry a second time, i.e. marriage becomes an Array of Marriage objects
|
215
|
+
|
216
|
+
julius.as(Person).marry "68-65 BC", :gender => "f", :name => 'Pompeia'
|
217
|
+
|
218
|
+
|
219
|
+
|
220
|
+
Person.get(julius["married"].first["spouse"]).name # => 'Cornelia'
|
221
|
+
Person.get(julius["married"].last["spouse"]).name # => 'Pompeia'
|
222
|
+
julius.has(Spouse).name # => 'Pompeia'
|
223
|
+
julius["married"].first["state"] # => 'married'
|
224
|
+
julius["married"].last["state"] # => 'married'
|
225
|
+
julius["married"].size # => 2
|
226
|
+
|
227
|
+
# ouch, two women!
|
228
|
+
|
229
|
+
|
230
|
+
we should let Spouse know that Marriage might be an array, so simply overwrite marriage with
|
231
|
+
julius is still married with Cornelia but he should not
|
232
|
+
|
233
|
+
if Cornelia died before his second marriage, it should'nt be a problem:
|
234
|
+
|
235
|
+
class Dead
|
236
|
+
|
237
|
+
# all callbacks of roles are called and defined like corresponding callbacks of the doc
|
238
|
+
before_save do
|
239
|
+
if _doc.is?(Spouse) && _doc['married_state'] == 'married'
|
240
|
+
Spouse.get(_doc.last_role(Married, 'married').spouse).widowed(self["date_of_death"])
|
241
|
+
end
|
242
|
+
true
|
243
|
+
end
|
244
|
+
|
245
|
+
end
|
246
|
+
|
247
|
+
class Person
|
248
|
+
def dies(date)
|
249
|
+
_doc.is(Dead) do |d|
|
250
|
+
d.date_of_death = date
|
251
|
+
end
|
252
|
+
end
|
253
|
+
end
|
254
|
+
|
255
|
+
class Married
|
256
|
+
def widow(date)
|
257
|
+
self["state"] = 'widowed'
|
258
|
+
self['end'] = date
|
259
|
+
_doc.save
|
260
|
+
end
|
261
|
+
end
|
262
|
+
|
263
|
+
class Spouse
|
264
|
+
def widowed(date)
|
265
|
+
self["married_state"] = 'widowed'
|
266
|
+
married { |m| m.widow(date) }
|
267
|
+
end
|
268
|
+
end
|
269
|
+
|
270
|
+
julius.as(Person).marry "84 BC", :gender => "f", :name => 'Cornelia'
|
271
|
+
julius.save
|
272
|
+
julius.reload
|
273
|
+
|
274
|
+
conny = ThingTank.get julius["married"]["spouse"]
|
275
|
+
conny.save
|
276
|
+
conny.reload
|
277
|
+
conny.as(Person).dies "68-65 BC"
|
278
|
+
conny.save
|
279
|
+
|
280
|
+
julius.reload
|
281
|
+
|
282
|
+
julius.as(Person).marry "68-65 BC", :gender => "f", :name => 'Pompeia'
|
283
|
+
|
284
|
+
julius["married"].size # => 2
|
285
|
+
Person.get(julius["married"].first["spouse"]).name # => 'Cornelia'
|
286
|
+
julius["married"].first["state"] # => 'widowed'
|
287
|
+
|
288
|
+
since julius is immortal, no one should be able to destroy him:
|
289
|
+
|
290
|
+
|
291
|
+
class Undestroyable < ThingTank::Role
|
292
|
+
before_destroy do
|
293
|
+
false # never allow to destroy
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
julius.is(Undestroyable)
|
298
|
+
julius.save # save the role
|
299
|
+
|
300
|
+
id = julius.id
|
301
|
+
julius = ThingTank.get id
|
302
|
+
|
303
|
+
julius.as(Undestroyable).destroy
|
304
|
+
ThingTank.get(id).nil? # => julius is still there
|
305
|
+
|
306
|
+
julius.destroy
|
307
|
+
|
308
|
+
ThingTank.get(id).nil? # => julius is still there
|
309
|
+
|
310
|
+
|
311
|
+
You may subclass Hanswurst to do further separation and mix the
|
312
|
+
native properties of Hanswursts / its subclasses with the roles properties.
|
313
|
+
|
314
|
+
|
315
|
+
== Contributing to thingtank
|
316
|
+
|
317
|
+
* Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet
|
318
|
+
* Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it
|
319
|
+
* Fork the project
|
320
|
+
* Start a feature/bugfix branch
|
321
|
+
* Commit and push until you are happy with your contribution
|
322
|
+
* Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
|
323
|
+
* Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.
|
324
|
+
|
325
|
+
== Copyright
|
326
|
+
|
327
|
+
Copyright (c) 2012 Marc Rene Arns. See LICENSE.txt for
|
328
|
+
further details.
|
329
|
+
|
data/Rakefile
ADDED
@@ -0,0 +1,45 @@
|
|
1
|
+
# encoding: utf-8
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'bundler'
|
5
|
+
begin
|
6
|
+
Bundler.setup(:default, :development)
|
7
|
+
rescue Bundler::BundlerError => e
|
8
|
+
$stderr.puts e.message
|
9
|
+
$stderr.puts "Run `bundle install` to install missing gems"
|
10
|
+
exit e.status_code
|
11
|
+
end
|
12
|
+
require 'rake'
|
13
|
+
|
14
|
+
require 'jeweler'
|
15
|
+
Jeweler::Tasks.new do |gem|
|
16
|
+
# gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
|
17
|
+
gem.name = "thingtank"
|
18
|
+
gem.homepage = "http://github.com/metakeule/thingtank"
|
19
|
+
gem.license = "MIT"
|
20
|
+
gem.summary = %Q{thin wrapper around couchrest}
|
21
|
+
gem.email = "Base64.decode64(bGludXhAbWFyY3JlbmVhcm5zLmRl\n)"
|
22
|
+
gem.authors = ["Marc Rene Arns"]
|
23
|
+
# dependencies defined in Gemfile
|
24
|
+
end
|
25
|
+
Jeweler::RubygemsDotOrgTasks.new
|
26
|
+
|
27
|
+
require 'rake/testtask'
|
28
|
+
Rake::TestTask.new(:test) do |test|
|
29
|
+
test.libs << 'lib' << 'test'
|
30
|
+
test.pattern = 'test/**/test_*.rb'
|
31
|
+
test.verbose = true
|
32
|
+
end
|
33
|
+
|
34
|
+
require 'rcov/rcovtask'
|
35
|
+
Rcov::RcovTask.new do |test|
|
36
|
+
test.libs << 'test'
|
37
|
+
test.pattern = 'test/**/test_*.rb'
|
38
|
+
test.verbose = true
|
39
|
+
test.rcov_opts << '--exclude "gems/*"'
|
40
|
+
end
|
41
|
+
|
42
|
+
task :default => :test
|
43
|
+
|
44
|
+
require 'yard'
|
45
|
+
YARD::Rake::YardocTask.new
|
data/VERSION
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
0.0.1
|
@@ -0,0 +1,50 @@
|
|
1
|
+
class Born < ThingTank::Role
|
2
|
+
property :birth_date, :alias => :born_at
|
3
|
+
property :birth_place
|
4
|
+
|
5
|
+
validates_presence_of :birth_date # make sure a date is given
|
6
|
+
end
|
7
|
+
|
8
|
+
# just in case he might die....we might want to have a date and maybe even a place
|
9
|
+
class Dead < ThingTank::Role
|
10
|
+
property :date_of_death
|
11
|
+
property :place_of_death
|
12
|
+
|
13
|
+
validates_presence_of :date_of_death
|
14
|
+
end
|
15
|
+
|
16
|
+
class Person < ThingTank::Role
|
17
|
+
property :name
|
18
|
+
property :gender
|
19
|
+
|
20
|
+
validates_presence_of :name
|
21
|
+
end
|
22
|
+
|
23
|
+
def test_bear_julius
|
24
|
+
julius = create({ :gender => :m, :name => 'Gaius Iulius Caesar' })
|
25
|
+
assert julius.could_be? Person
|
26
|
+
|
27
|
+
julius["birth_date"] = "100 BC"
|
28
|
+
julius["birth_place"] = "Rome"
|
29
|
+
|
30
|
+
assert julius.could_be? Born
|
31
|
+
|
32
|
+
julius.is(Born)
|
33
|
+
julius.is(Person)
|
34
|
+
julius.save
|
35
|
+
julius.reload
|
36
|
+
|
37
|
+
assert julius.is? Person
|
38
|
+
assert julius.is? Born
|
39
|
+
|
40
|
+
assert_equal 'Gaius Iulius Caesar', julius["name"]
|
41
|
+
assert_equal 'Rome', julius["birth_place"]
|
42
|
+
|
43
|
+
assert !julius["roles"].nil?
|
44
|
+
assert_equal 2, julius["roles"].size
|
45
|
+
assert julius["roles"].include? Person.to_s
|
46
|
+
assert julius["roles"].include? Born.to_s
|
47
|
+
assert_equal "100 BC", julius.is(Born).born_at
|
48
|
+
|
49
|
+
return julius
|
50
|
+
end
|
@@ -0,0 +1,81 @@
|
|
1
|
+
require_relative 'bear_julius.rb'
|
2
|
+
|
3
|
+
# he needs a marriage and a women
|
4
|
+
class Married < ThingTank::Role
|
5
|
+
property :date # the date of the marriage
|
6
|
+
property :end # when the marriage ended
|
7
|
+
property :spouse # doc_id of the spouse
|
8
|
+
property :state # state of the marriage
|
9
|
+
|
10
|
+
validates_presence_of :date
|
11
|
+
validates_presence_of :spouse
|
12
|
+
validate :spouse_should_be_a_person
|
13
|
+
|
14
|
+
# ensure that 'spouse' is a doc_id of a Person
|
15
|
+
def spouse_should_be_a_person
|
16
|
+
Person.get(self["spouse"]).valid?
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
# we want easy access to the name of the spouse
|
21
|
+
class Spouse < ThingTank::Role
|
22
|
+
property :married
|
23
|
+
property :married_state
|
24
|
+
|
25
|
+
validate :spouse_should_be_married
|
26
|
+
|
27
|
+
def married
|
28
|
+
self["married"]
|
29
|
+
end
|
30
|
+
|
31
|
+
def spouse_should_be_married
|
32
|
+
married["spouse"] == self["_id"]
|
33
|
+
end
|
34
|
+
|
35
|
+
def name
|
36
|
+
self["married_state"] == "married" ?
|
37
|
+
Person.get(married["spouse"]).name :
|
38
|
+
nil
|
39
|
+
end
|
40
|
+
|
41
|
+
def ex
|
42
|
+
self["married_state"] == "divorced" ?
|
43
|
+
Person.get(married["spouse"]).name :
|
44
|
+
nil
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
def test_first_marriage()
|
49
|
+
julius = test_bear_julius
|
50
|
+
conny = create :gender => "f", :name => 'Cornelia', :roles => ['Person']
|
51
|
+
|
52
|
+
julius["married"] = {"date" => "84 BC", "spouse" => conny.id}
|
53
|
+
julius["married_state"] = "married"
|
54
|
+
|
55
|
+
assert julius.is(Married, "married").valid?
|
56
|
+
assert julius.has(Spouse).valid?
|
57
|
+
|
58
|
+
julius.save
|
59
|
+
julius.reload
|
60
|
+
|
61
|
+
assert_equal 'Cornelia', julius.has(Spouse).name
|
62
|
+
|
63
|
+
end
|
64
|
+
|
65
|
+
|
66
|
+
def test_first_marriage_shortcuts()
|
67
|
+
julius = test_bear_julius
|
68
|
+
conny = create :gender => "f", :name => 'Cornelia', :roles => ['Person']
|
69
|
+
|
70
|
+
julius["married"] = {"date" => "84 BC", "spouse" => conny.id}
|
71
|
+
julius["married_state"] = "married"
|
72
|
+
|
73
|
+
assert (julius>Married-"married").valid?
|
74
|
+
assert (julius>Spouse).valid?
|
75
|
+
|
76
|
+
julius.save
|
77
|
+
julius.reload
|
78
|
+
|
79
|
+
assert_equal 'Cornelia', (julius>Spouse).name
|
80
|
+
|
81
|
+
end
|
@@ -0,0 +1,25 @@
|
|
1
|
+
require_relative 'bear_julius.rb'
|
2
|
+
|
3
|
+
class Undestroyable < ThingTank::Role
|
4
|
+
before_destroy do
|
5
|
+
false # never allow to destroy
|
6
|
+
end
|
7
|
+
end
|
8
|
+
|
9
|
+
|
10
|
+
|
11
|
+
def test_julius_immortal
|
12
|
+
julius = test_bear_julius()
|
13
|
+
julius.is(Undestroyable)
|
14
|
+
julius.save # save the role
|
15
|
+
|
16
|
+
id = julius.id
|
17
|
+
julius = load id
|
18
|
+
|
19
|
+
julius.as(Undestroyable).destroy
|
20
|
+
assert !load(id).nil? # => julius is still there
|
21
|
+
|
22
|
+
julius.destroy
|
23
|
+
|
24
|
+
assert !load(id).nil? # => julius is still there
|
25
|
+
end
|
@@ -0,0 +1,120 @@
|
|
1
|
+
require_relative 'first_marriage.rb'
|
2
|
+
|
3
|
+
require 'ruby-debug'
|
4
|
+
class Married
|
5
|
+
|
6
|
+
# marry a doc or hash
|
7
|
+
def marry(person)
|
8
|
+
person = ThingTank.new(person) if person.is_a?(Hash)
|
9
|
+
person.save
|
10
|
+
|
11
|
+
# assign the doc_id to spouse
|
12
|
+
self["spouse"] = person["_id"]
|
13
|
+
self["state"] = 'married'
|
14
|
+
_doc["married_state"] = 'married'
|
15
|
+
|
16
|
+
unless person["married"] && person.last_role(Married, "married").spouse == _doc["_id"]
|
17
|
+
person.add_role(Married, "married") do |m|
|
18
|
+
m.date = self["date"]
|
19
|
+
m.marry _doc
|
20
|
+
end
|
21
|
+
person.is(Spouse)
|
22
|
+
person.save
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
26
|
+
def divorce(date)
|
27
|
+
self["state"] = 'divorced'
|
28
|
+
self['end'] = date
|
29
|
+
_doc.save
|
30
|
+
spouse = Spouse.get(self["spouse"])
|
31
|
+
if spouse.married_state == "married"
|
32
|
+
spouse.divorce(date)
|
33
|
+
end
|
34
|
+
end
|
35
|
+
|
36
|
+
end
|
37
|
+
|
38
|
+
class Spouse
|
39
|
+
def married(&code)
|
40
|
+
_doc.last_role Married, "married", &code
|
41
|
+
end
|
42
|
+
|
43
|
+
def divorce(date)
|
44
|
+
self["married_state"] = 'divorced'
|
45
|
+
married { |m| m.divorce(date) }
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
class Person
|
50
|
+
def marry(date, person)
|
51
|
+
_doc.add_role Married, "married", do |m|
|
52
|
+
m.date = "84 BC"
|
53
|
+
m.marry person
|
54
|
+
end
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
def test_improved_marriage()
|
59
|
+
julius = test_bear_julius()
|
60
|
+
julius.as(Person).marry "84 BC", :gender => "f", :name => 'Cornelia'
|
61
|
+
|
62
|
+
julius.save
|
63
|
+
julius.reload
|
64
|
+
|
65
|
+
assert_equal 'Cornelia', julius.has(Spouse).name
|
66
|
+
assert_equal 'married', julius.has(Spouse).married_state
|
67
|
+
|
68
|
+
conny = load julius.last_role(Married,"married").spouse
|
69
|
+
|
70
|
+
assert_equal 'Gaius Iulius Caesar', conny.has(Spouse).name
|
71
|
+
assert_equal 'married', conny.has(Spouse).married_state
|
72
|
+
|
73
|
+
assert_equal julius['_id'], conny.last_role(Married,"married").spouse
|
74
|
+
assert_equal "84 BC", conny.last_role(Married,"married").date
|
75
|
+
|
76
|
+
return julius
|
77
|
+
end
|
78
|
+
|
79
|
+
def test_improved_marriage_divorce()
|
80
|
+
julius = test_bear_julius()
|
81
|
+
julius.as(Person).marry "84 BC", :gender => "f", :name => 'Cornelia'
|
82
|
+
julius.as(Spouse).divorce "85 BC"
|
83
|
+
|
84
|
+
assert_equal nil, julius.has(Spouse).name
|
85
|
+
assert_equal 'Cornelia', julius.has(Spouse).ex
|
86
|
+
assert_equal 'divorced', julius["married_state"]
|
87
|
+
assert_equal "85 BC", julius.last_role(Married,"married").end
|
88
|
+
|
89
|
+
conny = load julius.last_role(Married,"married").spouse
|
90
|
+
|
91
|
+
assert_equal nil, conny.has(Spouse).name
|
92
|
+
assert_equal 'Gaius Iulius Caesar', conny.has(Spouse).ex
|
93
|
+
assert_equal 'divorced', conny["married_state"]
|
94
|
+
|
95
|
+
assert_equal julius['_id'], conny.last_role(Married,"married").spouse
|
96
|
+
assert_equal "85 BC", conny.last_role(Married,"married").end
|
97
|
+
return julius
|
98
|
+
end
|
99
|
+
|
100
|
+
def test_improved_marriage_shortcuts()
|
101
|
+
julius = test_bear_julius()
|
102
|
+
(julius>Person).marry "84 BC", :gender => "f", :name => 'Cornelia'
|
103
|
+
|
104
|
+
julius.save
|
105
|
+
julius.reload
|
106
|
+
|
107
|
+
assert_equal 'Cornelia', (julius>Spouse).name
|
108
|
+
assert_equal 'married', (julius>Spouse).married_state
|
109
|
+
|
110
|
+
conny = load (julius>Married-"married").spouse
|
111
|
+
|
112
|
+
assert_equal 'Gaius Iulius Caesar', (conny>Spouse).name
|
113
|
+
assert_equal 'married', (conny>Spouse).married_state
|
114
|
+
|
115
|
+
assert_equal julius['_id'], (conny>Married-"married").spouse
|
116
|
+
assert_equal "84 BC", (conny>Married-"married").date
|
117
|
+
|
118
|
+
return julius
|
119
|
+
end
|
120
|
+
|