metaruby 1.0.0.rc1

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.
Files changed (43) hide show
  1. checksums.yaml +7 -0
  2. data/.gemtest +0 -0
  3. data/History.txt +1 -0
  4. data/Manifest.txt +40 -0
  5. data/README.md +318 -0
  6. data/Rakefile +39 -0
  7. data/lib/metaruby/class.rb +120 -0
  8. data/lib/metaruby/dsls/doc.rb +78 -0
  9. data/lib/metaruby/dsls/find_through_method_missing.rb +76 -0
  10. data/lib/metaruby/dsls.rb +2 -0
  11. data/lib/metaruby/gui/exception_view.rb +124 -0
  12. data/lib/metaruby/gui/html/button.rb +65 -0
  13. data/lib/metaruby/gui/html/collection.rb +103 -0
  14. data/lib/metaruby/gui/html/exception_view.css +8 -0
  15. data/lib/metaruby/gui/html/jquery.min.js +154 -0
  16. data/lib/metaruby/gui/html/jquery.selectfilter.js +65 -0
  17. data/lib/metaruby/gui/html/jquery.tagcloud.min.js +8 -0
  18. data/lib/metaruby/gui/html/jquery.tinysort.min.js +8 -0
  19. data/lib/metaruby/gui/html/list.rhtml +24 -0
  20. data/lib/metaruby/gui/html/page.css +55 -0
  21. data/lib/metaruby/gui/html/page.rb +283 -0
  22. data/lib/metaruby/gui/html/page.rhtml +13 -0
  23. data/lib/metaruby/gui/html/page_body.rhtml +17 -0
  24. data/lib/metaruby/gui/html/rock-website.css +694 -0
  25. data/lib/metaruby/gui/html.rb +16 -0
  26. data/lib/metaruby/gui/model_browser.rb +262 -0
  27. data/lib/metaruby/gui/model_selector.rb +266 -0
  28. data/lib/metaruby/gui/rendering_manager.rb +112 -0
  29. data/lib/metaruby/gui/ruby_constants_item_model.rb +253 -0
  30. data/lib/metaruby/gui.rb +9 -0
  31. data/lib/metaruby/inherited_attribute.rb +482 -0
  32. data/lib/metaruby/module.rb +158 -0
  33. data/lib/metaruby/registration.rb +157 -0
  34. data/lib/metaruby/test.rb +79 -0
  35. data/lib/metaruby.rb +17 -0
  36. data/manifest.xml +12 -0
  37. data/test/suite.rb +15 -0
  38. data/test/test_attributes.rb +323 -0
  39. data/test/test_class.rb +68 -0
  40. data/test/test_dsls.rb +49 -0
  41. data/test/test_module.rb +105 -0
  42. data/test/test_registration.rb +182 -0
  43. metadata +160 -0
@@ -0,0 +1,482 @@
1
+ require 'set'
2
+ require 'utilrb/module/dsl_attribute'
3
+ module MetaRuby
4
+ module Attributes
5
+ InheritedAttribute = Struct.new :single_value, :name, :accessor_name, :init
6
+
7
+ # The set of inherited attributes defined on this object
8
+ # @return [Array<InheritedAttribute>]
9
+ attribute(:inherited_attributes) { Array.new }
10
+
11
+ # Tests for the existence of an inherited attribute by its name
12
+ #
13
+ # @param [String] name the attribute name
14
+ # @return [Boolean] true if there is an attribute defined with the given
15
+ # name
16
+ def inherited_attribute_defined?(name)
17
+ inherited_attributes.any? { |ih| ih.name == name }
18
+ end
19
+
20
+ # Returns the inherited attribute definition that matches the given name
21
+ #
22
+ # @param [String] name the attribute name
23
+ # @return [InheritedAttribute] the attribute definition
24
+ # @raise [ArgumentError] if no attribute with that name exists
25
+ def inherited_attribute_by_name(name)
26
+ if attr = inherited_attributes.find { |ih| ih.name == name }
27
+ return attr
28
+ else raise ArgumentError, "#{self} has no inherited attribute called #{name}"
29
+ end
30
+ end
31
+
32
+ def included(mod)
33
+ mod.extend Attributes
34
+ end
35
+
36
+ # Defines an attribute that holds at most a single value
37
+ #
38
+ # @param [String] name the attribute name
39
+ # @return [InheritedAttribute] the attribute definition
40
+ # @raise [ArgumentError] if no attribute with that name exists
41
+ def inherited_single_value_attribute(name, &default_value)
42
+ dsl_attribute_name = "__dsl_attribute__#{name}"
43
+ ivar = "@#{dsl_attribute_name}"
44
+ dsl_attribute(dsl_attribute_name)
45
+ if default_value
46
+ define_method("#{dsl_attribute_name}_get_default") { default_value }
47
+ end
48
+
49
+ promotion_method = "promote_#{name}"
50
+ if method_defined?(promotion_method)
51
+ define_single_value_with_promotion("#{dsl_attribute_name}_get", promotion_method, ivar)
52
+ else
53
+ define_single_value_without_promotion("#{dsl_attribute_name}_get", ivar)
54
+ end
55
+ define_method(name) do |*args|
56
+ if args.empty? # Getter call
57
+ send("#{dsl_attribute_name}_get")
58
+ else # Setter call, delegate to the dsl_attribute implementation
59
+ send(dsl_attribute_name, *args)
60
+ end
61
+ end
62
+ nil
63
+ end
64
+
65
+ # Helper method for {#inherited_single_value_attribute} in case there
66
+ # are no promotion method(s) defined
67
+ def define_single_value_without_promotion(method_name, ivar)
68
+ class_eval <<-EOF, __FILE__, __LINE__+1
69
+ def #{method_name}
70
+ ancestors = self.ancestors
71
+ if ancestors.first != self
72
+ ancestors.unshift self
73
+ end
74
+
75
+ has_value = false
76
+ for klass in ancestors
77
+ if klass.instance_variable_defined?(:#{ivar})
78
+ has_value = true
79
+ value = klass.instance_variable_get(:#{ivar})
80
+ break
81
+ end
82
+ end
83
+
84
+ if !has_value && respond_to?(:#{method_name}_default)
85
+ # Look for default
86
+ has_value = true
87
+ value = send(:#{method_name}_default).call
88
+ base = nil
89
+ for klass in ancestors
90
+ if !klass.respond_to?(:#{method_name}_default)
91
+ break
92
+ end
93
+ base = klass
94
+ end
95
+ base.instance_variable_set :#{ivar}, value
96
+ end
97
+ value
98
+ end
99
+ EOF
100
+ end
101
+
102
+ # Helper method for {#inherited_single_value_attribute} in case there is
103
+ # a promotion method defined
104
+ def define_single_value_with_promotion(method_name, promotion_method_name, ivar)
105
+ class_eval <<-EOF, __FILE__, __LINE__+1
106
+ def #{method_name}
107
+ ancestors = self.ancestors
108
+ if ancestors.first != self
109
+ ancestors.unshift self
110
+ end
111
+
112
+ promotions = []
113
+ for klass in ancestors
114
+ if klass.instance_variable_defined?(:#{ivar})
115
+ has_value = true
116
+ value = klass.instance_variable_get(:#{ivar})
117
+ break
118
+ end
119
+ promotions.unshift(klass) if klass.respond_to?("#{promotion_method_name}")
120
+ end
121
+ if !has_value && respond_to?(:#{method_name}_default)
122
+ # Look for default
123
+ has_value = true
124
+ value = send(:#{method_name}_default).call
125
+ base = nil
126
+ promotions.clear
127
+ for klass in ancestors
128
+ if !klass.respond_to?(:#{method_name}_default)
129
+ break
130
+ end
131
+ base = klass
132
+ promotions.unshift(klass) if klass.respond_to?(:#{promotion_method_name})
133
+ end
134
+ promotions.shift
135
+ base.instance_variable_set :#{ivar}, value
136
+ end
137
+
138
+ if has_value
139
+ promotions.inject(value) { |v, k| k.#{promotion_method_name}(v) }
140
+ end
141
+ end
142
+ EOF
143
+ end
144
+
145
+ # Defines an attribute that holds a set of values, and defines the
146
+ # relevant methods and accessors to allow accessing it in a way that
147
+ # makes sense when embedded in a model hierarchy
148
+ #
149
+ # More specifically, it defines a <tt>each_#{name}(&iterator)</tt>
150
+ # instance method and a <tt>each_#{name}(&iterator)</tt>
151
+ # class method which iterates (in order) on
152
+ # - the instance #{name} attribute
153
+ # - the singleton class #{name} attribute
154
+ # - the class #{name} attribute
155
+ # - the superclass #{name} attribute
156
+ # - the superclass' superclass #{name} attribute
157
+ # ...
158
+ #
159
+ # This method can be used on modules, in which case the module is used as if
160
+ # it was part of the inheritance hierarchy.
161
+ #
162
+ # The +name+ option defines the enumeration method name (+value+ will
163
+ # define a +each_value+ method). +attribute_name+ defines the attribute
164
+ # name. +init+ is a block called to initialize the attribute.
165
+ # Valid options in +options+ are:
166
+ # map::
167
+ # If true, the attribute should respond to +[]+. In that case, the
168
+ # enumeration method is each_value(key = nil, uniq = false) If +key+ is
169
+ # given, we iterate on the values given by <tt>attribute[key]</tt>. If
170
+ # +uniq+ is true, the enumeration will yield at most one value for each
171
+ # +key+ found (so, if both +key+ and +uniq+ are given, the enumeration
172
+ # yields at most one value). See the examples below
173
+ # enum_with:: the enumeration method of the enumerable, if it is not +each+
174
+ #
175
+ # === Example
176
+ # Let's define some classes and look at the ancestor chain
177
+ #
178
+ # class A; end
179
+ # module M; end
180
+ # class B < A; include M end
181
+ # A.ancestors # => [A, Object, Kernel]
182
+ # B.ancestors # => [B, M, A, Object, Kernel]
183
+ #
184
+ # ==== Attributes for which 'map' is not set
185
+ #
186
+ # class A
187
+ # class << self
188
+ # inherited_attribute("value", "values") do
189
+ # Array.new
190
+ # end
191
+ # end
192
+ # end
193
+ # module M
194
+ # class << self
195
+ # extend MetaRuby::Attributes
196
+ # inherited_attribute("mod") do
197
+ # Array.new
198
+ # end
199
+ # end
200
+ # end
201
+ #
202
+ # A.values << 1 # => [1]
203
+ # B.values << 2 # => [2]
204
+ # M.mod << 1 # => [1]
205
+ # b = B.new
206
+ # class << b
207
+ # self.values << 3 # => [3]
208
+ # self.mod << 4 # => [4]
209
+ # end
210
+ # M.mod << 2 # => [1, 2]
211
+ #
212
+ # A.enum_for(:each_value).to_a # => [1]
213
+ # B.enum_for(:each_value).to_a # => [2, 1]
214
+ # b.singleton_class.enum_for(:each_value).to_a # => [3, 2, 1]
215
+ # b.singleton_class.enum_for(:each_mod).to_a # => [4, 1, 2]
216
+ #
217
+ # ==== Attributes for which 'map' is set
218
+ #
219
+ # class A
220
+ # class << self
221
+ # inherited_attribute("mapped", "map", :map => true) do
222
+ # Hash.new { |h, k| h[k] = Array.new }
223
+ # end
224
+ # end
225
+ # end
226
+ #
227
+ # A.map['name'] = 'A' # => "A"
228
+ # A.map['universe'] = 42
229
+ # B.map['name'] = 'B' # => "B"
230
+ # B.map['half_of_it'] = 21
231
+ #
232
+ # Let's see what happens if we don't specify the key option.
233
+ # A.enum_for(:each_mapped).to_a # => [["name", "A"], ["universe", 42]]
234
+ # If the +uniq+ option is set (the default), we see only B's value for 'name'
235
+ # B.enum_for(:each_mapped).to_a # => [["half_of_it", 21], ["name", "B"], ["universe", 42]]
236
+ # If the +uniq+ option is not set, we see both values for 'name'. Note that
237
+ # since 'map' is a Hash, the order of keys in one class is not guaranteed.
238
+ # Nonetheless, we have the guarantee that values from B appear before
239
+ # those from A
240
+ # B.enum_for(:each_mapped, nil, false).to_a # => [["half_of_it", 21], ["name", "B"], ["name", "A"], ["universe", 42]]
241
+ #
242
+ #
243
+ # Now, let's see how 'key' behaves
244
+ # A.enum_for(:each_mapped, 'name').to_a # => ["A"]
245
+ # B.enum_for(:each_mapped, 'name').to_a # => ["B"]
246
+ # B.enum_for(:each_mapped, 'name', false).to_a # => ["B", "A"]
247
+ #
248
+ def inherited_attribute(name, attribute_name = name, options = Hash.new, &init) # :nodoc:
249
+ # Set up the attribute accessor
250
+ attribute(attribute_name, &init)
251
+ class_eval { private "#{attribute_name}=" }
252
+
253
+ promote = method_defined?("promote_#{name}")
254
+ options[:enum_with] ||= :each
255
+
256
+ class_eval <<-EOF, __FILE__, __LINE__+1
257
+ def all_#{name}; each_#{name}.to_a end
258
+ def self_#{name}; @#{attribute_name} end
259
+ EOF
260
+
261
+ if options[:map]
262
+ class_eval <<-EOF, __FILE__, __LINE__+1
263
+ def find_#{name}(key)
264
+ raise ArgumentError, "nil cannot be used as a key in find_#{name}" if !key
265
+ each_#{name}(key, true) do |value|
266
+ return value
267
+ end
268
+ nil
269
+ end
270
+ def has_#{name}?(key)
271
+ ancestors = self.ancestors
272
+ if ancestors.first != self
273
+ ancestors.unshift self
274
+ end
275
+ for klass in ancestors
276
+ if klass.instance_variable_defined?(:@#{attribute_name})
277
+ return true if klass.#{attribute_name}.has_key?(key)
278
+ end
279
+ end
280
+ false
281
+ end
282
+ EOF
283
+ end
284
+
285
+ class_eval <<-EOF, __FILE__, __LINE__+1
286
+ def clear_#{attribute_name}
287
+ #{attribute_name}.clear
288
+ for klass in ancestors
289
+ if klass.instance_variable_defined?(:@#{attribute_name})
290
+ klass.#{attribute_name}.clear
291
+ end
292
+ end
293
+ end
294
+ EOF
295
+
296
+ if !promote
297
+ if options[:map]
298
+ class_eval(*Attributes.map_without_promotion(name, attribute_name, options))
299
+ else
300
+ class_eval(*Attributes.nomap_without_promotion(name, attribute_name, options))
301
+ end
302
+ else
303
+ if options[:map]
304
+ class_eval(*Attributes.map_with_promotion(name, attribute_name, options))
305
+ else
306
+ class_eval(*Attributes.nomap_with_promotion(name, attribute_name, options))
307
+ end
308
+ end
309
+ end
310
+
311
+ # Helper class that defines the iteration method for inherited_attribute
312
+ # when :map is set and there is not promotion method
313
+ def self.map_without_promotion(name, attribute_name, options)
314
+ code, file, line =<<-EOF, __FILE__, __LINE__+1
315
+ def each_#{name}(key = nil, uniq = true)
316
+ if !block_given?
317
+ return enum_for(:each_#{name}, key, uniq)
318
+ end
319
+
320
+ ancestors = self.ancestors
321
+ if ancestors.first != self
322
+ ancestors.unshift self
323
+ end
324
+ if key
325
+ for klass in ancestors
326
+ if klass.instance_variable_defined?(:@#{attribute_name})
327
+ if klass.#{attribute_name}.has_key?(key)
328
+ yield(klass.#{attribute_name}[key])
329
+ return self if uniq
330
+ end
331
+ end
332
+ end
333
+ elsif !uniq
334
+ for klass in ancestors
335
+ if klass.instance_variable_defined?(:@#{attribute_name})
336
+ klass.#{attribute_name}.#{options[:enum_with]} do |el|
337
+ yield(el)
338
+ end
339
+ end
340
+ end
341
+ else
342
+ seen = Set.new
343
+ for klass in ancestors
344
+ if klass.instance_variable_defined?(:@#{attribute_name})
345
+ klass.#{attribute_name}.#{options[:enum_with]} do |el|
346
+ unless seen.include?(el.first)
347
+ seen << el.first
348
+ yield(el)
349
+ end
350
+ end
351
+ end
352
+ end
353
+
354
+ end
355
+ self
356
+ end
357
+ EOF
358
+ return code, file, line
359
+ end
360
+
361
+ # Helper class that defines the iteration method for inherited_attribute
362
+ # when :map is not set and there is no promotion method
363
+ def self.nomap_without_promotion(name, attribute_name, options)
364
+ code, file, line =<<-EOF, __FILE__, __LINE__+1
365
+ def each_#{name}
366
+ if !block_given?
367
+ return enum_for(:each_#{name})
368
+ end
369
+
370
+ ancestors = self.ancestors
371
+ if ancestors.first != self
372
+ ancestors.unshift self
373
+ end
374
+ for klass in ancestors
375
+ if klass.instance_variable_defined?(:@#{attribute_name})
376
+ klass.#{attribute_name}.#{options[:enum_with]} { |el| yield(el) }
377
+ end
378
+ end
379
+ self
380
+ end
381
+ EOF
382
+ return code, file, line
383
+ end
384
+
385
+ # Helper class that defines the iteration method for inherited_attribute
386
+ # when :map is set and there is a promotion method
387
+ def self.map_with_promotion(name, attribute_name, options)
388
+ code, file, line =<<-EOF, __FILE__, __LINE__+1
389
+ def each_#{name}(key = nil, uniq = true)
390
+ if !block_given?
391
+ return enum_for(:each_#{name}, key, uniq)
392
+ end
393
+
394
+ ancestors = self.ancestors
395
+ if ancestors.first != self
396
+ ancestors.unshift self
397
+ end
398
+ if key
399
+ promotions = []
400
+ for klass in ancestors
401
+ if klass.instance_variable_defined?(:@#{attribute_name})
402
+ if klass.#{attribute_name}.has_key?(key)
403
+ value = klass.#{attribute_name}[key]
404
+ for p in promotions
405
+ value = p.promote_#{name}(key, value)
406
+ end
407
+ yield(value)
408
+ return self if uniq
409
+ end
410
+ end
411
+ promotions.unshift(klass) if klass.respond_to?("promote_#{name}")
412
+ end
413
+ elsif !uniq
414
+ promotions = []
415
+ for klass in ancestors
416
+ if klass.instance_variable_defined?(:@#{attribute_name})
417
+ klass.#{attribute_name}.#{options[:enum_with]} do |k, v|
418
+ for p in promotions
419
+ v = p.promote_#{name}(k, v)
420
+ end
421
+ yield(k, v)
422
+ end
423
+ end
424
+ promotions.unshift(klass) if klass.respond_to?("promote_#{name}")
425
+ end
426
+ else
427
+ seen = Set.new
428
+ promotions = []
429
+ for klass in ancestors
430
+ if klass.instance_variable_defined?(:@#{attribute_name})
431
+ klass.#{attribute_name}.#{options[:enum_with]} do |k, v|
432
+ unless seen.include?(k)
433
+ for p in promotions
434
+ v = p.promote_#{name}(k, v)
435
+ end
436
+ seen << k
437
+ yield(k, v)
438
+ end
439
+ end
440
+ end
441
+ promotions.unshift(klass) if klass.respond_to?("promote_#{name}")
442
+ end
443
+ end
444
+ self
445
+ end
446
+ EOF
447
+ return code, file, line
448
+ end
449
+
450
+ # Helper class that defines the iteration method for inherited_attribute
451
+ # when :map is not set and there is a promotion method
452
+ def self.nomap_with_promotion(name, attribute_name, options)
453
+ code, file, line =<<-EOF, __FILE__, __LINE__+1
454
+ def each_#{name}
455
+ if !block_given?
456
+ return enum_for(:each_#{name})
457
+ end
458
+
459
+ ancestors = self.ancestors
460
+ if ancestors.first != self
461
+ ancestors.unshift self
462
+ end
463
+ promotions = []
464
+ for klass in ancestors
465
+ if klass.instance_variable_defined?(:@#{attribute_name})
466
+ klass.#{attribute_name}.#{options[:enum_with]} do |value|
467
+ for p in promotions
468
+ value = p.promote_#{name}(value)
469
+ end
470
+ yield(value)
471
+ end
472
+ end
473
+ promotions.unshift(klass) if klass.respond_to?("promote_#{name}")
474
+ end
475
+ self
476
+ end
477
+ EOF
478
+ return code, file, line
479
+ end
480
+ end
481
+ end
482
+
@@ -0,0 +1,158 @@
1
+ require 'utilrb/value_set'
2
+ require 'utilrb/module/const_defined_here_p'
3
+ module MetaRuby
4
+ # Extend in modules that are used as models
5
+ #
6
+ # @example
7
+ # module MyBaseModel
8
+ # extend MetaRuby::ModelAsModule
9
+ # end
10
+ #
11
+ # Alternatively, one can create a module to describe the metamodel for our
12
+ # base model and then include it in the actual root model
13
+ #
14
+ # @example
15
+ # module MyBaseMetamodel
16
+ # include MetaRuby::ModelAsModule
17
+ # end
18
+ # module MyBaseModel
19
+ # extend MyBaseMetamodel
20
+ # end
21
+ #
22
+ module ModelAsModule
23
+ include Attributes
24
+ include Registration
25
+ extend Attributes
26
+
27
+ # @return [String] set or get the documentation text for this model
28
+ inherited_single_value_attribute :doc
29
+
30
+ def self.validate_constant_name(name)
31
+ if name !~ /^[A-Z]\w+$/
32
+ raise ArgumentError, "#{name} is not a valid model name"
33
+ end
34
+ end
35
+
36
+ # Common method that can be used to create and register a
37
+ # submodel-as-a-module on a provided namespace
38
+ #
39
+ # It is usually used to create specific DSL-like methods that allow to
40
+ # create these models
41
+ def self.create_and_register_submodel(namespace, name, base_model, *args, &block)
42
+ ModelAsModule.validate_constant_name(name)
43
+
44
+ if namespace.const_defined_here?(name)
45
+ model = namespace.const_get(name)
46
+ base_model.setup_submodel(model, *args, &block)
47
+ else
48
+ namespace.const_set(name, model = base_model.new_submodel(*args, &block))
49
+ model.permanent_model = if !namespace.respond_to?(:permanent_model?)
50
+ Registration.accessible_by_name?(namespace)
51
+ else namespace.permanent_model?
52
+ end
53
+ end
54
+
55
+ model
56
+ end
57
+
58
+ # The call trace at the point of definition. It is usually used to
59
+ # report to the user in which file this model got defined
60
+ #
61
+ # @return [Array[(String,Integer,Symbol)]] a list of (file,line,method)
62
+ # tuples as returned by #call_stack (from the facet gem)
63
+ attr_accessor :definition_location
64
+
65
+ # Set of models that this model provides
66
+ attribute(:parent_models) { Set.new }
67
+
68
+ # True if this model is a root model
69
+ attr_predicate :root?, true
70
+
71
+ # Sets a name on this model
72
+ #
73
+ # Only use this on 'anonymous models', i.e. on models that are not
74
+ # meant to be assigned on a Ruby constant
75
+ #
76
+ # @return [String] the assigned name
77
+ def name=(name)
78
+ def self.name
79
+ if @name then @name
80
+ else super
81
+ end
82
+ end
83
+ @name = name
84
+ end
85
+
86
+ # Set the root model. See {root_model}
87
+ attr_accessor :supermodel
88
+
89
+ # Creates a new DataServiceModel that is a submodel of +self+
90
+ #
91
+ # @param [Hash] options the option hash
92
+ # @option options [String] :name the submodel name. Use this option
93
+ # only for "anonymous" models, i.e. models that won't be
94
+ # registered on a Ruby constant
95
+ # @option options [Class] :type (self.class) the type of the submodel
96
+ #
97
+ def new_submodel(options = Hash.new, &block)
98
+ options, submodel_options = Kernel.filter_options options,
99
+ :name => nil, :type => self.class
100
+
101
+ model = options[:type].new
102
+ model.extend ModelAsModule
103
+ if options[:name]
104
+ model.name = options[:name].dup
105
+ end
106
+ model.definition_location = call_stack
107
+ setup_submodel(model, submodel_options, &block)
108
+ model
109
+ end
110
+
111
+ # Called when a new submodel has been created, on the newly created
112
+ # submodel
113
+ def setup_submodel(submodel, options = Hash.new, &block)
114
+ submodel.provides self
115
+
116
+ if block_given?
117
+ submodel.apply_block(&block)
118
+ end
119
+ end
120
+
121
+ # In the case of model-as-modules, we always deregister (regardless of
122
+ # the fact that +self+ is permanent or not). The reason for this is that
123
+ # the model-as-module hierarchy is much more dynamic than
124
+ # model-as-class. Who provides what can be changed after a #clear_model
125
+ # call.
126
+ def clear_model
127
+ super
128
+ if supermodel
129
+ supermodel.deregister_submodels([self])
130
+ end
131
+ @supermodel = nil
132
+ parent_models.clear
133
+ end
134
+
135
+ # Called to apply a model definition block on this model
136
+ #
137
+ # The definition class-eval's it
138
+ #
139
+ # @return [void]
140
+ def apply_block(&block)
141
+ class_eval(&block)
142
+ end
143
+
144
+ # Declares that this model also provides this other given model
145
+ def provides(model)
146
+ include model
147
+ if model.root?
148
+ self.supermodel = model
149
+ else
150
+ self.supermodel = model.supermodel
151
+ end
152
+ self.supermodel.register_submodel(self)
153
+ self.parent_models |= model.parent_models
154
+ self.parent_models << model
155
+ end
156
+ end
157
+ end
158
+