waw 0.2.2
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENCE.rdoc +25 -0
- data/README.rdoc +32 -0
- data/bin/waw +32 -0
- data/bin/waw-profile +26 -0
- data/bin/waw-start +26 -0
- data/bin/wspec +33 -0
- data/layouts/empty/Rakefile +14 -0
- data/layouts/empty/config.ru +5 -0
- data/layouts/empty/config/commons.cfg +31 -0
- data/layouts/empty/config/devel.cfg +21 -0
- data/layouts/empty/ignore +7 -0
- data/layouts/empty/logs/dontforgetme +0 -0
- data/layouts/empty/resources/messages.rs +1 -0
- data/layouts/empty/test/wspec/site_respond.wspec +3 -0
- data/layouts/empty/test/wspec/test_all.rb +13 -0
- data/layouts/empty/waw.deploy +1 -0
- data/layouts/empty/waw.routing +6 -0
- data/layouts/static/dependencies +1 -0
- data/layouts/static/public/.wawaccess +18 -0
- data/layouts/static/public/css/style.css +0 -0
- data/layouts/static/public/images/dontforgetme +0 -0
- data/layouts/static/public/js/project.js +0 -0
- data/layouts/static/public/pages/.wawaccess +30 -0
- data/layouts/static/public/pages/404.wtpl +1 -0
- data/layouts/static/public/pages/index.wtpl +5 -0
- data/layouts/static/public/templates/.wawaccess +9 -0
- data/layouts/static/public/templates/layout.wtpl +17 -0
- data/layouts/static/test/wspec/static_pages_are_served.wspec +21 -0
- data/layouts/static/waw.routing +8 -0
- data/lib/waw.rb +99 -0
- data/lib/waw/commands/command.rb +115 -0
- data/lib/waw/commands/profile_command.rb +66 -0
- data/lib/waw/commands/start_command.rb +59 -0
- data/lib/waw/config.rb +110 -0
- data/lib/waw/controller.rb +25 -0
- data/lib/waw/controllers/action/action.rb +91 -0
- data/lib/waw/controllers/action/action_utils.rb +30 -0
- data/lib/waw/controllers/action/js_generation.rb +116 -0
- data/lib/waw/controllers/action_controller.rb +133 -0
- data/lib/waw/controllers/error/backtrace.rb +54 -0
- data/lib/waw/controllers/error_handler.rb +62 -0
- data/lib/waw/controllers/json_controller.rb +31 -0
- data/lib/waw/controllers/no_cache.rb +22 -0
- data/lib/waw/controllers/static/match.rb +80 -0
- data/lib/waw/controllers/static/waw_access.rb +235 -0
- data/lib/waw/controllers/static/waw_access_dsl.rb +48 -0
- data/lib/waw/controllers/static_controller.rb +37 -0
- data/lib/waw/default_config.cfg +14 -0
- data/lib/waw/environment_utils.rb +57 -0
- data/lib/waw/errors.rb +4 -0
- data/lib/waw/ext.rb +3 -0
- data/lib/waw/ext/core.rb +4 -0
- data/lib/waw/ext/core/hash.rb +47 -0
- data/lib/waw/ext/core/logger.rb +10 -0
- data/lib/waw/ext/core/module.rb +20 -0
- data/lib/waw/ext/core/object.rb +29 -0
- data/lib/waw/ext/rack.rb +19 -0
- data/lib/waw/ext/rack/builder.rb +43 -0
- data/lib/waw/ext/rack/delegator.rb +51 -0
- data/lib/waw/ext/rack/urlmap.rb +55 -0
- data/lib/waw/ext/wlang.rb +1 -0
- data/lib/waw/ext/wlang/hosted_language.rb +21 -0
- data/lib/waw/fullstate.rb +8 -0
- data/lib/waw/fullstate/on_class.rb +37 -0
- data/lib/waw/fullstate/on_instance.rb +27 -0
- data/lib/waw/fullstate/variable.rb +36 -0
- data/lib/waw/kern.rb +6 -0
- data/lib/waw/kern/app.rb +48 -0
- data/lib/waw/kern/empty/waw.deploy +0 -0
- data/lib/waw/kern/empty/waw.routing +1 -0
- data/lib/waw/kern/freezed_state.rb +32 -0
- data/lib/waw/kern/hooks.rb +53 -0
- data/lib/waw/kern/lifecycle.rb +248 -0
- data/lib/waw/kern/living_state.rb +87 -0
- data/lib/waw/kern/utils.rb +27 -0
- data/lib/waw/resource_collection.rb +100 -0
- data/lib/waw/restart.rb +32 -0
- data/lib/waw/routing.rb +43 -0
- data/lib/waw/routing/action_routing.rb +78 -0
- data/lib/waw/routing/dsl.rb +45 -0
- data/lib/waw/routing/feedback.rb +23 -0
- data/lib/waw/routing/form_validation_feedback.rb +36 -0
- data/lib/waw/routing/javascript.rb +17 -0
- data/lib/waw/routing/redirect.rb +26 -0
- data/lib/waw/routing/refresh.rb +17 -0
- data/lib/waw/routing/routing_rule.rb +16 -0
- data/lib/waw/scope_utils.rb +69 -0
- data/lib/waw/session.rb +51 -0
- data/lib/waw/testing.rb +1 -0
- data/lib/waw/tools/mail.rb +4 -0
- data/lib/waw/tools/mail/mail.rb +119 -0
- data/lib/waw/tools/mail/mail_agent.rb +123 -0
- data/lib/waw/tools/mail/mailbox.rb +62 -0
- data/lib/waw/tools/mail/template.rb +38 -0
- data/lib/waw/utils/dsl_helper.rb +116 -0
- data/lib/waw/validation.rb +175 -0
- data/lib/waw/validation/and_validator.rb +27 -0
- data/lib/waw/validation/array_validations.rb +38 -0
- data/lib/waw/validation/boolean_validator.rb +32 -0
- data/lib/waw/validation/comparison_validations.rb +45 -0
- data/lib/waw/validation/date_validator.rb +31 -0
- data/lib/waw/validation/default_validator.rb +30 -0
- data/lib/waw/validation/dsl_ruby_extensions.rb +11 -0
- data/lib/waw/validation/errors.rb +17 -0
- data/lib/waw/validation/ext.rb +3 -0
- data/lib/waw/validation/file_validator.rb +30 -0
- data/lib/waw/validation/float_validator.rb +19 -0
- data/lib/waw/validation/helpers.rb +67 -0
- data/lib/waw/validation/integer_validator.rb +16 -0
- data/lib/waw/validation/isin_validator.rb +24 -0
- data/lib/waw/validation/mandatory_validator.rb +17 -0
- data/lib/waw/validation/missing_validator.rb +17 -0
- data/lib/waw/validation/not_validator.rb +20 -0
- data/lib/waw/validation/or_validator.rb +34 -0
- data/lib/waw/validation/regexp_validator.rb +29 -0
- data/lib/waw/validation/same_validator.rb +16 -0
- data/lib/waw/validation/signature.rb +157 -0
- data/lib/waw/validation/size_validations.rb +44 -0
- data/lib/waw/validation/string_validator.rb +15 -0
- data/lib/waw/validation/validator.rb +48 -0
- data/lib/waw/wawgen.rb +2 -0
- data/lib/waw/wawgen/create.rb +166 -0
- data/lib/waw/wawgen/project.rb +25 -0
- data/lib/waw/wspec.rb +5 -0
- data/lib/waw/wspec/browser.rb +240 -0
- data/lib/waw/wspec/dsl.rb +201 -0
- data/lib/waw/wspec/html_analysis.rb +136 -0
- data/lib/waw/wspec/html_analysis/tag.rb +56 -0
- data/lib/waw/wspec/runner.rb +70 -0
- data/lib/waw/wspec/scenario.rb +35 -0
- data/lib/waw/wspec/suite.rb +54 -0
- data/test/bricks/error_handler/config/test.cfg +2 -0
- data/test/bricks/error_handler/logs/webapp.log +1411 -0
- data/test/bricks/error_handler/test/error_handler.wspec +16 -0
- data/test/bricks/error_handler/waw.deploy +1 -0
- data/test/bricks/error_handler/waw.routing +27 -0
- data/test/integration/waw_create_integration_test.rb +24 -0
- data/test/spec/assumptions_spec.rb +30 -0
- data/test/spec/controllers/action_controller_spec.rb +14 -0
- data/test/spec/controllers/static/waw_access_spec.rb +112 -0
- data/test/spec/environment_utils_spec.rb +15 -0
- data/test/spec/ext/core/hash_spec.rb +58 -0
- data/test/spec/fixtures.rb +41 -0
- data/test/spec/fixtures/action/config/default.cfg +2 -0
- data/test/spec/fixtures/action/lib/action_controller_test.rb +12 -0
- data/test/spec/fixtures/action/waw.deploy +1 -0
- data/test/spec/fixtures/action/waw.routing +6 -0
- data/test/spec/fixtures/empty/waw.deploy +0 -0
- data/test/spec/fixtures/empty/waw.routing +0 -0
- data/test/spec/fullstate/on_class_spec.rb +59 -0
- data/test/spec/fullstate/on_instance_spec.rb +59 -0
- data/test/spec/fullstate/session_spec.rb +43 -0
- data/test/spec/fullstate/variable_spec.rb +55 -0
- data/test/spec/resource_collection_spec.rb +50 -0
- data/test/spec/test_all.rb +9 -0
- data/test/spec/tools/mail/mail_agent_spec.rb +116 -0
- data/test/spec/tools/mail/mail_spec.rb +56 -0
- data/test/spec/tools/mail/mailbox_spec.rb +57 -0
- data/test/spec/tools/mail/template_spec.rb +47 -0
- data/test/spec/validation/array_validation_spec.rb +63 -0
- data/test/spec/validation/array_validator_spec.rb +17 -0
- data/test/spec/validation/date_validation_spec.rb +35 -0
- data/test/spec/validation/default_validation_spec.rb +37 -0
- data/test/spec/validation/disjuctive_validation_spec.rb +33 -0
- data/test/spec/validation/errors_spec.rb +37 -0
- data/test/spec/validation/file_validator_spec.rb +34 -0
- data/test/spec/validation/mail_validation_spec.rb +51 -0
- data/test/spec/validation/missing_validation_spec.rb +43 -0
- data/test/spec/validation/same_validation_spec.rb +24 -0
- data/test/spec/validation/signature_intuition_spec.rb +37 -0
- data/test/spec/validation/signature_spec.rb +164 -0
- data/test/spec/validation/validation_spec.rb +28 -0
- data/test/spec/wspec/html_analysis/tag_spec.rb +38 -0
- data/test/spec/wspec/html_analysis_spec.rb +170 -0
- data/test/unit/test_all.rb +8 -0
- data/test/unit/waw/app_test.rb +126 -0
- data/test/unit/waw/app_test/config/commons.cfg +2 -0
- data/test/unit/waw/app_test/config/devel.cfg +1 -0
- data/test/unit/waw/config_test.rb +54 -0
- data/test/unit/waw/controllers/action_controller_test.rb +76 -0
- data/test/unit/waw/controllers/action_test.rb +35 -0
- data/test/unit/waw/controllers/example_action_controller_test.rb +24 -0
- data/test/unit/waw/controllers/multiple_action_controller_test.rb +78 -0
- data/test/unit/waw/controllers/static/example/css/example.css +1 -0
- data/test/unit/waw/controllers/static/example/index.html +1 -0
- data/test/unit/waw/controllers/static/example/js/example.js +1 -0
- data/test/unit/waw/controllers/static/example/pages/hello.wtpl +1 -0
- data/test/unit/waw/controllers/static/waw_access_test.rb +76 -0
- data/test/unit/waw/ext/rack_test.rb +74 -0
- data/test/unit/waw/resource_collection_test.rb +49 -0
- data/test/unit/waw/resources.txt +4 -0
- data/test/unit/waw/routing/routing_test.rb +26 -0
- data/test/unit/waw/utils/dsl_helper_test.rb +79 -0
- data/test/unit/waw/utils/dsl_helper_test_extensions1.rb +4 -0
- data/test/unit/waw/validation/signature_test.rb +193 -0
- data/test/unit/waw/validation_test.rb +319 -0
- data/test/unit/waw/wspec/html_analysis_test.html +81 -0
- data/test/unit/waw/wspec/html_analysis_test.rb +26 -0
- 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
|
data/lib/waw/wawgen.rb
ADDED
@@ -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
|
data/lib/waw/wspec.rb
ADDED
@@ -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
|