metaruby 1.0.0.rc1

Sign up to get free protection for your applications and to get access to all the features.
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
+