manveru-innate 2009.02.06
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/CHANGELOG +1409 -0
- data/COPYING +18 -0
- data/MANIFEST +100 -0
- data/README.md +485 -0
- data/Rakefile +139 -0
- data/example/app/retro_games.rb +57 -0
- data/example/app/whywiki_erb/layout/wiki.html.erb +15 -0
- data/example/app/whywiki_erb/spec/wiki.rb +19 -0
- data/example/app/whywiki_erb/start.rb +45 -0
- data/example/app/whywiki_erb/view/edit.html.erb +6 -0
- data/example/app/whywiki_erb/view/index.html.erb +10 -0
- data/example/custom_middleware.rb +43 -0
- data/example/error_handling.rb +31 -0
- data/example/hello.rb +12 -0
- data/example/howto_spec.rb +60 -0
- data/example/link.rb +35 -0
- data/example/providing_hash.rb +46 -0
- data/example/session.rb +42 -0
- data/innate.gemspec +118 -0
- data/lib/innate.rb +191 -0
- data/lib/innate/action.rb +156 -0
- data/lib/innate/adapter.rb +89 -0
- data/lib/innate/cache.rb +117 -0
- data/lib/innate/cache/api.rb +106 -0
- data/lib/innate/cache/drb.rb +58 -0
- data/lib/innate/cache/file_based.rb +39 -0
- data/lib/innate/cache/marshal.rb +17 -0
- data/lib/innate/cache/memory.rb +22 -0
- data/lib/innate/cache/yaml.rb +17 -0
- data/lib/innate/core_compatibility/basic_object.rb +9 -0
- data/lib/innate/core_compatibility/string.rb +3 -0
- data/lib/innate/current.rb +37 -0
- data/lib/innate/dynamap.rb +81 -0
- data/lib/innate/helper.rb +195 -0
- data/lib/innate/helper/aspect.rb +62 -0
- data/lib/innate/helper/cgi.rb +39 -0
- data/lib/innate/helper/flash.rb +36 -0
- data/lib/innate/helper/link.rb +55 -0
- data/lib/innate/helper/partial.rb +90 -0
- data/lib/innate/helper/redirect.rb +85 -0
- data/lib/innate/helper/send_file.rb +18 -0
- data/lib/innate/log.rb +23 -0
- data/lib/innate/log/color_formatter.rb +43 -0
- data/lib/innate/log/hub.rb +72 -0
- data/lib/innate/mock.rb +49 -0
- data/lib/innate/node.rb +471 -0
- data/lib/innate/options.rb +91 -0
- data/lib/innate/options/dsl.rb +155 -0
- data/lib/innate/request.rb +165 -0
- data/lib/innate/response.rb +18 -0
- data/lib/innate/route.rb +109 -0
- data/lib/innate/session.rb +104 -0
- data/lib/innate/session/flash.rb +94 -0
- data/lib/innate/setup.rb +23 -0
- data/lib/innate/spec.rb +42 -0
- data/lib/innate/state.rb +22 -0
- data/lib/innate/state/accessor.rb +130 -0
- data/lib/innate/state/fiber.rb +68 -0
- data/lib/innate/state/thread.rb +39 -0
- data/lib/innate/traited.rb +20 -0
- data/lib/innate/trinity.rb +22 -0
- data/lib/innate/version.rb +3 -0
- data/lib/innate/view.rb +67 -0
- data/lib/innate/view/erb.rb +17 -0
- data/lib/innate/view/none.rb +9 -0
- data/lib/rack/middleware_compiler.rb +62 -0
- data/lib/rack/reloader.rb +192 -0
- data/spec/example/hello.rb +14 -0
- data/spec/example/link.rb +29 -0
- data/spec/helper.rb +2 -0
- data/spec/innate/cache/common.rb +45 -0
- data/spec/innate/cache/marshal.rb +5 -0
- data/spec/innate/cache/memory.rb +5 -0
- data/spec/innate/cache/yaml.rb +5 -0
- data/spec/innate/dynamap.rb +22 -0
- data/spec/innate/helper.rb +66 -0
- data/spec/innate/helper/aspect.rb +80 -0
- data/spec/innate/helper/cgi.rb +37 -0
- data/spec/innate/helper/flash.rb +148 -0
- data/spec/innate/helper/link.rb +82 -0
- data/spec/innate/helper/partial.rb +66 -0
- data/spec/innate/helper/redirect.rb +148 -0
- data/spec/innate/helper/send_file.rb +21 -0
- data/spec/innate/helper/view/aspect_hello.erb +1 -0
- data/spec/innate/helper/view/locals.erb +1 -0
- data/spec/innate/helper/view/loop.erb +4 -0
- data/spec/innate/helper/view/num.erb +1 -0
- data/spec/innate/helper/view/partial.erb +1 -0
- data/spec/innate/helper/view/recursive.erb +8 -0
- data/spec/innate/mock.rb +84 -0
- data/spec/innate/node.rb +180 -0
- data/spec/innate/node/bar.html +1 -0
- data/spec/innate/node/foo.html.erb +1 -0
- data/spec/innate/node/with_layout.erb +3 -0
- data/spec/innate/options.rb +90 -0
- data/spec/innate/parameter.rb +154 -0
- data/spec/innate/request.rb +73 -0
- data/spec/innate/route.rb +129 -0
- data/spec/innate/session.rb +59 -0
- data/spec/innate/traited.rb +55 -0
- metadata +160 -0
@@ -0,0 +1,104 @@
|
|
1
|
+
module Innate
|
2
|
+
|
3
|
+
# Mostly ported from Ramaze, but behaves lazy, no session will be created if
|
4
|
+
# no session is used.
|
5
|
+
#
|
6
|
+
# The way we do it is to keep session data in memory until #flush is called,
|
7
|
+
# at which point it will be persisted completely into the cache, no question
|
8
|
+
# asked.
|
9
|
+
#
|
10
|
+
# NOTE:
|
11
|
+
# * You may store anything in here that you may also store in the
|
12
|
+
# corresponding store, usually it's best to keep it to things that are
|
13
|
+
# safe to Marshal.
|
14
|
+
|
15
|
+
class Session
|
16
|
+
attr_reader :cookie_set, :request, :response, :flash
|
17
|
+
|
18
|
+
def initialize(request, response)
|
19
|
+
@request, @response = request, response
|
20
|
+
@cookie_set = false
|
21
|
+
@cache_sid = nil
|
22
|
+
@flash = Flash.new(self)
|
23
|
+
end
|
24
|
+
|
25
|
+
def []=(key, value)
|
26
|
+
cache_sid[key] = value
|
27
|
+
end
|
28
|
+
|
29
|
+
def [](key)
|
30
|
+
cache_sid[key]
|
31
|
+
end
|
32
|
+
|
33
|
+
def delete(key)
|
34
|
+
cache_sid.delete(key)
|
35
|
+
end
|
36
|
+
|
37
|
+
def clear
|
38
|
+
cache.delete(sid)
|
39
|
+
@cache_sid = nil
|
40
|
+
end
|
41
|
+
|
42
|
+
def cache_sid
|
43
|
+
@cache_sid ||= cache[sid] || {}
|
44
|
+
end
|
45
|
+
|
46
|
+
def flush(response = @response)
|
47
|
+
return unless @cache_sid
|
48
|
+
return if @cache_sid.empty?
|
49
|
+
|
50
|
+
flash.rotate!
|
51
|
+
cache[sid] = cache_sid
|
52
|
+
set_cookie(response)
|
53
|
+
end
|
54
|
+
|
55
|
+
def sid
|
56
|
+
@sid ||= cookie || generate_sid
|
57
|
+
end
|
58
|
+
|
59
|
+
def cookie
|
60
|
+
@request.cookies[options.key]
|
61
|
+
end
|
62
|
+
|
63
|
+
def inspect
|
64
|
+
cache.inspect
|
65
|
+
end
|
66
|
+
|
67
|
+
private
|
68
|
+
|
69
|
+
def cache
|
70
|
+
Innate::Cache.session
|
71
|
+
end
|
72
|
+
|
73
|
+
def options
|
74
|
+
Innate.options[:session]
|
75
|
+
end
|
76
|
+
|
77
|
+
def set_cookie(response)
|
78
|
+
return if @cookie_set || cookie
|
79
|
+
|
80
|
+
@cookie_set = true
|
81
|
+
response.set_cookie(options.key, cookie_value)
|
82
|
+
end
|
83
|
+
|
84
|
+
def cookie_value
|
85
|
+
{ :value => sid,
|
86
|
+
:domain => options.domain,
|
87
|
+
:path => options.path,
|
88
|
+
:secure => options.secure,
|
89
|
+
:expires => options.expires }
|
90
|
+
end
|
91
|
+
|
92
|
+
def entropy
|
93
|
+
[ srand, rand, Time.now.to_f, rand, $$, rand, object_id ]
|
94
|
+
end
|
95
|
+
|
96
|
+
def generate_sid
|
97
|
+
begin
|
98
|
+
sid = Digest::SHA1.hexdigest(entropy.join)
|
99
|
+
end while cache[sid]
|
100
|
+
|
101
|
+
return sid
|
102
|
+
end
|
103
|
+
end
|
104
|
+
end
|
@@ -0,0 +1,94 @@
|
|
1
|
+
module Innate
|
2
|
+
class Session
|
3
|
+
|
4
|
+
# The purpose of this class is to act as a unifier of the previous
|
5
|
+
# and current flash.
|
6
|
+
#
|
7
|
+
# Flash means pairs of keys and values that are held only over one
|
8
|
+
# request/response cycle. So you can assign a key/value in the current
|
9
|
+
# session and retrieve it in the current and following request.
|
10
|
+
#
|
11
|
+
# Please see the Innate::Helper::Flash for details on the usage in your
|
12
|
+
# application.
|
13
|
+
class Flash
|
14
|
+
include Enumerable
|
15
|
+
|
16
|
+
def initialize(session)
|
17
|
+
@session = session
|
18
|
+
end
|
19
|
+
|
20
|
+
# iterate over the combined session
|
21
|
+
def each(&block)
|
22
|
+
combined.each(&block)
|
23
|
+
end
|
24
|
+
|
25
|
+
# the current session[:FLASH_PREVIOUS]
|
26
|
+
def previous
|
27
|
+
session[:FLASH_PREVIOUS] || {}
|
28
|
+
end
|
29
|
+
|
30
|
+
# the current session[:FLASH]
|
31
|
+
def current
|
32
|
+
session[:FLASH] ||= {}
|
33
|
+
end
|
34
|
+
|
35
|
+
# combined key/value pairs of previous and current
|
36
|
+
# current keys overshadow the old ones.
|
37
|
+
def combined
|
38
|
+
previous.merge(current)
|
39
|
+
end
|
40
|
+
|
41
|
+
# flash[key] in your Controller
|
42
|
+
def [](key)
|
43
|
+
combined[key]
|
44
|
+
end
|
45
|
+
|
46
|
+
# flash[key] = value in your Controller
|
47
|
+
def []=(key, value)
|
48
|
+
prev = session[:FLASH] || {}
|
49
|
+
prev[key] = value
|
50
|
+
session[:FLASH] = prev
|
51
|
+
end
|
52
|
+
|
53
|
+
# Inspects combined
|
54
|
+
def inspect
|
55
|
+
combined.inspect
|
56
|
+
end
|
57
|
+
|
58
|
+
# Delete a key
|
59
|
+
def delete(key)
|
60
|
+
previous.delete(key)
|
61
|
+
current.delete(key)
|
62
|
+
end
|
63
|
+
|
64
|
+
# check if combined is empty
|
65
|
+
def empty?
|
66
|
+
combined.empty?
|
67
|
+
end
|
68
|
+
|
69
|
+
# merge into current
|
70
|
+
def merge!(hash)
|
71
|
+
current.merge!(hash)
|
72
|
+
end
|
73
|
+
|
74
|
+
# merge on current
|
75
|
+
def merge(hash)
|
76
|
+
current.merge(hash)
|
77
|
+
end
|
78
|
+
|
79
|
+
# Rotation means current values are assigned as old values for the next
|
80
|
+
# request.
|
81
|
+
def rotate!
|
82
|
+
old = session.delete(:FLASH)
|
83
|
+
session[:FLASH_PREVIOUS] = old if old
|
84
|
+
end
|
85
|
+
|
86
|
+
private
|
87
|
+
|
88
|
+
# Associated session object
|
89
|
+
def session
|
90
|
+
@session
|
91
|
+
end
|
92
|
+
end
|
93
|
+
end
|
94
|
+
end
|
data/lib/innate/setup.rb
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
module Innate
|
2
|
+
def self.setup(&block)
|
3
|
+
Setup.new(&block)
|
4
|
+
end
|
5
|
+
|
6
|
+
class Setup
|
7
|
+
def initialize(&block)
|
8
|
+
instance_eval(&block) if block
|
9
|
+
end
|
10
|
+
|
11
|
+
def gem(*args)
|
12
|
+
raise
|
13
|
+
end
|
14
|
+
|
15
|
+
def start(options = {})
|
16
|
+
Innate.start(options)
|
17
|
+
end
|
18
|
+
|
19
|
+
def require(*libs)
|
20
|
+
libs.flatten.each{|lib| Kernel::require(lib) }
|
21
|
+
end
|
22
|
+
end
|
23
|
+
end
|
data/lib/innate/spec.rb
ADDED
@@ -0,0 +1,42 @@
|
|
1
|
+
require 'bacon'
|
2
|
+
|
3
|
+
Bacon.summary_on_exit
|
4
|
+
Bacon.extend(Bacon::TestUnitOutput)
|
5
|
+
|
6
|
+
innate = File.expand_path(File.join(File.dirname(__FILE__), '../innate'))
|
7
|
+
require(innate) unless defined?(Innate)
|
8
|
+
|
9
|
+
module Innate
|
10
|
+
# minimal middleware, no exception handling
|
11
|
+
middleware(:innate){|m| m.innate }
|
12
|
+
|
13
|
+
# skip merging of options
|
14
|
+
options.started = true
|
15
|
+
end
|
16
|
+
|
17
|
+
# Shortcut to use persistent cookies via Innate::Mock::Session
|
18
|
+
#
|
19
|
+
# Usage:
|
20
|
+
#
|
21
|
+
# describe 'something' do
|
22
|
+
# behaves_like :session
|
23
|
+
#
|
24
|
+
# should 'get some stuff' do
|
25
|
+
# session do |mock|
|
26
|
+
# mock.get('/')
|
27
|
+
# end
|
28
|
+
# end
|
29
|
+
# end
|
30
|
+
shared :session do
|
31
|
+
Innate.setup_dependencies
|
32
|
+
|
33
|
+
def session(&block)
|
34
|
+
Innate::Mock.session(&block)
|
35
|
+
end
|
36
|
+
end
|
37
|
+
|
38
|
+
shared :mock do
|
39
|
+
def get(*args)
|
40
|
+
Innate::Mock.get(*args)
|
41
|
+
end
|
42
|
+
end
|
data/lib/innate/state.rb
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
module Innate
|
2
|
+
state = options[:state]
|
3
|
+
|
4
|
+
if state == :Fiber
|
5
|
+
begin
|
6
|
+
require 'innate/state/fiber'
|
7
|
+
STATE = State::Fiber.new
|
8
|
+
rescue LoadError
|
9
|
+
require 'innate/state/thread'
|
10
|
+
STATE = State::Thread.new
|
11
|
+
end
|
12
|
+
else
|
13
|
+
require 'innate/state/thread'
|
14
|
+
STATE = State::Thread.new
|
15
|
+
end
|
16
|
+
|
17
|
+
# Log.debug("Innate keeps state with %p" % STATE.class)
|
18
|
+
|
19
|
+
def self.sync(&block)
|
20
|
+
STATE.sync(&block)
|
21
|
+
end
|
22
|
+
end
|
@@ -0,0 +1,130 @@
|
|
1
|
+
module Innate
|
2
|
+
# Simplify accessing STATE variables.
|
3
|
+
#
|
4
|
+
# Example:
|
5
|
+
#
|
6
|
+
# class Foo
|
7
|
+
# include Innate::StateAccessor
|
8
|
+
# state_accessor :session
|
9
|
+
#
|
10
|
+
# def calculate
|
11
|
+
# session[:num1] + session[:num2]
|
12
|
+
# end
|
13
|
+
# end
|
14
|
+
#
|
15
|
+
# Foo#calculate can now be called from anywhere in your application and it
|
16
|
+
# will have direct access to the session in the current request/response
|
17
|
+
# cycle in a thread-safe way without the need to explicitly pass the session
|
18
|
+
# along.
|
19
|
+
|
20
|
+
module StateAccessor
|
21
|
+
|
22
|
+
# Iterate over the names and yield accordingly.
|
23
|
+
# names are either objects responding to #to_sym or hashes.
|
24
|
+
#
|
25
|
+
# It's only used within this module to make the code readable.
|
26
|
+
#
|
27
|
+
# Used below.
|
28
|
+
|
29
|
+
def self.each(*names)
|
30
|
+
names.each do |name|
|
31
|
+
if name.respond_to?(:to_hash)
|
32
|
+
name.to_hash.each do |key, meth|
|
33
|
+
yield(key.to_sym, meth.to_sym)
|
34
|
+
end
|
35
|
+
else
|
36
|
+
key = meth = name.to_sym
|
37
|
+
yield(key, meth)
|
38
|
+
end
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
# Combined state_writer and state_reader.
|
43
|
+
# +initializer+ is a block that may be given and its result will be the new
|
44
|
+
# value in case the method created by state_reader was never called before
|
45
|
+
# and the value wasn't set before.
|
46
|
+
#
|
47
|
+
# Example:
|
48
|
+
#
|
49
|
+
# state_accessor(:session)
|
50
|
+
# state_accessor(:user){ session[:user] }
|
51
|
+
|
52
|
+
def state_accessor(*names, &initializer)
|
53
|
+
state_writer(*names)
|
54
|
+
state_reader(*names, &initializer)
|
55
|
+
end
|
56
|
+
|
57
|
+
# Writer accessor to STATE[key]=
|
58
|
+
#
|
59
|
+
# Example:
|
60
|
+
#
|
61
|
+
# class Foo
|
62
|
+
# include Innate::StateAccessor
|
63
|
+
# state_writer(:result)
|
64
|
+
#
|
65
|
+
# def calculate
|
66
|
+
# self.result = 42
|
67
|
+
# end
|
68
|
+
# end
|
69
|
+
#
|
70
|
+
# class Bar
|
71
|
+
# include Innate::StateAccessor
|
72
|
+
# state_reader(:result)
|
73
|
+
#
|
74
|
+
# def calculcate
|
75
|
+
# result * result
|
76
|
+
# end
|
77
|
+
# end
|
78
|
+
#
|
79
|
+
# Foo.new.calculate # => 42
|
80
|
+
# Bar.new.calculate # => 1764
|
81
|
+
|
82
|
+
def state_writer(*names)
|
83
|
+
StateAccessor.each(*names) do |key, meth|
|
84
|
+
class_eval("def %s=(obj) STATE[%p] = obj; end" % [meth, key])
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
# Reader accessor for STATE[key]
|
89
|
+
#
|
90
|
+
# Example:
|
91
|
+
#
|
92
|
+
# class Foo
|
93
|
+
# include Innate::StateAccessor
|
94
|
+
# state_reader(:session)
|
95
|
+
# state_reader(:random){ rand(100_000) }
|
96
|
+
#
|
97
|
+
# def calculate
|
98
|
+
# val1 = session[:num1] + session[:num2] + random
|
99
|
+
# val2 = session[:num1] + session[:num2] + random
|
100
|
+
# val1 == val2 # => true
|
101
|
+
# end
|
102
|
+
# end
|
103
|
+
#
|
104
|
+
# NOTE:
|
105
|
+
# If given +initializer+, there will be some performance impact since we
|
106
|
+
# cannot use class_eval and have to use define_method instead, we also
|
107
|
+
# have to check every time whether the initializer was executed already.
|
108
|
+
#
|
109
|
+
# In 1.8.x the overhead of define_method is 3x that of class_eval/def
|
110
|
+
# In 1.9.1 the overhead of define_method is 1.5x that of class_eval/def
|
111
|
+
#
|
112
|
+
# This may only be an issue for readers that are called a lot of times.
|
113
|
+
|
114
|
+
def state_reader(*names, &initializer)
|
115
|
+
StateAccessor.each(*names) do |key, meth|
|
116
|
+
if initializer
|
117
|
+
define_method(meth) do
|
118
|
+
unless STATE.key?(key)
|
119
|
+
STATE[key] = instance_eval(&initializer)
|
120
|
+
else
|
121
|
+
STATE[key]
|
122
|
+
end
|
123
|
+
end
|
124
|
+
else
|
125
|
+
class_eval("def %s; STATE[%p]; end" % [meth, key])
|
126
|
+
end
|
127
|
+
end
|
128
|
+
end
|
129
|
+
end
|
130
|
+
end
|
@@ -0,0 +1,68 @@
|
|
1
|
+
require 'fiber'
|
2
|
+
|
3
|
+
module Innate
|
4
|
+
# Innate subclasses Fiber to enable lightweight request/respose-cycle local
|
5
|
+
# variables.
|
6
|
+
#
|
7
|
+
# We do that by adding a state Hash to the Fiber instance on initalization
|
8
|
+
# which can be accessed by #[], #[]= and #key?. Other Hash methods are not
|
9
|
+
# necessary right now but may be added.
|
10
|
+
#
|
11
|
+
# We subclass to keep your Ruby clean and polished.
|
12
|
+
#
|
13
|
+
# TODO:
|
14
|
+
# * 1.9.1 includes a patch from matz that will call #initialize, so we can
|
15
|
+
# remove our ::new hack, just have to get my hands on a properly
|
16
|
+
# compiling version of 1.9.1
|
17
|
+
class Fiber < ::Fiber
|
18
|
+
attr_accessor :state
|
19
|
+
|
20
|
+
def self.new(*args)
|
21
|
+
instance = super
|
22
|
+
instance.state = {}
|
23
|
+
instance
|
24
|
+
end
|
25
|
+
|
26
|
+
def [](key)
|
27
|
+
state[key]
|
28
|
+
end
|
29
|
+
|
30
|
+
def []=(key, value)
|
31
|
+
state[key] = value
|
32
|
+
end
|
33
|
+
|
34
|
+
def key?(key)
|
35
|
+
state.key?(key)
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
module State
|
40
|
+
# Our accessor to the currently active Fiber, an instance of
|
41
|
+
# Innate::State::Fiber will be assigned to Innate::STATE if fibers are
|
42
|
+
# available.
|
43
|
+
class Fiber
|
44
|
+
def [](key)
|
45
|
+
Innate::Fiber.current[key]
|
46
|
+
end
|
47
|
+
|
48
|
+
def []=(key, value)
|
49
|
+
Innate::Fiber.current[key] = value
|
50
|
+
end
|
51
|
+
|
52
|
+
# We don't use Fiber in a concurrent manner and they don't run
|
53
|
+
# concurrently by themselves, so we directly #resume the Fiber to get the
|
54
|
+
# return value of +block+.
|
55
|
+
|
56
|
+
def wrap(&block)
|
57
|
+
Innate::Fiber.new(&block).resume
|
58
|
+
end
|
59
|
+
|
60
|
+
# In an environment where only Fiber is used there is no concurrency, so
|
61
|
+
# we don't have to lock with a Mutex.
|
62
|
+
|
63
|
+
def sync
|
64
|
+
yield
|
65
|
+
end
|
66
|
+
end
|
67
|
+
end
|
68
|
+
end
|