hanswurst 0.5.5 → 0.5.6

Sign up to get free protection for your applications and to get access to all the features.
data/README.md CHANGED
@@ -1,6 +1,8 @@
1
1
  hanswurst: Be a couch potato and play different roles
2
2
  =====================
3
3
 
4
+ <strong>hanswurst is discontinued in favour of [https://github.com/metakeule/thingtank](https://github.com/metakeule/thingtank) using CouchRest Model and a slightly different concept</strong>
5
+
4
6
  Hanswurst is a library that uses couch potato to create arbitrary
5
7
  objects that may have different roles. The roles determine the
6
8
  properties and they can be mixed and matched at will.
@@ -19,132 +21,231 @@ or in rvm:
19
21
  gem install hanswurst
20
22
 
21
23
 
22
- Example:
24
+ Examples:
23
25
  --------
24
26
 
25
- first you have to build your roles as normal couch potato classes
26
-
27
- class Person
28
- include CouchPotato::Persistence
27
+ To simplify the class building we let them all inherit from a role class
29
28
 
30
- property :last_name
31
- property :first_name
29
+ class Role
30
+ include CouchPotato::Persistence # add the couch potato features
31
+ include Hanswurst::Shares # allows to share roles
32
+ include Hanswurst::Delegates # delegate methods to methods of a property that is a role
33
+ include Hanswurst::Doc # access the doc from inside the role
32
34
 
33
- def name()
34
- "#{firstname} #{lastname}"
35
+ # simplify saving
36
+ def save
37
+ CouchPotato.database.save_document _doc
35
38
  end
39
+ end
40
+
41
+
42
+ Imagine a Caesar is born
43
+
44
+ class Birth < Role
45
+ property :date
46
+ property :place
36
47
 
37
- validates_presence_of :last_name
48
+ validates_presence_of :date # make sure a date is given
38
49
  end
39
50
 
40
- class Product
41
- include CouchPotato::Persistence
51
+ # just in case he might die....we might want to have a date and maybe even a place
52
+ class Dead < Birth
53
+ end
42
54
 
43
- property :price
44
- property :in_stock
55
+ class Person < Role
56
+ property :name
57
+ property :gender
58
+ shares :birth => Birth # requires the shared property :birth which is a Birth
59
+ shares :dead => Dead # requires the shared property :dead which is a Dead
45
60
  end
46
61
 
47
- then you may use them as you like
62
+ julius = Hanswurst.new
63
+ julius.person = Person.new # the shared property :birth is propagated
64
+ julius.person.gender = "m"
65
+ julius.person.name = 'Gaius Iulius Caesar'
48
66
 
49
- hw = Hanswurst.new
50
- hw << {:person => Person} # first role then class
51
- hw.person.first_name = 'Diederich'
52
- hw.person.last_name = 'Hessling' # an RoleNotValid error is raised if hw.person.last_name is missing
67
+ # we could directly setup the properties and don't have to do 'julius.birth = Birth.new' first
68
+ julius.birth.date = "100 BC"
69
+ julius.birth.place = "Rome"
53
70
 
54
- # you may also use some syntatic sugar with 'as', the following does the same thing as above
55
- hw = Hanswurst.new :person => Person.new(:first_name => 'Diederich', :last_name => 'Hessling')
56
-
57
- # this is also possible
58
- hw.product = Product.new :price => 20, :in_stock => 1
71
+ julius.person.save # doen't matter on with role you call save, see Role#save above
59
72
 
60
- id = CouchPotato.database.save_document hw
73
+ julius_id = julius._id
61
74
 
62
75
  later...
63
76
 
64
- hw = CouchPotato.database.load_document id
77
+ julius = CouchPotato.database.load_document julius_id
78
+ julius.person.name # => 'Gaius Iulius Caesar'
79
+ julius.birth.place # => "Rome"
65
80
 
66
- hw.person.first_name # => 'Diederich'
67
- hw.person.name # => 'Diederich Hessling'
68
- hw.product.price # => 20
69
81
 
70
- you may also set roles as properties of other roles
82
+ when he is adult, he wants to marry. now things are getting a bit more complicated:
71
83
 
72
- class Address
73
- include CouchPotato::Persistence
84
+ # he needs a marriage and a women
85
+ class Marriage < Role
86
+ property :date # the date of the marriage
87
+ property :end # when the marriage ended
88
+ property :spouse # doc_id of the spouse
89
+ property :state # current state of the marriage, e.g. 'married', 'widowed', 'divorced'
74
90
 
75
- property :street
76
- property :zip
77
- property :city
91
+ # ensure that 'spouse' is a fkey (doc_id) of a Person and that we have only one of them
92
+ validates :spouse, :hanswurst => {:class => Person, :fkey => true, :max => 1}
78
93
  end
79
94
 
80
- class Person
81
- include CouchPotato::Persistence
82
-
83
- property :address_privat
84
- property :address_work
85
-
86
- # gets invalid unless address_privat / address_work are instances of Address
87
- validates :address_privat, :hanswurst => {:class => Address}
88
- validates :address_work, :hanswurst => {:class => Address}
89
-
90
- property :last_name
91
- property :first_name
95
+ # and we don't want to load the spouses document manually just to get her name. so here is a role that does it for us
96
+ class Spouse < Role
97
+ shares :marriage => Marriage # depends on the shared marriage role
98
+
99
+ # convenience method to get name of the current spouse by doc.spouse.name
100
+ def name
101
+ CouchPotato.database.load_document(id).person.name if (id=marriage_spouse)
102
+ end
103
+
104
+ def marriage_spouse
105
+ m = marriage()
106
+ return m.spouse if m and m.status == 'married'
107
+ return nil
108
+ end
109
+
110
+ def marriage()
111
+ _doc.marriage # here we access the shared marriage role via the _doc (Hanswurst document)
112
+ end
92
113
  end
93
114
 
94
- hw = Hanswurst.new :employee => Person.new(:last_name => 'Hessling')
95
- emp = hw.employee
96
- emp.address_privat = Address.new :city => 'Berlin'
97
- emp.address_work = Address.new :city => 'Potsdam'
98
115
 
99
- as comfort you might delegate to some roles property
116
+ now we could easily get julius married
117
+
118
+ conny = Hanswurst.new
119
+ conny.person = Person.new :gender => "f", :name => 'Cornelia'
120
+ conny.save
121
+
122
+ julius.marriage = Marriage.new
123
+ julius.marriage.date = "84 BC"
124
+ julius.marriage.spouse = conny._id
125
+ julius.marriage.state = 'married'
126
+
127
+ julius.spouse.name # => 'Cornelia'
128
+
129
+ while that is nice, let see if we could make it more comfortable:
130
+
131
+ class Marriage
132
+ shares :spouse => Spouse # we need a spouse as role of the doc
100
133
 
101
- class Address
102
- include CouchPotato::Persistence
134
+ # marry a person that is or is not already saved or part of a Hanswurst
135
+ def marry(person)
103
136
 
104
- property :street
105
- property :zip
106
- property :city
137
+ # attach it to a hanswurst, if it is not already
138
+ person = Hanswurst.new(:person => person).person unless person.respond_to? :_doc # if it responds to :_doc it already has a hanswurst doc attached to it
139
+
140
+ # save the person if it is not already
141
+ person.save unless person._doc._id
142
+
143
+ # assign the doc_id to spouse
144
+ self.spouse = person._doc._id
145
+ self.state = 'married'
146
+
147
+ # now look if she is already married to him and make her marry him
148
+ unless self._doc && self._doc._id && person._doc.marriage_spouse == self._doc._id
149
+ person._doc.marriage = Marriage.new :date => self.date
150
+ person._doc.marriage.marry self
151
+ person.save
152
+ end
153
+ end
107
154
  end
108
155
 
109
- class Person
110
- include CouchPotato::Persistence
111
- include Hanswurst::Delegates
156
+ it now becomes much less work and Cornelia also knows that she is married to Julius
157
+
158
+ conny = Person.new(:gender => "f", :name => 'Cornelia')
159
+ julius.marriage = Marriage.new :date => "84 BC"
160
+ julius.marriage.marry conny
112
161
 
113
- property :address
162
+ julius.spouse.name # => 'Cornelia'
163
+ conny.spouse.name # => 'Gaius Iulius Caesar'
114
164
 
115
- delegates [:street,:zip,:city] => :address
165
+ julius could even marry a second time, i.e. marriage becomes an Array of Marriage objects
166
+
167
+ marriage = Marriage.new :date => "68-65 BC"
168
+ marriage.marry Person.new(:gender => "f", :name => 'Pompeia')
169
+ julius.marriage << marriage
170
+
171
+ # ouch!
172
+ julius.spouse.name # => Error
173
+
174
+ we should let Spouse know that Marriage might be an array, so simply overwrite marriage with
175
+
176
+ class Spouse
177
+ def marriage()
178
+ [_doc.marriage].flatten.last
179
+ end
116
180
  end
117
181
 
118
- hw = Hanswurst.new :employee => Person.new(:last_name => 'Hessling'), :address => Address.new
119
- emp = hw.employee
120
- emp.city = 'Berlin' # => sets emp.addess.city = 'Berlin'
121
- emp.city # => gets emp.addess.city
182
+ now it works:
183
+
184
+ julius.spouse.name # => 'Pompeia'
185
+
186
+ julius.marriage.size # => 2
187
+ julius.marriage.first.spouse.name # => 'Cornelia'
122
188
 
123
- you might also share dependancies
189
+ # oops!
190
+ julius.marriage.first.status # => 'married'
124
191
 
125
- class Address
126
- include CouchPotato::Persistence
192
+ julius is still married with Cornelia but he should not
127
193
 
128
- property :street
129
- property :zip
130
- property :city
194
+ if Cornelia died before his second marriage, it should'nt be a problem:
195
+
196
+ class Dead
197
+ # all callbacks of roles are called and defined like corresponding callbacks of the doc
198
+ before_save do
199
+ if _doc.spouse && marriage=_doc.spouse.marriage
200
+ marriage.end = self.date # marriage ends with date of dead
201
+ marriage.state = 'widowed'
202
+ end
203
+ end
131
204
  end
132
205
 
133
206
  class Person
134
- include CouchPotato::Persistence
135
- include Hanswurst::Shares
136
-
137
- shares :address_privat => Address
138
- shares :address_work => Address
139
-
140
- property :last_name
141
- property :first_name
207
+ def die(date)
208
+ _doc.dead = Dead.new :date => date
209
+ end
142
210
  end
143
211
 
144
- hw = Hanswurst.new :employee => Person.new(:last_name => 'Hessling')
145
- hw.address_privat.city = 'Berlin'
146
- hw.address_work.city = 'Potsdam'
147
-
212
+ # ok first replay the marriage of conny and julius to prevent errors
213
+ conny.marriage = nil
214
+ conny.save
215
+
216
+ julius.marriage = Marriage.new :date => "84 BC"
217
+ julius.marriage.marry conny
218
+
219
+ # now conny dies
220
+ conny.die "68-65 BC"
221
+
222
+ # and julius may marry Pompeia
223
+ marriage = Marriage.new :date => "68-65 BC"
224
+ marriage.marry Person.new(:gender => "f", :name => 'Pompeia')
225
+ julius.marriage << marriage
226
+
227
+ julius.marriage.first.status # => 'widowed'
228
+ julius.marriage.first.spouse.name # => 'Cornelia'
229
+
230
+ julius.marriage.last.status # => 'married'
231
+ julius.marriage.last.spouse.name # => 'Pompeia'
232
+
233
+ julius.spouse.name # => 'Pompeia'
234
+
235
+ since julius is immortal, no one should be able to destroy him:
236
+
237
+ class Undestroyable < Role
238
+ before_save do
239
+ false # never allow to destroy
240
+ end
241
+ end
242
+
243
+ julius.immortal = Undestroyable.new
244
+
245
+ CouchPotato.database.destroy_document julius
246
+
247
+ CouchPotato.database.load_document julius._id # => julius is still there
248
+
148
249
  All views are attached to the hanswurst design document
149
250
  You may create general views:
150
251
 
@@ -152,10 +253,10 @@ You may create general views:
152
253
 
153
254
  or views specific for a role
154
255
 
155
- Hanswurst.view_for :employee, :all, :key => :created_at
256
+ Hanswurst.view_for :immortal, :all, :key => :created_at
156
257
 
157
258
  # execute them this way
158
- CouchPotato.database.view Hanswurst.employee_all
259
+ CouchPotato.database.view Hanswurst.immortal_all
159
260
 
160
261
  the same works with lists.
161
262
 
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.5
1
+ 0.5.6
@@ -0,0 +1,45 @@
1
+ require_relative 'role.rb'
2
+
3
+ class Birth < Role
4
+ property :date
5
+ property :place
6
+
7
+ validates_presence_of :date # make sure a date is given
8
+ end
9
+
10
+ # just in case he might die....we might want to have a date and maybe even a place
11
+ class Dead < Birth
12
+ end
13
+
14
+ class Person < Role
15
+ property :name
16
+ property :gender
17
+ shares :birth => Birth # requires the shared property :birth which is a Birth
18
+ shares :dead => Dead # requires the shared property :dead which is a Dead
19
+ end
20
+
21
+
22
+ # include this in the tests
23
+ module HanswurstExampleTests
24
+
25
+ def bear_julius
26
+ julius = Hanswurst.new
27
+ julius.person = Person.new # the shared property :birth is propagated
28
+ julius.person.gender = "m"
29
+ julius.person.name = 'Gaius Iulius Caesar'
30
+
31
+ # we could directly setup the properties and don't have to do 'julius.birth = Birth.new' first
32
+ julius.birth.date = "100 BC"
33
+ julius.birth.place = "Rome"
34
+
35
+ julius.person.save # doen't matter on with role you call save, see Role#save above
36
+
37
+ julius = julius.person.load
38
+
39
+ assert_equal 'Gaius Iulius Caesar', julius.person.name
40
+ assert_equal 'Rome', julius.birth.place
41
+
42
+ return julius
43
+ end
44
+
45
+ end
@@ -0,0 +1,64 @@
1
+ require_relative 'bear_julius.rb'
2
+
3
+ # he needs a marriage and a women
4
+ class Marriage < Role
5
+ #shares :person => Person # we need to be a person in order to marry
6
+
7
+ property :date # the date of the marriage
8
+ property :end # when the marriage ended
9
+ property :spouse # doc_id of the spouse
10
+ property :state # current state of the marriage, e.g. 'married', 'widowed', 'divorced'
11
+
12
+ # ensure that 'spouse' is a fkey (doc_id) of a Person and that we have only one of them
13
+ validates :spouse, :hanswurst => {:class => Person, :fkey => true, :max => 1}
14
+ end
15
+
16
+ # and we don't want to load the spouses document manually just to get her name. so here is a role that does it for us
17
+ class Spouse < Role
18
+ # shares :marriage => Marriage # depends on the shared marriage role
19
+
20
+ # convenience method to get name of the current spouse by doc.spouse.name
21
+ def name
22
+ if (id=marriage_spouse)
23
+ return CouchPotato.database.load_document(id).person.name
24
+ end
25
+ return nil
26
+ end
27
+
28
+ def marriage_spouse
29
+ m = marriage()
30
+ return m.spouse if m and m.state == 'married'
31
+ return nil
32
+ end
33
+
34
+ def marriage()
35
+ _doc.marriage # here we access the shared marriage role via the _doc (Hanswurst document)
36
+ end
37
+ end
38
+
39
+ # include this in the tests
40
+ module HanswurstExampleTests
41
+
42
+ def first_marriage()
43
+ julius = bear_julius
44
+
45
+ conny = Hanswurst.new :person => Person.new(:gender => "f", :name => 'Cornelia')
46
+ conny.person.save
47
+
48
+ julius.marriage = Marriage.new
49
+ julius.marriage.date = "84 BC"
50
+ julius.marriage.spouse = conny._id
51
+ julius.marriage.state = 'married'
52
+
53
+ julius.spouse = Spouse.new
54
+
55
+ assert_equal 'Cornelia', julius.spouse.name
56
+
57
+ julius.person.save
58
+ julius = julius.person.load
59
+
60
+ assert_equal 'Cornelia', julius.spouse.name
61
+ end
62
+
63
+ end
64
+
@@ -0,0 +1,25 @@
1
+ require_relative 'bear_julius.rb'
2
+
3
+ class Undestroyable < Role
4
+ before_destroy do
5
+ false # never allow to destroy
6
+ end
7
+ end
8
+
9
+
10
+
11
+ module HanswurstExampleTests
12
+ def julius_immortal
13
+ julius = bear_julius()
14
+ julius.immortal = Undestroyable.new
15
+ julius.immortal.save
16
+
17
+ id = julius._id
18
+ julius = load id
19
+
20
+ CouchPotato.database.destroy_document julius
21
+
22
+ assert !load(id).nil? # => julius is still there
23
+ end
24
+ end
25
+
@@ -0,0 +1,50 @@
1
+ require_relative 'first_marriage.rb'
2
+
3
+ class Marriage
4
+ shares :spouse => Spouse # we need a spouse as role of the doc
5
+
6
+ # marry a person that is or is not already saved or part of a Hanswurst
7
+ def marry(person)
8
+ # attach it to a hanswurst, if it has not one yet
9
+ person = Hanswurst.new(:person => person).person unless person.respond_to?(:_doc) && person._doc
10
+
11
+ # save the person if it is not already
12
+ person.save unless person._doc._id
13
+
14
+ # assign the doc_id to spouse
15
+ self.spouse = person._doc._id
16
+ self.state = 'married'
17
+
18
+ unless person._doc.marriage
19
+ person._doc.marriage = Marriage.new :date => self.date
20
+ end
21
+
22
+ # now look if she is already married to him and make her marry him
23
+ unless self._doc && self._doc._id && person._doc.spouse.marriage_spouse == self._doc._id
24
+ person._doc.marriage.marry self._doc.person
25
+ person.save
26
+ end
27
+ end
28
+ end
29
+
30
+ class Spouse
31
+ def marriage()
32
+ [_doc.marriage].flatten.last
33
+ end
34
+ end
35
+
36
+ # include this in the tests
37
+ module HanswurstExampleTests
38
+ def improved_marriage()
39
+ julius = bear_julius()
40
+ julius.marriage = Marriage.new :date => "84 BC"
41
+ julius.marriage.marry Person.new(:gender => "f", :name => 'Cornelia')
42
+ conny = load julius.spouse.marriage_spouse
43
+ assert_equal 'married', julius.marriage.state
44
+ assert_equal 'Cornelia', julius.spouse.name
45
+ assert_equal 'Gaius Iulius Caesar', conny.spouse.name
46
+ return julius
47
+ end
48
+ end
49
+
50
+
data/examples/role.rb ADDED
@@ -0,0 +1,24 @@
1
+
2
+ # class used by the other examples
3
+ class Role
4
+ include CouchPotato::Persistence # add the couch potato features
5
+ include Hanswurst::Shares # allows to share roles
6
+ include Hanswurst::Delegates # delegate methods to methods of a property that is a role
7
+ include Hanswurst::Doc # access the doc from inside the role
8
+
9
+ # simplify saving
10
+ def save
11
+ CouchPotato.database.save_document _doc
12
+ end
13
+
14
+ def load
15
+ CouchPotato.database.load_document _doc._id
16
+ end
17
+ end
18
+
19
+ # include this in the tests
20
+ module HanswurstExampleTests
21
+ def load(doc_id)
22
+ CouchPotato.database.load_document doc_id
23
+ end
24
+ end
@@ -0,0 +1,69 @@
1
+ require_relative 'marriage_improvement.rb'
2
+
3
+ class Dead
4
+
5
+ # all callbacks of roles are called and defined like corresponding callbacks of the doc
6
+ before_save do
7
+ if _doc.spouse && marriage=_doc.spouse.marriage
8
+ doc = CouchPotato.database.load_document(marriage.spouse)
9
+ m = doc.spouse.marriage
10
+ m.state = 'widowed'
11
+ m.end = self.date
12
+ CouchPotato.database.save_document doc
13
+ end
14
+ true
15
+ end
16
+ end
17
+
18
+ class Person
19
+ def die(date)
20
+ _doc.dead = Dead.new :date => date
21
+ end
22
+ end
23
+
24
+ # include this in the tests
25
+ module HanswurstExampleTests
26
+
27
+ def second_marriage()
28
+ julius = improved_marriage()
29
+ julius.marriage << Marriage.new(:date => "68-65 BC")
30
+ julius.marriage.last.marry Person.new(:gender => "f", :name => 'Pompeia')
31
+
32
+ assert_equal 2, julius.marriage.size
33
+ assert_equal 'Cornelia', load(julius.marriage.first.spouse).person.name
34
+ assert_equal 'Pompeia', load(julius.marriage.last.spouse).person.name
35
+ assert_equal 'Pompeia', julius.spouse.name
36
+ assert_equal 'married', julius.marriage.first.state
37
+ assert_equal 'married', julius.marriage.last.state
38
+
39
+ return julius
40
+ end
41
+
42
+ def second_marriage_after_conny_died()
43
+ julius = bear_julius()
44
+ conny = Person.new(:gender => "f", :name => 'Cornelia')
45
+ julius.marriage = Marriage.new :date => "84 BC" # we need to attach marriage first (before calling marry), so that it has a _doc
46
+ julius.marriage.marry conny
47
+ julius.person.save
48
+
49
+ conny = load(julius.spouse.marriage_spouse)
50
+ conny.person.save
51
+ conny = conny.person.load
52
+ conny.person.die "68-65 BC"
53
+ conny.person.save # terminates the marriage
54
+
55
+ julius = julius.person.load
56
+
57
+ julius.marriage << Marriage.new(:date => "68-65 BC") # we need to attach marriage first (before calling marry), so that it has a _doc
58
+ julius.marriage.last.marry Person.new(:gender => "f", :name => 'Pompeia')
59
+
60
+ assert_equal 2, julius.marriage.size
61
+ assert_equal 'Cornelia', load(julius.marriage.first.spouse).person.name
62
+ assert_equal 'widowed', julius.marriage.first.state
63
+ assert_equal 'Pompeia', load(julius.marriage.last.spouse).person.name
64
+ assert_equal 'married', julius.marriage.last.state
65
+ assert_equal 'Pompeia', julius.spouse.name
66
+ end
67
+
68
+ end
69
+
data/lib/hanswurst.rb CHANGED
@@ -5,10 +5,25 @@ require_relative File.join('hanswurst', 'class_methods')
5
5
  require_relative File.join('hanswurst', 'method_missing')
6
6
  require_relative File.join('hanswurst', 'instance_methods')
7
7
  require_relative File.join('hanswurst', 'delegates')
8
+ require_relative File.join('hanswurst', 'doc')
8
9
  require_relative File.join('hanswurst', 'shares')
9
10
  require_relative File.join('hanswurst', 'as')
10
11
  require_relative File.join('hanswurst', 'callbacks')
11
12
 
13
+ =begin
14
+ TODO
15
+
16
+ - throw errors where it makes sense
17
+ - make a module to allow the execution of callbacks of roles of roles
18
+ - improve the inheritence situation of Hanswurst
19
+ - meta module to collect metainformation about roles (by overwriting of methods of method_missing
20
+ - add possibility to dynamically create roles from a hash and to save this role-designs in the design document of hanswurst and recreate them wenn fetching data from database
21
+ - improve documentation
22
+ - performance tests
23
+ - examples for views
24
+
25
+ =end
26
+
12
27
  # Each Hanswurst may have a different combination of roles. It's a very flexible way to be a couch potato.
13
28
  class Hanswurst
14
29
  include CouchPotato::Persistence
data/lib/hanswurst/as.rb CHANGED
@@ -3,17 +3,29 @@ class Hanswurst
3
3
 
4
4
  # helper for easy access to role attributes
5
5
  class As
6
- def initialize(doc, role)
6
+ def initialize(doc, role, rolename)
7
7
  @doc = doc
8
8
  @role = role
9
+ @rolename = rolename.to_sym
10
+ if @role.respond_to? :_doc=
11
+ @role._doc = @doc
12
+ end
9
13
  end
10
14
 
11
15
  def _role
12
16
  @role
13
17
  end
14
18
 
15
- def _id
16
- @doc._id
19
+ def << (new_role)
20
+ if new_role.class == Hanswurst::As
21
+ new_role = new_role._role
22
+ end
23
+ if @role.is_a? Array
24
+ @role << new_role
25
+ else
26
+ @role = [@role, new_role]
27
+ @doc.send :"#{@rolename}=", @role
28
+ end
17
29
  end
18
30
 
19
31
  def _doc
@@ -22,13 +34,29 @@ class Hanswurst
22
34
 
23
35
  # we suppose, we have a subrole
24
36
  def method_missing(meth, *args, &code)
25
- # a hack around http://api.rubyonrails.org/classes/ActiveModel/Dirty.html
26
- # since ActiveModel::Dirty does not recognize when property is a hash and hashentries do change
27
- @doc.update_roles! if meth =~ /=$/
37
+ rolename = meth
38
+ if meth.to_s =~ /^(.*)=$/
39
+ rolename = $1
40
+ case args.first
41
+ when Hanswurst::As
42
+ args = [args.first._role]
43
+ when Array
44
+ first = args.first.collect do |e|
45
+ e.class == Hanswurst::As ? e._role : e
46
+ end
47
+ args = [first]
48
+ end
49
+ end
28
50
 
29
51
  obj = @role.send(meth, *args)
30
- obj = self.class.new(@doc, @role.send(meth, *args)) if obj.is_a? CouchPotato::Persistence
52
+ obj = self.class.new(@doc, @role.send(meth, *args), rolename) if obj.is_a? CouchPotato::Persistence
31
53
  obj.instance_eval(&code) if code
54
+
55
+ # a hack around http://api.rubyonrails.org/classes/ActiveModel/Dirty.html
56
+ # since ActiveModel::Dirty does not recognize when property is a hash and hashentries do change
57
+ if meth.to_s =~ /^(.*)=$/
58
+ @doc.update_roles!
59
+ end
32
60
  obj
33
61
  end
34
62
  end
@@ -0,0 +1,15 @@
1
+ class Hanswurst
2
+ module Doc
3
+ def self.included(mod)
4
+ mod.class_eval do
5
+ def _doc=(doc)
6
+ @_doc = doc
7
+ end
8
+
9
+ def _doc
10
+ @_doc
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
@@ -89,6 +89,9 @@ class Hanswurst
89
89
 
90
90
  # set instance for a role
91
91
  def set(role, val)
92
+ if val.respond_to? :_doc=
93
+ val._doc = self
94
+ end
92
95
  self.hanswurst_data ||= {}
93
96
  self.hanswurst_data.update role.to_s => val
94
97
  update_roles!
@@ -121,7 +124,7 @@ class Hanswurst
121
124
  create(role.to_s)
122
125
  end
123
126
  if role_obj = hanswurst_data[role.to_s]
124
- obj = As.new(self, role_obj)
127
+ obj = As.new(self, role_obj, role)
125
128
  obj.instance_eval(&code) if code
126
129
  return obj
127
130
  end
@@ -6,11 +6,16 @@ class Hanswurst
6
6
  case meth
7
7
  when /^([^=]+)\=$/ # obj.role = ... # => we set a role
8
8
  role=$1
9
+ if value.class == Hanswurst::As
10
+ return self.send meth, value._role
11
+ end
9
12
  add_role(role, value.class) unless role_exists?(role)
10
13
  set_role(role, value) if role_exists?(role)
11
14
  return as(meth)
12
15
  else
13
16
  return as(meth) if role_exists?(meth.to_s)
17
+ return super if meth == :hanswurst_data_will_change!
18
+ return nil
14
19
  end
15
20
  super
16
21
  end
@@ -0,0 +1,11 @@
1
+ require 'helper'
2
+ require 'bear_julius'
3
+
4
+ class TestBearJulius < Test::Unit::TestCase
5
+
6
+ should "bear a julius" do
7
+ recreate_db()
8
+ bear_julius()
9
+ end
10
+
11
+ end
@@ -0,0 +1,11 @@
1
+ require 'helper'
2
+ require 'first_marriage'
3
+
4
+ class TestFirstMarriage < Test::Unit::TestCase
5
+
6
+ should "make the first marriage" do
7
+ recreate_db()
8
+ first_marriage()
9
+ end
10
+
11
+ end
@@ -0,0 +1,11 @@
1
+ require 'helper'
2
+ require 'immortal_julius'
3
+
4
+ class TestImmortalJulius < Test::Unit::TestCase
5
+
6
+ should "be an immortal julius" do
7
+ recreate_db()
8
+ julius_immortal()
9
+ end
10
+
11
+ end
@@ -0,0 +1,15 @@
1
+ require 'helper'
2
+ require 'marriage_improvement'
3
+
4
+ class TestImprovedMarriage < Test::Unit::TestCase
5
+
6
+ should "make the improved marriage" do
7
+ recreate_db()
8
+ improved_marriage()
9
+ end
10
+
11
+ should "make a second marriage" do
12
+ second_marriage()
13
+ end
14
+
15
+ end
@@ -0,0 +1,15 @@
1
+ require 'helper'
2
+ require 'second_marriage'
3
+
4
+ class TestSecondMarriage < Test::Unit::TestCase
5
+
6
+ should "make a second marriage" do
7
+ recreate_db()
8
+ second_marriage()
9
+ end
10
+
11
+ should "make a second marriage after conny died" do
12
+ recreate_db()
13
+ second_marriage_after_conny_died()
14
+ end
15
+ end
data/test/helper.rb CHANGED
@@ -15,5 +15,18 @@ $LOAD_PATH.unshift(File.dirname(__FILE__))
15
15
  $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
16
16
  require 'hanswurst'
17
17
 
18
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'examples'))
18
19
 
19
20
 
21
+ CouchPotato::Config.database_name = ENV['COUCH_POTATO_TEST_DB']
22
+
23
+ module HanswurstExampleTests
24
+ def recreate_db
25
+ CouchPotato.couchrest_database.recreate!
26
+ end
27
+ end
28
+
29
+ class Test::Unit::TestCase
30
+ include HanswurstExampleTests
31
+
32
+ end
@@ -1,12 +1,10 @@
1
1
  require 'helper'
2
- require 'hanswurst'
3
-
4
- CouchPotato::Config.database_name = ENV['COUCH_POTATO_TEST_DB']
5
2
 
6
3
 
7
4
  class Pers
8
5
 
9
6
  include CouchPotato::Persistence
7
+ include Hanswurst::Doc
10
8
  property :firstname
11
9
  property :lastname
12
10
 
@@ -17,6 +15,7 @@ class Pers
17
15
  attr_accessor :callbacks
18
16
 
19
17
  before_save do
18
+ p [:before_save, _doc._id] if _doc
20
19
  @callbacks ||= []
21
20
  @callbacks << :before_save
22
21
  end
@@ -261,6 +260,23 @@ class TestCouch < Test::Unit::TestCase
261
260
  assert_equal 'Donald', o.person.firstname
262
261
  end
263
262
 
263
+ should "add roles" do
264
+ o = Hanswurst.new
265
+ o.product = Prod.new( :article_number => 'abc', :name => 'prod1' )
266
+ save o
267
+ o = load(o._id)
268
+
269
+ o.product << Prod.new( :article_number => 'def', :name => 'prod2' )
270
+
271
+ save o
272
+ #p [o.product]
273
+ o = load(o._id)
274
+
275
+ assert_equal 'prod1', o.product.first.name
276
+ assert_equal 'prod2', o.product.last.name
277
+ assert_equal 2, o.product.size
278
+ end
279
+
264
280
  should "update role properties" do
265
281
  o = Hanswurst.new
266
282
  o.product = Prod.new( :article_number => 'xyz', :name => 'prod1' )
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: hanswurst
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.5.5
4
+ version: 0.5.6
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,11 +9,11 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-01-20 00:00:00.000000000Z
12
+ date: 2012-02-05 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: couch_potato
16
- requirement: &15523120 !ruby/object:Gem::Requirement
16
+ requirement: &16666360 !ruby/object:Gem::Requirement
17
17
  none: false
18
18
  requirements:
19
19
  - - ! '>='
@@ -21,10 +21,10 @@ dependencies:
21
21
  version: '0'
22
22
  type: :runtime
23
23
  prerelease: false
24
- version_requirements: *15523120
24
+ version_requirements: *16666360
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: yard
27
- requirement: &15522500 !ruby/object:Gem::Requirement
27
+ requirement: &16665560 !ruby/object:Gem::Requirement
28
28
  none: false
29
29
  requirements:
30
30
  - - ~>
@@ -32,10 +32,10 @@ dependencies:
32
32
  version: 0.6.0
33
33
  type: :development
34
34
  prerelease: false
35
- version_requirements: *15522500
35
+ version_requirements: *16665560
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: bundler
38
- requirement: &15521920 !ruby/object:Gem::Requirement
38
+ requirement: &16664780 !ruby/object:Gem::Requirement
39
39
  none: false
40
40
  requirements:
41
41
  - - ~>
@@ -43,10 +43,10 @@ dependencies:
43
43
  version: 1.0.0
44
44
  type: :development
45
45
  prerelease: false
46
- version_requirements: *15521920
46
+ version_requirements: *16664780
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: jeweler
49
- requirement: &15521320 !ruby/object:Gem::Requirement
49
+ requirement: &16664060 !ruby/object:Gem::Requirement
50
50
  none: false
51
51
  requirements:
52
52
  - - ~>
@@ -54,10 +54,10 @@ dependencies:
54
54
  version: 1.5.2
55
55
  type: :development
56
56
  prerelease: false
57
- version_requirements: *15521320
57
+ version_requirements: *16664060
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: rcov
60
- requirement: &15520780 !ruby/object:Gem::Requirement
60
+ requirement: &16663240 !ruby/object:Gem::Requirement
61
61
  none: false
62
62
  requirements:
63
63
  - - ! '>='
@@ -65,10 +65,10 @@ dependencies:
65
65
  version: '0'
66
66
  type: :development
67
67
  prerelease: false
68
- version_requirements: *15520780
68
+ version_requirements: *16663240
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: mocha
71
- requirement: &15520200 !ruby/object:Gem::Requirement
71
+ requirement: &16662440 !ruby/object:Gem::Requirement
72
72
  none: false
73
73
  requirements:
74
74
  - - ! '>='
@@ -76,10 +76,10 @@ dependencies:
76
76
  version: '0'
77
77
  type: :development
78
78
  prerelease: false
79
- version_requirements: *15520200
79
+ version_requirements: *16662440
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: shoulda
82
- requirement: &15495700 !ruby/object:Gem::Requirement
82
+ requirement: &16659800 !ruby/object:Gem::Requirement
83
83
  none: false
84
84
  requirements:
85
85
  - - ! '>='
@@ -87,10 +87,10 @@ dependencies:
87
87
  version: '0'
88
88
  type: :development
89
89
  prerelease: false
90
- version_requirements: *15495700
90
+ version_requirements: *16659800
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: linecache19
93
- requirement: &15495060 !ruby/object:Gem::Requirement
93
+ requirement: &16658920 !ruby/object:Gem::Requirement
94
94
  none: false
95
95
  requirements:
96
96
  - - ! '>='
@@ -98,10 +98,10 @@ dependencies:
98
98
  version: '0'
99
99
  type: :development
100
100
  prerelease: false
101
- version_requirements: *15495060
101
+ version_requirements: *16658920
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: ruby-debug19
104
- requirement: &15494420 !ruby/object:Gem::Requirement
104
+ requirement: &16658180 !ruby/object:Gem::Requirement
105
105
  none: false
106
106
  requirements:
107
107
  - - ! '>='
@@ -109,10 +109,10 @@ dependencies:
109
109
  version: '0'
110
110
  type: :development
111
111
  prerelease: false
112
- version_requirements: *15494420
112
+ version_requirements: *16658180
113
113
  - !ruby/object:Gem::Dependency
114
114
  name: couch_potato
115
- requirement: &15493800 !ruby/object:Gem::Requirement
115
+ requirement: &16657360 !ruby/object:Gem::Requirement
116
116
  none: false
117
117
  requirements:
118
118
  - - ! '>='
@@ -120,7 +120,7 @@ dependencies:
120
120
  version: '0'
121
121
  type: :runtime
122
122
  prerelease: false
123
- version_requirements: *15493800
123
+ version_requirements: *16657360
124
124
  description: flexible enhancement of couch potato
125
125
  email: ! 'Base64.decode64(''bGludXhAbWFyY3JlbmVhcm5zLmRl
126
126
 
@@ -137,15 +137,27 @@ files:
137
137
  - README.md
138
138
  - Rakefile
139
139
  - VERSION
140
+ - examples/bear_julius.rb
141
+ - examples/first_marriage.rb
142
+ - examples/immortal_julius.rb
143
+ - examples/marriage_improvement.rb
144
+ - examples/role.rb
145
+ - examples/second_marriage.rb
140
146
  - lib/hanswurst.rb
141
147
  - lib/hanswurst/as.rb
142
148
  - lib/hanswurst/callbacks.rb
143
149
  - lib/hanswurst/class_methods.rb
144
150
  - lib/hanswurst/delegates.rb
151
+ - lib/hanswurst/doc.rb
145
152
  - lib/hanswurst/instance_methods.rb
146
153
  - lib/hanswurst/method_missing.rb
147
154
  - lib/hanswurst/shares.rb
148
155
  - lib/hanswurst/validator.rb
156
+ - test/examples/test_bear_julius.rb
157
+ - test/examples/test_first_marriage.rb
158
+ - test/examples/test_immortal_julius.rb
159
+ - test/examples/test_marriage_improvement.rb
160
+ - test/examples/test_second_marriage.rb
149
161
  - test/helper.rb
150
162
  - test/test_hanswurst.rb
151
163
  homepage: http://github.com/metakeule/hanswurst
@@ -163,7 +175,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
163
175
  version: '0'
164
176
  segments:
165
177
  - 0
166
- hash: -1349200765658973839
178
+ hash: 1041771107013312987
167
179
  required_rubygems_version: !ruby/object:Gem::Requirement
168
180
  none: false
169
181
  requirements:
@@ -172,10 +184,21 @@ required_rubygems_version: !ruby/object:Gem::Requirement
172
184
  version: '0'
173
185
  requirements: []
174
186
  rubyforge_project:
175
- rubygems_version: 1.8.6
187
+ rubygems_version: 1.8.15
176
188
  signing_key:
177
189
  specification_version: 3
178
190
  summary: Be a couch potato and play different roles
179
191
  test_files:
192
+ - examples/bear_julius.rb
193
+ - examples/first_marriage.rb
194
+ - examples/immortal_julius.rb
195
+ - examples/marriage_improvement.rb
196
+ - examples/role.rb
197
+ - examples/second_marriage.rb
198
+ - test/examples/test_bear_julius.rb
199
+ - test/examples/test_first_marriage.rb
200
+ - test/examples/test_immortal_julius.rb
201
+ - test/examples/test_marriage_improvement.rb
202
+ - test/examples/test_second_marriage.rb
180
203
  - test/helper.rb
181
204
  - test/test_hanswurst.rb