hanswurst 0.5.5 → 0.5.6

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/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