innate 2009.04

Sign up to get free protection for your applications and to get access to all the features.
Files changed (128) hide show
  1. data/CHANGELOG +2981 -0
  2. data/COPYING +18 -0
  3. data/MANIFEST +127 -0
  4. data/README.md +563 -0
  5. data/Rakefile +35 -0
  6. data/example/app/retro_games.rb +60 -0
  7. data/example/app/todo/layout/default.xhtml +11 -0
  8. data/example/app/todo/spec/todo.rb +63 -0
  9. data/example/app/todo/start.rb +51 -0
  10. data/example/app/todo/view/index.xhtml +39 -0
  11. data/example/app/whywiki_erb/layout/wiki.html.erb +15 -0
  12. data/example/app/whywiki_erb/spec/wiki.rb +19 -0
  13. data/example/app/whywiki_erb/start.rb +42 -0
  14. data/example/app/whywiki_erb/view/edit.erb +6 -0
  15. data/example/app/whywiki_erb/view/index.erb +12 -0
  16. data/example/custom_middleware.rb +35 -0
  17. data/example/hello.rb +11 -0
  18. data/example/howto_spec.rb +35 -0
  19. data/example/link.rb +27 -0
  20. data/example/provides.rb +31 -0
  21. data/example/session.rb +38 -0
  22. data/innate.gemspec +29 -0
  23. data/lib/innate.rb +269 -0
  24. data/lib/innate/action.rb +150 -0
  25. data/lib/innate/adapter.rb +76 -0
  26. data/lib/innate/cache.rb +134 -0
  27. data/lib/innate/cache/api.rb +128 -0
  28. data/lib/innate/cache/drb.rb +58 -0
  29. data/lib/innate/cache/file_based.rb +41 -0
  30. data/lib/innate/cache/marshal.rb +17 -0
  31. data/lib/innate/cache/memory.rb +22 -0
  32. data/lib/innate/cache/yaml.rb +17 -0
  33. data/lib/innate/current.rb +37 -0
  34. data/lib/innate/dynamap.rb +96 -0
  35. data/lib/innate/helper.rb +183 -0
  36. data/lib/innate/helper/aspect.rb +124 -0
  37. data/lib/innate/helper/cgi.rb +54 -0
  38. data/lib/innate/helper/flash.rb +36 -0
  39. data/lib/innate/helper/link.rb +94 -0
  40. data/lib/innate/helper/redirect.rb +85 -0
  41. data/lib/innate/helper/render.rb +87 -0
  42. data/lib/innate/helper/send_file.rb +26 -0
  43. data/lib/innate/log.rb +20 -0
  44. data/lib/innate/log/color_formatter.rb +43 -0
  45. data/lib/innate/log/hub.rb +73 -0
  46. data/lib/innate/middleware_compiler.rb +65 -0
  47. data/lib/innate/mock.rb +49 -0
  48. data/lib/innate/node.rb +1025 -0
  49. data/lib/innate/options.rb +37 -0
  50. data/lib/innate/options/dsl.rb +202 -0
  51. data/lib/innate/options/stub.rb +7 -0
  52. data/lib/innate/request.rb +141 -0
  53. data/lib/innate/response.rb +23 -0
  54. data/lib/innate/route.rb +110 -0
  55. data/lib/innate/session.rb +121 -0
  56. data/lib/innate/session/flash.rb +94 -0
  57. data/lib/innate/spec.rb +23 -0
  58. data/lib/innate/state.rb +27 -0
  59. data/lib/innate/state/accessor.rb +130 -0
  60. data/lib/innate/state/fiber.rb +74 -0
  61. data/lib/innate/state/thread.rb +47 -0
  62. data/lib/innate/traited.rb +85 -0
  63. data/lib/innate/trinity.rb +18 -0
  64. data/lib/innate/version.rb +3 -0
  65. data/lib/innate/view.rb +60 -0
  66. data/lib/innate/view/erb.rb +15 -0
  67. data/lib/innate/view/etanni.rb +36 -0
  68. data/lib/innate/view/none.rb +9 -0
  69. data/spec/example/app/retro_games.rb +30 -0
  70. data/spec/example/hello.rb +13 -0
  71. data/spec/example/link.rb +25 -0
  72. data/spec/example/provides.rb +16 -0
  73. data/spec/example/session.rb +22 -0
  74. data/spec/helper.rb +10 -0
  75. data/spec/innate/action/layout.rb +107 -0
  76. data/spec/innate/action/layout/file_layout.xhtml +1 -0
  77. data/spec/innate/cache/common.rb +47 -0
  78. data/spec/innate/cache/marshal.rb +5 -0
  79. data/spec/innate/cache/memory.rb +5 -0
  80. data/spec/innate/cache/yaml.rb +5 -0
  81. data/spec/innate/dynamap.rb +22 -0
  82. data/spec/innate/helper.rb +86 -0
  83. data/spec/innate/helper/aspect.rb +75 -0
  84. data/spec/innate/helper/cgi.rb +37 -0
  85. data/spec/innate/helper/flash.rb +118 -0
  86. data/spec/innate/helper/link.rb +139 -0
  87. data/spec/innate/helper/redirect.rb +160 -0
  88. data/spec/innate/helper/render.rb +133 -0
  89. data/spec/innate/helper/send_file.rb +21 -0
  90. data/spec/innate/helper/view/aspect_hello.xhtml +1 -0
  91. data/spec/innate/helper/view/locals.xhtml +1 -0
  92. data/spec/innate/helper/view/loop.xhtml +4 -0
  93. data/spec/innate/helper/view/num.xhtml +1 -0
  94. data/spec/innate/helper/view/partial.xhtml +1 -0
  95. data/spec/innate/helper/view/recursive.xhtml +7 -0
  96. data/spec/innate/mock.rb +84 -0
  97. data/spec/innate/node/mapping.rb +37 -0
  98. data/spec/innate/node/node.rb +134 -0
  99. data/spec/innate/node/resolve.rb +82 -0
  100. data/spec/innate/node/view/another_layout/another_layout.xhtml +3 -0
  101. data/spec/innate/node/view/bar.xhtml +1 -0
  102. data/spec/innate/node/view/foo.html.xhtml +1 -0
  103. data/spec/innate/node/view/only_view.xhtml +1 -0
  104. data/spec/innate/node/view/with_layout.xhtml +1 -0
  105. data/spec/innate/node/wrap_action_call.rb +83 -0
  106. data/spec/innate/options.rb +115 -0
  107. data/spec/innate/parameter.rb +154 -0
  108. data/spec/innate/provides.rb +99 -0
  109. data/spec/innate/provides/list.html.xhtml +1 -0
  110. data/spec/innate/provides/list.txt.xhtml +1 -0
  111. data/spec/innate/request.rb +77 -0
  112. data/spec/innate/route.rb +135 -0
  113. data/spec/innate/session.rb +54 -0
  114. data/spec/innate/state/fiber.rb +58 -0
  115. data/spec/innate/state/thread.rb +40 -0
  116. data/spec/innate/traited.rb +55 -0
  117. data/tasks/bacon.rake +66 -0
  118. data/tasks/changelog.rake +18 -0
  119. data/tasks/gem.rake +22 -0
  120. data/tasks/gem_installer.rake +76 -0
  121. data/tasks/grancher.rake +12 -0
  122. data/tasks/install_dependencies.rake +4 -0
  123. data/tasks/manifest.rake +4 -0
  124. data/tasks/rcov.rake +19 -0
  125. data/tasks/release.rake +51 -0
  126. data/tasks/reversion.rake +8 -0
  127. data/tasks/setup.rake +28 -0
  128. metadata +181 -0
@@ -0,0 +1,121 @@
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
+ # We keep session data in memory until #flush is called, at which point it
7
+ # will be persisted completely into the cache, no question asked.
8
+ #
9
+ # You may store anything in here that you may also store in the corresponding
10
+ # store, usually it's best to keep it to things that are safe to Marshal.
11
+ #
12
+ # The default time of expiration is *
13
+ #
14
+ # Time.at(2147483647) # => Tue Jan 19 12:14:07 +0900 2038
15
+ #
16
+ # Hopefully we all have 64bit systems by then.
17
+
18
+ class Session
19
+ include Optioned
20
+
21
+ options.dsl do
22
+ o "Key for the session cookie",
23
+ :key, 'innate.sid'
24
+ o "Domain the cookie relates to, unspecified if false",
25
+ :domain, false
26
+ o "Path the cookie relates to",
27
+ :path, '/'
28
+ o "Use secure cookie",
29
+ :secure, false
30
+ o "Time of cookie expiration",
31
+ :expires, Time.at((1 << 31) - 1)
32
+ end
33
+
34
+ attr_reader :cookie_set, :request, :response, :flash
35
+
36
+ def initialize(request, response)
37
+ @request, @response = request, response
38
+ @cookie_set = false
39
+ @cache_sid = nil
40
+ @flash = Flash.new(self)
41
+ end
42
+
43
+ def []=(key, value)
44
+ cache_sid[key] = value
45
+ end
46
+
47
+ def [](key)
48
+ cache_sid[key]
49
+ end
50
+
51
+ def delete(key)
52
+ cache_sid.delete(key)
53
+ end
54
+
55
+ def clear
56
+ cache.delete(sid)
57
+ @cache_sid = nil
58
+ end
59
+
60
+ def cache_sid
61
+ @cache_sid ||= cache[sid] || {}
62
+ end
63
+
64
+ def flush(response = @response)
65
+ return if !@cache_sid or @cache_sid.empty?
66
+
67
+ flash.rotate!
68
+ ttl = (Time.at(cookie_value[:expires]) - Time.now).to_i
69
+ cache.store(sid, cache_sid, :ttl => ttl)
70
+ set_cookie(response)
71
+ end
72
+
73
+ def sid
74
+ @sid ||= cookie || generate_sid
75
+ end
76
+
77
+ def cookie
78
+ @request.cookies[options.key]
79
+ end
80
+
81
+ def inspect
82
+ cache.inspect
83
+ end
84
+
85
+ private
86
+
87
+ def cache
88
+ Innate::Cache.session
89
+ end
90
+
91
+ def set_cookie(response)
92
+ return if @cookie_set || cookie
93
+
94
+ @cookie_set = true
95
+ response.set_cookie(options.key, cookie_value)
96
+ end
97
+
98
+ def cookie_value
99
+ { :value => sid,
100
+ :domain => options.domain,
101
+ :path => options.path,
102
+ :secure => options.secure,
103
+ :expires => options.expires }
104
+ end
105
+
106
+ def generate_sid
107
+ begin sid = sid_algorithm end while cache[sid]
108
+ sid
109
+ end
110
+
111
+ begin
112
+ require 'securerandom'
113
+ def sid_algorithm; SecureRandom.hex(32); end
114
+ rescue LoadError
115
+ def sid_algorithm
116
+ entropy = [ srand, rand, Time.now.to_f, rand, $$, rand, object_id ]
117
+ Digest::SHA2.hexdigest(entropy.join)
118
+ end
119
+ end
120
+ end
121
+ 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
+ begin; require 'rubygems'; rescue LoadError; end
2
+
3
+ require 'bacon'
4
+ require 'rack/test'
5
+ require(File.expand_path("#{__FILE__}/../")) unless defined?(Innate)
6
+
7
+ Bacon.summary_on_exit
8
+
9
+ module Innate
10
+ # minimal middleware, no exception handling
11
+ middleware(:spec){|m| m.innate }
12
+
13
+ # skip starting adapter
14
+ options.started = true
15
+ options.mode = :spec
16
+ end
17
+
18
+ shared :mock do
19
+ Innate.setup_dependencies
20
+ extend Rack::Test::Methods
21
+
22
+ def app; Innate.middleware; end
23
+ end
@@ -0,0 +1,27 @@
1
+ module Innate
2
+ begin
3
+ require 'innate/state/fiber'
4
+ STATE = State::Fiber.new
5
+ rescue LoadError
6
+ require 'innate/state/thread'
7
+ STATE = State::Thread.new
8
+ end
9
+
10
+ module SingletonMethods
11
+ # Use this method to achieve thread-safety for sensitive operations.
12
+ #
13
+ # This should be of most use when manipulating files to prevent other
14
+ # threads from doing the same, no other code will be scheduled during
15
+ # execution of this method.
16
+ #
17
+ # @param [Proc] block the things you want to execute
18
+ # @see State::Thread#sync State::Fiber#sync
19
+ def sync(&block)
20
+ STATE.sync(&block)
21
+ end
22
+
23
+ def defer(&block)
24
+ STATE.defer(&block)
25
+ end
26
+ end
27
+ 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,74 @@
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
+ class Fiber < ::Fiber
13
+ attr_accessor :state
14
+
15
+ def initialize(*args)
16
+ super
17
+ @state = {}
18
+ end
19
+
20
+ def [](key)
21
+ state[key]
22
+ end
23
+
24
+ def []=(key, value)
25
+ state[key] = value
26
+ end
27
+
28
+ def key?(key)
29
+ state.key?(key)
30
+ end
31
+
32
+ def keys
33
+ state.keys
34
+ end
35
+ end
36
+
37
+ module State
38
+ # Our accessor to the currently active Fiber, an instance of
39
+ # Innate::State::Fiber will be assigned to Innate::STATE if fibers are
40
+ # available.
41
+ class Fiber
42
+ def [](key)
43
+ ::Fiber.current[key]
44
+ end
45
+
46
+ def []=(key, value)
47
+ ::Fiber.current[key] = value
48
+ end
49
+
50
+ # We don't use Fiber in a concurrent manner and they don't run
51
+ # concurrently by themselves, so we directly #resume the Fiber to get the
52
+ # return value of +block+.
53
+
54
+ def wrap(&block)
55
+ Innate::Fiber.new(&block).resume
56
+ end
57
+
58
+ # In an environment where only Fiber is used there is no concurrency, so
59
+ # we don't have to lock with a Mutex.
60
+
61
+ def sync
62
+ yield
63
+ end
64
+
65
+ def defer
66
+ a = Innate::Fiber.current
67
+ ::Thread.new do
68
+ b = Innate::Fiber.new{ a.keys.each{|k| b[k] = a[k] }; yield }
69
+ b.resume
70
+ end
71
+ end
72
+ end
73
+ end
74
+ end