ww 0.2.0
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/Rakefile +112 -0
- data/lib/ww.rb +23 -0
- data/lib/ww/double.rb +34 -0
- data/lib/ww/double/mock.rb +33 -0
- data/lib/ww/double/spy.rb +32 -0
- data/lib/ww/double/spy/request.rb +48 -0
- data/lib/ww/double/stub.rb +14 -0
- data/lib/ww/servlet.rb +19 -0
- data/lib/ww/spy_eye.html.haml +95 -0
- data/lib/ww/spy_eye.rb +25 -0
- data/lib/ww/store.rb +33 -0
- metadata +105 -0
data/Rakefile
ADDED
@@ -0,0 +1,112 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rake'
|
3
|
+
require 'rake/clean'
|
4
|
+
require 'rake/testtask'
|
5
|
+
require 'rake/packagetask'
|
6
|
+
require 'rake/gempackagetask'
|
7
|
+
require 'rake/rdoctask'
|
8
|
+
require 'fileutils'
|
9
|
+
require 'spec/rake/spectask'
|
10
|
+
require 'lib/ww'
|
11
|
+
include FileUtils
|
12
|
+
|
13
|
+
NAME = "ww"
|
14
|
+
AUTHOR = "moro"
|
15
|
+
EMAIL = "moronatural@gmail.com"
|
16
|
+
DESCRIPTION = "Double Web, framework to build double Web server."
|
17
|
+
HOMEPAGE = "http://github.com/moro/#{NAME}/"
|
18
|
+
BIN_FILES = %w( )
|
19
|
+
|
20
|
+
VERS = Ww::Version
|
21
|
+
REV = File.read(".svn/entries")[/committed-rev="(d+)"/, 1] rescue nil
|
22
|
+
CLEAN.include ['**/.*.sw?', '*.gem', '.config']
|
23
|
+
RDOC_OPTS = [
|
24
|
+
'--title', "#{NAME} documentation",
|
25
|
+
"--charset", "utf-8",
|
26
|
+
"--opname", "index.html",
|
27
|
+
"--line-numbers",
|
28
|
+
"--main", "README.rdoc",
|
29
|
+
"--inline-source",
|
30
|
+
]
|
31
|
+
|
32
|
+
task :default => [:spec]
|
33
|
+
task :package => [:clean]
|
34
|
+
|
35
|
+
desc "Run all specs in spec directory"
|
36
|
+
Spec::Rake::SpecTask.new(:spec) do |t|
|
37
|
+
t.spec_opts = %w[--colour --format progress --loadby --reverse]
|
38
|
+
t.spec_files = FileList['spec/**/*_spec.rb']
|
39
|
+
end
|
40
|
+
|
41
|
+
spec = Gem::Specification.new do |s|
|
42
|
+
s.name = NAME
|
43
|
+
s.version = VERS
|
44
|
+
s.platform = Gem::Platform::RUBY
|
45
|
+
s.has_rdoc = true
|
46
|
+
# s.extra_rdoc_files = ["README.rdoc", "ChangeLog"]
|
47
|
+
s.rdoc_options += RDOC_OPTS + ['--exclude', '^(examples|extras)/']
|
48
|
+
s.summary = DESCRIPTION
|
49
|
+
s.description = DESCRIPTION
|
50
|
+
s.author = AUTHOR
|
51
|
+
s.email = EMAIL
|
52
|
+
s.homepage = HOMEPAGE
|
53
|
+
s.executables = BIN_FILES
|
54
|
+
s.bindir = "bin"
|
55
|
+
s.require_path = "lib"
|
56
|
+
s.test_files = Dir["test/*_test.rb"]
|
57
|
+
|
58
|
+
s.add_dependency('sinatra', '>=0.9.4')
|
59
|
+
s.add_dependency('rack', '>=1.0.1')
|
60
|
+
s.add_dependency('json')
|
61
|
+
#s.required_ruby_version = '>= 1.8.2'
|
62
|
+
|
63
|
+
s.files = %w(Rakefile) +
|
64
|
+
Dir.glob("{bin,doc,test,lib,templates,generator,extras,website,script}/**/*") +
|
65
|
+
Dir.glob("ext/**/*.{h,c,rb}") +
|
66
|
+
Dir.glob("examples/**/*.rb") +
|
67
|
+
Dir.glob("tools/*.rb") +
|
68
|
+
Dir.glob("rails/*.rb")
|
69
|
+
|
70
|
+
s.extensions = FileList["ext/**/extconf.rb"].to_a
|
71
|
+
end
|
72
|
+
|
73
|
+
Rake::GemPackageTask.new(spec) do |p|
|
74
|
+
p.need_tar = true
|
75
|
+
p.gem_spec = spec
|
76
|
+
end
|
77
|
+
|
78
|
+
task :install do
|
79
|
+
name = "#{NAME}-#{VERS}.gem"
|
80
|
+
sh %{rake package}
|
81
|
+
sh %{gem install pkg/#{name}}
|
82
|
+
end
|
83
|
+
|
84
|
+
task :uninstall => [:clean] do
|
85
|
+
sh %{gem uninstall #{NAME}}
|
86
|
+
end
|
87
|
+
|
88
|
+
desc 'Show information about the gem.'
|
89
|
+
task :debug_gem do
|
90
|
+
puts spec.to_ruby
|
91
|
+
end
|
92
|
+
|
93
|
+
desc 'Update gem spec'
|
94
|
+
task :gemspec do
|
95
|
+
open("#{NAME}.gemspec", 'w').write spec.to_ruby
|
96
|
+
end
|
97
|
+
|
98
|
+
|
99
|
+
Rake::RDocTask.new do |rdoc|
|
100
|
+
rdoc.rdoc_dir = 'html'
|
101
|
+
rdoc.options += RDOC_OPTS
|
102
|
+
rdoc.template = "resh"
|
103
|
+
#rdoc.template = "#{ENV['template']}.rb" if ENV['template']
|
104
|
+
if ENV['DOC_FILES']
|
105
|
+
rdoc.rdoc_files.include(ENV['DOC_FILES'].split(/,\s*/))
|
106
|
+
else
|
107
|
+
rdoc.rdoc_files.include('README.rdoc', 'ChangeLog')
|
108
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
109
|
+
rdoc.rdoc_files.include('ext/**/*.c')
|
110
|
+
end
|
111
|
+
end
|
112
|
+
|
data/lib/ww.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
require 'rubygems'
|
2
|
+
require 'rack'
|
3
|
+
|
4
|
+
module Ww
|
5
|
+
autoload :Servlet, 'ww/servlet'
|
6
|
+
autoload :SpyEye, 'ww/spy_eye'
|
7
|
+
|
8
|
+
Version = '0.2.0'
|
9
|
+
|
10
|
+
def to_app(spy_eye_path = "/spy", &block)
|
11
|
+
Rack::Builder.new {
|
12
|
+
use Rack::ShowExceptions
|
13
|
+
|
14
|
+
servlet = Servlet.base(&block)
|
15
|
+
spy = SpyEye.new(servlet)
|
16
|
+
|
17
|
+
map(spy_eye_path) { run spy }
|
18
|
+
map("/") { run servlet }
|
19
|
+
}
|
20
|
+
end
|
21
|
+
module_function :to_app
|
22
|
+
end
|
23
|
+
|
data/lib/ww/double.rb
ADDED
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'ww/double/stub'
|
2
|
+
require 'ww/double/mock'
|
3
|
+
require 'ww/double/spy'
|
4
|
+
|
5
|
+
module Ww
|
6
|
+
module Double
|
7
|
+
class Error < RuntimeError; end
|
8
|
+
class MockError < Error ; end
|
9
|
+
|
10
|
+
MODULES = [Stub, Mock, Spy].each do |mod|
|
11
|
+
include mod
|
12
|
+
end
|
13
|
+
|
14
|
+
def self.extended(base)
|
15
|
+
MODULES.each do |mod|
|
16
|
+
base.send(:include, mod::InstanceMethods) if mod.const_defined?("InstanceMethods")
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
20
|
+
def unbound_action(klass, mname, block)
|
21
|
+
ret = nil
|
22
|
+
klass.module_eval do
|
23
|
+
begin
|
24
|
+
define_method(mname, &block)
|
25
|
+
ret = instance_method(mname)
|
26
|
+
ensure
|
27
|
+
remove_method(mname) if instance_methods.include?(mname)
|
28
|
+
end
|
29
|
+
end
|
30
|
+
return ret
|
31
|
+
end
|
32
|
+
module_function :unbound_action
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module Ww
|
2
|
+
module Double
|
3
|
+
module Mock
|
4
|
+
class Expectation
|
5
|
+
def executed!; @e = true; end
|
6
|
+
def executed?; !!@e; end
|
7
|
+
attr_reader :identifier
|
8
|
+
def initialize(verb, path)
|
9
|
+
@identifier = "_mock_ #{verb.to_s.upcase} #{path}"
|
10
|
+
end
|
11
|
+
end
|
12
|
+
|
13
|
+
def mock(verb, path, &block)
|
14
|
+
expectations << (expect = Expectation.new(verb, path))
|
15
|
+
action = Double.unbound_action(self, expect.identifier, block)
|
16
|
+
|
17
|
+
stub(verb, path) do |*args|
|
18
|
+
expect.executed!
|
19
|
+
action.bind(self).call(*args)
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
def verify
|
24
|
+
raise MockError unless expectations.all? {|mock| mock.executed? }
|
25
|
+
end
|
26
|
+
|
27
|
+
private
|
28
|
+
def expectations
|
29
|
+
@expectations ||= []
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,32 @@
|
|
1
|
+
require 'ww/store'
|
2
|
+
require 'ww/double/spy/request'
|
3
|
+
|
4
|
+
module Ww
|
5
|
+
module Double
|
6
|
+
module Spy
|
7
|
+
def spy(verb, path, &block)
|
8
|
+
action = Double.unbound_action(self, "_spy_ #{verb.to_s.upcase} #{path}", block)
|
9
|
+
|
10
|
+
stub(verb, path) do |*args|
|
11
|
+
spy!
|
12
|
+
action.bind(self).call(*args)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
def requests
|
17
|
+
@requests ||= Store.new
|
18
|
+
end
|
19
|
+
|
20
|
+
def store(req)
|
21
|
+
requests.store(Request.new(req))
|
22
|
+
end
|
23
|
+
|
24
|
+
module InstanceMethods
|
25
|
+
def spy!
|
26
|
+
self.class.store(@request)
|
27
|
+
end
|
28
|
+
alias stump! spy!
|
29
|
+
end
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
@@ -0,0 +1,48 @@
|
|
1
|
+
require 'rack/request'
|
2
|
+
|
3
|
+
autoload :JSON, 'json'
|
4
|
+
autoload :YAML, 'yaml'
|
5
|
+
|
6
|
+
module Ww::Double::Spy
|
7
|
+
class Request < ::Rack::Request
|
8
|
+
attr_reader :time
|
9
|
+
@@req_parsers = []
|
10
|
+
|
11
|
+
def self.regist_request_parser(pattern, &block)
|
12
|
+
@@req_parsers << [pattern, block]
|
13
|
+
end
|
14
|
+
|
15
|
+
regist_request_parser %r{[application|text]/json} do |body|
|
16
|
+
JSON.parse(body)
|
17
|
+
end
|
18
|
+
|
19
|
+
regist_request_parser %r{[application|text]/json} do |body|
|
20
|
+
JSON.parse(body)
|
21
|
+
end
|
22
|
+
|
23
|
+
regist_request_parser %r{[application|text]/yaml} do |body|
|
24
|
+
YAML.load(body)
|
25
|
+
end
|
26
|
+
|
27
|
+
def initialize(req_or_env)
|
28
|
+
env = req_or_env.is_a?(Rack::Request) ? req_or_env.env : req_or_env
|
29
|
+
super(env.dup)
|
30
|
+
@time = Time.now
|
31
|
+
end
|
32
|
+
|
33
|
+
def parsed_body
|
34
|
+
return "" if get?
|
35
|
+
return self.POST if form_data?
|
36
|
+
|
37
|
+
@body ||=
|
38
|
+
begin
|
39
|
+
body.read
|
40
|
+
ensure
|
41
|
+
body.rewind
|
42
|
+
end
|
43
|
+
|
44
|
+
_, processor = @@req_parsers.detect{|re, ignore| re =~ media_type }
|
45
|
+
processor ? processor.call(@body) : @body
|
46
|
+
end
|
47
|
+
end
|
48
|
+
end
|
data/lib/ww/servlet.rb
ADDED
@@ -0,0 +1,19 @@
|
|
1
|
+
require 'sinatra/base'
|
2
|
+
require 'monitor'
|
3
|
+
|
4
|
+
module Ww
|
5
|
+
autoload :Double, 'ww/double'
|
6
|
+
|
7
|
+
class Servlet < Sinatra::Base
|
8
|
+
class << self
|
9
|
+
def base(&block)
|
10
|
+
Class.new(self).tap do |k|
|
11
|
+
k.extend Double
|
12
|
+
k.extend MonitorMixin
|
13
|
+
k.class_eval(&block)
|
14
|
+
end
|
15
|
+
end
|
16
|
+
end
|
17
|
+
end
|
18
|
+
end
|
19
|
+
|
@@ -0,0 +1,95 @@
|
|
1
|
+
:ruby
|
2
|
+
require 'pp'
|
3
|
+
title = "#{requests.size} requests - Double Web"
|
4
|
+
!!!
|
5
|
+
%html
|
6
|
+
%head
|
7
|
+
%title&= title
|
8
|
+
%script{:src=>"http://ajax.googleapis.com/ajax/libs/jquery/1.3.1/jquery.min.js"}
|
9
|
+
%style{:type=>"text/css"}
|
10
|
+
:sass
|
11
|
+
body
|
12
|
+
width: 80%
|
13
|
+
margin: auto
|
14
|
+
|
15
|
+
h1
|
16
|
+
font-size: 10px
|
17
|
+
|
18
|
+
div.stump
|
19
|
+
font-size: 10px
|
20
|
+
border: 1px solid silver
|
21
|
+
padding: 0 1em
|
22
|
+
|
23
|
+
h2
|
24
|
+
font-size: 10px
|
25
|
+
color: gray
|
26
|
+
|
27
|
+
span.method, span.path
|
28
|
+
font-size: 16px
|
29
|
+
font-weight: normal
|
30
|
+
font-family: 'Courier', monospace
|
31
|
+
|
32
|
+
span.method
|
33
|
+
color: #090
|
34
|
+
|
35
|
+
span.path
|
36
|
+
color: black
|
37
|
+
|
38
|
+
span.time, span.source
|
39
|
+
color: blue
|
40
|
+
|
41
|
+
h3
|
42
|
+
font-size: small
|
43
|
+
border-bottom: 1px solid green
|
44
|
+
background: #cfb
|
45
|
+
padding: 2px 0 0 2px
|
46
|
+
|
47
|
+
span.media_type
|
48
|
+
color: blue
|
49
|
+
font-size: small
|
50
|
+
font-weight: normal
|
51
|
+
font-family: 'Courier', monospace
|
52
|
+
|
53
|
+
.headers table, div.body pre
|
54
|
+
border: 1px dashed green
|
55
|
+
margin: 1em
|
56
|
+
padding: 1em
|
57
|
+
|
58
|
+
div.body pre
|
59
|
+
overflow: auto
|
60
|
+
font-size: 12px
|
61
|
+
%body
|
62
|
+
%h1&= title
|
63
|
+
- requests.each do |wreq|
|
64
|
+
%div.stump{:id => wreq.object_id}
|
65
|
+
%h2
|
66
|
+
%span.method&= wreq.request_method
|
67
|
+
%span.path&= wreq.fullpath
|
68
|
+
on
|
69
|
+
%span.time&= wreq.time.iso8601
|
70
|
+
from
|
71
|
+
%span.source&= wreq.ip
|
72
|
+
%div
|
73
|
+
.headers
|
74
|
+
%h3 Headers
|
75
|
+
%table
|
76
|
+
- wreq.env.each do |k,v|
|
77
|
+
- next if k =~ /\A[a-z]/
|
78
|
+
%tr
|
79
|
+
%th&= k
|
80
|
+
%td&= v
|
81
|
+
|
82
|
+
- unless wreq.get?
|
83
|
+
.body
|
84
|
+
%h3
|
85
|
+
Body
|
86
|
+
%span.media_type= wreq.media_type
|
87
|
+
%pre&= wreq.parsed_body.pretty_inspect
|
88
|
+
|
89
|
+
:javascript
|
90
|
+
jQuery(function(){
|
91
|
+
$("div.stump .headers h3").click(function(){ $(this).next("table").toggle() });
|
92
|
+
$("div.stump .body h3").click(function(){ $(this).next("pre").toggle() });
|
93
|
+
$("div.stump .headers table").hide();
|
94
|
+
$("div.stump .body pre").hide();
|
95
|
+
});
|
data/lib/ww/spy_eye.rb
ADDED
@@ -0,0 +1,25 @@
|
|
1
|
+
require 'forwardable'
|
2
|
+
require 'time'
|
3
|
+
require 'haml'
|
4
|
+
|
5
|
+
module Ww
|
6
|
+
class SpyEye
|
7
|
+
extend Forwardable
|
8
|
+
def_delegator :@servlet, :requests
|
9
|
+
|
10
|
+
def initialize(servlet)
|
11
|
+
@servlet = servlet
|
12
|
+
end
|
13
|
+
|
14
|
+
def call(env)
|
15
|
+
[200, {"Content-Type" => "text/html"}, template.render(self)]
|
16
|
+
end
|
17
|
+
|
18
|
+
private
|
19
|
+
def template
|
20
|
+
path = ::File.expand_path("spy_eye.html.haml", ::File.dirname(__FILE__))
|
21
|
+
Haml::Engine.new(::File.read(path))
|
22
|
+
end
|
23
|
+
end
|
24
|
+
end
|
25
|
+
|
data/lib/ww/store.rb
ADDED
@@ -0,0 +1,33 @@
|
|
1
|
+
require 'monitor'
|
2
|
+
require 'forwardable'
|
3
|
+
|
4
|
+
module Ww
|
5
|
+
class Store
|
6
|
+
include Enumerable
|
7
|
+
include MonitorMixin
|
8
|
+
extend Forwardable
|
9
|
+
|
10
|
+
def_delegators :storage, *[:size, :first, :last, :[], :empty?]
|
11
|
+
|
12
|
+
def initialize
|
13
|
+
super()
|
14
|
+
@store = []
|
15
|
+
end
|
16
|
+
|
17
|
+
def storage
|
18
|
+
synchronize { @store.dup }
|
19
|
+
end
|
20
|
+
|
21
|
+
def store(obj)
|
22
|
+
synchronize{ @store << obj }
|
23
|
+
end
|
24
|
+
|
25
|
+
def each(descendant = true, &block)
|
26
|
+
(descendant ? storage.reverse : storage).each(&block)
|
27
|
+
end
|
28
|
+
|
29
|
+
def clear!
|
30
|
+
synchronize{ @store.clear }
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
metadata
ADDED
@@ -0,0 +1,105 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: ww
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.2.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- moro
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2010-01-14 00:00:00 +09:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: sinatra
|
17
|
+
type: :runtime
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 0.9.4
|
24
|
+
version:
|
25
|
+
- !ruby/object:Gem::Dependency
|
26
|
+
name: rack
|
27
|
+
type: :runtime
|
28
|
+
version_requirement:
|
29
|
+
version_requirements: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - ">="
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: 1.0.1
|
34
|
+
version:
|
35
|
+
- !ruby/object:Gem::Dependency
|
36
|
+
name: json
|
37
|
+
type: :runtime
|
38
|
+
version_requirement:
|
39
|
+
version_requirements: !ruby/object:Gem::Requirement
|
40
|
+
requirements:
|
41
|
+
- - ">="
|
42
|
+
- !ruby/object:Gem::Version
|
43
|
+
version: "0"
|
44
|
+
version:
|
45
|
+
description: Double Web, framework to build double Web server.
|
46
|
+
email: moronatural@gmail.com
|
47
|
+
executables: []
|
48
|
+
|
49
|
+
extensions: []
|
50
|
+
|
51
|
+
extra_rdoc_files: []
|
52
|
+
|
53
|
+
files:
|
54
|
+
- Rakefile
|
55
|
+
- lib/ww/double/mock.rb
|
56
|
+
- lib/ww/double/spy/request.rb
|
57
|
+
- lib/ww/double/spy.rb
|
58
|
+
- lib/ww/double/stub.rb
|
59
|
+
- lib/ww/double.rb
|
60
|
+
- lib/ww/servlet.rb
|
61
|
+
- lib/ww/spy_eye.html.haml
|
62
|
+
- lib/ww/spy_eye.rb
|
63
|
+
- lib/ww/store.rb
|
64
|
+
- lib/ww.rb
|
65
|
+
has_rdoc: true
|
66
|
+
homepage: http://github.com/moro/ww/
|
67
|
+
licenses: []
|
68
|
+
|
69
|
+
post_install_message:
|
70
|
+
rdoc_options:
|
71
|
+
- --title
|
72
|
+
- ww documentation
|
73
|
+
- --charset
|
74
|
+
- utf-8
|
75
|
+
- --opname
|
76
|
+
- index.html
|
77
|
+
- --line-numbers
|
78
|
+
- --main
|
79
|
+
- README.rdoc
|
80
|
+
- --inline-source
|
81
|
+
- --exclude
|
82
|
+
- ^(examples|extras)/
|
83
|
+
require_paths:
|
84
|
+
- lib
|
85
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
86
|
+
requirements:
|
87
|
+
- - ">="
|
88
|
+
- !ruby/object:Gem::Version
|
89
|
+
version: "0"
|
90
|
+
version:
|
91
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
92
|
+
requirements:
|
93
|
+
- - ">="
|
94
|
+
- !ruby/object:Gem::Version
|
95
|
+
version: "0"
|
96
|
+
version:
|
97
|
+
requirements: []
|
98
|
+
|
99
|
+
rubyforge_project:
|
100
|
+
rubygems_version: 1.3.5
|
101
|
+
signing_key:
|
102
|
+
specification_version: 3
|
103
|
+
summary: Double Web, framework to build double Web server.
|
104
|
+
test_files: []
|
105
|
+
|