hanswurst 0.5.4 → 0.5.5

Sign up to get free protection for your applications and to get access to all the features.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.5.4
1
+ 0.5.5
data/lib/hanswurst.rb CHANGED
@@ -1,390 +1,23 @@
1
1
  require 'couch_potato'
2
2
 
3
- class HanswurstValidator < ActiveModel::EachValidator
4
- def doc_is_valid_role?(doc, roles)
5
- !(roles & doc.hanswurst_roles.keys).empty?
6
- end
7
-
8
- def is_valid_klass?(klasses, value)
9
- valid = false
10
- klasses.each do |klass|
11
- valid = true if value.is_a? klass
12
- end
13
- valid
14
- end
15
-
16
- def doc_is_valid_klass?(doc, klasses)
17
- !(klasses & doc.hanswurst_roles.values).empty?
18
- end
19
-
20
-
21
- def validate_each(record, attribute, values)
22
- #p options
23
- #p record
24
- return if values.nil?
25
- values = values.values if values.class == Hash
26
- values = [values] unless values.class == Array
27
- valid = true
28
- max = options[:max]
29
- if max && values.size > max
30
- record.errors.add attribute, "must not have more than #{max} entries"
31
- return
32
- end
33
- fkey = options[:fkey] || false
34
- if fkey
35
- values = values.collect do |id|
36
- CouchPotato.database.load_document id
37
- end
38
- end
39
-
40
- if klass = options[:class]
41
- klasses = [klass].flatten
42
- if fkey
43
- klasses = klasses.collect{ |k| k.to_s }
44
- values.each do |doc|
45
- valid = false unless doc && doc.hanswurst_roles && doc_is_valid_klass?(doc, klasses)
46
- end
47
- record.errors.add attribute, "must be a doc id of a class #{klasses.join(' or ')}" unless valid
48
- else
49
- values.each do |thing|
50
- valid = false unless is_valid_klass?(klasses, thing)
51
- end
52
- record.errors.add attribute, "must be an instance of #{klasses.join(' or ')}" unless valid
53
- end
54
- elsif role = options[:role]
55
- roles = [role].flatten.collect{ |r| r.to_s }
56
- if fkey
57
- values.each do |doc|
58
- valid = false unless doc && doc.hanswurst_roles && doc_is_valid_role?(doc, roles)
59
- end
60
- record.errors.add attribute, "must be a doc id of a role #{roles.join(' or ')}" unless valid
61
- else
62
- klasses = roles.collect do |role_alias|
63
- klass = Hanswurst.role_class(role_alias.to_sym)
64
- record.errors.add(attribute, "could not find role #{role_alias} with Hanswurst.role_class. Did you register it with Hanswurst.register_role() ?") if klass.nil?
65
- klass
66
- end
67
- values.each do |thing|
68
- valid = false unless is_valid_klass?(klasses, thing)
69
- end
70
- record.errors.add attribute, "must be an instance of #{klasses.join(' or ')}" unless valid
71
- end
72
-
73
- end
74
- #roles = options[:in] || [options[:with]]
75
- end
76
- end
77
-
3
+ require_relative File.join('hanswurst', 'validator')
4
+ require_relative File.join('hanswurst', 'class_methods')
5
+ require_relative File.join('hanswurst', 'method_missing')
6
+ require_relative File.join('hanswurst', 'instance_methods')
7
+ require_relative File.join('hanswurst', 'delegates')
8
+ require_relative File.join('hanswurst', 'shares')
9
+ require_relative File.join('hanswurst', 'as')
10
+ require_relative File.join('hanswurst', 'callbacks')
78
11
 
79
12
  # Each Hanswurst may have a different combination of roles. It's a very flexible way to be a couch potato.
80
13
  class Hanswurst
81
- module Delegates
82
- def self.included(mod)
83
- mod.instance_eval do
84
- def delegates(hsh)
85
- @delegations ||= {}
86
- hsh.each do |prop,role|
87
- if prop.is_a? Array
88
- prop.each do |pr|
89
- @delegations.update pr => role
90
- end
91
- else
92
- @delegations.update prop => role
93
- end
94
- end
95
- end
96
-
97
- def delegations
98
- @delegations || {}
99
- end
100
- end
101
- end
102
-
103
- # shortcuts view_for_[role], list_for_[role]
104
- def method_missing(meth, *args, &code)
105
- meth.to_s =~ /^([^=]+)=?$/
106
- unified_meth = $1
107
- if role = self.class.delegations[unified_meth.to_sym]
108
- self.send(role).send(meth, *args, &code)
109
- else
110
- super
111
- end
112
- end
113
- end
114
-
115
- module Shares
116
- def self.included(mod)
117
- mod.instance_eval do
118
- def shares(hsh)
119
- hsh = {hsh => hsh} if hsh.is_a? Symbol
120
- @shared_dependancies ||= {}
121
- @shared_dependancies.update hsh
122
- end
123
-
124
- def shared_dependancies
125
- @shared_dependancies || {}
126
- end
127
- end
128
- end
129
- end
130
-
131
- # error raised when a role is not valid
132
- class RoleNotValid < Exception
133
- def initialize(role, value)
134
- @role = role
135
- @value = value
136
- end
137
-
138
- def message()
139
- errors = @value.errors.messages.collect do |field, err|
140
- "#{field} #{err.join('/')}"
141
- end.join(" # ")
142
- "#{@role} not valid: #{errors}"
143
- end
144
- end
145
-
146
- # helper for easy access to role attributes
147
- class As
148
- def initialize(doc, role)
149
- @doc = doc
150
- @role = role
151
- end
152
-
153
- def _role
154
- @role
155
- end
156
-
157
- def _id
158
- @doc._id
159
- end
160
-
161
- def _doc
162
- @doc
163
- end
164
-
165
- # we suppose, we have a subrole
166
- def method_missing(meth, *args, &code)
167
- # a hack around http://api.rubyonrails.org/classes/ActiveModel/Dirty.html
168
- # since ActiveModel::Dirty does not recognize when property is a hash and hashentries do change
169
- @doc.update_roles! if meth =~ /=$/
170
-
171
- obj = @role.send(meth, *args)
172
- if obj.is_a? CouchPotato::Persistence
173
- obj = self.class.new(@doc, @role.send(meth, *args))
174
- end
175
- obj.instance_eval(&code) if code
176
- obj
177
- end
178
- end
179
-
180
- # class methods for Hanswurst
181
- module ClassMethods
182
-
183
- # get class obj from a classname
184
- def getClass(classname)
185
- classname.split('::').inject Kernel do |c,name| c = c.const_get name; end
186
- end
187
-
188
- # hsh is role_alias => klass where role_alias should be a symbol
189
- def register_role(hsh)
190
- @roles ||= {}
191
- @roles.update hsh
192
- end
193
-
194
- # returns the role klass for the role_alias (role_alias should be a symbol)
195
- def role_class(role_alias)
196
- @roles ||= {}
197
- @roles[role_alias]
198
- end
199
-
200
- # creates a view to show only documents of the role +role_alias+
201
- def role_view(role_alias, viewname, options)
202
- # I keep this commented out to inform the reader that we don't want this in order to easily reuse general lists
203
- #options[:list] &&= :"#{role_alias}_#{options[:list]}"
204
- options[:conditions] &&= " && ( #{options[:conditions]} )"
205
- options[:conditions] = "(doc.hanswurst_roles.#{role_alias} !== undefined)#{options[:conditions]}"
206
- self.view :"#{role_alias}_#{viewname}", options
207
- end
208
-
209
- alias :view_for :role_view
210
-
211
- # creates a list for documents of the role +role_alias+
212
- def role_list(role_alias, listname, val)
213
- self.list :"#{role_alias}_#{listname}", val.to_s
214
- end
215
-
216
- alias :list_for :role_list
217
-
218
- # shortcuts view_for_[role], list_for_[role]
219
- def method_missing(meth, *args, &code)
220
- case meth
221
- when /^view_for_(.+)$/
222
- send(:view_for, $1, *args)
223
- when /^list_for_(.+)$/
224
- send(:list_for, $1, *args)
225
- else
226
- super
227
- end
228
- end
229
- end
230
-
231
- extend ClassMethods
232
-
233
14
  include CouchPotato::Persistence
234
-
235
- module MethodMissing
236
- # dispatches self.role calls and self.role= setters
237
- def method_missing(meth, *args, &code)
238
- value = args.first
239
- case meth
240
- when /^([^=]+)\=$/ # obj.role = ... # => we set a role
241
- role=$1
242
- add_role(role, value.class) unless role_exists?(role)
243
- set_role(role, value) if role_exists?(role)
244
- return as(meth)
245
- else
246
- return as(meth) if role_exists?(meth.to_s)
247
- end
248
- super
249
- end
250
- end
15
+ include MethodMissing
16
+ include Callbacks
17
+ extend ClassMethods
251
18
 
252
19
  property :hanswurst_roles
253
20
  property :hanswurst_data
254
-
255
- # check if every role is valid before saving
256
- before_save do
257
- self.hanswurst_data.each do |role,val|
258
- unless val.valid?
259
- raise RoleNotValid.new(role, val)
260
- end
261
- end
262
- end
263
-
264
- include MethodMissing
265
-
266
- def initialize(hsh={})
267
- unless hsh.empty?
268
- hsh.each do |role, obj|
269
- self.send(:"#{role}=", obj)
270
- end
271
- end
272
- end
273
-
274
- # copy the doc including the roles
275
- def copy()
276
- attributes = self.to_hash
277
- attributes.delete "ruby_class"
278
- attributes.delete "_id"
279
- attributes.delete "_rev"
280
- attributes.delete :created_at
281
- attributes.delete :updated_at
282
- hanswust_data = attributes.delete :hanswurst_data
283
- obj = self.class.new attributes
284
- hanswust_data.each do |role,val|
285
- obj.send(:"#{role}=", val.dup)
286
- end
287
- obj
288
- end
289
-
290
- def <<(hsh)
291
- add_roles(hsh)
292
- end
293
-
294
- # a hack around http://api.rubyonrails.org/classes/ActiveModel/Dirty.html
295
- # since ActiveModel::Dirty does not recognize when property is a hash and hashentries do change
296
- def update_roles!
297
- self.hanswurst_data_will_change!
298
- end
299
-
300
- private
301
-
302
- # add a role
303
- def add_role(role, klass, &code)
304
- add_roles(role => klass)
305
- as(role, &code)
306
- end
307
-
308
- def handle_shared(role, klass)
309
- if klass.respond_to?(:shared_dependancies)
310
- shared = {}
311
- klass.shared_dependancies.each do |shared_role, shared_class|
312
- shared_class = Hanswurst.role_class(shared_class) if shared_class.is_a? Symbol
313
- shared[shared_role] = shared_class unless role_exists? shared_role
314
- end
315
- add_roles shared
316
- end
317
- end
318
-
319
- # add more hanswurst_roles at once
320
- def add_roles(hsh)
321
- self.hanswurst_data ||= {}
322
- self.hanswurst_roles ||= {}
323
- hsh.each do |role,klass|
324
- raise "class expected: #{klass.inspect}" unless klass.is_a? Class
325
- handle_shared(role, klass)
326
- self.hanswurst_roles.update role.to_s => klass.name
327
- end
328
- update_roles!
329
- self.hanswurst_roles
330
- end
331
-
332
- # does he have a role
333
- def role_exists?(role)
334
- self.hanswurst_roles and self.hanswurst_roles[role.to_s]
335
- end
336
-
337
- # does he have hanswurst_data for the role
338
- def hanswurst_data_exists?(role)
339
- self.hanswurst_data and self.hanswurst_data[role.to_s]
340
- end
341
-
342
- # set a role
343
- def set_role(role, value)
344
- value ? set(role, value) : delete_role(role)
345
- end
346
-
347
- # delete a role
348
- def delete_role(role)
349
- delete role # delete the hanswurst_data
350
- self.hanswurst_roles.delete role.to_s # and the role itself
351
- end
352
-
353
- # set instance for a role
354
- def set(role, val)
355
- self.hanswurst_data ||= {}
356
- self.hanswurst_data.update role.to_s => val
357
- update_roles!
358
- end
359
-
360
- # create a new instance of a role
361
- def create(role)
362
- raise "unknown role #{role.inspect}" unless role_exists? role.to_s
363
- self.hanswurst_data ||= {}
364
- self.hanswurst_data[role.to_s] = self.class.getClass(self.hanswurst_roles[role.to_s]).new
365
- update_roles!
366
- end
367
-
368
- # delete a role
369
- def delete(role)
370
- self.hanswurst_data ||= {}
371
- self.hanswurst_data.delete(role.to_s)
372
- update_roles!
373
- #self.hanswurst_data = {} # we have to do this, otherwise it won't save?!
374
- #self.hanswurst_data = hsh
375
- end
376
-
377
- def as(role, &code)
378
- return nil unless role_exists?(role)
379
- unless hanswurst_data[role.to_s]
380
- create(role.to_s)
381
- end
382
- if role_obj = hanswurst_data[role.to_s]
383
- obj = As.new(self, role_obj)
384
- obj.instance_eval(&code) if code
385
- return obj
386
- end
387
- nil
388
- end
389
21
  end
390
22
 
23
+
@@ -0,0 +1,35 @@
1
+ # Each Hanswurst may have a different combination of roles. It's a very flexible way to be a couch potato.
2
+ class Hanswurst
3
+
4
+ # helper for easy access to role attributes
5
+ class As
6
+ def initialize(doc, role)
7
+ @doc = doc
8
+ @role = role
9
+ end
10
+
11
+ def _role
12
+ @role
13
+ end
14
+
15
+ def _id
16
+ @doc._id
17
+ end
18
+
19
+ def _doc
20
+ @doc
21
+ end
22
+
23
+ # we suppose, we have a subrole
24
+ 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 =~ /=$/
28
+
29
+ obj = @role.send(meth, *args)
30
+ obj = self.class.new(@doc, @role.send(meth, *args)) if obj.is_a? CouchPotato::Persistence
31
+ obj.instance_eval(&code) if code
32
+ obj
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,68 @@
1
+ class Hanswurst
2
+ module Callbacks
3
+ def self.included(base)
4
+ base.class_eval do
5
+ # mimic the create_document method from https://github.com/langalex/couch_potato/blob/master/lib/couch_potato/database.rb
6
+ before_create do
7
+ ok = true
8
+ self.hanswurst_data.each do |role,document|
9
+ if document.is_a?(CouchPotato::Persistence)
10
+ document.errors.clear
11
+ document.created_at ||= (Time.zone || Time).now
12
+ document.updated_at = (Time.zone || Time).now
13
+ (ok = false) if false == document.run_callbacks(:validation_on_save) do
14
+ (ok = false) if false == document.run_callbacks(:validation_on_create) do
15
+ raise(CouchPotato::Database::ValidationsFailedError.new(["role: " + role].concat(document.errors.full_messages))) unless database.send(:valid_document?, document)
16
+ end
17
+ end
18
+
19
+ (ok = false) if false == document.run_callbacks(:save) do
20
+ (ok = false) if false == document.run_callbacks(:create) do
21
+ true
22
+ end
23
+ end
24
+ end
25
+ end
26
+ ok
27
+ end
28
+
29
+ # mimic the update_document method from https://github.com/langalex/couch_potato/blob/master/lib/couch_potato/database.rb
30
+ before_update do
31
+ ok = true
32
+ self.hanswurst_data.each do |role,document|
33
+ if document.is_a?(CouchPotato::Persistence)
34
+ document.errors.clear
35
+ document.created_at ||= (Time.zone || Time).now
36
+ document.updated_at = (Time.zone || Time).now
37
+ (ok = false) if false == document.run_callbacks(:validation_on_save) do
38
+ (ok = false) if false == document.run_callbacks(:validation_on_update) do
39
+ raise(CouchPotato::Database::ValidationsFailedError.new(["role: " + role].concat(document.errors.full_messages))) unless database.send(:valid_document?, document)
40
+ end
41
+ end
42
+
43
+ (ok = false) if false == document.run_callbacks(:save) do
44
+ (ok = false) if false == document.run_callbacks(:update) do
45
+ true
46
+ end
47
+ end
48
+ end
49
+ end
50
+ ok
51
+ end
52
+
53
+ # mimic the destroy_document method from https://github.com/langalex/couch_potato/blob/master/lib/couch_potato/database.rb
54
+ before_destroy do
55
+ ok = true
56
+ self.hanswurst_data.each do |role,document|
57
+ if document.is_a?(CouchPotato::Persistence)
58
+ (ok = false) if false == document.run_callbacks(:destroy) do
59
+ true
60
+ end
61
+ end
62
+ end
63
+ ok
64
+ end
65
+ end
66
+ end
67
+ end
68
+ end
@@ -0,0 +1,55 @@
1
+
2
+ # TODO allow subclasses of Hanswurst to have the role registry (or handle it via callback)
3
+
4
+ class Hanswurst
5
+ # class methods for Hanswurst
6
+ module ClassMethods
7
+
8
+ # get class obj from a classname
9
+ def getClass(classname)
10
+ classname.split('::').inject Kernel do |c,name| c = c.const_get name; end
11
+ end
12
+
13
+ # hsh is role_alias => klass where role_alias should be a symbol
14
+ def register_role(hsh)
15
+ @roles ||= {}
16
+ @roles.update hsh
17
+ end
18
+
19
+ # returns the role klass for the role_alias (role_alias should be a symbol)
20
+ def role_class(role_alias)
21
+ @roles ||= {}
22
+ @roles[role_alias]
23
+ end
24
+
25
+ # creates a view to show only documents of the role +role_alias+
26
+ def role_view(role_alias, viewname, options)
27
+ # I keep this commented out to inform the reader that we don't want this in order to easily reuse general lists
28
+ #options[:list] &&= :"#{role_alias}_#{options[:list]}"
29
+ options[:conditions] &&= " && ( #{options[:conditions]} )"
30
+ options[:conditions] = "(doc.hanswurst_roles.#{role_alias} !== undefined)#{options[:conditions]}"
31
+ self.view :"#{role_alias}_#{viewname}", options
32
+ end
33
+
34
+ alias :view_for :role_view
35
+
36
+ # creates a list for documents of the role +role_alias+
37
+ def role_list(role_alias, listname, val)
38
+ self.list :"#{role_alias}_#{listname}", val.to_s
39
+ end
40
+
41
+ alias :list_for :role_list
42
+
43
+ # shortcuts view_for_[role], list_for_[role]
44
+ def method_missing(meth, *args, &code)
45
+ case meth
46
+ when /^view_for_(.+)$/
47
+ send(:view_for, $1, *args)
48
+ when /^list_for_(.+)$/
49
+ send(:list_for, $1, *args)
50
+ else
51
+ super
52
+ end
53
+ end
54
+ end
55
+ end
@@ -0,0 +1,35 @@
1
+ class Hanswurst
2
+ module Delegates
3
+ def self.included(mod)
4
+ mod.instance_eval do
5
+ def delegates(hsh)
6
+ @delegations ||= {}
7
+ hsh.each do |prop,role|
8
+ if prop.is_a? Array
9
+ prop.each do |pr|
10
+ @delegations.update pr => role
11
+ end
12
+ else
13
+ @delegations.update prop => role
14
+ end
15
+ end
16
+ end
17
+
18
+ def delegations
19
+ @delegations || {}
20
+ end
21
+ end
22
+ end
23
+
24
+ # shortcuts view_for_[role], list_for_[role]
25
+ def method_missing(meth, *args, &code)
26
+ meth.to_s =~ /^([^=]+)=?$/
27
+ unified_meth = $1
28
+ if role = self.class.delegations[unified_meth.to_sym]
29
+ self.send(role).send(meth, *args, &code)
30
+ else
31
+ super
32
+ end
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,130 @@
1
+ class Hanswurst
2
+ def initialize(hsh={})
3
+ unless hsh.empty?
4
+ hsh.each do |role, obj|
5
+ self.send(:"#{role}=", obj)
6
+ end
7
+ end
8
+ end
9
+
10
+ # copy the doc including the roles
11
+ def copy()
12
+ attributes = self.to_hash
13
+ attributes.delete "ruby_class"
14
+ attributes.delete "_id"
15
+ attributes.delete "_rev"
16
+ attributes.delete :created_at
17
+ attributes.delete :updated_at
18
+ hanswust_data = attributes.delete :hanswurst_data
19
+ obj = self.class.new attributes
20
+ hanswust_data.each do |role,val|
21
+ obj.send(:"#{role}=", val.dup)
22
+ end
23
+ obj
24
+ end
25
+
26
+ def <<(hsh)
27
+ add_roles(hsh)
28
+ end
29
+
30
+ # a hack around http://api.rubyonrails.org/classes/ActiveModel/Dirty.html
31
+ # since ActiveModel::Dirty does not recognize when property is a hash and hashentries do change
32
+ def update_roles!
33
+ self.hanswurst_data_will_change!
34
+ true
35
+ end
36
+
37
+ private
38
+
39
+ # add a role
40
+ def add_role(role, klass, &code)
41
+ add_roles(role => klass)
42
+ as(role, &code)
43
+ end
44
+
45
+ def handle_shared(role, klass)
46
+ if klass.respond_to?(:shared_dependancies)
47
+ shared = {}
48
+ klass.shared_dependancies.each do |shared_role, shared_class|
49
+ shared_class = Hanswurst.role_class(shared_class) if shared_class.is_a? Symbol
50
+ shared[shared_role] = shared_class unless role_exists? shared_role
51
+ end
52
+ add_roles shared
53
+ end
54
+ end
55
+
56
+ # add more hanswurst_roles at once
57
+ def add_roles(hsh)
58
+ self.hanswurst_data ||= {}
59
+ self.hanswurst_roles ||= {}
60
+ hsh.each do |role,klass|
61
+ raise "class expected: #{klass.inspect}" unless klass.is_a? Class
62
+ handle_shared(role, klass)
63
+ self.hanswurst_roles.update role.to_s => klass.name
64
+ end
65
+ update_roles!
66
+ self.hanswurst_roles
67
+ end
68
+
69
+ # does he have a role
70
+ def role_exists?(role)
71
+ self.hanswurst_roles and self.hanswurst_roles[role.to_s]
72
+ end
73
+
74
+ # does he have hanswurst_data for the role
75
+ def hanswurst_data_exists?(role)
76
+ self.hanswurst_data and self.hanswurst_data[role.to_s]
77
+ end
78
+
79
+ # set a role
80
+ def set_role(role, value)
81
+ value ? set(role, value) : delete_role(role)
82
+ end
83
+
84
+ # delete a role
85
+ def delete_role(role)
86
+ return false unless delete(role) # delete the hanswurst_data
87
+ self.hanswurst_roles.delete role.to_s # and the role itself
88
+ end
89
+
90
+ # set instance for a role
91
+ def set(role, val)
92
+ self.hanswurst_data ||= {}
93
+ self.hanswurst_data.update role.to_s => val
94
+ update_roles!
95
+ end
96
+
97
+ # create a new instance of a role
98
+ def create(role)
99
+ raise "unknown role #{role.inspect}" unless role_exists? role.to_s
100
+ self.hanswurst_data ||= {}
101
+ self.hanswurst_data[role.to_s] = self.class.getClass(self.hanswurst_roles[role.to_s]).new
102
+ update_roles!
103
+ end
104
+
105
+ # delete a role
106
+ def delete(role)
107
+ self.hanswurst_data ||= {}
108
+ doc = self.hanswurst_data[role.to_s]
109
+ if doc && doc.is_a?(CouchPotato::Persistence)
110
+ return false if false == doc.run_callbacks(:destroy) do
111
+ self.hanswurst_data.delete(role.to_s)
112
+ update_roles!
113
+ end
114
+ end
115
+ true
116
+ end
117
+
118
+ def as(role, &code)
119
+ return nil unless role_exists?(role)
120
+ unless hanswurst_data[role.to_s]
121
+ create(role.to_s)
122
+ end
123
+ if role_obj = hanswurst_data[role.to_s]
124
+ obj = As.new(self, role_obj)
125
+ obj.instance_eval(&code) if code
126
+ return obj
127
+ end
128
+ nil
129
+ end
130
+ end
@@ -0,0 +1,18 @@
1
+ class Hanswurst
2
+ module MethodMissing
3
+ # dispatches self.role calls and self.role= setters
4
+ def method_missing(meth, *args, &code)
5
+ value = args.first
6
+ case meth
7
+ when /^([^=]+)\=$/ # obj.role = ... # => we set a role
8
+ role=$1
9
+ add_role(role, value.class) unless role_exists?(role)
10
+ set_role(role, value) if role_exists?(role)
11
+ return as(meth)
12
+ else
13
+ return as(meth) if role_exists?(meth.to_s)
14
+ end
15
+ super
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ class Hanswurst
2
+ module Shares
3
+ def self.included(mod)
4
+ mod.instance_eval do
5
+ def shares(hsh)
6
+ hsh = {hsh => hsh} if hsh.is_a? Symbol
7
+ @shared_dependancies ||= {}
8
+ @shared_dependancies.update hsh
9
+ end
10
+
11
+ def shared_dependancies
12
+ @shared_dependancies || {}
13
+ end
14
+ end
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,74 @@
1
+ class HanswurstValidator < ActiveModel::EachValidator
2
+ def doc_is_valid_role?(doc, roles)
3
+ !(roles & doc.hanswurst_roles.keys).empty?
4
+ end
5
+
6
+ def is_valid_klass?(klasses, value)
7
+ valid = false
8
+ klasses.each do |klass|
9
+ valid = true if value.is_a? klass
10
+ end
11
+ valid
12
+ end
13
+
14
+ def doc_is_valid_klass?(doc, klasses)
15
+ !(klasses & doc.hanswurst_roles.values).empty?
16
+ end
17
+
18
+
19
+ def validate_each(record, attribute, values)
20
+ #p options
21
+ #p record
22
+ return if values.nil?
23
+ values = values.values if values.class == Hash
24
+ values = [values] unless values.class == Array
25
+ valid = true
26
+ max = options[:max]
27
+ if max && values.size > max
28
+ record.errors.add attribute, "must not have more than #{max} entries"
29
+ return
30
+ end
31
+ fkey = options[:fkey] || false
32
+ if fkey
33
+ values = values.collect do |id|
34
+ CouchPotato.database.load_document id
35
+ end
36
+ end
37
+
38
+ if klass = options[:class]
39
+ klasses = [klass].flatten
40
+ if fkey
41
+ klasses = klasses.collect{ |k| k.to_s }
42
+ values.each do |doc|
43
+ valid = false unless doc && doc.hanswurst_roles && doc_is_valid_klass?(doc, klasses)
44
+ end
45
+ record.errors.add attribute, "must be a doc id of a class #{klasses.join(' or ')}" unless valid
46
+ else
47
+ values.each do |thing|
48
+ valid = false unless is_valid_klass?(klasses, thing)
49
+ end
50
+ record.errors.add attribute, "must be an instance of #{klasses.join(' or ')}" unless valid
51
+ end
52
+ elsif role = options[:role]
53
+ roles = [role].flatten.collect{ |r| r.to_s }
54
+ if fkey
55
+ values.each do |doc|
56
+ valid = false unless doc && doc.hanswurst_roles && doc_is_valid_role?(doc, roles)
57
+ end
58
+ record.errors.add attribute, "must be a doc id of a role #{roles.join(' or ')}" unless valid
59
+ else
60
+ klasses = roles.collect do |role_alias|
61
+ klass = Hanswurst.role_class(role_alias.to_sym)
62
+ record.errors.add(attribute, "could not find role #{role_alias} with Hanswurst.role_class. Did you register it with Hanswurst.register_role() ?") if klass.nil?
63
+ klass
64
+ end
65
+ values.each do |thing|
66
+ valid = false unless is_valid_klass?(klasses, thing)
67
+ end
68
+ record.errors.add attribute, "must be an instance of #{klasses.join(' or ')}" unless valid
69
+ end
70
+
71
+ end
72
+ #roles = options[:in] || [options[:with]]
73
+ end
74
+ end
@@ -5,6 +5,7 @@ CouchPotato::Config.database_name = ENV['COUCH_POTATO_TEST_DB']
5
5
 
6
6
 
7
7
  class Pers
8
+
8
9
  include CouchPotato::Persistence
9
10
  property :firstname
10
11
  property :lastname
@@ -12,6 +13,57 @@ class Pers
12
13
  def name(seperator)
13
14
  "#{lastname}#{seperator}#{firstname}"
14
15
  end
16
+
17
+ attr_accessor :callbacks
18
+
19
+ before_save do
20
+ @callbacks ||= []
21
+ @callbacks << :before_save
22
+ end
23
+
24
+ after_save do
25
+ @callbacks ||= []
26
+ @callbacks << :after_save
27
+ end
28
+
29
+ before_create do
30
+ @callbacks ||= []
31
+ @callbacks << :before_create
32
+ end
33
+
34
+ after_create do
35
+ @callbacks ||= []
36
+ @callbacks << :after_create
37
+ end
38
+
39
+ before_update do
40
+ @callbacks ||= []
41
+ @callbacks << :before_update
42
+ end
43
+
44
+ after_update do
45
+ @callbacks ||= []
46
+ @callbacks << :after_update
47
+ end
48
+
49
+ before_destroy do
50
+ @callbacks ||= []
51
+ @callbacks << :before_destroy
52
+ end
53
+
54
+ after_destroy do
55
+ @callbacks ||= []
56
+ @callbacks << :after_destroy
57
+ end
58
+ end
59
+
60
+ class PreventsDeletion
61
+ include CouchPotato::Persistence
62
+ property :data
63
+
64
+ before_destroy do
65
+ false
66
+ end
15
67
  end
16
68
 
17
69
  class Prod
@@ -55,18 +107,11 @@ end
55
107
  class F
56
108
  include CouchPotato::Persistence
57
109
 
58
- #depends_on 'd' => D
59
-
60
110
  property :d
61
-
62
111
  property :f
63
-
64
112
  property :a # should be an instance of A
65
-
66
113
  property :perss # should be an Array of instances of Pers
67
-
68
- property :h # should be a Hash
69
-
114
+ property :h # should be a Hash
70
115
  end
71
116
 
72
117
  class G
@@ -142,6 +187,10 @@ class TestCouch < Test::Unit::TestCase
142
187
  CouchPotato.database.view v
143
188
  end
144
189
 
190
+ def destroy(doc)
191
+ CouchPotato.database.destroy_document doc
192
+ end
193
+
145
194
  def update(id)
146
195
  doc = load id
147
196
  yield doc
@@ -153,6 +202,52 @@ class TestCouch < Test::Unit::TestCase
153
202
  recreate_db()
154
203
  end
155
204
 
205
+ should "invoke callbacks" do
206
+ o = Hanswurst.new
207
+ o.pers = Pers.new( :lastname => 'Duck' )
208
+ save o
209
+ assert_equal [:before_save, :before_create, :after_create, :after_save], o.pers.callbacks
210
+
211
+ o = load o._id
212
+ o.pers.lastname = 'Doc'
213
+ save o
214
+ assert_equal [:before_save, :before_update, :after_update, :after_save], o.pers.callbacks
215
+
216
+ id = o._id
217
+ o = load o._id
218
+ pers = o.pers
219
+ destroy o
220
+ assert_equal [:before_destroy, :after_destroy], pers.callbacks
221
+
222
+ o = load id
223
+ assert_equal nil, o
224
+
225
+ o = Hanswurst.new
226
+ o.pers = Pers.new( :lastname => 'Duck' )
227
+ save o
228
+
229
+ o = load o._id
230
+ pers = o.pers
231
+ o.pers = nil
232
+ assert_equal [:before_destroy, :after_destroy], pers.callbacks
233
+
234
+ o = Hanswurst.new
235
+ o.prevents = PreventsDeletion.new :data => 'something'
236
+ save o
237
+ o = load o._id
238
+ o.prevents = nil
239
+ assert_equal 'something', o.prevents.data
240
+
241
+ o = Hanswurst.new
242
+ o.prevents = PreventsDeletion.new :data => 'something'
243
+ save o
244
+ id = o._id
245
+ o = load o._id
246
+ destroy o
247
+ o = load id
248
+ assert_equal 'something', o.prevents.data
249
+ end
250
+
156
251
  should "save stuff" do
157
252
  o = Hanswurst.new
158
253
  o.product = Prod.new( :article_number => 'xyz', :name => 'prod1' )
@@ -241,11 +336,19 @@ class TestCouch < Test::Unit::TestCase
241
336
  assert_equal 'hiho', o.thing.a
242
337
  end
243
338
 
339
+ should "destroy a doc" do
340
+ o = Hanswurst.new
341
+ o.pers = Pers.new
342
+ o.pers.lastname = "hiho"
343
+ save o
344
+ o = load(o._id)
345
+ destroy o
346
+ end
244
347
 
245
348
  should "not save when validation fails" do
246
349
  o = Hanswurst.new
247
350
  o.product = Prod.new( :article_number => 'xyz')
248
- assert_raises Hanswurst::RoleNotValid do
351
+ assert_raises CouchPotato::Database::ValidationsFailedError do
249
352
  save o
250
353
  end
251
354
  end
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.4
4
+ version: 0.5.5
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-19 00:00:00.000000000Z
12
+ date: 2012-01-20 00:00:00.000000000Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: couch_potato
16
- requirement: &19722520 !ruby/object:Gem::Requirement
16
+ requirement: &15523120 !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: *19722520
24
+ version_requirements: *15523120
25
25
  - !ruby/object:Gem::Dependency
26
26
  name: yard
27
- requirement: &19721800 !ruby/object:Gem::Requirement
27
+ requirement: &15522500 !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: *19721800
35
+ version_requirements: *15522500
36
36
  - !ruby/object:Gem::Dependency
37
37
  name: bundler
38
- requirement: &19720980 !ruby/object:Gem::Requirement
38
+ requirement: &15521920 !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: *19720980
46
+ version_requirements: *15521920
47
47
  - !ruby/object:Gem::Dependency
48
48
  name: jeweler
49
- requirement: &19720300 !ruby/object:Gem::Requirement
49
+ requirement: &15521320 !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: *19720300
57
+ version_requirements: *15521320
58
58
  - !ruby/object:Gem::Dependency
59
59
  name: rcov
60
- requirement: &19719700 !ruby/object:Gem::Requirement
60
+ requirement: &15520780 !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: *19719700
68
+ version_requirements: *15520780
69
69
  - !ruby/object:Gem::Dependency
70
70
  name: mocha
71
- requirement: &19719020 !ruby/object:Gem::Requirement
71
+ requirement: &15520200 !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: *19719020
79
+ version_requirements: *15520200
80
80
  - !ruby/object:Gem::Dependency
81
81
  name: shoulda
82
- requirement: &19686960 !ruby/object:Gem::Requirement
82
+ requirement: &15495700 !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: *19686960
90
+ version_requirements: *15495700
91
91
  - !ruby/object:Gem::Dependency
92
92
  name: linecache19
93
- requirement: &19686300 !ruby/object:Gem::Requirement
93
+ requirement: &15495060 !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: *19686300
101
+ version_requirements: *15495060
102
102
  - !ruby/object:Gem::Dependency
103
103
  name: ruby-debug19
104
- requirement: &19685720 !ruby/object:Gem::Requirement
104
+ requirement: &15494420 !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: *19685720
112
+ version_requirements: *15494420
113
113
  - !ruby/object:Gem::Dependency
114
114
  name: couch_potato
115
- requirement: &19685160 !ruby/object:Gem::Requirement
115
+ requirement: &15493800 !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: *19685160
123
+ version_requirements: *15493800
124
124
  description: flexible enhancement of couch potato
125
125
  email: ! 'Base64.decode64(''bGludXhAbWFyY3JlbmVhcm5zLmRl
126
126
 
@@ -138,6 +138,14 @@ files:
138
138
  - Rakefile
139
139
  - VERSION
140
140
  - lib/hanswurst.rb
141
+ - lib/hanswurst/as.rb
142
+ - lib/hanswurst/callbacks.rb
143
+ - lib/hanswurst/class_methods.rb
144
+ - lib/hanswurst/delegates.rb
145
+ - lib/hanswurst/instance_methods.rb
146
+ - lib/hanswurst/method_missing.rb
147
+ - lib/hanswurst/shares.rb
148
+ - lib/hanswurst/validator.rb
141
149
  - test/helper.rb
142
150
  - test/test_hanswurst.rb
143
151
  homepage: http://github.com/metakeule/hanswurst
@@ -155,7 +163,7 @@ required_ruby_version: !ruby/object:Gem::Requirement
155
163
  version: '0'
156
164
  segments:
157
165
  - 0
158
- hash: -3336574218003098224
166
+ hash: -1349200765658973839
159
167
  required_rubygems_version: !ruby/object:Gem::Requirement
160
168
  none: false
161
169
  requirements: