mob-dalli 1.1.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/Gemfile +11 -0
- data/History.md +226 -0
- data/LICENSE +20 -0
- data/Performance.md +85 -0
- data/README.md +182 -0
- data/Rakefile +39 -0
- data/Upgrade.md +45 -0
- data/lib/action_dispatch/middleware/session/dalli_store.rb +76 -0
- data/lib/active_support/cache/dalli_store.rb +184 -0
- data/lib/active_support/cache/dalli_store23.rb +172 -0
- data/lib/dalli/client.rb +291 -0
- data/lib/dalli/compatibility.rb +52 -0
- data/lib/dalli/memcache-client.rb +1 -0
- data/lib/dalli/options.rb +46 -0
- data/lib/dalli/ring.rb +105 -0
- data/lib/dalli/server.rb +532 -0
- data/lib/dalli/socket.rb +170 -0
- data/lib/dalli/version.rb +3 -0
- data/lib/mob-dalli.rb +47 -0
- data/lib/rack/session/dalli.rb +52 -0
- data/mob-dalli.gemspec +31 -0
- data/test/abstract_unit.rb +282 -0
- data/test/benchmark_test.rb +170 -0
- data/test/helper.rb +39 -0
- data/test/memcached_mock.rb +126 -0
- data/test/test_active_support.rb +201 -0
- data/test/test_compatibility.rb +33 -0
- data/test/test_dalli.rb +450 -0
- data/test/test_encoding.rb +51 -0
- data/test/test_failover.rb +107 -0
- data/test/test_network.rb +54 -0
- data/test/test_ring.rb +85 -0
- data/test/test_sasl.rb +83 -0
- data/test/test_session_store.rb +225 -0
- data/test/test_synchrony.rb +175 -0
- metadata +172 -0
data/lib/dalli/socket.rb
ADDED
@@ -0,0 +1,170 @@
|
|
1
|
+
begin
|
2
|
+
require 'kgio'
|
3
|
+
puts "Using kgio socket IO" if defined?($TESTING) && $TESTING
|
4
|
+
|
5
|
+
class Dalli::Server::KSocket < Kgio::Socket
|
6
|
+
attr_accessor :options
|
7
|
+
|
8
|
+
def kgio_wait_readable
|
9
|
+
IO.select([self], nil, nil, options[:timeout]) || raise(Timeout::Error, "IO timeout")
|
10
|
+
end
|
11
|
+
|
12
|
+
def kgio_wait_writable
|
13
|
+
IO.select(nil, [self], nil, options[:timeout]) || raise(Timeout::Error, "IO timeout")
|
14
|
+
end
|
15
|
+
|
16
|
+
def self.open(host, port, options = {})
|
17
|
+
addr = Socket.pack_sockaddr_in(port, host)
|
18
|
+
sock = start(addr)
|
19
|
+
sock.options = options
|
20
|
+
sock.kgio_wait_writable
|
21
|
+
sock
|
22
|
+
end
|
23
|
+
|
24
|
+
alias :write :kgio_write
|
25
|
+
|
26
|
+
def readfull(count)
|
27
|
+
value = ''
|
28
|
+
loop do
|
29
|
+
value << kgio_read!(count - value.bytesize)
|
30
|
+
break if value.bytesize == count
|
31
|
+
end
|
32
|
+
value
|
33
|
+
end
|
34
|
+
|
35
|
+
end
|
36
|
+
|
37
|
+
|
38
|
+
|
39
|
+
if ::Kgio.respond_to?(:wait_readable=)
|
40
|
+
::Kgio.wait_readable = :kgio_wait_readable
|
41
|
+
::Kgio.wait_writable = :kgio_wait_writable
|
42
|
+
end
|
43
|
+
|
44
|
+
rescue LoadError
|
45
|
+
|
46
|
+
puts "Using standard socket IO (#{RUBY_DESCRIPTION})" if defined?($TESTING) && $TESTING
|
47
|
+
if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'jruby'
|
48
|
+
|
49
|
+
class Dalli::Server::KSocket < TCPSocket
|
50
|
+
attr_accessor :options
|
51
|
+
|
52
|
+
def self.open(host, port, options = {})
|
53
|
+
sock = new(host, port)
|
54
|
+
sock.options = { :host => host, :port => port }.merge(options)
|
55
|
+
sock
|
56
|
+
end
|
57
|
+
|
58
|
+
def readfull(count)
|
59
|
+
value = ''
|
60
|
+
begin
|
61
|
+
loop do
|
62
|
+
value << read_nonblock(count - value.bytesize)
|
63
|
+
break if value.bytesize == count
|
64
|
+
end
|
65
|
+
rescue Errno::EAGAIN, Errno::EWOULDBLOCK
|
66
|
+
if IO.select([self], nil, nil, options[:timeout])
|
67
|
+
retry
|
68
|
+
else
|
69
|
+
raise Timeout::Error, "IO timeout: #{options.inspect}"
|
70
|
+
end
|
71
|
+
end
|
72
|
+
value
|
73
|
+
end
|
74
|
+
|
75
|
+
end
|
76
|
+
|
77
|
+
else
|
78
|
+
|
79
|
+
class Dalli::Server::KSocket < Socket
|
80
|
+
attr_accessor :options
|
81
|
+
|
82
|
+
def self.open(host, port, options = {})
|
83
|
+
# All this ugly code to ensure proper Socket connect timeout
|
84
|
+
addr = Socket.getaddrinfo(host, nil)
|
85
|
+
sock = new(Socket.const_get(addr[0][0]), Socket::SOCK_STREAM, 0)
|
86
|
+
sock.options = { :host => host, :port => port }.merge(options)
|
87
|
+
begin
|
88
|
+
sock.connect_nonblock(Socket.pack_sockaddr_in(port, addr[0][3]))
|
89
|
+
rescue Errno::EINPROGRESS
|
90
|
+
resp = IO.select(nil, [sock], nil, sock.options[:timeout])
|
91
|
+
begin
|
92
|
+
sock.connect_nonblock(Socket.pack_sockaddr_in(port, addr[0][3]))
|
93
|
+
rescue Errno::EISCONN
|
94
|
+
end
|
95
|
+
end
|
96
|
+
sock
|
97
|
+
end
|
98
|
+
|
99
|
+
def readfull(count)
|
100
|
+
value = ''
|
101
|
+
begin
|
102
|
+
loop do
|
103
|
+
value << sysread(count - value.bytesize)
|
104
|
+
break if value.bytesize == count
|
105
|
+
end
|
106
|
+
rescue Errno::EAGAIN, Errno::EWOULDBLOCK
|
107
|
+
if IO.select([self], nil, nil, options[:timeout])
|
108
|
+
retry
|
109
|
+
else
|
110
|
+
raise Timeout::Error, "IO timeout: #{options.inspect}"
|
111
|
+
end
|
112
|
+
end
|
113
|
+
value
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
begin
|
121
|
+
require 'em-synchrony'
|
122
|
+
puts "Defining alternate em-synchrony socket IO" if defined?($TESTING) && $TESTING
|
123
|
+
|
124
|
+
class Dalli::Server::AsyncSocket < EventMachine::Synchrony::TCPSocket
|
125
|
+
# XXX. need to somehow implement the timeout option.
|
126
|
+
# EM::Synchrony::TCPsocket does give you any timeouts. To implement it we'd have
|
127
|
+
# to change that class to take a timeout parameter that it would then set on
|
128
|
+
# the Deferrable objects it waits on.
|
129
|
+
attr_accessor :options
|
130
|
+
|
131
|
+
def self.open(host, port, options = {})
|
132
|
+
sock = new(host, port)
|
133
|
+
sock.options = { :host => host, :port => port }.merge(options)
|
134
|
+
sock
|
135
|
+
end
|
136
|
+
|
137
|
+
def readfull(count)
|
138
|
+
value = ''
|
139
|
+
loop do
|
140
|
+
value << read(count - value.bytesize)
|
141
|
+
break if value.bytesize == count
|
142
|
+
end
|
143
|
+
value
|
144
|
+
end
|
145
|
+
|
146
|
+
end
|
147
|
+
|
148
|
+
rescue LoadError
|
149
|
+
puts "Could not define alternate em-synchrony socket IO" if defined?($TESTING) && $TESTING
|
150
|
+
end
|
151
|
+
|
152
|
+
require 'rbconfig'
|
153
|
+
if RbConfig::CONFIG['host_os'] =~ /mingw|mswin/
|
154
|
+
class Dalli::Server::USocket
|
155
|
+
def initialize(*args)
|
156
|
+
raise Dalli::DalliError, "Unix sockets are not supported on Windows platform."
|
157
|
+
end
|
158
|
+
end
|
159
|
+
else
|
160
|
+
class Dalli::Server::USocket < UNIXSocket
|
161
|
+
def readfull(count)
|
162
|
+
value = ''
|
163
|
+
loop do
|
164
|
+
value << read(count - value.bytesize)
|
165
|
+
break if value.bytesize == count
|
166
|
+
end
|
167
|
+
value
|
168
|
+
end
|
169
|
+
end
|
170
|
+
end
|
data/lib/mob-dalli.rb
ADDED
@@ -0,0 +1,47 @@
|
|
1
|
+
require 'dalli/client'
|
2
|
+
require 'dalli/ring'
|
3
|
+
require 'dalli/server'
|
4
|
+
require 'dalli/socket'
|
5
|
+
require 'dalli/version'
|
6
|
+
require 'dalli/options'
|
7
|
+
|
8
|
+
unless ''.respond_to?(:bytesize)
|
9
|
+
class String
|
10
|
+
alias_method :bytesize, :size
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
module Dalli
|
15
|
+
# generic error
|
16
|
+
class DalliError < RuntimeError; end
|
17
|
+
# socket/server communication error
|
18
|
+
class NetworkError < DalliError; end
|
19
|
+
# no server available/alive error
|
20
|
+
class RingError < DalliError; end
|
21
|
+
# application error in marshalling
|
22
|
+
class MarshalError < DalliError; end
|
23
|
+
|
24
|
+
def self.logger
|
25
|
+
@logger ||= (rails_logger || default_logger)
|
26
|
+
end
|
27
|
+
|
28
|
+
def self.rails_logger
|
29
|
+
(defined?(Rails) && Rails.respond_to?(:logger) && Rails.logger) ||
|
30
|
+
(defined?(RAILS_DEFAULT_LOGGER) && RAILS_DEFAULT_LOGGER.respond_to?(:debug) && RAILS_DEFAULT_LOGGER)
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.default_logger
|
34
|
+
require 'logger'
|
35
|
+
l = Logger.new(STDOUT)
|
36
|
+
l.level = Logger::INFO
|
37
|
+
l
|
38
|
+
end
|
39
|
+
|
40
|
+
def self.logger=(logger)
|
41
|
+
@logger = logger
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
if defined?(RAILS_VERSION) && RAILS_VERSION < '3'
|
46
|
+
raise Dalli::DalliError, "Dalli #{Dalli::VERSION} does not support Rails version < 3.0"
|
47
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
require 'rack/session/abstract/id'
|
2
|
+
require 'dalli'
|
3
|
+
|
4
|
+
module Rack
|
5
|
+
module Session
|
6
|
+
class Dalli < Abstract::ID
|
7
|
+
attr_reader :pool
|
8
|
+
|
9
|
+
DEFAULT_OPTIONS = Abstract::ID::DEFAULT_OPTIONS.merge \
|
10
|
+
:namespace => 'rack:session',
|
11
|
+
:memcache_server => 'localhost:11211'
|
12
|
+
|
13
|
+
def initialize(app, options={})
|
14
|
+
super
|
15
|
+
mserv = @default_options[:memcache_server]
|
16
|
+
mopts = @default_options.reject{|k,v| !MemCache::DEFAULT_OPTIONS.include? k }
|
17
|
+
@pool = options[:cache] || Dalli::Client.new(mserv, mopts)
|
18
|
+
end
|
19
|
+
|
20
|
+
def generate_sid
|
21
|
+
loop do
|
22
|
+
sid = super
|
23
|
+
break sid unless @pool.get(sid)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
|
27
|
+
def get_session(env, sid)
|
28
|
+
unless sid and session = @pool.get(sid)
|
29
|
+
sid, session = generate_sid, {}
|
30
|
+
unless @pool.add(sid, session)
|
31
|
+
raise "Session collision on '#{sid.inspect}'"
|
32
|
+
end
|
33
|
+
end
|
34
|
+
[sid, session]
|
35
|
+
end
|
36
|
+
|
37
|
+
def set_session(env, session_id, new_session, options)
|
38
|
+
expiry = options[:expire_after]
|
39
|
+
expiry = expiry.nil? ? 0 : expiry + 1
|
40
|
+
|
41
|
+
@pool.set session_id, new_session, expiry
|
42
|
+
session_id
|
43
|
+
end
|
44
|
+
|
45
|
+
def destroy_session(env, session_id, options)
|
46
|
+
@pool.delete(session_id)
|
47
|
+
generate_sid unless options[:drop]
|
48
|
+
end
|
49
|
+
|
50
|
+
end
|
51
|
+
end
|
52
|
+
end
|
data/mob-dalli.gemspec
ADDED
@@ -0,0 +1,31 @@
|
|
1
|
+
require './lib/dalli/version'
|
2
|
+
|
3
|
+
Gem::Specification.new do |s|
|
4
|
+
s.name = %q{mob-dalli}
|
5
|
+
s.version = Dalli::VERSION
|
6
|
+
|
7
|
+
s.authors = ["Mike Perham", "Aaron Daniel"]
|
8
|
+
s.date = Time.now.utc.strftime("%Y-%m-%d")
|
9
|
+
s.description = %q{High performance memcached client for Ruby}
|
10
|
+
s.email = %q{mperham@gmail.com}
|
11
|
+
s.files = Dir.glob("lib/**/*") + [
|
12
|
+
"LICENSE",
|
13
|
+
"README.md",
|
14
|
+
"History.md",
|
15
|
+
"Rakefile",
|
16
|
+
"Gemfile",
|
17
|
+
"mob-dalli.gemspec",
|
18
|
+
"Performance.md",
|
19
|
+
"Upgrade.md",
|
20
|
+
]
|
21
|
+
s.homepage = %q{http://github.com/amdtech/dalli}
|
22
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
23
|
+
s.require_paths = ["lib"]
|
24
|
+
s.summary = %q{High performance memcached client for Ruby}
|
25
|
+
s.test_files = Dir.glob("test/**/*")
|
26
|
+
s.add_development_dependency(%q<shoulda>, [">= 0"])
|
27
|
+
s.add_development_dependency(%q<mocha>, [">= 0"])
|
28
|
+
s.add_development_dependency(%q<rails>, [">= 3.0.0"])
|
29
|
+
s.add_development_dependency(%q<memcache-client>, [">= 1.8.5"])
|
30
|
+
end
|
31
|
+
|
@@ -0,0 +1,282 @@
|
|
1
|
+
# Used to test the full Rails stack.
|
2
|
+
# Stolen from the Rails 3.0 source.
|
3
|
+
# Needed for the session store tests.
|
4
|
+
require 'active_support/core_ext/kernel/reporting'
|
5
|
+
require 'active_support/core_ext/string/encoding'
|
6
|
+
if "ruby".encoding_aware?
|
7
|
+
# These are the normal settings that will be set up by Railties
|
8
|
+
# TODO: Have these tests support other combinations of these values
|
9
|
+
silence_warnings do
|
10
|
+
Encoding.default_internal = "UTF-8"
|
11
|
+
Encoding.default_external = "UTF-8"
|
12
|
+
end
|
13
|
+
end
|
14
|
+
|
15
|
+
require 'test/unit'
|
16
|
+
require 'abstract_controller'
|
17
|
+
require 'action_controller'
|
18
|
+
require 'action_view'
|
19
|
+
require 'action_dispatch'
|
20
|
+
require 'active_support/dependencies'
|
21
|
+
require 'action_controller/caching'
|
22
|
+
require 'action_controller/caching/sweeping'
|
23
|
+
|
24
|
+
require 'pp' # require 'pp' early to prevent hidden_methods from not picking up the pretty-print methods until too late
|
25
|
+
|
26
|
+
module Rails
|
27
|
+
def self.logger
|
28
|
+
@logger ||= begin
|
29
|
+
l = Logger.new(STDOUT)
|
30
|
+
l.level = Logger::INFO; l
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
# Monkey patch the old routes initialization to be silenced.
|
36
|
+
class ActionDispatch::Routing::DeprecatedMapper
|
37
|
+
def initialize_with_silencer(*args)
|
38
|
+
ActiveSupport::Deprecation.silence { initialize_without_silencer(*args) }
|
39
|
+
end
|
40
|
+
alias_method_chain :initialize, :silencer
|
41
|
+
end
|
42
|
+
|
43
|
+
ActiveSupport::Dependencies.hook!
|
44
|
+
|
45
|
+
# Show backtraces for deprecated behavior for quicker cleanup.
|
46
|
+
ActiveSupport::Deprecation.debug = true
|
47
|
+
|
48
|
+
ORIGINAL_LOCALES = I18n.available_locales.map {|locale| locale.to_s }.sort
|
49
|
+
|
50
|
+
module RackTestUtils
|
51
|
+
def body_to_string(body)
|
52
|
+
if body.respond_to?(:each)
|
53
|
+
str = ""
|
54
|
+
body.each {|s| str << s }
|
55
|
+
str
|
56
|
+
else
|
57
|
+
body
|
58
|
+
end
|
59
|
+
end
|
60
|
+
extend self
|
61
|
+
end
|
62
|
+
|
63
|
+
module SetupOnce
|
64
|
+
extend ActiveSupport::Concern
|
65
|
+
|
66
|
+
included do
|
67
|
+
cattr_accessor :setup_once_block
|
68
|
+
self.setup_once_block = nil
|
69
|
+
|
70
|
+
setup :run_setup_once
|
71
|
+
end
|
72
|
+
|
73
|
+
module ClassMethods
|
74
|
+
def setup_once(&block)
|
75
|
+
self.setup_once_block = block
|
76
|
+
end
|
77
|
+
end
|
78
|
+
|
79
|
+
private
|
80
|
+
def run_setup_once
|
81
|
+
if self.setup_once_block
|
82
|
+
self.setup_once_block.call
|
83
|
+
self.setup_once_block = nil
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
SharedTestRoutes = ActionDispatch::Routing::RouteSet.new
|
89
|
+
|
90
|
+
module ActiveSupport
|
91
|
+
class TestCase
|
92
|
+
include SetupOnce
|
93
|
+
# Hold off drawing routes until all the possible controller classes
|
94
|
+
# have been loaded.
|
95
|
+
setup_once do
|
96
|
+
SharedTestRoutes.draw do
|
97
|
+
match ':controller(/:action(/:id))'
|
98
|
+
end
|
99
|
+
|
100
|
+
ActionController::IntegrationTest.app.routes.draw do
|
101
|
+
match ':controller(/:action(/:id))'
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
105
|
+
end
|
106
|
+
|
107
|
+
class RoutedRackApp
|
108
|
+
attr_reader :routes
|
109
|
+
|
110
|
+
def initialize(routes, &blk)
|
111
|
+
@routes = routes
|
112
|
+
@stack = ActionDispatch::MiddlewareStack.new(&blk).build(@routes)
|
113
|
+
end
|
114
|
+
|
115
|
+
def call(env)
|
116
|
+
@stack.call(env)
|
117
|
+
end
|
118
|
+
end
|
119
|
+
|
120
|
+
class BasicController
|
121
|
+
attr_accessor :request
|
122
|
+
|
123
|
+
def config
|
124
|
+
@config ||= ActiveSupport::InheritableOptions.new(ActionController::Base.config).tap do |config|
|
125
|
+
# VIEW TODO: View tests should not require a controller
|
126
|
+
public_dir = File.expand_path("../fixtures/public", __FILE__)
|
127
|
+
config.assets_dir = public_dir
|
128
|
+
config.javascripts_dir = "#{public_dir}/javascripts"
|
129
|
+
config.stylesheets_dir = "#{public_dir}/stylesheets"
|
130
|
+
config
|
131
|
+
end
|
132
|
+
end
|
133
|
+
end
|
134
|
+
|
135
|
+
class ActionDispatch::IntegrationTest < ActiveSupport::TestCase
|
136
|
+
setup do
|
137
|
+
@routes = SharedTestRoutes
|
138
|
+
end
|
139
|
+
end
|
140
|
+
|
141
|
+
class ActionController::IntegrationTest < ActiveSupport::TestCase
|
142
|
+
def self.build_app(routes = nil)
|
143
|
+
RoutedRackApp.new(routes || ActionDispatch::Routing::RouteSet.new) do |middleware|
|
144
|
+
middleware.use "ActionDispatch::ShowExceptions"
|
145
|
+
middleware.use "ActionDispatch::Callbacks"
|
146
|
+
middleware.use "ActionDispatch::ParamsParser"
|
147
|
+
middleware.use "ActionDispatch::Cookies"
|
148
|
+
middleware.use "ActionDispatch::Flash"
|
149
|
+
middleware.use "ActionDispatch::Head"
|
150
|
+
yield(middleware) if block_given?
|
151
|
+
end
|
152
|
+
end
|
153
|
+
|
154
|
+
self.app = build_app
|
155
|
+
|
156
|
+
# Stub Rails dispatcher so it does not get controller references and
|
157
|
+
# simply return the controller#action as Rack::Body.
|
158
|
+
class StubDispatcher < ::ActionDispatch::Routing::RouteSet::Dispatcher
|
159
|
+
protected
|
160
|
+
def controller_reference(controller_param)
|
161
|
+
controller_param
|
162
|
+
end
|
163
|
+
|
164
|
+
def dispatch(controller, action, env)
|
165
|
+
[200, {'Content-Type' => 'text/html'}, ["#{controller}##{action}"]]
|
166
|
+
end
|
167
|
+
end
|
168
|
+
|
169
|
+
def self.stub_controllers
|
170
|
+
old_dispatcher = ActionDispatch::Routing::RouteSet::Dispatcher
|
171
|
+
ActionDispatch::Routing::RouteSet.module_eval { remove_const :Dispatcher }
|
172
|
+
ActionDispatch::Routing::RouteSet.module_eval { const_set :Dispatcher, StubDispatcher }
|
173
|
+
yield ActionDispatch::Routing::RouteSet.new
|
174
|
+
ensure
|
175
|
+
ActionDispatch::Routing::RouteSet.module_eval { remove_const :Dispatcher }
|
176
|
+
ActionDispatch::Routing::RouteSet.module_eval { const_set :Dispatcher, old_dispatcher }
|
177
|
+
end
|
178
|
+
|
179
|
+
def with_routing(&block)
|
180
|
+
temporary_routes = ActionDispatch::Routing::RouteSet.new
|
181
|
+
old_app, self.class.app = self.class.app, self.class.build_app(temporary_routes)
|
182
|
+
old_routes = SharedTestRoutes
|
183
|
+
silence_warnings { Object.const_set(:SharedTestRoutes, temporary_routes) }
|
184
|
+
|
185
|
+
yield temporary_routes
|
186
|
+
ensure
|
187
|
+
self.class.app = old_app
|
188
|
+
silence_warnings { Object.const_set(:SharedTestRoutes, old_routes) }
|
189
|
+
end
|
190
|
+
|
191
|
+
def with_autoload_path(path)
|
192
|
+
path = File.join(File.dirname(__FILE__), "fixtures", path)
|
193
|
+
if ActiveSupport::Dependencies.autoload_paths.include?(path)
|
194
|
+
yield
|
195
|
+
else
|
196
|
+
begin
|
197
|
+
ActiveSupport::Dependencies.autoload_paths << path
|
198
|
+
yield
|
199
|
+
ensure
|
200
|
+
ActiveSupport::Dependencies.autoload_paths.reject! {|p| p == path}
|
201
|
+
ActiveSupport::Dependencies.clear
|
202
|
+
end
|
203
|
+
end
|
204
|
+
end
|
205
|
+
end
|
206
|
+
|
207
|
+
# Temporary base class
|
208
|
+
class Rack::TestCase < ActionController::IntegrationTest
|
209
|
+
def self.testing(klass = nil)
|
210
|
+
if klass
|
211
|
+
@testing = "/#{klass.name.underscore}".sub!(/_controller$/, '')
|
212
|
+
else
|
213
|
+
@testing
|
214
|
+
end
|
215
|
+
end
|
216
|
+
|
217
|
+
def get(thing, *args)
|
218
|
+
if thing.is_a?(Symbol)
|
219
|
+
super("#{self.class.testing}/#{thing}", *args)
|
220
|
+
else
|
221
|
+
super
|
222
|
+
end
|
223
|
+
end
|
224
|
+
|
225
|
+
def assert_body(body)
|
226
|
+
assert_equal body, Array.wrap(response.body).join
|
227
|
+
end
|
228
|
+
|
229
|
+
def assert_status(code)
|
230
|
+
assert_equal code, response.status
|
231
|
+
end
|
232
|
+
|
233
|
+
def assert_response(body, status = 200, headers = {})
|
234
|
+
assert_body body
|
235
|
+
assert_status status
|
236
|
+
headers.each do |header, value|
|
237
|
+
assert_header header, value
|
238
|
+
end
|
239
|
+
end
|
240
|
+
|
241
|
+
def assert_content_type(type)
|
242
|
+
assert_equal type, response.headers["Content-Type"]
|
243
|
+
end
|
244
|
+
|
245
|
+
def assert_header(name, value)
|
246
|
+
assert_equal value, response.headers[name]
|
247
|
+
end
|
248
|
+
end
|
249
|
+
|
250
|
+
class ActionController::Base
|
251
|
+
def self.test_routes(&block)
|
252
|
+
routes = ActionDispatch::Routing::RouteSet.new
|
253
|
+
routes.draw(&block)
|
254
|
+
include routes.url_helpers
|
255
|
+
end
|
256
|
+
end
|
257
|
+
|
258
|
+
class ::ApplicationController < ActionController::Base
|
259
|
+
end
|
260
|
+
|
261
|
+
module ActionController
|
262
|
+
class Base
|
263
|
+
include ActionController::Testing
|
264
|
+
end
|
265
|
+
|
266
|
+
Base.view_paths = []
|
267
|
+
|
268
|
+
class TestCase
|
269
|
+
include ActionDispatch::TestProcess
|
270
|
+
|
271
|
+
setup do
|
272
|
+
@routes = SharedTestRoutes
|
273
|
+
end
|
274
|
+
end
|
275
|
+
end
|
276
|
+
|
277
|
+
# This stub emulates the Railtie including the URL helpers from a Rails application
|
278
|
+
module ActionController
|
279
|
+
class Base
|
280
|
+
include SharedTestRoutes.url_helpers
|
281
|
+
end
|
282
|
+
end
|