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,37 @@
1
+ module Innate
2
+ # this has to be run after a couple of other files have been required
3
+
4
+ options.dsl do
5
+ o "Innate::start will not start an adapter if true",
6
+ :started, false
7
+
8
+ o "Will send ::setup to each element during Innate::start",
9
+ :setup, [Innate::Cache, Innate::Node]
10
+
11
+ o "Trap this signal to issue shutdown, nil/false to disable trap",
12
+ :trap, 'SIGINT'
13
+
14
+ o "The compiler for middleware",
15
+ :middleware_compiler, Innate::MiddlewareCompiler
16
+
17
+ o "Indicates which default middleware to use, (:dev|:live)",
18
+ :mode, :dev
19
+
20
+ o "The directories this application resides in",
21
+ :roots, [File.dirname($0)]
22
+
23
+ o "The directories containing static files to be served",
24
+ :publics, ['public']
25
+
26
+ o "Directories containing the view templates",
27
+ :views, ['view']
28
+
29
+ o "Directories containing the layout templates",
30
+ :layouts, ['layout']
31
+
32
+ o "Prefix used to create relative links",
33
+ :prefix, '/'
34
+
35
+ trigger(:mode){|v| Innate.middleware_recompile(v) }
36
+ end
37
+ end
@@ -0,0 +1,202 @@
1
+ module Innate
2
+ # Provides a minimal DSL to describe options with defaults and metadata.
3
+ #
4
+ # The example below should demonstrate the major features, note that key
5
+ # lookup wanders up the hierarchy until there is a match found or the parent
6
+ # of the Options class is itself, in which case nil will be returned.
7
+ #
8
+ # Usage:
9
+ #
10
+ # class Calculator
11
+ # @options = Options.new(:foo)
12
+ # def self.options; @options; end
13
+ #
14
+ # options.dsl do
15
+ # o "Which method to use", :method, :plus
16
+ # o "Default arguments", :args, [1, 2]
17
+ # sub(:minus){ o("Default arguments", :args, [4, 3]) }
18
+ # end
19
+ #
20
+ # def self.calculate(method = nil, *args)
21
+ # method ||= options[:method]
22
+ # args = args.empty? ? options[method, :args] : args
23
+ # self.send(method, *args)
24
+ # end
25
+ #
26
+ # def self.plus(n1, n2)
27
+ # n1 + n2
28
+ # end
29
+ #
30
+ # def self.minus(n1, n2)
31
+ # n1 - n2
32
+ # end
33
+ # end
34
+ #
35
+ # Calculator.calculate
36
+ # # => 3
37
+ # Calculator.options[:method] = :minus
38
+ # # => :minus
39
+ # Calculator.calculate
40
+ # # => 1
41
+ # Calculator.calculate(:plus, 4, 5)
42
+ # # => 9
43
+ #
44
+ class Options
45
+ def initialize(name, parent = self)
46
+ @name, @parent, = name, parent
47
+ @hash = {}
48
+ yield(self) if block_given?
49
+ end
50
+
51
+ # Shortcut for instance_eval
52
+ def dsl(&block)
53
+ instance_eval(&block) if block
54
+ self
55
+ end
56
+
57
+ # Create a new Options instance with +name+ and pass +block+ on to its #dsl.
58
+ # Assigns the new instance to the +name+ Symbol on current instance.
59
+ def sub(name, &block)
60
+ name = name.to_sym
61
+
62
+ case found = @hash[name]
63
+ when Options
64
+ found.dsl(&block)
65
+ else
66
+ found = @hash[name] = Options.new(name, self).dsl(&block)
67
+ end
68
+
69
+ found
70
+ end
71
+
72
+ # Store an option in the Options instance.
73
+ #
74
+ # @param [#to_s] doc describing the purpose of this option
75
+ # @param [#to_sym] key used to access
76
+ # @param [Object] value may be anything
77
+ # @param [Hash] other optional Hash containing meta-data
78
+ # :doc, :value keys will be ignored
79
+ def option(doc, key, value, other = {}, &block)
80
+ trigger = block || other[:trigger]
81
+ convert = {:doc => doc.to_s, :value => value, :trigger => trigger}
82
+ @hash[key.to_sym] = other.merge(convert)
83
+ end
84
+ alias o option
85
+
86
+ # To avoid lookup on the parent, we can set a default to the internal Hash.
87
+ # Parameters as in {Options#o}, but without the +key+.
88
+ def default(doc, value, other = {})
89
+ @hash.default = other.merge(:doc => doc, :value => value)
90
+ end
91
+
92
+ # Add a block that will be called when a new value is set.
93
+ def trigger(key, &block)
94
+ @hash[key.to_sym][:trigger] = block
95
+ end
96
+
97
+ # Try to retrieve the corresponding Hash for the passed keys, will try to
98
+ # retrieve the key from a parent if no match is found on the current
99
+ # instance. If multiple keys are passed it will try to find a matching
100
+ # child and pass the request on to it.
101
+ def get(key, *keys)
102
+ if keys.empty?
103
+ if value = @hash[key.to_sym]
104
+ value
105
+ elsif @parent != self
106
+ @parent.get(key)
107
+ else
108
+ nil
109
+ end
110
+ elsif sub_options = get(key)
111
+ sub_options.get(*keys)
112
+ end
113
+ end
114
+
115
+ # @param [Array] keys
116
+ # @param [Object] value
117
+ def set_value(keys, value)
118
+ get(*keys)[:value] = value
119
+ end
120
+
121
+ # Retrieve only the :value from the value hash if found via +keys+.
122
+ def [](*keys)
123
+ if value = get(*keys)
124
+ value.is_a?(Hash) ? value[:value] : value
125
+ end
126
+ end
127
+
128
+ # Assign new :value to the value hash on the current instance.
129
+ #
130
+ # TODO: allow arbitrary assignments
131
+ def []=(key, value)
132
+ if ns = @hash[key.to_sym]
133
+ ns[:value] = value
134
+ ns[:trigger].call(value) if ns[:trigger].respond_to?(:call)
135
+ elsif existing = get(key)
136
+ option(existing[:doc].to_s.dup, key, value)
137
+ else
138
+ raise(ArgumentError, "No key for %p exists" % [key])
139
+ end
140
+ end
141
+
142
+ def method_missing(meth, *args)
143
+ case meth.to_s
144
+ when /^(.*)=$/
145
+ self[$1] = args.first
146
+ else
147
+ self[meth]
148
+ end
149
+ end
150
+
151
+ def merge!(hash)
152
+ hash.each_pair do |key, value|
153
+ set_value(key.to_s.split('.'), value)
154
+ end
155
+ end
156
+
157
+ def to_hash
158
+ @hash
159
+ end
160
+
161
+ def each_option(&block)
162
+ @hash.each(&block)
163
+ end
164
+
165
+ def each_pair
166
+ @hash.each do |key, values|
167
+ yield(key, self[key])
168
+ end
169
+ end
170
+
171
+ def inspect
172
+ @hash.inspect
173
+ end
174
+
175
+ def pretty_print(q)
176
+ q.pp_hash @hash
177
+ end
178
+ end
179
+
180
+ # extend your class with this
181
+ module Optioned
182
+ def self.included(into)
183
+ into.extend(SingletonMethods)
184
+
185
+ snaked = into.name.split('::').last
186
+ snaked = snaked.gsub(/\B[A-Z][^A-Z]/, '_\&').downcase.gsub(' ', '_')
187
+
188
+ options = Innate.options.sub(snaked)
189
+ into.instance_variable_set(:@options, options)
190
+ end
191
+
192
+ module SingletonMethods
193
+ attr_reader :options
194
+ end
195
+
196
+ private
197
+
198
+ def options
199
+ self.class.options
200
+ end
201
+ end
202
+ end
@@ -0,0 +1,7 @@
1
+ module Innate
2
+ # this is just a stub so other modules can register their options in here,
3
+ # the real options are set in lib/innate.rb
4
+
5
+ class << self; attr_reader :options; end
6
+ @options = Options.new('Innate')
7
+ end
@@ -0,0 +1,141 @@
1
+ module Innate
2
+
3
+ # Subclass Rack::Request and add some convenient methods.
4
+ #
5
+ # An instance is available via the #request method in your Node.
6
+ #
7
+ # NOTE:
8
+ # Please make sure to read the documentation of Rack::Request together with
9
+ # this, as there are a lot of features available.
10
+ #
11
+ # A list of methods from Rack::Request so you get a gist of it:
12
+ #
13
+ # ## Generally
14
+ #
15
+ # * body
16
+ # * cookies
17
+ # * env
18
+ # * fullpath
19
+ # * host
20
+ # * port
21
+ # * scheme
22
+ # * url
23
+ #
24
+ # ## ENV shortcuts
25
+ #
26
+ # * accept_encoding
27
+ # * content_charset
28
+ # * content_length
29
+ # * content_type
30
+ # * ip
31
+ # * media_type
32
+ # * media_type_params
33
+ # * path_info
34
+ # * path_info=
35
+ # * query_string
36
+ # * referrer
37
+ # * script_name
38
+ # * script_name=
39
+ # * xhr?
40
+ #
41
+ # ## Query request method
42
+ #
43
+ # * delete?
44
+ # * get?
45
+ # * head?
46
+ # * post?
47
+ # * put?
48
+ # * request_method
49
+ #
50
+ # ## parameter handling
51
+ #
52
+ # * []
53
+ # * []=
54
+ # * form_data?
55
+ # * params
56
+ # * values_at
57
+
58
+ class Request < Rack::Request
59
+ # Currently handled request from Innate::STATE[:request]
60
+ # Call it from anywhere via Innate::Request.current
61
+ def self.current
62
+ Current.request
63
+ end
64
+
65
+ # Let's allow #[] to act like #values_at.
66
+ #
67
+ # Usage given a GET request like /hey?foo=duh&bar=doh
68
+ #
69
+ # request[:foo, :bar] # => ['duh', 'doh']
70
+ #
71
+ # Both +value+ and the elements of +keys+ will be turned into String by #to_s.
72
+ def [](value, *keys)
73
+ return super(value) if keys.empty?
74
+ [value, *keys].map{|key| super(key) }
75
+ end
76
+
77
+ # the full request URI provided by Rack::Request
78
+ # e.g. "http://localhost:7000/controller/action?foo=bar.xhtml"
79
+ def request_uri
80
+ env['REQUEST_URI'] || env['PATH_INFO']
81
+ end
82
+
83
+ # Answers with a subset of request.params with only the key/value pairs for
84
+ # which you pass the keys.
85
+ # Valid keys are objects that respond to :to_s
86
+ #
87
+ # @example usage
88
+ #
89
+ # request.params
90
+ # # => {'name' => 'jason', 'age' => '45', 'job' => 'lumberjack'}
91
+ # request.subset('name')
92
+ # # => {'name' => 'jason'}
93
+ # request.subset(:name, :job)
94
+ # # => {'name' => 'jason', 'job' => 'lumberjack'}
95
+
96
+ def subset(*keys)
97
+ keys = keys.map{|key| key.to_s }
98
+ params.reject{|key, value| not keys.include?(key) }
99
+ end
100
+
101
+ # Try to figure out the domain we are running on, this might work for some
102
+ # deployments but fail for others, given the combination of servers in
103
+ # front.
104
+ #
105
+ # @example usage
106
+ #
107
+ # domain
108
+ # # => #<URI::HTTPS:0xb769ecb0 URL:https://localhost:7000/>
109
+ # domain('/foo')
110
+ # # => #<URI::HTTPS:0xb769ecb0 URL:https://localhost:7000/foo>
111
+ #
112
+ # @param [#to_s] path
113
+ #
114
+ # @return [URI]
115
+ #
116
+ # @api external
117
+ # @author manveru
118
+ def domain(path = nil, options = {})
119
+ uri = URI(self.url)
120
+ uri.path = path.to_s if path
121
+ uri.query = nil unless options[:keep_query]
122
+ uri
123
+ end
124
+
125
+ ipv4 = %w[ 127.0.0.1/32 192.168.0.0/16 172.16.0.0/12 10.0.0.0/8 169.254.0.0/16 ]
126
+ ipv6 = %w[ fc00::/7 fe80::/10 fec0::/10 ::1 ]
127
+ LOCAL = (ipv4 + ipv6).map{|a| IPAddr.new(a)} unless defined?(LOCAL)
128
+
129
+ # Request is from a local network?
130
+ # Checks both IPv4 and IPv6
131
+ # Answer is true if the IP address making the request is from local network.
132
+ # Optional argument address can be used to check any IP address.
133
+
134
+ def local_net?(address = ip)
135
+ addr = IPAddr.new(address)
136
+ LOCAL.find{|range| range.include?(addr) }
137
+ rescue ArgumentError => ex
138
+ raise ArgumentError, ex unless ex.message == 'invalid address'
139
+ end
140
+ end
141
+ end
@@ -0,0 +1,23 @@
1
+ module Innate
2
+ class Response < Rack::Response
3
+ include Optioned
4
+
5
+ options.dsl do
6
+ o "Default headers, will not override headers already set",
7
+ :headers, {'Content-Type' => 'text/html'}
8
+ end
9
+
10
+ def reset
11
+ self.status = 200
12
+ self.header.delete('Content-Type')
13
+ body.clear
14
+ self.length = 0
15
+ self
16
+ end
17
+
18
+ def finish
19
+ options.headers.each{|key, value| self[key] ||= value }
20
+ super
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,110 @@
1
+ module Innate
2
+ # Innate support simple routing using string, regex and lambda based routers.
3
+ # Route are stored in a dictionary, which supports hash-like access but
4
+ # preserves order, so routes are evaluated in the order they are added.
5
+ #
6
+ # This middleware should wrap Innate::DynaMap.
7
+ #
8
+ # String routers are the simplest way to route in Innate. One path is
9
+ # translated into another:
10
+ #
11
+ # Innate::Route[ '/foo' ] = '/bar'
12
+ # '/foo' => '/bar'
13
+ #
14
+ # Regex routers allow matching against paths using regex. Matches within
15
+ # your regex using () are substituted in the new path using printf-like
16
+ # syntax.
17
+ #
18
+ # Innate::Route[ %r!^/(\d+)\.te?xt$! ] = "/text/%d"
19
+ # '/123.txt' => '/text/123'
20
+ # '/789.text' => '/text/789'
21
+ #
22
+ # For more complex routing, lambda routers can be used. Lambda routers are
23
+ # passed in the current path and request object, and must return either a new
24
+ # path string, or nil.
25
+ #
26
+ # Innate::Route[ 'name of route' ] = lambda{ |path, request|
27
+ # '/bar' if path == '/foo' and request[:bar] == '1'
28
+ # }
29
+ # '/foo' => '/foo'
30
+ # '/foo?bar=1' => '/bar'
31
+ #
32
+ # Lambda routers can also use this alternative syntax:
33
+ #
34
+ # Innate::Route('name of route') do |path, request|
35
+ # '/bar' if path == '/foo' and request[:bar] == '1'
36
+ # end
37
+ #
38
+ # NOTE: Use self::ROUTES notation in singleton methods to force correct
39
+ # lookup.
40
+
41
+ class Route
42
+ ROUTES = []
43
+
44
+ def self.[](key)
45
+ found = self::ROUTES.assoc(key)
46
+ found[1] if found
47
+ end
48
+
49
+ def self.[]=(key, value)
50
+ self::ROUTES.delete_if{|k,v| k == key }
51
+ self::ROUTES << [key, value]
52
+ end
53
+
54
+ def self.clear
55
+ self::ROUTES.clear
56
+ end
57
+
58
+ def initialize(app = Innate::DynaMap)
59
+ @app = app
60
+ end
61
+
62
+ def call(env)
63
+ path = env['PATH_INFO']
64
+ path << '/' if path.empty?
65
+
66
+ if modified = resolve(path)
67
+ Log.debug("%s routes %p to %p" % [self.class.name, path, modified])
68
+ env['PATH_INFO'] = modified
69
+ end
70
+
71
+ @app.call(env)
72
+ end
73
+
74
+ def resolve(path)
75
+ self.class::ROUTES.each do |key, value|
76
+ if key.is_a?(Regexp)
77
+ md = path.match(key)
78
+ return value % md.to_a[1..-1] if md
79
+
80
+ elsif value.respond_to?(:call)
81
+ new_path = value.call(path, Current.request)
82
+ return new_path if new_path
83
+
84
+ elsif value.respond_to?(:to_str)
85
+ return value.to_str if path == key
86
+
87
+ else
88
+ Log.error("Invalid route %p => %p" % [key, value])
89
+ end
90
+ end
91
+
92
+ nil
93
+ end
94
+ end
95
+
96
+ # Identical with Innate::Route, but is called before any Node::call is made
97
+ class Rewrite < Route
98
+ ROUTES = []
99
+ end
100
+
101
+ module SingletonMethods
102
+ def Route(key, value = nil, &block)
103
+ Route[key] = value || block
104
+ end
105
+
106
+ def Rewrite(key, value = nil, &block)
107
+ Rewrite[key] = value || block
108
+ end
109
+ end
110
+ end