waw 0.2.2

Sign up to get free protection for your applications and to get access to all the features.
Files changed (199) hide show
  1. data/LICENCE.rdoc +25 -0
  2. data/README.rdoc +32 -0
  3. data/bin/waw +32 -0
  4. data/bin/waw-profile +26 -0
  5. data/bin/waw-start +26 -0
  6. data/bin/wspec +33 -0
  7. data/layouts/empty/Rakefile +14 -0
  8. data/layouts/empty/config.ru +5 -0
  9. data/layouts/empty/config/commons.cfg +31 -0
  10. data/layouts/empty/config/devel.cfg +21 -0
  11. data/layouts/empty/ignore +7 -0
  12. data/layouts/empty/logs/dontforgetme +0 -0
  13. data/layouts/empty/resources/messages.rs +1 -0
  14. data/layouts/empty/test/wspec/site_respond.wspec +3 -0
  15. data/layouts/empty/test/wspec/test_all.rb +13 -0
  16. data/layouts/empty/waw.deploy +1 -0
  17. data/layouts/empty/waw.routing +6 -0
  18. data/layouts/static/dependencies +1 -0
  19. data/layouts/static/public/.wawaccess +18 -0
  20. data/layouts/static/public/css/style.css +0 -0
  21. data/layouts/static/public/images/dontforgetme +0 -0
  22. data/layouts/static/public/js/project.js +0 -0
  23. data/layouts/static/public/pages/.wawaccess +30 -0
  24. data/layouts/static/public/pages/404.wtpl +1 -0
  25. data/layouts/static/public/pages/index.wtpl +5 -0
  26. data/layouts/static/public/templates/.wawaccess +9 -0
  27. data/layouts/static/public/templates/layout.wtpl +17 -0
  28. data/layouts/static/test/wspec/static_pages_are_served.wspec +21 -0
  29. data/layouts/static/waw.routing +8 -0
  30. data/lib/waw.rb +99 -0
  31. data/lib/waw/commands/command.rb +115 -0
  32. data/lib/waw/commands/profile_command.rb +66 -0
  33. data/lib/waw/commands/start_command.rb +59 -0
  34. data/lib/waw/config.rb +110 -0
  35. data/lib/waw/controller.rb +25 -0
  36. data/lib/waw/controllers/action/action.rb +91 -0
  37. data/lib/waw/controllers/action/action_utils.rb +30 -0
  38. data/lib/waw/controllers/action/js_generation.rb +116 -0
  39. data/lib/waw/controllers/action_controller.rb +133 -0
  40. data/lib/waw/controllers/error/backtrace.rb +54 -0
  41. data/lib/waw/controllers/error_handler.rb +62 -0
  42. data/lib/waw/controllers/json_controller.rb +31 -0
  43. data/lib/waw/controllers/no_cache.rb +22 -0
  44. data/lib/waw/controllers/static/match.rb +80 -0
  45. data/lib/waw/controllers/static/waw_access.rb +235 -0
  46. data/lib/waw/controllers/static/waw_access_dsl.rb +48 -0
  47. data/lib/waw/controllers/static_controller.rb +37 -0
  48. data/lib/waw/default_config.cfg +14 -0
  49. data/lib/waw/environment_utils.rb +57 -0
  50. data/lib/waw/errors.rb +4 -0
  51. data/lib/waw/ext.rb +3 -0
  52. data/lib/waw/ext/core.rb +4 -0
  53. data/lib/waw/ext/core/hash.rb +47 -0
  54. data/lib/waw/ext/core/logger.rb +10 -0
  55. data/lib/waw/ext/core/module.rb +20 -0
  56. data/lib/waw/ext/core/object.rb +29 -0
  57. data/lib/waw/ext/rack.rb +19 -0
  58. data/lib/waw/ext/rack/builder.rb +43 -0
  59. data/lib/waw/ext/rack/delegator.rb +51 -0
  60. data/lib/waw/ext/rack/urlmap.rb +55 -0
  61. data/lib/waw/ext/wlang.rb +1 -0
  62. data/lib/waw/ext/wlang/hosted_language.rb +21 -0
  63. data/lib/waw/fullstate.rb +8 -0
  64. data/lib/waw/fullstate/on_class.rb +37 -0
  65. data/lib/waw/fullstate/on_instance.rb +27 -0
  66. data/lib/waw/fullstate/variable.rb +36 -0
  67. data/lib/waw/kern.rb +6 -0
  68. data/lib/waw/kern/app.rb +48 -0
  69. data/lib/waw/kern/empty/waw.deploy +0 -0
  70. data/lib/waw/kern/empty/waw.routing +1 -0
  71. data/lib/waw/kern/freezed_state.rb +32 -0
  72. data/lib/waw/kern/hooks.rb +53 -0
  73. data/lib/waw/kern/lifecycle.rb +248 -0
  74. data/lib/waw/kern/living_state.rb +87 -0
  75. data/lib/waw/kern/utils.rb +27 -0
  76. data/lib/waw/resource_collection.rb +100 -0
  77. data/lib/waw/restart.rb +32 -0
  78. data/lib/waw/routing.rb +43 -0
  79. data/lib/waw/routing/action_routing.rb +78 -0
  80. data/lib/waw/routing/dsl.rb +45 -0
  81. data/lib/waw/routing/feedback.rb +23 -0
  82. data/lib/waw/routing/form_validation_feedback.rb +36 -0
  83. data/lib/waw/routing/javascript.rb +17 -0
  84. data/lib/waw/routing/redirect.rb +26 -0
  85. data/lib/waw/routing/refresh.rb +17 -0
  86. data/lib/waw/routing/routing_rule.rb +16 -0
  87. data/lib/waw/scope_utils.rb +69 -0
  88. data/lib/waw/session.rb +51 -0
  89. data/lib/waw/testing.rb +1 -0
  90. data/lib/waw/tools/mail.rb +4 -0
  91. data/lib/waw/tools/mail/mail.rb +119 -0
  92. data/lib/waw/tools/mail/mail_agent.rb +123 -0
  93. data/lib/waw/tools/mail/mailbox.rb +62 -0
  94. data/lib/waw/tools/mail/template.rb +38 -0
  95. data/lib/waw/utils/dsl_helper.rb +116 -0
  96. data/lib/waw/validation.rb +175 -0
  97. data/lib/waw/validation/and_validator.rb +27 -0
  98. data/lib/waw/validation/array_validations.rb +38 -0
  99. data/lib/waw/validation/boolean_validator.rb +32 -0
  100. data/lib/waw/validation/comparison_validations.rb +45 -0
  101. data/lib/waw/validation/date_validator.rb +31 -0
  102. data/lib/waw/validation/default_validator.rb +30 -0
  103. data/lib/waw/validation/dsl_ruby_extensions.rb +11 -0
  104. data/lib/waw/validation/errors.rb +17 -0
  105. data/lib/waw/validation/ext.rb +3 -0
  106. data/lib/waw/validation/file_validator.rb +30 -0
  107. data/lib/waw/validation/float_validator.rb +19 -0
  108. data/lib/waw/validation/helpers.rb +67 -0
  109. data/lib/waw/validation/integer_validator.rb +16 -0
  110. data/lib/waw/validation/isin_validator.rb +24 -0
  111. data/lib/waw/validation/mandatory_validator.rb +17 -0
  112. data/lib/waw/validation/missing_validator.rb +17 -0
  113. data/lib/waw/validation/not_validator.rb +20 -0
  114. data/lib/waw/validation/or_validator.rb +34 -0
  115. data/lib/waw/validation/regexp_validator.rb +29 -0
  116. data/lib/waw/validation/same_validator.rb +16 -0
  117. data/lib/waw/validation/signature.rb +157 -0
  118. data/lib/waw/validation/size_validations.rb +44 -0
  119. data/lib/waw/validation/string_validator.rb +15 -0
  120. data/lib/waw/validation/validator.rb +48 -0
  121. data/lib/waw/wawgen.rb +2 -0
  122. data/lib/waw/wawgen/create.rb +166 -0
  123. data/lib/waw/wawgen/project.rb +25 -0
  124. data/lib/waw/wspec.rb +5 -0
  125. data/lib/waw/wspec/browser.rb +240 -0
  126. data/lib/waw/wspec/dsl.rb +201 -0
  127. data/lib/waw/wspec/html_analysis.rb +136 -0
  128. data/lib/waw/wspec/html_analysis/tag.rb +56 -0
  129. data/lib/waw/wspec/runner.rb +70 -0
  130. data/lib/waw/wspec/scenario.rb +35 -0
  131. data/lib/waw/wspec/suite.rb +54 -0
  132. data/test/bricks/error_handler/config/test.cfg +2 -0
  133. data/test/bricks/error_handler/logs/webapp.log +1411 -0
  134. data/test/bricks/error_handler/test/error_handler.wspec +16 -0
  135. data/test/bricks/error_handler/waw.deploy +1 -0
  136. data/test/bricks/error_handler/waw.routing +27 -0
  137. data/test/integration/waw_create_integration_test.rb +24 -0
  138. data/test/spec/assumptions_spec.rb +30 -0
  139. data/test/spec/controllers/action_controller_spec.rb +14 -0
  140. data/test/spec/controllers/static/waw_access_spec.rb +112 -0
  141. data/test/spec/environment_utils_spec.rb +15 -0
  142. data/test/spec/ext/core/hash_spec.rb +58 -0
  143. data/test/spec/fixtures.rb +41 -0
  144. data/test/spec/fixtures/action/config/default.cfg +2 -0
  145. data/test/spec/fixtures/action/lib/action_controller_test.rb +12 -0
  146. data/test/spec/fixtures/action/waw.deploy +1 -0
  147. data/test/spec/fixtures/action/waw.routing +6 -0
  148. data/test/spec/fixtures/empty/waw.deploy +0 -0
  149. data/test/spec/fixtures/empty/waw.routing +0 -0
  150. data/test/spec/fullstate/on_class_spec.rb +59 -0
  151. data/test/spec/fullstate/on_instance_spec.rb +59 -0
  152. data/test/spec/fullstate/session_spec.rb +43 -0
  153. data/test/spec/fullstate/variable_spec.rb +55 -0
  154. data/test/spec/resource_collection_spec.rb +50 -0
  155. data/test/spec/test_all.rb +9 -0
  156. data/test/spec/tools/mail/mail_agent_spec.rb +116 -0
  157. data/test/spec/tools/mail/mail_spec.rb +56 -0
  158. data/test/spec/tools/mail/mailbox_spec.rb +57 -0
  159. data/test/spec/tools/mail/template_spec.rb +47 -0
  160. data/test/spec/validation/array_validation_spec.rb +63 -0
  161. data/test/spec/validation/array_validator_spec.rb +17 -0
  162. data/test/spec/validation/date_validation_spec.rb +35 -0
  163. data/test/spec/validation/default_validation_spec.rb +37 -0
  164. data/test/spec/validation/disjuctive_validation_spec.rb +33 -0
  165. data/test/spec/validation/errors_spec.rb +37 -0
  166. data/test/spec/validation/file_validator_spec.rb +34 -0
  167. data/test/spec/validation/mail_validation_spec.rb +51 -0
  168. data/test/spec/validation/missing_validation_spec.rb +43 -0
  169. data/test/spec/validation/same_validation_spec.rb +24 -0
  170. data/test/spec/validation/signature_intuition_spec.rb +37 -0
  171. data/test/spec/validation/signature_spec.rb +164 -0
  172. data/test/spec/validation/validation_spec.rb +28 -0
  173. data/test/spec/wspec/html_analysis/tag_spec.rb +38 -0
  174. data/test/spec/wspec/html_analysis_spec.rb +170 -0
  175. data/test/unit/test_all.rb +8 -0
  176. data/test/unit/waw/app_test.rb +126 -0
  177. data/test/unit/waw/app_test/config/commons.cfg +2 -0
  178. data/test/unit/waw/app_test/config/devel.cfg +1 -0
  179. data/test/unit/waw/config_test.rb +54 -0
  180. data/test/unit/waw/controllers/action_controller_test.rb +76 -0
  181. data/test/unit/waw/controllers/action_test.rb +35 -0
  182. data/test/unit/waw/controllers/example_action_controller_test.rb +24 -0
  183. data/test/unit/waw/controllers/multiple_action_controller_test.rb +78 -0
  184. data/test/unit/waw/controllers/static/example/css/example.css +1 -0
  185. data/test/unit/waw/controllers/static/example/index.html +1 -0
  186. data/test/unit/waw/controllers/static/example/js/example.js +1 -0
  187. data/test/unit/waw/controllers/static/example/pages/hello.wtpl +1 -0
  188. data/test/unit/waw/controllers/static/waw_access_test.rb +76 -0
  189. data/test/unit/waw/ext/rack_test.rb +74 -0
  190. data/test/unit/waw/resource_collection_test.rb +49 -0
  191. data/test/unit/waw/resources.txt +4 -0
  192. data/test/unit/waw/routing/routing_test.rb +26 -0
  193. data/test/unit/waw/utils/dsl_helper_test.rb +79 -0
  194. data/test/unit/waw/utils/dsl_helper_test_extensions1.rb +4 -0
  195. data/test/unit/waw/validation/signature_test.rb +193 -0
  196. data/test/unit/waw/validation_test.rb +319 -0
  197. data/test/unit/waw/wspec/html_analysis_test.html +81 -0
  198. data/test/unit/waw/wspec/html_analysis_test.rb +26 -0
  199. metadata +272 -0
@@ -0,0 +1,44 @@
1
+ module Waw
2
+ module Validation
3
+ # Validation linked to the length of the string
4
+ module SizeValidations
5
+ module Methods
6
+
7
+ # Checks that it has a size
8
+ def has_size?(val) val.respond_to?(:size) end
9
+
10
+ # Builds a validator that verifies that the length is greater than
11
+ # a specified value
12
+ def >(value)
13
+ Validator.new {|*vals| Validation.argument_safe{ vals.all?{|val| has_size?(val) and (val.size > value)} }}
14
+ end
15
+
16
+ # Builds a validator that verifies that the length is greater-or-equal to
17
+ # a specified value
18
+ def >=(value)
19
+ Validator.new {|*vals| Validation.argument_safe{ vals.all?{|val| has_size?(val) and (val.size >= value)} }}
20
+ end
21
+
22
+ # Builds a validator that verifies that the length is less than
23
+ # a specified value
24
+ def <(value)
25
+ Validator.new {|*vals| Validation.argument_safe{ vals.all?{|val| has_size?(val) and (val.size < value)} }}
26
+ end
27
+
28
+ # Builds a validator that verifies that the length is less-or-equal to
29
+ # a specified value
30
+ def <=(value)
31
+ Validator.new {|*vals| Validation.argument_safe{ vals.all?{|val| has_size?(val) and (val.size <= value)} }}
32
+ end
33
+
34
+ # Builds a validator that verifies that the length is equal to a
35
+ # specified value
36
+ def ==(value)
37
+ Validator.new {|*vals| Validation.argument_safe{ vals.all?{|val| has_size?(val) and (val.size == value)} }}
38
+ end
39
+
40
+ end
41
+ extend Methods
42
+ end # module SizeValidations
43
+ end # module Validation
44
+ end # module Waw
@@ -0,0 +1,15 @@
1
+ module Waw
2
+ module Validation
3
+ class StringValidator < Validator
4
+
5
+ def validate(*values)
6
+ values.all?{|v| String===v}
7
+ end
8
+
9
+ def convert_and_validate(*values)
10
+ [validate(*values), values]
11
+ end
12
+
13
+ end
14
+ end # module Validation
15
+ end # module Waw
@@ -0,0 +1,48 @@
1
+ module Waw
2
+ module Validation
3
+ #
4
+ # A validator reusable class.
5
+ #
6
+ class Validator
7
+ include ::Waw::Validation::Helpers
8
+
9
+ # Creates a validator instance that takes a block as validation code
10
+ def initialize(&block)
11
+ @block = block
12
+ end
13
+
14
+ # Calls the block installed at initialization time
15
+ def validate(*values)
16
+ raise "Missing validation block on #{self}" unless @block
17
+ @block.call(*values)
18
+ end
19
+ def ===(*args)
20
+ validate(*args)
21
+ end
22
+
23
+ # Converts and validate
24
+ def convert_and_validate(*values)
25
+ validate(*values) ? [true, values] : [false, values]
26
+ end
27
+ def =~(*args)
28
+ convert_and_validate(*args)
29
+ end
30
+
31
+ # Negates this validator
32
+ def not
33
+ NotValidator.new(self)
34
+ end
35
+
36
+ # Creates a validator by disjunction
37
+ def |(validator)
38
+ OrValidator.new(self, Waw::Validation.to_validator(validator))
39
+ end
40
+
41
+ # Creates a validator by conjunction
42
+ def &(validator)
43
+ AndValidator.new(self, Waw::Validation.to_validator(validator))
44
+ end
45
+
46
+ end # class Validator
47
+ end # module Validation
48
+ end # module Waw
@@ -0,0 +1,2 @@
1
+ require 'waw/wawgen/project'
2
+ require 'waw/wawgen/create'
@@ -0,0 +1,166 @@
1
+ require 'optparse'
2
+ require 'fileutils'
3
+ module Waw
4
+ module Wawgen
5
+ # Implementation of 'waw create'
6
+ class Create
7
+
8
+ # Which layout
9
+ attr_reader :layout
10
+
11
+ # Force overrides?
12
+ attr_reader :force
13
+
14
+ # Creates a command instance
15
+ def initialize
16
+ @layout = 'empty'
17
+ @force = false
18
+ end
19
+
20
+ # Parses commandline options provided as an array of Strings.
21
+ def options
22
+ @options ||= OptionParser.new do |opt|
23
+ opt.program_name = File.basename $0
24
+ opt.version = WLang::VERSION
25
+ opt.release = nil
26
+ opt.summary_indent = ' ' * 4
27
+ banner = <<-EOF
28
+ # Usage waw create [options] ProjectName
29
+
30
+ # Creates an initial waw project structure
31
+ EOF
32
+ opt.banner = banner.gsub(/[ \t]+# /, "")
33
+
34
+ opt.separator nil
35
+ opt.separator "Options:"
36
+
37
+ opt.on("--force", "-f", "Erase any exsting file on conflict") do |value|
38
+ @force = true
39
+ end
40
+
41
+ opt.on("--layout=LAYOUT", "-l", "Use a specific waw layout (default empty)") do |value|
42
+ @layout = value
43
+ end
44
+
45
+ opt.on("--verbose", "-v", "Display extra progress as we parse.") do |value|
46
+ @verbosity = 2
47
+ end
48
+
49
+ # No argument, shows at tail. This will print an options summary.
50
+ # Try it and see!
51
+ opt.on_tail("-h", "--help", "Show this message") do
52
+ puts options
53
+ exit
54
+ end
55
+
56
+ # Another typical switch to print the version.
57
+ opt.on_tail("--version", "Show version") do
58
+ puts "waw version " << Waw::VERSION << " (c) University of Louvain, Bernard & Louis Lambeau"
59
+ exit
60
+ end
61
+
62
+ opt.separator nil
63
+ end
64
+ end
65
+
66
+ # Runs the command
67
+ def run(argv)
68
+ # parse options
69
+ rest = options.parse!(argv)
70
+ if rest.length != 1
71
+ puts options
72
+ exit(-1)
73
+ end
74
+
75
+ # check project name
76
+ project_name = rest[0]
77
+ exit("Invalid project name #{project_name}, must start with [a-zA-Z]") unless /^[a-zA-Z]/ =~ project_name
78
+ if project_name =~ /^[a-z]/
79
+ project_name = (project_name[0...1].upcase + project_name[1..-1])
80
+ puts "Warning: using #{project_name} as project name for ruby needs..."
81
+ end
82
+
83
+ # create project
84
+ project = ::Waw::Wawgen::Project.new(project_name)
85
+ begin
86
+ generate(project)
87
+ rescue WLang::Error => ex
88
+ puts ex.message
89
+ puts ex.wlang_backtrace.join("\n")
90
+ puts ex.backtrace.join("\n")
91
+ end
92
+ end
93
+
94
+ # Puts an error message and exits
95
+ def exit(msg)
96
+ puts msg
97
+ Kernel.exit(-1)
98
+ end
99
+
100
+ def generate_recursively(project, layout_folder, target_folder)
101
+ # Generate files now
102
+ Dir.new(layout_folder).each do |file|
103
+ next if ['.', '..', 'dontforgetme'].include?(file)
104
+ next if 'dependencies'==file
105
+ target = File.join(target_folder, file.gsub('project', project.lowname))
106
+ puts "Generating #{target} from #{layout}/#{file}"
107
+
108
+ if File.directory?(source=File.join(layout_folder, file))
109
+ FileUtils.mkdir_p target unless File.exists?(target)
110
+ generate_recursively(project, source, target)
111
+ else
112
+ File.open(target, 'w') do |io|
113
+ if /jquery/ =~ file
114
+ io << File.read(File.join(layout_folder, file))
115
+ else
116
+ context = {"project" => project}
117
+ io << WLang.file_instantiate(File.join(layout_folder, file), context, 'wlang/active-string', :parentheses).to_s
118
+ end
119
+ end
120
+ end
121
+ end
122
+ end
123
+
124
+ # Generates a given layout for a specific project
125
+ def generate_layout(project, layout)
126
+ # Locate the layout folder
127
+ layout_folder = File.join(File.dirname(__FILE__), '..', '..', '..', 'layouts', layout)
128
+ puts File.expand_path(layout_folder)
129
+ exit("Unknown layout #{layout}") unless File.exists?(layout_folder)
130
+
131
+ # Handle dependencies
132
+ dependencies_file = File.join(layout_folder, 'dependencies')
133
+ if File.exists?(dependencies_file)
134
+ File.readlines(dependencies_file).each do |line|
135
+ next if /^#/ =~ (line = line.strip)
136
+ line.split(/\s/).each do |word|
137
+ generate_layout(project, word)
138
+ end
139
+ end
140
+ end
141
+
142
+ # Let recursive generation occur
143
+ puts "Generating recursively project, #{layout_folder}, #{project.folder}"
144
+ generate_recursively(project, layout_folder, project.folder)
145
+ end
146
+
147
+ # Generates the project
148
+ def generate(project)
149
+ # A small debug message and we start
150
+ puts "Generating project with names #{project.upname} inside #{project.lowname} using layout #{layout}"
151
+
152
+ # 1) Create the output folder if it not exists
153
+ if File.exists?(project.folder) and not(force)
154
+ exit("The project folder #{project.lowname} already exists. Remove it first")
155
+ else
156
+ FileUtils.rm_rf project.folder if File.exists?(project.folder)
157
+ FileUtils.mkdir_p project.folder
158
+ end
159
+
160
+ generate_layout(project, layout)
161
+ FileUtils.chmod 0755, File.join(project.root, 'config.ru')
162
+ end
163
+
164
+ end # class Create
165
+ end # module Wawgen
166
+ end # module Waw
@@ -0,0 +1,25 @@
1
+ module Waw
2
+ module Wawgen
3
+ class Project
4
+
5
+ # Upper case name of the project
6
+ attr_reader :upname
7
+
8
+ # Lower case name of the project
9
+ attr_reader :lowname
10
+
11
+ # Creates a project instance
12
+ def initialize(name, folder=nil)
13
+ @upname, @lowname = name, WLang::encode(name, 'ruby/method-case')
14
+ @folder = folder
15
+ end
16
+
17
+ # Returns root folder
18
+ def root
19
+ @folder ||= lowname
20
+ end
21
+ alias :folder :root
22
+
23
+ end # class Project
24
+ end # module Wawgen
25
+ end # module Waw
@@ -0,0 +1,5 @@
1
+ require 'waw/wspec/html_analysis'
2
+ require 'waw/wspec/browser'
3
+ require 'waw/wspec/suite'
4
+ require 'waw/wspec/dsl'
5
+ require 'waw/wspec/scenario'
@@ -0,0 +1,240 @@
1
+ require 'net/http'
2
+ require 'uri'
3
+ module Waw
4
+ module WSpec
5
+ # A fake browser for waw application testing
6
+ class Browser
7
+ include Waw::WSpec::HTMLAnalysis
8
+
9
+ # Current browser location
10
+ attr_reader :location
11
+
12
+ # Current server response
13
+ attr_reader :response
14
+
15
+ # The last action result
16
+ attr_reader :last_action_result
17
+
18
+ # Creates a browser instance
19
+ def initialize(location = nil)
20
+ self.location = location if location
21
+ end
22
+
23
+ #################################################################### URI utilities
24
+
25
+ # Checks if something is an URI
26
+ def is_uri?(uri)
27
+ URI::Generic===uri
28
+ end
29
+
30
+ # Ensures that something is an uri or convert it.
31
+ def ensure_uri(uri)
32
+ raise ArgumentError, "ensure_uri: uri may not be nil" if uri.nil?
33
+ is_uri?(uri) ? uri : URI.parse(uri)
34
+ end
35
+
36
+ # Extracts the base of an URI
37
+ def extract_base(uri)
38
+ raise ArgumentError, "extract_base: uri may not be nil" if uri.nil?
39
+ ensure_uri("#{uri.scheme}://#{uri.host}#{uri.port ? (':' + uri.port.to_s) : ''}/")
40
+ end
41
+
42
+ # Computes the new location if a relative uri is followed
43
+ def relative_uri(uri)
44
+ raise ArgumentError, "relative_uri: uri may not be nil" if uri.nil?
45
+ uri = ensure_uri(uri)
46
+ if uri.path[0...1] == '/'
47
+ new_location = base.dup
48
+ new_location.path = uri.path
49
+ new_location.query = uri.query
50
+ new_location
51
+ else
52
+ new_location = base.dup
53
+ new_location.path = '/' + uri.path
54
+ new_location.query = uri.query
55
+ new_location
56
+ end
57
+ end
58
+
59
+ #################################################################### Query utilities
60
+
61
+ # Looks for the base of the website
62
+ def base
63
+ @base ||= find_base
64
+ end
65
+
66
+ # Finds the base of the current location
67
+ def find_base
68
+ if contents and (found = tag("base", {:href => /.*/}, contents))
69
+ ensure_uri(found[:href])
70
+ else
71
+ extract_base(location)
72
+ end
73
+ end
74
+
75
+ # Checks if the last request waw answered by a 404 not found
76
+ def is404
77
+ (Net::HTTPNotFound === response)
78
+ end
79
+ alias :is404? :is404
80
+
81
+ # Checks if the last request waw answered by a 200 OK
82
+ def is200
83
+ (Net::HTTPSuccess === response)
84
+ end
85
+ alias :is200? :is200
86
+
87
+ # Returns the current shown contents
88
+ def contents
89
+ response ? response.read_body : nil
90
+ end
91
+ alias :browser_contents :contents
92
+
93
+ #################################################################### Location set
94
+
95
+ # Go to a relative position
96
+ def go_relative(uri)
97
+ self.location = relative_uri(uri)
98
+ end
99
+
100
+ # Sets the current location
101
+ def location=(loc)
102
+ if (loc = ensure_uri(loc)).relative?
103
+ go_relative(loc)
104
+ else
105
+ @location, @response = fetch(loc)
106
+ @location
107
+ end
108
+ end
109
+
110
+ # Fetches the headers only and returns it without keeping the result in browser
111
+ # state
112
+ def headers_fetch(uri)
113
+ # Fetch the result at that location
114
+ if (loc = ensure_uri(uri)).relative?
115
+ headers_fetch(relative_uri(uri))
116
+ else
117
+ response = Net::HTTP.start(loc.host, loc.port) do |http|
118
+ headers = @cookie ? {'Cookie' => @cookie} : {}
119
+ http.head(loc.path, headers)
120
+ end
121
+ end
122
+ end
123
+
124
+ # Refreshes the browser
125
+ def refresh
126
+ self.location = location
127
+ end
128
+
129
+ # Simulates a click. Support relative as well as absolute paths.
130
+ # Raises a URI::InvalidURIError if the given href seems invalid.
131
+ def click_href(href)
132
+ uri = URI.parse(href)
133
+ if uri.relative?
134
+ go_relative(uri)
135
+ else
136
+ case uri.scheme
137
+ when "http"
138
+ self.location = href
139
+ else
140
+ raise ArgumentError, "This browser does not support #{href} location"
141
+ end
142
+ end
143
+ end
144
+
145
+ #################################################################### Server invocation utilities
146
+
147
+ # Applies the action routing for a given action
148
+ def apply_action_routing(action, result)
149
+ @last_action_result = result
150
+ action.routing.apply_on_browser(result, self) if action.routing
151
+ result.extend(Waw::Routing::Methods)
152
+ result
153
+ end
154
+
155
+ # Invokes an action server side, decodes json response an applies action routing.
156
+ # Returns the action result.
157
+ def invoke_action(action, args = {})
158
+ raise ArgumentError, "Browser.invoke_action expects an ActionController::Action as first parameter"\
159
+ unless ::Waw::ActionController::Action===action
160
+ location, response = fetch(relative_uri(action.uri), :post, args)
161
+ apply_action_routing(action, JSON.parse(response.body))
162
+ self.response
163
+ end
164
+
165
+ # Invokes a service server side and returns HTTP response
166
+ def invoke_service(service, args = {})
167
+ raise ArgumentError, "Browser.invoke_service expects a String as first parameter"\
168
+ unless String===service
169
+ location, response = fetch(relative_uri(service), :post, args)
170
+ self.response
171
+ end
172
+
173
+ # Invokes a server service with arguments and decoding method
174
+ def server_invoke(service, args, decode_method = nil)
175
+ location, response = fetch(relative_uri(service), :post, args)
176
+ response
177
+ end
178
+
179
+ #################################################################### Private section
180
+ # Clean cache after fetch
181
+ def clean_post_fetch
182
+ @base = nil
183
+ end
184
+
185
+ # Fetchs a given location
186
+ def fetch(uri, method = :get, data = {}, limit = 10)
187
+ # You should choose better exception.
188
+ raise 'HTTP redirect too deep' if limit == 0
189
+
190
+ # Fetch the result at that location
191
+ location = ensure_uri(uri)
192
+ response = Net::HTTP.start(location.host, location.port) do |http|
193
+ headers = @cookie ? {'Cookie' => @cookie} : {}
194
+ case method
195
+ when :get
196
+ path = location.path
197
+ path += '?' + location.query if location.query
198
+ http.request_get(path, headers)
199
+ when :post
200
+ req = Net::HTTP::Post.new(location.path, headers)
201
+ req.form_data = data.unsymbolize_keys
202
+ http.request(req)
203
+ else
204
+ raise ArgumentError, "Invalid fetch method #{method}"
205
+ end
206
+ end
207
+
208
+ # If a cookie is requested save it
209
+ @cookie = response['set-cookie']
210
+
211
+ # Catch the response, following redirections
212
+ result = case response
213
+ when Net::HTTPRedirection
214
+ fetch(response['location'] || response['Location'], :get, {}, limit - 1)
215
+ else
216
+ [location, response]
217
+ end
218
+
219
+ # Cleans cache and returns result
220
+ clean_post_fetch
221
+ result
222
+ end
223
+
224
+ #################################################################### Helpers to save context
225
+
226
+ # Installs the browser context
227
+ def install_context(location, response, cookie)
228
+ @location, @response, @cookie = location, response, cookie
229
+ self
230
+ end
231
+
232
+ # Duplicates this browser instance, with internal state
233
+ def dup
234
+ Browser.new.install_context(@location, @response, @cookie)
235
+ end
236
+
237
+ end # class Browser
238
+ class ServerError < StandardError; end
239
+ end # module WSpec
240
+ end # module Waw