innate 2009.04

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 (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