rjspotter-innate 2009.06.29

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/AUTHORS +10 -0
  2. data/CHANGELOG +3261 -0
  3. data/COPYING +18 -0
  4. data/MANIFEST +127 -0
  5. data/README.md +563 -0
  6. data/Rakefile +39 -0
  7. data/example/app/retro_games.rb +60 -0
  8. data/example/app/todo/layout/default.xhtml +11 -0
  9. data/example/app/todo/spec/todo.rb +63 -0
  10. data/example/app/todo/start.rb +51 -0
  11. data/example/app/todo/view/index.xhtml +39 -0
  12. data/example/app/whywiki_erb/layout/wiki.html.erb +15 -0
  13. data/example/app/whywiki_erb/spec/wiki.rb +19 -0
  14. data/example/app/whywiki_erb/start.rb +42 -0
  15. data/example/app/whywiki_erb/view/edit.erb +6 -0
  16. data/example/app/whywiki_erb/view/index.erb +12 -0
  17. data/example/custom_middleware.rb +35 -0
  18. data/example/hello.rb +11 -0
  19. data/example/howto_spec.rb +35 -0
  20. data/example/link.rb +27 -0
  21. data/example/provides.rb +31 -0
  22. data/example/session.rb +38 -0
  23. data/innate.gemspec +41 -0
  24. data/lib/innate.rb +269 -0
  25. data/lib/innate/action.rb +137 -0
  26. data/lib/innate/adapter.rb +76 -0
  27. data/lib/innate/cache.rb +134 -0
  28. data/lib/innate/cache/api.rb +128 -0
  29. data/lib/innate/cache/drb.rb +58 -0
  30. data/lib/innate/cache/file_based.rb +44 -0
  31. data/lib/innate/cache/marshal.rb +20 -0
  32. data/lib/innate/cache/memory.rb +21 -0
  33. data/lib/innate/cache/yaml.rb +20 -0
  34. data/lib/innate/current.rb +35 -0
  35. data/lib/innate/dynamap.rb +96 -0
  36. data/lib/innate/helper.rb +185 -0
  37. data/lib/innate/helper/aspect.rb +124 -0
  38. data/lib/innate/helper/cgi.rb +54 -0
  39. data/lib/innate/helper/flash.rb +36 -0
  40. data/lib/innate/helper/link.rb +94 -0
  41. data/lib/innate/helper/redirect.rb +85 -0
  42. data/lib/innate/helper/render.rb +152 -0
  43. data/lib/innate/helper/send_file.rb +26 -0
  44. data/lib/innate/log.rb +20 -0
  45. data/lib/innate/log/color_formatter.rb +49 -0
  46. data/lib/innate/log/hub.rb +77 -0
  47. data/lib/innate/middleware_compiler.rb +65 -0
  48. data/lib/innate/mock.rb +49 -0
  49. data/lib/innate/node.rb +1029 -0
  50. data/lib/innate/options.rb +37 -0
  51. data/lib/innate/options/dsl.rb +205 -0
  52. data/lib/innate/options/stub.rb +7 -0
  53. data/lib/innate/request.rb +141 -0
  54. data/lib/innate/response.rb +24 -0
  55. data/lib/innate/route.rb +114 -0
  56. data/lib/innate/session.rb +133 -0
  57. data/lib/innate/session/flash.rb +94 -0
  58. data/lib/innate/spec.rb +1 -0
  59. data/lib/innate/spec/bacon.rb +28 -0
  60. data/lib/innate/state.rb +26 -0
  61. data/lib/innate/state/accessor.rb +130 -0
  62. data/lib/innate/traited.rb +90 -0
  63. data/lib/innate/trinity.rb +18 -0
  64. data/lib/innate/version.rb +3 -0
  65. data/lib/innate/view.rb +97 -0
  66. data/lib/innate/view/erb.rb +14 -0
  67. data/lib/innate/view/etanni.rb +33 -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 +121 -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 +115 -0
  86. data/spec/innate/helper/link.rb +139 -0
  87. data/spec/innate/helper/redirect.rb +171 -0
  88. data/spec/innate/helper/render.rb +165 -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/modes.rb +61 -0
  98. data/spec/innate/node/mapping.rb +37 -0
  99. data/spec/innate/node/node.rb +135 -0
  100. data/spec/innate/node/resolve.rb +82 -0
  101. data/spec/innate/node/view/another_layout/another_layout.xhtml +3 -0
  102. data/spec/innate/node/view/bar.xhtml +1 -0
  103. data/spec/innate/node/view/foo.html.xhtml +1 -0
  104. data/spec/innate/node/view/only_view.xhtml +1 -0
  105. data/spec/innate/node/view/with_layout.xhtml +1 -0
  106. data/spec/innate/node/wrap_action_call.rb +83 -0
  107. data/spec/innate/options.rb +123 -0
  108. data/spec/innate/parameter.rb +154 -0
  109. data/spec/innate/provides.rb +99 -0
  110. data/spec/innate/provides/list.html.xhtml +1 -0
  111. data/spec/innate/provides/list.txt.xhtml +1 -0
  112. data/spec/innate/request.rb +79 -0
  113. data/spec/innate/route.rb +135 -0
  114. data/spec/innate/session.rb +58 -0
  115. data/spec/innate/traited.rb +55 -0
  116. data/tasks/authors.rake +30 -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_setup.rake +99 -0
  121. data/tasks/grancher.rake +12 -0
  122. data/tasks/manifest.rake +4 -0
  123. data/tasks/rcov.rake +19 -0
  124. data/tasks/release.rake +53 -0
  125. data/tasks/reversion.rake +8 -0
  126. data/tasks/setup.rake +6 -0
  127. data/tasks/ycov.rake +84 -0
  128. metadata +218 -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,205 @@
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}
82
+ convert[:trigger] = trigger if trigger
83
+ @hash[key.to_sym] = other.merge(convert)
84
+ end
85
+ alias o option
86
+
87
+ # To avoid lookup on the parent, we can set a default to the internal Hash.
88
+ # Parameters as in {Options#o}, but without the +key+.
89
+ def default(doc, value, other = {})
90
+ @hash.default = other.merge(:doc => doc, :value => value)
91
+ end
92
+
93
+ # Add a block that will be called when a new value is set.
94
+ def trigger(key, &block)
95
+ @hash[key.to_sym][:trigger] = block
96
+ end
97
+
98
+ # Try to retrieve the corresponding Hash for the passed keys, will try to
99
+ # retrieve the key from a parent if no match is found on the current
100
+ # instance. If multiple keys are passed it will try to find a matching
101
+ # child and pass the request on to it.
102
+ def get(key, *keys)
103
+ if keys.empty?
104
+ if value = @hash[key.to_sym]
105
+ value
106
+ elsif @parent != self
107
+ @parent.get(key)
108
+ else
109
+ nil
110
+ end
111
+ elsif sub_options = get(key)
112
+ sub_options.get(*keys)
113
+ end
114
+ end
115
+
116
+ # @param [Array] keys
117
+ # @param [Object] value
118
+ def set_value(keys, value)
119
+ got = get(*keys)
120
+ return got[:value] = value if got
121
+ raise(IndexError, "There is no option available for %p" % [keys])
122
+ end
123
+
124
+ # Retrieve only the :value from the value hash if found via +keys+.
125
+ def [](*keys)
126
+ if value = get(*keys)
127
+ value.is_a?(Hash) ? value[:value] : value
128
+ end
129
+ end
130
+
131
+ # Assign new :value to the value hash on the current instance.
132
+ #
133
+ # TODO: allow arbitrary assignments
134
+ def []=(key, value)
135
+ if ns = @hash[key.to_sym]
136
+ ns[:value] = value
137
+ ns[:trigger].call(value) if ns[:trigger].respond_to?(:call)
138
+ elsif existing = get(key)
139
+ option(existing[:doc].to_s.dup, key, value)
140
+ else
141
+ raise(ArgumentError, "No key for %p exists" % [key])
142
+ end
143
+ end
144
+
145
+ def method_missing(meth, *args)
146
+ case meth.to_s
147
+ when /^(.*)=$/
148
+ self[$1] = args.first
149
+ else
150
+ self[meth]
151
+ end
152
+ end
153
+
154
+ def merge!(hash)
155
+ hash.each_pair do |key, value|
156
+ set_value(key.to_s.split('.'), value)
157
+ end
158
+ end
159
+
160
+ def to_hash
161
+ @hash
162
+ end
163
+
164
+ def each_option(&block)
165
+ @hash.each(&block)
166
+ end
167
+
168
+ def each_pair
169
+ @hash.each do |key, values|
170
+ yield(key, self[key])
171
+ end
172
+ end
173
+
174
+ def inspect
175
+ @hash.inspect
176
+ end
177
+
178
+ def pretty_print(q)
179
+ q.pp_hash @hash
180
+ end
181
+ end
182
+
183
+ # extend your class with this
184
+ module Optioned
185
+ def self.included(into)
186
+ into.extend(SingletonMethods)
187
+
188
+ snaked = into.name.split('::').last
189
+ snaked = snaked.gsub(/\B[A-Z][^A-Z]/, '_\&').downcase.gsub(' ', '_')
190
+
191
+ options = Innate.options.sub(snaked)
192
+ into.instance_variable_set(:@options, options)
193
+ end
194
+
195
+ module SingletonMethods
196
+ attr_reader :options
197
+ end
198
+
199
+ private
200
+
201
+ def options
202
+ self.class.options
203
+ end
204
+ end
205
+ 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 Thread.current[: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,24 @@
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
+ Current.session.flush(self)
21
+ super
22
+ end
23
+ end
24
+ end
@@ -0,0 +1,114 @@
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
+ # Please note that Rack::File is put before Route and Rewrite, that means
9
+ # that you cannot apply routes to static files unless you add your own route
10
+ # middleware before.
11
+ #
12
+ # String routers are the simplest way to route in Innate. One path is
13
+ # translated into another:
14
+ #
15
+ # Innate::Route[ '/foo' ] = '/bar'
16
+ # '/foo' => '/bar'
17
+ #
18
+ # Regex routers allow matching against paths using regex. Matches within
19
+ # your regex using () are substituted in the new path using printf-like
20
+ # syntax.
21
+ #
22
+ # Innate::Route[ %r!^/(\d+)\.te?xt$! ] = "/text/%d"
23
+ # '/123.txt' => '/text/123'
24
+ # '/789.text' => '/text/789'
25
+ #
26
+ # For more complex routing, lambda routers can be used. Lambda routers are
27
+ # passed in the current path and request object, and must return either a new
28
+ # path string, or nil.
29
+ #
30
+ # Innate::Route[ 'name of route' ] = lambda{ |path, request|
31
+ # '/bar' if path == '/foo' and request[:bar] == '1'
32
+ # }
33
+ # '/foo' => '/foo'
34
+ # '/foo?bar=1' => '/bar'
35
+ #
36
+ # Lambda routers can also use this alternative syntax:
37
+ #
38
+ # Innate::Route('name of route') do |path, request|
39
+ # '/bar' if path == '/foo' and request[:bar] == '1'
40
+ # end
41
+ #
42
+ # NOTE: Use self::ROUTES notation in singleton methods to force correct
43
+ # lookup.
44
+
45
+ class Route
46
+ ROUTES = []
47
+
48
+ def self.[](key)
49
+ found = self::ROUTES.assoc(key)
50
+ found[1] if found
51
+ end
52
+
53
+ def self.[]=(key, value)
54
+ self::ROUTES.delete_if{|k,v| k == key }
55
+ self::ROUTES << [key, value]
56
+ end
57
+
58
+ def self.clear
59
+ self::ROUTES.clear
60
+ end
61
+
62
+ def initialize(app = Innate::DynaMap)
63
+ @app = app
64
+ end
65
+
66
+ def call(env)
67
+ path = env['PATH_INFO']
68
+ path << '/' if path.empty?
69
+
70
+ if modified = resolve(path)
71
+ Log.debug("%s routes %p to %p" % [self.class.name, path, modified])
72
+ env['PATH_INFO'] = modified
73
+ end
74
+
75
+ @app.call(env)
76
+ end
77
+
78
+ def resolve(path)
79
+ self.class::ROUTES.each do |key, value|
80
+ if key.is_a?(Regexp)
81
+ md = path.match(key)
82
+ return value % md.to_a[1..-1] if md
83
+
84
+ elsif value.respond_to?(:call)
85
+ new_path = value.call(path, Current.request)
86
+ return new_path if new_path
87
+
88
+ elsif value.respond_to?(:to_str)
89
+ return value.to_str if path == key
90
+
91
+ else
92
+ Log.error("Invalid route %p => %p" % [key, value])
93
+ end
94
+ end
95
+
96
+ nil
97
+ end
98
+ end
99
+
100
+ # Identical with Innate::Route, but is called before any Node::call is made
101
+ class Rewrite < Route
102
+ ROUTES = []
103
+ end
104
+
105
+ module SingletonMethods
106
+ def Route(key, value = nil, &block)
107
+ Route[key] = value || block
108
+ end
109
+
110
+ def Rewrite(key, value = nil, &block)
111
+ Rewrite[key] = value || block
112
+ end
113
+ end
114
+ end