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.
Files changed (101) hide show
  1. data/CHANGELOG +1409 -0
  2. data/COPYING +18 -0
  3. data/MANIFEST +100 -0
  4. data/README.md +485 -0
  5. data/Rakefile +139 -0
  6. data/example/app/retro_games.rb +57 -0
  7. data/example/app/whywiki_erb/layout/wiki.html.erb +15 -0
  8. data/example/app/whywiki_erb/spec/wiki.rb +19 -0
  9. data/example/app/whywiki_erb/start.rb +45 -0
  10. data/example/app/whywiki_erb/view/edit.html.erb +6 -0
  11. data/example/app/whywiki_erb/view/index.html.erb +10 -0
  12. data/example/custom_middleware.rb +43 -0
  13. data/example/error_handling.rb +31 -0
  14. data/example/hello.rb +12 -0
  15. data/example/howto_spec.rb +60 -0
  16. data/example/link.rb +35 -0
  17. data/example/providing_hash.rb +46 -0
  18. data/example/session.rb +42 -0
  19. data/innate.gemspec +118 -0
  20. data/lib/innate.rb +191 -0
  21. data/lib/innate/action.rb +156 -0
  22. data/lib/innate/adapter.rb +89 -0
  23. data/lib/innate/cache.rb +117 -0
  24. data/lib/innate/cache/api.rb +106 -0
  25. data/lib/innate/cache/drb.rb +58 -0
  26. data/lib/innate/cache/file_based.rb +39 -0
  27. data/lib/innate/cache/marshal.rb +17 -0
  28. data/lib/innate/cache/memory.rb +22 -0
  29. data/lib/innate/cache/yaml.rb +17 -0
  30. data/lib/innate/core_compatibility/basic_object.rb +9 -0
  31. data/lib/innate/core_compatibility/string.rb +3 -0
  32. data/lib/innate/current.rb +37 -0
  33. data/lib/innate/dynamap.rb +81 -0
  34. data/lib/innate/helper.rb +195 -0
  35. data/lib/innate/helper/aspect.rb +62 -0
  36. data/lib/innate/helper/cgi.rb +39 -0
  37. data/lib/innate/helper/flash.rb +36 -0
  38. data/lib/innate/helper/link.rb +55 -0
  39. data/lib/innate/helper/partial.rb +90 -0
  40. data/lib/innate/helper/redirect.rb +85 -0
  41. data/lib/innate/helper/send_file.rb +18 -0
  42. data/lib/innate/log.rb +23 -0
  43. data/lib/innate/log/color_formatter.rb +43 -0
  44. data/lib/innate/log/hub.rb +72 -0
  45. data/lib/innate/mock.rb +49 -0
  46. data/lib/innate/node.rb +471 -0
  47. data/lib/innate/options.rb +91 -0
  48. data/lib/innate/options/dsl.rb +155 -0
  49. data/lib/innate/request.rb +165 -0
  50. data/lib/innate/response.rb +18 -0
  51. data/lib/innate/route.rb +109 -0
  52. data/lib/innate/session.rb +104 -0
  53. data/lib/innate/session/flash.rb +94 -0
  54. data/lib/innate/setup.rb +23 -0
  55. data/lib/innate/spec.rb +42 -0
  56. data/lib/innate/state.rb +22 -0
  57. data/lib/innate/state/accessor.rb +130 -0
  58. data/lib/innate/state/fiber.rb +68 -0
  59. data/lib/innate/state/thread.rb +39 -0
  60. data/lib/innate/traited.rb +20 -0
  61. data/lib/innate/trinity.rb +22 -0
  62. data/lib/innate/version.rb +3 -0
  63. data/lib/innate/view.rb +67 -0
  64. data/lib/innate/view/erb.rb +17 -0
  65. data/lib/innate/view/none.rb +9 -0
  66. data/lib/rack/middleware_compiler.rb +62 -0
  67. data/lib/rack/reloader.rb +192 -0
  68. data/spec/example/hello.rb +14 -0
  69. data/spec/example/link.rb +29 -0
  70. data/spec/helper.rb +2 -0
  71. data/spec/innate/cache/common.rb +45 -0
  72. data/spec/innate/cache/marshal.rb +5 -0
  73. data/spec/innate/cache/memory.rb +5 -0
  74. data/spec/innate/cache/yaml.rb +5 -0
  75. data/spec/innate/dynamap.rb +22 -0
  76. data/spec/innate/helper.rb +66 -0
  77. data/spec/innate/helper/aspect.rb +80 -0
  78. data/spec/innate/helper/cgi.rb +37 -0
  79. data/spec/innate/helper/flash.rb +148 -0
  80. data/spec/innate/helper/link.rb +82 -0
  81. data/spec/innate/helper/partial.rb +66 -0
  82. data/spec/innate/helper/redirect.rb +148 -0
  83. data/spec/innate/helper/send_file.rb +21 -0
  84. data/spec/innate/helper/view/aspect_hello.erb +1 -0
  85. data/spec/innate/helper/view/locals.erb +1 -0
  86. data/spec/innate/helper/view/loop.erb +4 -0
  87. data/spec/innate/helper/view/num.erb +1 -0
  88. data/spec/innate/helper/view/partial.erb +1 -0
  89. data/spec/innate/helper/view/recursive.erb +8 -0
  90. data/spec/innate/mock.rb +84 -0
  91. data/spec/innate/node.rb +180 -0
  92. data/spec/innate/node/bar.html +1 -0
  93. data/spec/innate/node/foo.html.erb +1 -0
  94. data/spec/innate/node/with_layout.erb +3 -0
  95. data/spec/innate/options.rb +90 -0
  96. data/spec/innate/parameter.rb +154 -0
  97. data/spec/innate/request.rb +73 -0
  98. data/spec/innate/route.rb +129 -0
  99. data/spec/innate/session.rb +59 -0
  100. data/spec/innate/traited.rb +55 -0
  101. 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
@@ -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
@@ -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
@@ -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