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.
- data/History.txt +3 -0
- data/Manifest.txt +51 -13
- data/Rakefile +6 -1
- data/builtin/controllers/rtml_controller.rb +12 -1
- data/builtin/models/rtml/document.rb +24 -18
- data/builtin/models/rtml/document_model_object.rb +6 -0
- data/builtin/models/rtml/dom/collections/element_set.rb +6 -0
- data/builtin/models/rtml/dom/collections/property_set.rb +11 -0
- data/builtin/models/rtml/dom/element.rb +79 -27
- data/builtin/models/rtml/dom/frontend_element.rb +41 -20
- data/builtin/models/rtml/dom/property.rb +43 -26
- data/builtin/models/rtml/dom/screen_element.rb +13 -0
- data/builtin/widgets/document_variable_processing.rb +42 -18
- data/builtin/widgets/element_builder.rb +4 -4
- data/builtin/widgets/screen_variable_processing.rb +2 -2
- data/builtin/widgets/screen_variants.rb +31 -4
- data/builtin/widgets/screens.rb +13 -1
- data/builtin/widgets/static_content.rb +20 -6
- data/do_profile.rb +15 -0
- data/lib/extensions/action_controller/response.rb +0 -18
- data/lib/extensions/action_controller/routing/route_set.rb +28 -18
- data/lib/extensions/hpricot/doc.rb +3 -3
- data/lib/extensions/hpricot/elem.rb +3 -3
- data/lib/extensions/string.rb +2 -18
- data/lib/rtml.rb +0 -12
- data/lib/rtml/assigns.rb +32 -0
- data/lib/rtml/controller/document_generator.rb +9 -0
- data/lib/rtml/controller/render_helpers.rb +42 -18
- data/lib/rtml/controller/state.rb +2 -1
- data/lib/rtml/dependencies.rb +20 -15
- data/lib/rtml/dsl.rb +10 -10
- data/lib/rtml/environment.rb +13 -1
- data/lib/rtml/errors/application_error.rb +5 -0
- data/lib/rtml/errors/simulation_error.rb +4 -0
- data/lib/rtml/errors/variable_error.rb +5 -0
- data/lib/rtml/high_level/variable_manager.rb +11 -7
- data/lib/rtml/inherited_instance_variables.rb +8 -1
- data/lib/rtml/links.rb +17 -0
- data/lib/rtml/rules/dom_validation.rb +1 -0
- data/lib/rtml/test/builtin_variables.rb +33 -0
- data/lib/rtml/test/resemblance_test.rb +97 -0
- data/lib/rtml/test/screen.rb +126 -0
- data/lib/rtml/test/simulator.rb +240 -0
- data/lib/rtml/test/simulator_post_processors/base.rb +7 -0
- data/lib/rtml/test/simulator_post_processors/card_parsers.rb +32 -0
- data/lib/rtml/test/simulator_post_processors/receipt.rb +15 -0
- data/lib/rtml/test/simulator_post_processors/submit.rb +15 -0
- data/lib/rtml/test/spec.rb +14 -7
- data/lib/rtml/test/spec/matchers.rb +24 -0
- data/lib/rtml/test/tml_application.rb +331 -0
- data/lib/rtml/test/unit.rb +13 -0
- data/lib/rtml/test/variable_scope.rb +146 -0
- data/lib/rtml/version.rb +1 -1
- data/lib/rtml/widget.rb +26 -14
- data/lib/rtml/widget_core/class_methods.rb +8 -4
- data/lib/rtml/widget_core/widget_accessor_instance_methods.rb +6 -6
- data/lib/rtml/widgets.rb +22 -3
- data/lib/rtml_routes.rb +1 -1
- data/rails_generators/rtml/rtml_generator.rb +3 -0
- data/rails_generators/rtml/templates/db/migrate/20100513165226_add_options_to_rtml_documents.rb +9 -0
- data/rails_generators/rtml/templates/db/migrate/20100513165242_remove_dom_elements_mirror.rb +16 -0
- data/rails_generators/rtml/templates/db/migrate/20100513165249_remove_dom_properties_mirror.rb +16 -0
- data/rails_generators/rtml/templates/lib/tasks/rtml.rake +1 -1
- data/rtml.gemspec +65 -0
- data/spec/controllers/rtml_controller_spec.rb +1 -1
- data/spec/integration/post_tests_spec.rb +8 -0
- data/spec/lib/rtml/high_level/variable_manager_spec.rb +8 -0
- data/spec/lib/rtml/routes_spec.rb +23 -22
- data/spec/lib/rtml/test/simulator/receipt_spec.rb +18 -0
- data/spec/lib/rtml/test/simulator_spec.rb +185 -0
- data/spec/lib/rtml/test/tml_application_spec.rb +119 -0
- data/spec/lib/rtml/test/variable_scope_spec.rb +65 -0
- data/spec/lib/rtml/widget_spec.rb +1 -0
- data/spec/lib/rtml/widgets_spec.rb +30 -0
- data/spec/models/rtml/document_spec.rb +8 -0
- data/spec/models/rtml/dom/screen_element_spec.rb +15 -0
- data/spec/models/rtml/instruction_spec.rb +2 -2
- data/spec/rtml_action_spec.rb +25 -0
- data/spec/spec_helper.rb +31 -1
- data/spec/support/app/controllers/post_tests_controller.rb +11 -0
- data/spec/support/app/views/inherited/instance_variables_test/display.rtml.erb +1 -0
- data/spec/support/config/boot.rb +1 -0
- data/spec/support/config/routes.rb +3 -2
- data/spec/support/db/rtml_test_db.sqlite3 +0 -0
- data/spec/support/raw_tml/avs.tml +27 -0
- data/spec/support/raw_tml/document_level_events.tml +18 -0
- data/spec/support/raw_tml/empty_screen.tml +15 -0
- data/spec/support/raw_tml/enter_amount.tml +40 -0
- data/spec/support/raw_tml/foreign_receiver.tml +10 -0
- data/spec/support/raw_tml/foreign_reference.tml +10 -0
- data/spec/support/raw_tml/hello_world.tml +13 -0
- data/spec/support/raw_tml/loop_x_times.tml +39 -0
- data/spec/support/raw_tml/one_screen_with_setvar.tml +8 -0
- data/spec/support/raw_tml/receipt.tml +15 -0
- data/spec/support/raw_tml/simulator.tml +122 -0
- data/spec/support/raw_tml/tmlvar_reference.tml +34 -0
- data/spec/support/raw_tml/user_input.tml +47 -0
- data/spec/support/raw_tml/valid_document.tml +6 -0
- data/spec/support/rspec/example_groups.rb +1 -1
- data/spec/support/rspec/matchers.rb +0 -11
- data/spec/widgets/document_variable_processing_spec.rb +25 -39
- data/spec/widgets/element_builder_spec.rb +4 -0
- data/spec/widgets/event_listener_spec.rb +9 -0
- data/spec/widgets/highlevel_variable_processing_spec.rb +27 -2
- data/spec/widgets/screen_variable_processing_spec.rb +34 -0
- data/spec/widgets/screens_spec.rb +22 -0
- data/spec/widgets/simulator_post_processors/card_parsers_spec.rb +70 -0
- data/spec/widgets/simulator_post_processors/submit_spec.rb +44 -0
- data/tasks/stats.rake +10 -0
- data/test/test_rtml_generator.rb +3 -0
- metadata +55 -49
- data/builtin/widgets/subroutine.rb +0 -54
- data/lib/rtml/high_level/subroutine.rb +0 -22
- data/lib/rtml/reverse_engineering/crawler.rb +0 -58
- data/lib/rtml/reverse_engineering/simulator.rb +0 -269
- data/lib/rtml/reverse_engineering/simulator/casting.rb +0 -9
- data/lib/rtml/reverse_engineering/simulator/snapshot.rb +0 -18
- data/lib/rtml/reverse_engineering/simulator/variable_lookup.rb +0 -32
- data/lib/rtml/reverse_engineering/simulator/variable_value.rb +0 -105
- data/spec/lib/rtml/reverse_engineering/crawler_spec.rb +0 -24
- data/spec/lib/rtml/reverse_engineering/simulator/variable_value_spec.rb +0 -120
- data/spec/lib/rtml/reverse_engineering/simulator_spec.rb +0 -96
- data/spec/support/config/tml_dom_ruleset.rb +0 -82
- data/spec/widgets/subroutine_spec.rb +0 -109
|
@@ -1,22 +0,0 @@
|
|
|
1
|
-
class Rtml::HighLevel::Subroutine
|
|
2
|
-
def initialize(name, parent, options = {}, &blk)
|
|
3
|
-
@block = blk
|
|
4
|
-
@name = name
|
|
5
|
-
@parent = parent
|
|
6
|
-
end
|
|
7
|
-
|
|
8
|
-
def call(context, args)
|
|
9
|
-
method_name = @name
|
|
10
|
-
block = @block
|
|
11
|
-
|
|
12
|
-
(class << context; self; end).instance_eval do
|
|
13
|
-
define_method method_name, &block
|
|
14
|
-
new_method = instance_method(method_name)
|
|
15
|
-
define_method method_name do |arguments|
|
|
16
|
-
new_method.bind(self).call(arguments)
|
|
17
|
-
end
|
|
18
|
-
end
|
|
19
|
-
|
|
20
|
-
context.send(method_name, args)
|
|
21
|
-
end
|
|
22
|
-
end
|
|
@@ -1,58 +0,0 @@
|
|
|
1
|
-
class Rtml::ReverseEngineering::Crawler
|
|
2
|
-
attr_reader :simulator, :session
|
|
3
|
-
delegate :tml, :route, :set, :to => :simulator
|
|
4
|
-
delegate :response, :request, :controller, :get, :post, :put, :delete, :to => :session
|
|
5
|
-
|
|
6
|
-
def tml=(tml)
|
|
7
|
-
@path = nil
|
|
8
|
-
simulator.tml = tml
|
|
9
|
-
end
|
|
10
|
-
|
|
11
|
-
def initialize(options = {})
|
|
12
|
-
@simulator = Rtml::ReverseEngineering::Simulator.new(options.delete(:tml))
|
|
13
|
-
@session = ActionController::Integration::Session.new
|
|
14
|
-
end
|
|
15
|
-
|
|
16
|
-
def visit(path, screen = nil)
|
|
17
|
-
path, screen = split_path_and_screen_name_out_of(path) unless screen
|
|
18
|
-
unless path.blank? or path == @path
|
|
19
|
-
get @path = path
|
|
20
|
-
format = response.template.template_format
|
|
21
|
-
if format == :rtml then simulator.tml = response.body
|
|
22
|
-
else raise Rtml::Errors::InvalidFormat, "Response must be in :rtml format; found #{format.inspect}:\n#{response.body}"
|
|
23
|
-
end
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
@simulator.visit screen
|
|
27
|
-
end
|
|
28
|
-
|
|
29
|
-
def split_path_and_screen_name_out_of(path)
|
|
30
|
-
path, screen = path.split(/#/)
|
|
31
|
-
if screen =~ /\?/
|
|
32
|
-
screen, query = screen.split(/\?/)
|
|
33
|
-
path = "#{path}?#{query}"
|
|
34
|
-
end
|
|
35
|
-
[path, screen]
|
|
36
|
-
end
|
|
37
|
-
|
|
38
|
-
def current_screen
|
|
39
|
-
route.last
|
|
40
|
-
end
|
|
41
|
-
|
|
42
|
-
private
|
|
43
|
-
# Since we're using ActionController::Integration::Session instead of including ActionController::TestProcess,
|
|
44
|
-
# this method isn't used any more and should really be removed.
|
|
45
|
-
def setup_controller_request_and_response
|
|
46
|
-
# How to get this info into a TestResult so we don't have to create 2 requests?! TestResult seems to always override
|
|
47
|
-
# the env with Rack::MockRequest.env_for("/"), even though it looks like it SHOULD be merging the two. Bug?
|
|
48
|
-
temp_request = ActionController::Request.new(Rack::MockRequest.env_for(@path))
|
|
49
|
-
@path_params = ActionController::Routing::Routes.recognize_path(temp_request.path)
|
|
50
|
-
@request = ActionController::TestRequest.new
|
|
51
|
-
@request.remote_addr = '123.231.12.3'
|
|
52
|
-
@response = ActionController::TestResponse.new
|
|
53
|
-
@controller = @path_params[:controller].to_controller.new
|
|
54
|
-
@controller.request = @request
|
|
55
|
-
@controller.params = temp_request.parameters
|
|
56
|
-
@controller.send(:initialize_current_url)
|
|
57
|
-
end
|
|
58
|
-
end
|
|
@@ -1,269 +0,0 @@
|
|
|
1
|
-
class Rtml::ReverseEngineering::Simulator
|
|
2
|
-
include Rtml::Rules::DomValidation
|
|
3
|
-
include Rtml::ReverseEngineering::Simulator::VariableLookup
|
|
4
|
-
attr_reader :tml
|
|
5
|
-
|
|
6
|
-
def initialize(tml = nil)
|
|
7
|
-
self.tml = tml if tml
|
|
8
|
-
end
|
|
9
|
-
|
|
10
|
-
def set(values)
|
|
11
|
-
variables.merge!(values.with_indifferent_access)
|
|
12
|
-
end
|
|
13
|
-
|
|
14
|
-
def route
|
|
15
|
-
@stack.last ? @stack.last[4] : []
|
|
16
|
-
end
|
|
17
|
-
|
|
18
|
-
# Visits the specified screen. Evaluates variable assignments and follows TML logical flow, but does not interact
|
|
19
|
-
# with hotkeys, hyperlinks, etc.
|
|
20
|
-
#
|
|
21
|
-
# Timeouts are not evaluated because they *usually* indicate that some interaction is required by the user.
|
|
22
|
-
#
|
|
23
|
-
# The return value is the last screen to be visited with the above exceptions. Circular paths return the last
|
|
24
|
-
# screen in the path before it would loop over; external references return the URL of the reference.
|
|
25
|
-
def visit(screen_name = nil)
|
|
26
|
-
raise Rtml::Errors::ScreenNotFound, "No screens found to visit in RTML document!" unless first_screen
|
|
27
|
-
screen_name = first_screen['id'] if screen_name.blank?
|
|
28
|
-
response = analyze screen_name, :ignore_pathlist => true, :interaction => :none
|
|
29
|
-
response.kind_of?(Symbol) ? route.last || response : response
|
|
30
|
-
end
|
|
31
|
-
|
|
32
|
-
# Analyzes the specified path (which can be one or more symbols and/or strings) and verifies
|
|
33
|
-
# that screens with the specified IDs are followed, in the specified order. If any screen is encountered
|
|
34
|
-
# which is NOT in the list, no error is raised as long as the next screen in the list is visited at some point.
|
|
35
|
-
# Returns one of the following values:
|
|
36
|
-
#
|
|
37
|
-
# :completion - each screen was visited in the specified order.
|
|
38
|
-
# :assertion - a TML assertion error was raised in the form of the "assert" screen being visited.
|
|
39
|
-
# :no_route - The TML pathway did not result in the specified screens being visited in the expected order.
|
|
40
|
-
# :dead_end - The TML pathway ended abruptly, and did not visit all of the specified screens before doing so.
|
|
41
|
-
# :circular - The TML pathway was a circular route which never touched one or more nodes.
|
|
42
|
-
# :error - Processing ended abruptly, for an unknown reason.
|
|
43
|
-
# a String - The TML pathway encountered a link to an external document, and not an anchor. The String returned
|
|
44
|
-
# is the reference to the external document.
|
|
45
|
-
#
|
|
46
|
-
# This method also builds the #route up, so that you can analyze the order in which screens were visited.
|
|
47
|
-
#
|
|
48
|
-
def analyze(*path)
|
|
49
|
-
raise Rtml::Errors::ScreenNotFound, "No screens found to visit in RTML document!" unless first_screen
|
|
50
|
-
options = path.extract_options!
|
|
51
|
-
path.flatten!
|
|
52
|
-
raise ArgumentError, "Expected at least one screen ID as an argument" unless path.length > 0
|
|
53
|
-
@ignore_pathlist = path.empty? || options.delete(:ignore_pathlist)
|
|
54
|
-
@interaction = options.delete(:interaction) || options.delete(:interactions) || :all
|
|
55
|
-
@recursion_limit = options.delete(:recursion_limit) || options.delete(:max_depth) || 150
|
|
56
|
-
|
|
57
|
-
r = catch(:completion) do
|
|
58
|
-
current = path.shift || first_screen['id']
|
|
59
|
-
#route << current
|
|
60
|
-
@pathlist = path
|
|
61
|
-
catch(:abort) do
|
|
62
|
-
trace(find_screen(current))
|
|
63
|
-
:error
|
|
64
|
-
end
|
|
65
|
-
end
|
|
66
|
-
r
|
|
67
|
-
end
|
|
68
|
-
|
|
69
|
-
def tml=(value)
|
|
70
|
-
reset!
|
|
71
|
-
case value
|
|
72
|
-
when String then @tml = Hpricot::XML(value).root
|
|
73
|
-
when Hpricot::Doc then @tml = value.root
|
|
74
|
-
when Hpricot::Elem then @tml = value
|
|
75
|
-
else raise ArgumentError, "Expected TML to be a String, Hpricot::Doc or Hpricot::Elem"
|
|
76
|
-
end
|
|
77
|
-
|
|
78
|
-
parse_tml
|
|
79
|
-
end
|
|
80
|
-
|
|
81
|
-
def find_screen(id)
|
|
82
|
-
rt = screens.select { |scr| scr['id'] == id.to_s }
|
|
83
|
-
raise Rtml::Errors::ScreenNotFound, "Could not find screen with ID '#{id}'" if rt.empty?
|
|
84
|
-
scr = rt.shift
|
|
85
|
-
raise Rtml::Errors::ScreenNotFound, "Multiple ambiguous screens found with ID '#{id}'" unless rt.empty?
|
|
86
|
-
scr
|
|
87
|
-
end
|
|
88
|
-
|
|
89
|
-
private
|
|
90
|
-
# Returns all interactions that the simulator is allowed to perform in the form of an array.
|
|
91
|
-
def interactions
|
|
92
|
-
if @interaction.kind_of?(Symbol)
|
|
93
|
-
case @interaction
|
|
94
|
-
when :all then [:all, :key, :timeout, :href]
|
|
95
|
-
when :none then []
|
|
96
|
-
when :key, :timeout, :href then [@interaction]
|
|
97
|
-
end
|
|
98
|
-
else
|
|
99
|
-
@interaction
|
|
100
|
-
end
|
|
101
|
-
end
|
|
102
|
-
|
|
103
|
-
|
|
104
|
-
def trace(screen)
|
|
105
|
-
route << screen['id']
|
|
106
|
-
take_snapshot(screen)
|
|
107
|
-
unless @ignore_pathlist
|
|
108
|
-
@pathlist.shift if screen['id'] == @pathlist.first.to_s
|
|
109
|
-
throw :completion, :completion if @pathlist.empty?
|
|
110
|
-
end
|
|
111
|
-
|
|
112
|
-
process_variable_assignments(screen)
|
|
113
|
-
|
|
114
|
-
candidates = href_candidates(screen)
|
|
115
|
-
throw :completion, :completion unless candidates.empty? || interactions.include?(:href)
|
|
116
|
-
|
|
117
|
-
candidates.concat next_screen_for(screen)
|
|
118
|
-
throw :abort, :dead_end if candidates.empty?
|
|
119
|
-
|
|
120
|
-
abort_with = nil
|
|
121
|
-
anchors_for(candidates).each do |next_screen|
|
|
122
|
-
if next_screen.kind_of?(String)# == :external
|
|
123
|
-
throw :abort, next_screen # :external
|
|
124
|
-
elsif next_screen['id'] == 'assert'
|
|
125
|
-
throw :abort, :assertion
|
|
126
|
-
else
|
|
127
|
-
push_stack
|
|
128
|
-
abort_with = catch(:abort) do
|
|
129
|
-
trace(next_screen)
|
|
130
|
-
nil
|
|
131
|
-
end
|
|
132
|
-
throw :abort, abort_with if @ignore_pathlist
|
|
133
|
-
pop_stack if abort_with
|
|
134
|
-
end
|
|
135
|
-
end
|
|
136
|
-
throw :abort, abort_with if abort_with
|
|
137
|
-
end
|
|
138
|
-
|
|
139
|
-
# Finds the various +setvar+ elements in the specified screen and updates the simulator's variable states to match.
|
|
140
|
-
def process_variable_assignments(screen)
|
|
141
|
-
setvars = screen / "setvar"
|
|
142
|
-
setvars.each do |setvar|
|
|
143
|
-
variables[setvar['name']] = Rtml::ReverseEngineering::Simulator::VariableValue.new(setvar, variables, vardecs).value
|
|
144
|
-
end
|
|
145
|
-
end
|
|
146
|
-
|
|
147
|
-
# Finds all hyperlinks within the given screen and returns them.
|
|
148
|
-
#
|
|
149
|
-
def href_candidates(screen)
|
|
150
|
-
(screen / "a").select { |elem| elem['href'] }.collect { |elem| elem['href'] }
|
|
151
|
-
end
|
|
152
|
-
|
|
153
|
-
# Takes a "snapshot" of the state of the simulator, with the given screen as the index.
|
|
154
|
-
# This is used to track visited screens; it is a safe assumption that if all variables are identical
|
|
155
|
-
# to a given snapshot with the given screen, then this will result in a circular TML path.
|
|
156
|
-
# This method will throw :abort, :circular if this condition is true.
|
|
157
|
-
def take_snapshot(screen)
|
|
158
|
-
snapshot = Rtml::ReverseEngineering::Simulator::Snapshot.new(screen, variables)
|
|
159
|
-
if snapshots.length > @recursion_limit
|
|
160
|
-
raise Rtml::Errors::ProcessingError, "Recursion error: Probable infinite loop (or :recursion_limit is too low)"
|
|
161
|
-
end
|
|
162
|
-
if snapshots.include?(snapshot)
|
|
163
|
-
throw :abort, :circular
|
|
164
|
-
end
|
|
165
|
-
snapshots << snapshot
|
|
166
|
-
end
|
|
167
|
-
|
|
168
|
-
# returns the list of possible screenflow branches following the specified screen.
|
|
169
|
-
def next_screen_for(screen)
|
|
170
|
-
candidates = screen['next'] ? [screen['next']] : []
|
|
171
|
-
next_element = (screen / "next").shift
|
|
172
|
-
if next_element
|
|
173
|
-
candidates = find_adequate_variants(next_element) + [next_element['uri']]
|
|
174
|
-
end
|
|
175
|
-
|
|
176
|
-
candidates
|
|
177
|
-
end
|
|
178
|
-
|
|
179
|
-
# Returns array containing the screen element for each string in the array, or the original string if it is not found.
|
|
180
|
-
def anchors_for(candidates)
|
|
181
|
-
candidates.collect do |screen_id|
|
|
182
|
-
if screen_id =~ /^#/
|
|
183
|
-
find_screen(screen_id[1..-1]) # to drop the '#'
|
|
184
|
-
else
|
|
185
|
-
screen_id
|
|
186
|
-
#:external
|
|
187
|
-
end
|
|
188
|
-
end
|
|
189
|
-
end
|
|
190
|
-
|
|
191
|
-
# Returns the first variant in the Hpricot::Elem which matches the current variable values, or nil if none match.
|
|
192
|
-
def find_adequate_variants(elem)
|
|
193
|
-
variants = elem / "variant"
|
|
194
|
-
|
|
195
|
-
candidates = []
|
|
196
|
-
variants.each do |variant|
|
|
197
|
-
uri, lo, op, ro, key, timeout =
|
|
198
|
-
variant['uri'], variant['lo'], variant['op'] || "equal", variant['ro'], variant['key'], variant['timeout']
|
|
199
|
-
%w(uri lo op ro key timeout).each { |lv| eval("#{lv} = resolve(#{lv}, variables, vardecs)") }
|
|
200
|
-
if (interactions.include?(:key) && key) || (interactions.include?(:timeout) && timeout)
|
|
201
|
-
candidates << uri
|
|
202
|
-
elsif key || timeout # if we're not interacting with them, pause to allow user interaction
|
|
203
|
-
throw :completion, :completion
|
|
204
|
-
else
|
|
205
|
-
# only follow if it's a match
|
|
206
|
-
ro = lo.kind_of?(Fixnum) ? ro.to_i : ro.to_s
|
|
207
|
-
|
|
208
|
-
match = case op
|
|
209
|
-
when 'equal' then lo == ro
|
|
210
|
-
when 'not_equal' then lo != ro
|
|
211
|
-
when 'less' then lo < ro
|
|
212
|
-
when 'less_or_equal' then lo <= ro
|
|
213
|
-
when 'contains' then lo.to_s[ro.to_s]
|
|
214
|
-
else raise Rtml::InvalidOperation, "Invalid operation: #{op}"
|
|
215
|
-
end
|
|
216
|
-
candidates << uri and break if match
|
|
217
|
-
end
|
|
218
|
-
end
|
|
219
|
-
|
|
220
|
-
candidates #candidates.empty? ? nil : candidates
|
|
221
|
-
end
|
|
222
|
-
|
|
223
|
-
def first_screen
|
|
224
|
-
(tml / "screen").first
|
|
225
|
-
end
|
|
226
|
-
|
|
227
|
-
# resets the various tracking variables
|
|
228
|
-
def reset!
|
|
229
|
-
@stack = [ ]
|
|
230
|
-
#@stack.clear
|
|
231
|
-
push_stack
|
|
232
|
-
end
|
|
233
|
-
|
|
234
|
-
def push_stack
|
|
235
|
-
@stack.push [ screens.dup, snapshots.dup, variables.dup, vardecs.dup, route.dup ]
|
|
236
|
-
end
|
|
237
|
-
|
|
238
|
-
def pop_stack
|
|
239
|
-
@stack.pop
|
|
240
|
-
end
|
|
241
|
-
|
|
242
|
-
def screens
|
|
243
|
-
@stack.last ? @stack.last[0] : []
|
|
244
|
-
end
|
|
245
|
-
|
|
246
|
-
def snapshots
|
|
247
|
-
@stack.last ? @stack.last[1] : []
|
|
248
|
-
end
|
|
249
|
-
|
|
250
|
-
def variables
|
|
251
|
-
@stack.last ? @stack.last[2] : {}
|
|
252
|
-
end
|
|
253
|
-
|
|
254
|
-
def vardecs
|
|
255
|
-
@stack.last ? @stack.last[3] : {}
|
|
256
|
-
end
|
|
257
|
-
|
|
258
|
-
# Validates the given TML, and then finds the +screen+ elements and tracks them.
|
|
259
|
-
def parse_tml
|
|
260
|
-
validate_tml(@tml)
|
|
261
|
-
(@tml / "vardcl").each do |elem|
|
|
262
|
-
vardecs[elem['name']] = elem['type'].to_sym
|
|
263
|
-
end
|
|
264
|
-
|
|
265
|
-
(@tml / "screen").each do |elem|
|
|
266
|
-
screens << elem
|
|
267
|
-
end
|
|
268
|
-
end
|
|
269
|
-
end
|
|
@@ -1,9 +0,0 @@
|
|
|
1
|
-
module Rtml::ReverseEngineering::Simulator::Casting
|
|
2
|
-
# Returns an instance of +Rtml::ReverseEngineering::Simulator+ with self as the initialization argument.
|
|
3
|
-
def to_tml_simulator
|
|
4
|
-
Rtml::ReverseEngineering::Simulator.new(self)
|
|
5
|
-
end
|
|
6
|
-
|
|
7
|
-
alias to_rtml_simulator to_tml_simulator
|
|
8
|
-
alias to_simulator to_tml_simulator
|
|
9
|
-
end
|
|
@@ -1,18 +0,0 @@
|
|
|
1
|
-
# This class is used to contain a single +screen+ element (an instance of Hpricot::Elem) as well as any variable
|
|
2
|
-
# assignments that have been made. Basically, it is simply used by +Rtml::ReverseEngineering::Simulator+ to check
|
|
3
|
-
# for circular pathways. If two identical snapshots would be created, then it stands to reason that the resultant
|
|
4
|
-
# pathways for those two snapshots will also be identical. If you're following a path and creating snapshots at every
|
|
5
|
-
# step of the way, and two snapshots match, then using that logic, those two snapshots would indicate a circular
|
|
6
|
-
# pathway. (It comes down to either that, or waiting to catch a stack overflow error, which is less than optimal.)
|
|
7
|
-
#
|
|
8
|
-
class Rtml::ReverseEngineering::Simulator::Snapshot
|
|
9
|
-
attr_reader :screen, :variables
|
|
10
|
-
|
|
11
|
-
def initialize(screen, variables)
|
|
12
|
-
@screen, @variables = screen, variables.dup
|
|
13
|
-
end
|
|
14
|
-
|
|
15
|
-
def ==(other_snapshot)
|
|
16
|
-
@screen == other_snapshot.screen && variables == other_snapshot.variables
|
|
17
|
-
end
|
|
18
|
-
end
|
|
@@ -1,32 +0,0 @@
|
|
|
1
|
-
module Rtml::ReverseEngineering::Simulator::VariableLookup
|
|
2
|
-
# Checks a value to see if it references a TML variable; if so, it resolves that variable into its value;
|
|
3
|
-
# otherwise, the original value is returned.
|
|
4
|
-
def resolve(value, all_variables, known_variable_types)
|
|
5
|
-
if value =~ /^tmlvar:(.*)$/
|
|
6
|
-
lookup_variable_value($~[1], all_variables, known_variable_types)
|
|
7
|
-
else
|
|
8
|
-
value
|
|
9
|
-
end
|
|
10
|
-
end
|
|
11
|
-
|
|
12
|
-
# Finds the variable in the all_variables hash whose name matches the specified name (string or symbol), and then
|
|
13
|
-
# returns its value. If that value is an instance of +Rtml::ReverseEngineering::Simulator::VariableValue+, calls
|
|
14
|
-
# the #value method on that object instead.
|
|
15
|
-
#
|
|
16
|
-
# If the variable is not found, then a default value will be returned depending on its type.
|
|
17
|
-
def lookup_variable_value(name, all_variables, known_variable_types)
|
|
18
|
-
all_variables.each do |var_name, varval|
|
|
19
|
-
if var_name.to_s == name
|
|
20
|
-
return(varval.kind_of?(Rtml::ReverseEngineering::Simulator::VariableValue) ? varval.value : varval)
|
|
21
|
-
end
|
|
22
|
-
end
|
|
23
|
-
default_value_for_type(known_variable_types[name])
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def default_value_for_type(type)
|
|
27
|
-
case type
|
|
28
|
-
when :integer then 1
|
|
29
|
-
else "generic"
|
|
30
|
-
end
|
|
31
|
-
end
|
|
32
|
-
end
|
|
@@ -1,105 +0,0 @@
|
|
|
1
|
-
# Used by +Rtml::ReverseEngineering::Simulator+ to track the value of a variable. Attempts
|
|
2
|
-
# to build a logical value by processing the setvar's :lo, :op and :ro attributes. References to
|
|
3
|
-
# other variables are automatically resolved. References to nonexistent values assume built-in variables
|
|
4
|
-
# and assign generic values to them.
|
|
5
|
-
#
|
|
6
|
-
# Implements a #== method to see if one variable's value is equal to another's, though this may not always be accurate.
|
|
7
|
-
#
|
|
8
|
-
class Rtml::ReverseEngineering::Simulator::VariableValue
|
|
9
|
-
include Rtml::ReverseEngineering::Simulator::VariableLookup
|
|
10
|
-
attr_reader :value
|
|
11
|
-
|
|
12
|
-
def initialize(setvar, all_variables, known_variable_types)
|
|
13
|
-
@setvar, @all_variables, @known_variable_types = setvar, all_variables, known_variable_types
|
|
14
|
-
@value = setvar.to_s
|
|
15
|
-
|
|
16
|
-
%w(lo op ro).each { |i| instance_variable_set("@#{i}", resolve(self.send(i), all_variables, known_variable_types)) }
|
|
17
|
-
catch(:done) do
|
|
18
|
-
check_for_assignment
|
|
19
|
-
check_for_operation
|
|
20
|
-
end
|
|
21
|
-
|
|
22
|
-
do_casting
|
|
23
|
-
check_boundaries
|
|
24
|
-
end
|
|
25
|
-
|
|
26
|
-
def ==(other)
|
|
27
|
-
self.value == other.value
|
|
28
|
-
end
|
|
29
|
-
|
|
30
|
-
private
|
|
31
|
-
attr_reader :setvar, :all_variables, :known_variable_types
|
|
32
|
-
|
|
33
|
-
def type
|
|
34
|
-
@type ||= known_variable_types[setvar['name']]
|
|
35
|
-
end
|
|
36
|
-
|
|
37
|
-
def check_boundaries
|
|
38
|
-
case type
|
|
39
|
-
when :integer
|
|
40
|
-
unless (-999999999..999999999).include? @value
|
|
41
|
-
raise RulesViolationError, "TML numbers must be no more than 9 digits in length: #{@value}"
|
|
42
|
-
end
|
|
43
|
-
end
|
|
44
|
-
end
|
|
45
|
-
|
|
46
|
-
def do_casting
|
|
47
|
-
@value = case type
|
|
48
|
-
when :integer then value.to_i
|
|
49
|
-
else value.to_s
|
|
50
|
-
end
|
|
51
|
-
end
|
|
52
|
-
|
|
53
|
-
def check_for_operation
|
|
54
|
-
unless setvar['op'].blank?
|
|
55
|
-
method_name = "perform_operation_#{setvar['op']}"
|
|
56
|
-
@value = self.send(method_name)
|
|
57
|
-
throw :done
|
|
58
|
-
end
|
|
59
|
-
rescue NoMethodError
|
|
60
|
-
raise Rtml::Error::InvalidOperation
|
|
61
|
-
end
|
|
62
|
-
|
|
63
|
-
def check_for_assignment
|
|
64
|
-
if setvar['op'].blank? && !setvar['lo'].blank?
|
|
65
|
-
@value = setvar['lo']
|
|
66
|
-
throw :done
|
|
67
|
-
end
|
|
68
|
-
end
|
|
69
|
-
|
|
70
|
-
def lo; @lo ||= setvar['lo']; end
|
|
71
|
-
def op; @op ||= setvar['op']; end
|
|
72
|
-
def ro; @ro ||= setvar['ro']; end
|
|
73
|
-
|
|
74
|
-
def perform_operation_plus
|
|
75
|
-
case type
|
|
76
|
-
when :integer then lo.to_i + ro.to_i
|
|
77
|
-
when :string then lo.to_s + ro.to_s
|
|
78
|
-
when :date then "#{lo.to_s} + #{ro.to_s}"
|
|
79
|
-
else raise Rtml::Errors::InvalidOperation
|
|
80
|
-
end
|
|
81
|
-
end
|
|
82
|
-
|
|
83
|
-
def perform_operation_minus
|
|
84
|
-
case type
|
|
85
|
-
when :integer then lo.to_i - ro.to_i
|
|
86
|
-
when :string then lo.to_s[0..-(ro.to_i+1)]
|
|
87
|
-
when :date then "#{lo.to_s} - #{ro.to_s}"
|
|
88
|
-
else raise Rtml::Errors::InvalidOperation
|
|
89
|
-
end
|
|
90
|
-
end
|
|
91
|
-
|
|
92
|
-
def perform_operation_number
|
|
93
|
-
case type
|
|
94
|
-
when :integer then lo.split(/;/).length
|
|
95
|
-
else raise Rtml::Errors::InvalidOperation
|
|
96
|
-
end
|
|
97
|
-
end
|
|
98
|
-
|
|
99
|
-
def perform_operation_item
|
|
100
|
-
case type
|
|
101
|
-
when :string then lo.split(/;/)[ro.to_i]
|
|
102
|
-
else raise Rtml::Errors::InvalidOperation
|
|
103
|
-
end
|
|
104
|
-
end
|
|
105
|
-
end
|