rtml 2.0.3 → 2.0.4

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 (124) hide show
  1. data/History.txt +3 -0
  2. data/Manifest.txt +51 -13
  3. data/Rakefile +6 -1
  4. data/builtin/controllers/rtml_controller.rb +12 -1
  5. data/builtin/models/rtml/document.rb +24 -18
  6. data/builtin/models/rtml/document_model_object.rb +6 -0
  7. data/builtin/models/rtml/dom/collections/element_set.rb +6 -0
  8. data/builtin/models/rtml/dom/collections/property_set.rb +11 -0
  9. data/builtin/models/rtml/dom/element.rb +79 -27
  10. data/builtin/models/rtml/dom/frontend_element.rb +41 -20
  11. data/builtin/models/rtml/dom/property.rb +43 -26
  12. data/builtin/models/rtml/dom/screen_element.rb +13 -0
  13. data/builtin/widgets/document_variable_processing.rb +42 -18
  14. data/builtin/widgets/element_builder.rb +4 -4
  15. data/builtin/widgets/screen_variable_processing.rb +2 -2
  16. data/builtin/widgets/screen_variants.rb +31 -4
  17. data/builtin/widgets/screens.rb +13 -1
  18. data/builtin/widgets/static_content.rb +20 -6
  19. data/do_profile.rb +15 -0
  20. data/lib/extensions/action_controller/response.rb +0 -18
  21. data/lib/extensions/action_controller/routing/route_set.rb +28 -18
  22. data/lib/extensions/hpricot/doc.rb +3 -3
  23. data/lib/extensions/hpricot/elem.rb +3 -3
  24. data/lib/extensions/string.rb +2 -18
  25. data/lib/rtml.rb +0 -12
  26. data/lib/rtml/assigns.rb +32 -0
  27. data/lib/rtml/controller/document_generator.rb +9 -0
  28. data/lib/rtml/controller/render_helpers.rb +42 -18
  29. data/lib/rtml/controller/state.rb +2 -1
  30. data/lib/rtml/dependencies.rb +20 -15
  31. data/lib/rtml/dsl.rb +10 -10
  32. data/lib/rtml/environment.rb +13 -1
  33. data/lib/rtml/errors/application_error.rb +5 -0
  34. data/lib/rtml/errors/simulation_error.rb +4 -0
  35. data/lib/rtml/errors/variable_error.rb +5 -0
  36. data/lib/rtml/high_level/variable_manager.rb +11 -7
  37. data/lib/rtml/inherited_instance_variables.rb +8 -1
  38. data/lib/rtml/links.rb +17 -0
  39. data/lib/rtml/rules/dom_validation.rb +1 -0
  40. data/lib/rtml/test/builtin_variables.rb +33 -0
  41. data/lib/rtml/test/resemblance_test.rb +97 -0
  42. data/lib/rtml/test/screen.rb +126 -0
  43. data/lib/rtml/test/simulator.rb +240 -0
  44. data/lib/rtml/test/simulator_post_processors/base.rb +7 -0
  45. data/lib/rtml/test/simulator_post_processors/card_parsers.rb +32 -0
  46. data/lib/rtml/test/simulator_post_processors/receipt.rb +15 -0
  47. data/lib/rtml/test/simulator_post_processors/submit.rb +15 -0
  48. data/lib/rtml/test/spec.rb +14 -7
  49. data/lib/rtml/test/spec/matchers.rb +24 -0
  50. data/lib/rtml/test/tml_application.rb +331 -0
  51. data/lib/rtml/test/unit.rb +13 -0
  52. data/lib/rtml/test/variable_scope.rb +146 -0
  53. data/lib/rtml/version.rb +1 -1
  54. data/lib/rtml/widget.rb +26 -14
  55. data/lib/rtml/widget_core/class_methods.rb +8 -4
  56. data/lib/rtml/widget_core/widget_accessor_instance_methods.rb +6 -6
  57. data/lib/rtml/widgets.rb +22 -3
  58. data/lib/rtml_routes.rb +1 -1
  59. data/rails_generators/rtml/rtml_generator.rb +3 -0
  60. data/rails_generators/rtml/templates/db/migrate/20100513165226_add_options_to_rtml_documents.rb +9 -0
  61. data/rails_generators/rtml/templates/db/migrate/20100513165242_remove_dom_elements_mirror.rb +16 -0
  62. data/rails_generators/rtml/templates/db/migrate/20100513165249_remove_dom_properties_mirror.rb +16 -0
  63. data/rails_generators/rtml/templates/lib/tasks/rtml.rake +1 -1
  64. data/rtml.gemspec +65 -0
  65. data/spec/controllers/rtml_controller_spec.rb +1 -1
  66. data/spec/integration/post_tests_spec.rb +8 -0
  67. data/spec/lib/rtml/high_level/variable_manager_spec.rb +8 -0
  68. data/spec/lib/rtml/routes_spec.rb +23 -22
  69. data/spec/lib/rtml/test/simulator/receipt_spec.rb +18 -0
  70. data/spec/lib/rtml/test/simulator_spec.rb +185 -0
  71. data/spec/lib/rtml/test/tml_application_spec.rb +119 -0
  72. data/spec/lib/rtml/test/variable_scope_spec.rb +65 -0
  73. data/spec/lib/rtml/widget_spec.rb +1 -0
  74. data/spec/lib/rtml/widgets_spec.rb +30 -0
  75. data/spec/models/rtml/document_spec.rb +8 -0
  76. data/spec/models/rtml/dom/screen_element_spec.rb +15 -0
  77. data/spec/models/rtml/instruction_spec.rb +2 -2
  78. data/spec/rtml_action_spec.rb +25 -0
  79. data/spec/spec_helper.rb +31 -1
  80. data/spec/support/app/controllers/post_tests_controller.rb +11 -0
  81. data/spec/support/app/views/inherited/instance_variables_test/display.rtml.erb +1 -0
  82. data/spec/support/config/boot.rb +1 -0
  83. data/spec/support/config/routes.rb +3 -2
  84. data/spec/support/db/rtml_test_db.sqlite3 +0 -0
  85. data/spec/support/raw_tml/avs.tml +27 -0
  86. data/spec/support/raw_tml/document_level_events.tml +18 -0
  87. data/spec/support/raw_tml/empty_screen.tml +15 -0
  88. data/spec/support/raw_tml/enter_amount.tml +40 -0
  89. data/spec/support/raw_tml/foreign_receiver.tml +10 -0
  90. data/spec/support/raw_tml/foreign_reference.tml +10 -0
  91. data/spec/support/raw_tml/hello_world.tml +13 -0
  92. data/spec/support/raw_tml/loop_x_times.tml +39 -0
  93. data/spec/support/raw_tml/one_screen_with_setvar.tml +8 -0
  94. data/spec/support/raw_tml/receipt.tml +15 -0
  95. data/spec/support/raw_tml/simulator.tml +122 -0
  96. data/spec/support/raw_tml/tmlvar_reference.tml +34 -0
  97. data/spec/support/raw_tml/user_input.tml +47 -0
  98. data/spec/support/raw_tml/valid_document.tml +6 -0
  99. data/spec/support/rspec/example_groups.rb +1 -1
  100. data/spec/support/rspec/matchers.rb +0 -11
  101. data/spec/widgets/document_variable_processing_spec.rb +25 -39
  102. data/spec/widgets/element_builder_spec.rb +4 -0
  103. data/spec/widgets/event_listener_spec.rb +9 -0
  104. data/spec/widgets/highlevel_variable_processing_spec.rb +27 -2
  105. data/spec/widgets/screen_variable_processing_spec.rb +34 -0
  106. data/spec/widgets/screens_spec.rb +22 -0
  107. data/spec/widgets/simulator_post_processors/card_parsers_spec.rb +70 -0
  108. data/spec/widgets/simulator_post_processors/submit_spec.rb +44 -0
  109. data/tasks/stats.rake +10 -0
  110. data/test/test_rtml_generator.rb +3 -0
  111. metadata +55 -49
  112. data/builtin/widgets/subroutine.rb +0 -54
  113. data/lib/rtml/high_level/subroutine.rb +0 -22
  114. data/lib/rtml/reverse_engineering/crawler.rb +0 -58
  115. data/lib/rtml/reverse_engineering/simulator.rb +0 -269
  116. data/lib/rtml/reverse_engineering/simulator/casting.rb +0 -9
  117. data/lib/rtml/reverse_engineering/simulator/snapshot.rb +0 -18
  118. data/lib/rtml/reverse_engineering/simulator/variable_lookup.rb +0 -32
  119. data/lib/rtml/reverse_engineering/simulator/variable_value.rb +0 -105
  120. data/spec/lib/rtml/reverse_engineering/crawler_spec.rb +0 -24
  121. data/spec/lib/rtml/reverse_engineering/simulator/variable_value_spec.rb +0 -120
  122. data/spec/lib/rtml/reverse_engineering/simulator_spec.rb +0 -96
  123. data/spec/support/config/tml_dom_ruleset.rb +0 -82
  124. data/spec/widgets/subroutine_spec.rb +0 -109
@@ -0,0 +1,13 @@
1
+ require 'test/unit'
2
+
3
+ class Test::Unit::TestCase
4
+ def assert_tml_resembles(expected_hash, tml)
5
+ assert Rtml::Test::ResemblanceTest.new(expected_hash, target).pass?,
6
+ "Expected #{expected_hash.inspect} to resemble:\n\n#{tml}"
7
+ end
8
+
9
+ def assert_tml_not_resemble(expected_hash, tml)
10
+ assert !Rtml::Test::ResemblanceTest.new(expected_hash, target).pass?,
11
+ "Expected #{expected_hash.inspect} not to resemble:\n\n#{tml}"
12
+ end
13
+ end
@@ -0,0 +1,146 @@
1
+ class Rtml::Test::VariableScope
2
+ class Variable
3
+ attr_reader :type, :perms, :format
4
+ attr_reader :value
5
+ attr_reader :name
6
+
7
+ def initialize(name, options = {})
8
+ options.stringify_keys!.reverse_merge(default_options)
9
+ @name = name.to_s
10
+ %w(type perms format).each do |key|
11
+ instance_variable_set("@#{key}", options[key].to_s) if options.key?(key)
12
+ instance_variable_set("@#{key}", default_options[key]) unless instance_variable_get("@#{key}")
13
+ end
14
+ self.value = options.delete('value') if options.key?('value')
15
+ end
16
+
17
+ # TODO: implement :format option
18
+ def perform_operation(op)
19
+ if op.keys?(:lo, :op) && op[:op] == 'number' # list length
20
+ raise Rtml::Errors::VariableError, "No :ro expected" if op.key?(:ro)
21
+ self.value = (op[:lo].to_s.split(/\;/).length)
22
+ elsif op.key?(:lo) && !op.keys?(:op, :ro) # assignment
23
+ self.value = op[:lo]
24
+ elsif op.keys?(:lo, :op, :ro) # operation
25
+ self.value = case op[:op]
26
+ when 'plus' then cast_value(op[:lo]) + cast_value(op[:ro])
27
+ when 'minus' then cast_value(op[:lo]) - cast_value(op[:ro])
28
+ when 'format' then raise Rtml::Errors::VariableError, "setvar[op=format] not yet implemented"
29
+ when 'item' then op[:lo].to_s.split(/\;/)[op[:ro].to_i]
30
+ else raise Rtml::Errors::VariableError, "Unknown operation: #{op[:op].inspect}"
31
+ end
32
+ else
33
+ raise Rtml::Errors::VariableError, "Expected :lo, :op, and :ro; or just :lo for assignment"
34
+ end
35
+ end
36
+
37
+ def value=(value)
38
+ @value = cast_value(value)
39
+ end
40
+
41
+ def cast_value(value)
42
+ case type
43
+ when 'integer' then value.to_i
44
+ when 'string' then value.to_s
45
+ when 'opaque' then value.to_s
46
+ when 'date' then value.kind_of?(DateTime) ? value : DateTime.parse(value.to_s)
47
+ else raise "Unknown type: #{type.inspect}"
48
+ end
49
+ end
50
+
51
+ private
52
+ def default_options
53
+ {'type' => 'string', 'perms' => 'rwxrw', 'format' => nil, 'value' => ''}
54
+ end
55
+ end
56
+
57
+ def initialize
58
+ @variables = {}
59
+ end
60
+
61
+ # Returns a "snapshot" of this variable scope's current state. All known variables are returned, along with their
62
+ # types, values, etc.
63
+ def snapshot
64
+ @variables.values.collect do |variable|
65
+ { :type => variable.type, :perms => variable.perms, :format => variable.format, :name => variable.name,
66
+ :value => variable.value }
67
+ end
68
+ end
69
+
70
+ def inspect
71
+ @variables.collect do |name, variable|
72
+ "#{name}={value:#{variable.value.inspect} type:#{variable.type.inspect} perms:#{variable.perms.inspect} format:#{variable.format.inspect}}"
73
+ end.join("; ")
74
+ end
75
+
76
+ # Accepts a hash according to the TML rules (for example, keys should be :lo, :op, :ro)
77
+ def true_condition?(condition)
78
+ condition.reverse_merge!(:op => 'equal')
79
+ raise Rtml::Errors::VariableError, "Expected :lo, :op and :ro" unless condition.keys?(:lo, :op, :ro)
80
+ lo = literal_value(condition[:lo])
81
+ ro = literal_value(condition[:ro])
82
+ case condition[:op]
83
+ when 'equal' then lo == ro
84
+ when 'not_equal' then lo != ro
85
+ when 'less' then lo < ro
86
+ when 'less_or_equal' then lo <= ro
87
+ when 'contains' then lo =~ /#{Regexp::escape ro}/
88
+ else raise Rtml::Errors::VariableError, "Invalid operation: #{condition[:op].inspect}"
89
+ end
90
+ end
91
+
92
+ def update(name, value)
93
+ find_variable(name).value = literal_value(value)
94
+ end
95
+
96
+ # Accepts a hash, and updates each listed variable with the values in the hash.
97
+ def update_with(hash)
98
+ hash.each do |name, value|
99
+ update(name, value)
100
+ end
101
+ end
102
+
103
+ def literal_value(value_or_variable_name)
104
+ if value_or_variable_name.kind_of?(String) && value_or_variable_name =~ /^tmlvar\:/
105
+ value(value_or_variable_name[7..-1])
106
+ else
107
+ value_or_variable_name
108
+ end
109
+ end
110
+
111
+ def perform_operation_on(variable_name, operation)
112
+ operation[:lo] = literal_value(operation[:lo]) if operation.key?(:lo)
113
+ operation[:ro] = literal_value(operation[:ro]) if operation.key?(:ro)
114
+ find_variable(variable_name).perform_operation(operation)
115
+ end
116
+
117
+ def exist?(name)
118
+ name = name.to_s unless name.kind_of?(String)
119
+ @variables[name]
120
+ end
121
+
122
+ def find_variable(name)
123
+ name = name.to_s unless name.kind_of?(String)
124
+ @variables[name] || raise(Rtml::Errors::VariableError, "Undeclared variable #{name.inspect}")
125
+ end
126
+
127
+ def declare_variable(name, options = {})
128
+ name = name.to_s unless name.kind_of?(String)
129
+ if @variables.key?(name)
130
+ raise Rtml::Errors::VariableError, "Already declared variable #{name.inspect} (it's a #{@variables[name].type})"
131
+ end
132
+ @variables[name] = Variable.new(name, options)
133
+ end
134
+
135
+ def variable_names
136
+ @variables.keys
137
+ end
138
+
139
+ def value(name)
140
+ find_variable(name).value
141
+ end
142
+
143
+ alias_method :[], :value
144
+ alias_method :[]=, :update
145
+ alias_method :assign, :update
146
+ end
@@ -2,7 +2,7 @@ module Rtml
2
2
  module VERSION
3
3
  MAJOR = 2
4
4
  MINOR = 0
5
- TINY = 3
5
+ TINY = 4
6
6
 
7
7
  STRING = (defined?(PRE) ? [MAJOR, MINOR, TINY, PRE] : [MAJOR, MINOR, TINY]).join('.')
8
8
  end
@@ -1,3 +1,5 @@
1
+ require 'rtml/assigns'
2
+
1
3
  # The Widget is the driving force behind RubyTML. It is interaction with Widgets of all shapes and sizes that produces
2
4
  # a document model and, finally, the TML document itself. A Widget is so named because it is a generic class that can
3
5
  # be made to encapsulate any desired functionality. The Rtml::Widget base class takes care of interfacing content
@@ -35,6 +37,8 @@
35
37
  # ruby script/generate widget my_widget [entry_point1 entry_point2 . . .]
36
38
  #
37
39
  class Rtml::Widget
40
+ include Rtml::Assigns
41
+
38
42
  # These are the accessors that RDoc uses to define what a particular RTML-specific attribute maps to.
39
43
  #
40
44
  # There are 3 kinds of accessors:
@@ -60,17 +64,22 @@ class Rtml::Widget
60
64
  class_inheritable_array :targets
61
65
  class_inheritable_array :shared_variables
62
66
  class_inheritable_array :entry_points
67
+ class_inheritable_array :rtml_protected_instance_variables
63
68
  read_inheritable_attribute(:targets) || write_inheritable_attribute(:targets, [])
64
69
  read_inheritable_attribute(:shared_variables) || write_inheritable_attribute(:shared_variables, [])
65
70
  read_inheritable_attribute(:entry_points) || write_inheritable_attribute(:entry_points, [])
71
+ read_inheritable_attribute(:rtml_protected_instance_variables) || write_inheritable_attribute(:rtml_protected_instance_variables, %w(
72
+ @document @parent @source_type
73
+ ))
66
74
  attr_reader :parent
67
75
  attr_reader :source_type
68
76
  extend Rtml::WidgetCore::ClassMethods
69
77
 
70
78
  def validate_parent(*options)
71
79
  options = options.flatten.collect { |o| o.kind_of?(String) ? o : o.to_s }
72
- unless options.include?(parent.name)
73
- raise ArgumentError, "Expected parent to be one of #{options.to_sentence(:language => :en)}; found #{parent.name}"
80
+ unless options.include?(parent.name) || options.include?(parent.class.name) ||
81
+ ((options.include?(:document) || options.include?('document')) && parent.kind_of?(Rtml::Document))
82
+ raise ArgumentError, "Expected parent to be one of #{options.to_sentence}; found #{parent.name}"
74
83
  end
75
84
  end
76
85
 
@@ -83,21 +92,24 @@ class Rtml::Widget
83
92
  @widget_id ||= (@@widget_id_tracker += 1)
84
93
  end
85
94
 
95
+ # Any method other than one of this widget's entry points will be delegated into #parent.
96
+ # The parent's methods are not known until runtime, and it's extraordinarily slow to generate
97
+ # delegation for those methods on the fly. Method_missing is the happy middle ground.
98
+ def method_missing(name, *args, &block)
99
+ name = name.to_s unless name.kind_of?(String)
100
+ return parent.send(name, *args, &block) unless entry_points.include?(name) || !parent.respond_to?(name)
101
+ # FIXME: Why does super raise an ArgumentError("no id given")?
102
+ raise NoMethodError, "Method missing: #{name}"
103
+ end
104
+
105
+ def respond_to?(*a, &b)
106
+ super || parent.respond_to?(*a, &b)
107
+ end
108
+
86
109
  def initialize(parent)
87
110
  @parent = parent
88
111
  set_source_type!
89
112
  parent.widget_instances << self
90
- # proxy parent methods into parent, unless they exist for widget already
91
- singleton_class = (class << self; self; end)
92
- (@parent.public_methods - self.public_methods).each do |proxy_method|
93
- # don't delegate entry points -- this addresses the infinite loop issue in which calling a method that's been
94
- # defined as an entry point but hasn't been created in the Widget itself would recurse back into the parent
95
- # object forever.
96
- unless entry_points.include? proxy_method
97
- singleton_class.send(:delegate, proxy_method, :to => :parent)
98
- end
99
- end
100
-
101
113
 
102
114
  self.class.shared_variables.each do |sv|
103
115
  unless @parent.instance_variable_get("@#{sv[:name]}")
@@ -144,7 +156,7 @@ class Rtml::Widget
144
156
  when Rtml::Document
145
157
  @source_type = :document
146
158
  else
147
- @source_type = parent.name.to_sym
159
+ @source_type = parent.respond_to?(:name) ? parent.name.to_sym : parent.class.name.to_sym
148
160
  end
149
161
  end
150
162
  end
@@ -106,10 +106,13 @@ module Rtml::WidgetCore::ClassMethods
106
106
 
107
107
  # Goes through each of the targets of this Widget, looks up its mapping in Rtml::Widgets.mapping, and calls
108
108
  # proxy_module.append_features(mapping) for each of those mappings.
109
+ #
109
110
  def reinclude_proxy_module
110
111
  targets.each do |target|
111
112
  Rtml::Widgets.mapping(target).imbue(proxy_module)
112
113
  end
114
+ # FIXME: Um... Maybe this is fine, but it seems out of place.
115
+ Rtml::Widgets.bases.each { |base| Rtml::Widgets.add_proxies_to_base(base) }
113
116
  end
114
117
 
115
118
  # Specifies entry points, which are names of methods that can be called from the targets to invoke this Widget.
@@ -119,10 +122,9 @@ module Rtml::WidgetCore::ClassMethods
119
122
  #
120
123
  def entry_point(*names)
121
124
  names.each do |name|
122
- name = name.to_s
125
+ #name = name.to_sym unless name.kind_of?(Symbol)
123
126
  unless entry_points.include?(name)
124
- entry_points << name
125
- add_entry_point(name)
127
+ entry_points << add_entry_point(name)
126
128
  end
127
129
  end
128
130
  end
@@ -135,6 +137,7 @@ module Rtml::WidgetCore::ClassMethods
135
137
  # raise Rtml::Errors::RulesViolationError,
136
138
  # "Widget entry point #{entry_point} cannot be defined because it already exists!"
137
139
  # end
140
+ #entry_point = entry_point.to_sym unless entry_point.is_a?(Symbol)
138
141
  entry_point = entry_point.to_s if entry_point.is_a?(Symbol)
139
142
 
140
143
  enter_from_hash = "#{entry_point}_from_hash"
@@ -174,6 +177,7 @@ module Rtml::WidgetCore::ClassMethods
174
177
  end_code
175
178
  proxy_module.class_eval code, __FILE__, line
176
179
  reinclude_proxy_module
180
+ entry_point
177
181
  end
178
182
 
179
183
  # Returns true if the specified class is a valid target of this Widget
@@ -181,7 +185,7 @@ module Rtml::WidgetCore::ClassMethods
181
185
  if base.respond_to?(:name)
182
186
  name = base.name.underscore.sub(/^.*\//, '')
183
187
  targets.each do |target|
184
- if base.kind_of?(ActiveRecord::Base) # it's an instance, so we should check the tag name, not the class name
188
+ if base.kind_of?(ActiveRecord::Base) || base.kind_of?(Rtml::DocumentModelObject) # it's an instance, so we should check the tag name, not the class name
185
189
  return true if target == base.name
186
190
  else
187
191
  return true if target == name
@@ -5,13 +5,13 @@ module Rtml::WidgetCore::WidgetAccessorInstanceMethods
5
5
 
6
6
  delegate :class_widget_proxies, :class_widget_entry_points, :to => 'self.class'
7
7
 
8
- def singleton_widgets; (class << self; self; end).class_widgets end
9
- def singleton_widget_proxies; (class << self; self; end).class_widget_proxies end
10
- def singleton_widget_entry_points; (class << self; self; end).class_widget_entry_points end
8
+ def singleton_widgets; (class << self; self; end).class_widgets ||= [] end
9
+ def singleton_widget_proxies; (class << self; self; end).class_widget_proxies ||= [] end
10
+ def singleton_widget_entry_points; (class << self; self; end).class_widget_entry_points ||= [] end
11
11
 
12
- def widgets; singleton_widgets + class_widgets end
13
- def widget_proxies; singleton_widget_proxies + class_widget_proxies end
14
- def widget_entry_points; singleton_widget_entry_points + class_widget_entry_points end
12
+ def widgets; singleton_widgets + (self.class_widgets ||= []) end
13
+ def widget_proxies; singleton_widget_proxies + (self.class_widget_proxies ||= []) end
14
+ def widget_entry_points; singleton_widget_entry_points + (self.class_widget_entry_points ||= []) end
15
15
 
16
16
  alias class_widget_methods class_widget_entry_points
17
17
  alias singleton_widget_methods singleton_widget_entry_points
@@ -10,6 +10,9 @@
10
10
 
11
11
  module Rtml::Widgets
12
12
  class << self
13
+ # A list of all objects that have included Rtml::Widgets, marking them as compatible with Widgets.
14
+ attr_reader :bases
15
+
13
16
  # The Widget mapping is a hash whose keys are symbols and whose values are instances of Module. The symbols
14
17
  # represent either RTML models or TML tag names, while the Modules represent the proxy from the representation's
15
18
  # class into the Widget itself. This way, the Module can be mixed-in with the target, and Widgets added after
@@ -25,12 +28,14 @@ module Rtml::Widgets
25
28
  # target_instance.say_hi # => hello
26
29
  # target_instance.say_bye # => goodbye
27
30
  def mapping(key)
31
+ name = (key.respond_to?(:name) ? key.name : key.to_s).underscore
28
32
  @widget_mapping ||= {}
29
- @widget_mapping[key.to_s] ||= Rtml::WidgetCore::WidgetProxy.new
33
+ @widget_mapping[name] ||= Rtml::WidgetCore::WidgetProxy.new
30
34
  end
31
35
 
32
36
  # Returns all Widgets which affect the specified target.
33
37
  def affecting(target)
38
+ target = (target.respond_to?(:name) ? target.name : target.to_s).underscore
34
39
  mapping(target).proxied_widgets
35
40
  end
36
41
 
@@ -44,14 +49,28 @@ module Rtml::Widgets
44
49
  base.send(:extend, Rtml::WidgetCore::WidgetAccessorClassMethods)
45
50
  load_widgets
46
51
  add_proxies_to_base(base)
52
+
53
+ base.class_eval do
54
+ if defined?(initialize)
55
+ def initialize_with_widgets(*args, &block)
56
+ initialize_without_widgets(*args, &block)
57
+ Rtml::Widgets.add_proxies_to_base(self)
58
+ end
59
+ alias_method_chain :initialize, :widgets
60
+ else
61
+ def initialize
62
+ Rtml::Widgets.add_proxies_to_base(self)
63
+ end
64
+ end
65
+ end
47
66
  end
48
67
 
49
68
  # Mixes the various proxy methods into the specified base.
50
69
  def add_proxies_to_base(base)
51
- target_name = base.respond_to?(:name) ? base.name.underscore.sub(/^.*\//, '') : nil
70
+ target_name = base.respond_to?(:name) ? (base.name || base.class.name).underscore.sub(/^.*\//, '') : nil
52
71
  (@widget_mapping ||= {}).each do |key, mixin|
53
72
  klass = base
54
- if base.kind_of? ActiveRecord::Base
73
+ if base.kind_of?(ActiveRecord::Base) || base.kind_of?(Rtml::DocumentModelObject)
55
74
  klass = class << base; self; end
56
75
  end
57
76
 
@@ -1,3 +1,3 @@
1
1
  ActionController::Routing::Routes.draw do |map|
2
- map.connect_rtml :controller => 'rtml'
2
+ map.connect_rtml :controller => 'rtml', :no_index => true
3
3
  end
@@ -59,6 +59,9 @@ class RtmlGenerator < Rails::Generator::Base
59
59
  20100127173146_add_parent_type_to_rtml_dom_elements.rb
60
60
  20100208114234_create_rtml_states.rb
61
61
  20100303021609_add_x_and_y_to_rtml_instructions.rb
62
+ 20100513165226_add_options_to_rtml_documents.rb
63
+ 20100513165242_remove_dom_elements_mirror.rb
64
+ 20100513165249_remove_dom_properties_mirror.rb
62
65
  )
63
66
 
64
67
  end
@@ -0,0 +1,9 @@
1
+ class AddOptionsToRtmlDocuments < ActiveRecord::Migration
2
+ def self.up
3
+ add_column :rtml_documents, :options, :text
4
+ end
5
+
6
+ def self.down
7
+ remove_column :rtml_documents, :options
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ class RemoveDomElementsMirror < ActiveRecord::Migration
2
+ def self.up
3
+ drop_table :rtml_dom_elements
4
+ end
5
+
6
+ def self.down
7
+ create_table :rtml_dom_elements do |t|
8
+ t.string :name
9
+ t.string :type, :default => 'Rtml::Dom::Element'
10
+
11
+ t.references :parent
12
+ t.references :document
13
+ t.timestamps
14
+ end
15
+ end
16
+ end
@@ -0,0 +1,16 @@
1
+ class RemoveDomPropertiesMirror < ActiveRecord::Migration
2
+ def self.up
3
+ drop_table :rtml_dom_properties
4
+ end
5
+
6
+ def self.down
7
+ create_table :rtml_dom_properties do |t|
8
+ t.string :name
9
+ t.text :value
10
+ t.string :type
11
+
12
+ t.references :element
13
+ t.timestamps
14
+ end
15
+ end
16
+ end
@@ -5,9 +5,9 @@ end
5
5
 
6
6
  # This is a tiny bit faster than the bootstrapper because we can skip some of the dependencies, which matters a lot more
7
7
  # in potentially-often-run rake tasks.
8
- module Rtml; module WidgetCore; end; end
9
8
  require 'rtml'
10
9
  unless defined?(Rtml::Widget)
10
+ module Rtml; module WidgetCore; end; end
11
11
  require 'rtml/widget_core/class_methods'
12
12
  require 'rtml/widget'
13
13
  end