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,17 @@
1
+ require 'yaml/store'
2
+
3
+ module Innate
4
+ class Cache
5
+ # Keeps every cache in a separate file like this:
6
+ #
7
+ # /tmp/innate-cache-yaml/delta-manveru-session.yaml
8
+ class YAML
9
+ include Cache::API
10
+ include Cache::FileBased
11
+
12
+ STORE = ::YAML::Store
13
+ DIR = 'innate-cache-yaml'
14
+ EXT = '.yaml'
15
+ end
16
+ end
17
+ end
@@ -0,0 +1,9 @@
1
+ class BasicObject
2
+ # Remove all but these methods
3
+ # NOTE: __id__ is not there in 1.9, but would give a warning in 1.8
4
+ KEEP = %w[== equal? ! != instance_eval instance_exec __send__ __id__]
5
+
6
+ (instance_methods - KEEP).each do |im|
7
+ undef_method(im)
8
+ end
9
+ end unless defined?(BasicObject)
@@ -0,0 +1,3 @@
1
+ class String # Dirty hack, but Rack needs it?
2
+ alias each each_line unless 'String'.respond_to?(:each)
3
+ end
@@ -0,0 +1,37 @@
1
+ require 'innate/request'
2
+ require 'innate/response'
3
+
4
+ module Innate
5
+ # Uses STATE to scope request/response/session per Fiber/Thread so we can
6
+ # reach them from anywhere in the code without passing around the objects
7
+ # directly.
8
+ class Current
9
+ extend Trinity
10
+
11
+ def initialize(app, *rest)
12
+ if rest.empty?
13
+ @app = app
14
+ else
15
+ @app = Rack::Cascade.new([app, *rest])
16
+ end
17
+ end
18
+
19
+ # Wrap into STATE, run setup and call the app inside STATE.
20
+
21
+ def call(env)
22
+ STATE.wrap do
23
+ setup(env)
24
+ @app.call(env)
25
+ end
26
+ end
27
+
28
+ # Setup new Request/Response/Session for this request/response cycle
29
+
30
+ def setup(env)
31
+ req = STATE[:request] = Request.new(env)
32
+ res = STATE[:response] = Response.new
33
+ STATE[:actions] = []
34
+ STATE[:session] = Session.new(req, res)
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,81 @@
1
+ module Innate
2
+
3
+ # This is a dynamic routing mapper used to outsmart Rack::URLMap
4
+ # Every time a mapping is changed a new Rack::URLMap will be put into
5
+ # Innate::DynaMap::CACHE[:map]
6
+ class DynaMap
7
+ MAP = {}
8
+ CACHE = {}
9
+
10
+ # Delegate the call to the current Rack::URLMap instance.
11
+ #
12
+ # NOTE: Currently Rack::URLMap will destructively modify PATH_INFO and
13
+ # SCRIPT_NAME, which leads to incorrect routing as parts of the
14
+ # PATH_INFO are cut out if they matched once.
15
+ # Here I repair this damage and hope that my patch to rack will be
16
+ # accepted.
17
+ def self.call(env)
18
+ if app = CACHE[:map]
19
+ script_name, path_info = env['SCRIPT_NAME'], env['PATH_INFO']
20
+ answer = app.call(env)
21
+ env.merge!('SCRIPT_NAME' => script_name, 'PATH_INFO' => path_info)
22
+ answer
23
+ else
24
+ raise "Nothing mapped yet"
25
+ end
26
+ end
27
+
28
+ # Map node to location, create a new Rack::URLMap instance and cache it.
29
+ def self.map(location, node)
30
+ return unless location
31
+ MAP[location.to_s] = node
32
+ CACHE[:map] = Rack::URLMap.new(MAP)
33
+ end
34
+ end
35
+
36
+ module_function
37
+
38
+ # Maps the given +object+ or +block+ to +location+, +object+ must respond to
39
+ # #call in order to be of any use.
40
+ #
41
+ # Usage with passed +object+:
42
+ #
43
+ # Innate.map('/', lambda{|env| [200, {}, "Hello, World"] })
44
+ # Innate.at('/').call({}) # => [200, {}, "Hello, World"]
45
+ #
46
+ # Usage with passed +block+:
47
+ #
48
+ # Innate.map('/'){|env| [200, {}, ['Hello, World!']] }
49
+ # Innate.at('/').call({})
50
+ def map(location, object = nil, &block)
51
+ DynaMap.map(location, object || block)
52
+ end
53
+
54
+ # Answer with object at +location+.
55
+ #
56
+ # Usage:
57
+ #
58
+ # class Hello
59
+ # include Innate::Node
60
+ # map '/'
61
+ # end
62
+ #
63
+ # Innate.at('/') # => Hello
64
+ def at(location)
65
+ DynaMap::MAP[location.to_s]
66
+ end
67
+
68
+ # Returns one of the paths the given +object+ is mapped to.
69
+ #
70
+ # Usage:
71
+ #
72
+ # class Hello
73
+ # include Innate::Node
74
+ # map '/'
75
+ # end
76
+ #
77
+ # Innate.to(Hello) # => '/'
78
+ def to(object)
79
+ DynaMap::MAP.invert[object]
80
+ end
81
+ end
@@ -0,0 +1,195 @@
1
+ module Innate
2
+
3
+ # Acts as name-space for helpers
4
+ module Helper
5
+ # Public instance methods of helpers in here will be recognized as actions
6
+ LOOKUP = EXPOSE = Set.new
7
+
8
+ # Usually called from Innate::Node::included
9
+ # We also include Innate::Trinity here, as it may be needed in models when
10
+ # you use helper methods there.
11
+ def self.included(into)
12
+ into.extend(HelperAccess)
13
+ into.__send__(:include, Trinity)
14
+ end
15
+ end
16
+
17
+ # Provides access to #helper method without polluting the name-space any
18
+ # further.
19
+ module HelperAccess
20
+
21
+ # Convenience method used by Innate::Node.
22
+ #
23
+ # Usage:
24
+ #
25
+ # class Hi
26
+ # extend Innate::HelperAccess
27
+ # helper :cgi, :link, :aspect
28
+ # end
29
+ #
30
+ # This will require the helpers and call:
31
+ #
32
+ # Hi.include(Innate::Helper::CGI)
33
+ # Hi.include(Innate::Helper::Link)
34
+ # Hi.include(Innate::Helper::Aspect)
35
+ def helper(*helpers)
36
+ HelpersHelper.each_include(self, *helpers)
37
+ end
38
+ end
39
+
40
+ # Here come the utility methods used from the HelperAccess#helper method, we
41
+ # do this to keep method count at a minimum and because HelpersHelper is such
42
+ # an awesome name that just can't be wasted.
43
+ #
44
+ # Usage if you want to only extend with helpers:
45
+ #
46
+ # class Hi
47
+ # Innate::HelpersHelper.each_extend(self, :cgi, :link, :aspect)
48
+ # end
49
+ #
50
+ # Usage if you only want to include helpers:
51
+ #
52
+ # class Hi
53
+ # Innate::HelpersHelper.each_include(self, :cgi, :link, :aspect)
54
+ # end
55
+ #
56
+ # Usage for iteration:
57
+ #
58
+ # Innate::HelpersHelper.each(:cgi, :link, :aspect) do |mod|
59
+ # p mod
60
+ # end
61
+ #
62
+ # Usage for translating helpers to modules:
63
+ #
64
+ # p Innate::HelpersHelper.each(:cgi, :link, :aspect)
65
+ module HelpersHelper
66
+ EXTS = %w[rb so bundle]
67
+
68
+ # By default, lib/innate/ is added to the PATH, you may add your
69
+ # application root here so innate will look in your own helper/ directory.
70
+ PATH = []
71
+
72
+ # The namespaces that may container helper modules.
73
+ # Lookup is done from left to right.
74
+ POOL = []
75
+
76
+ # all of the following are singleton methods
77
+
78
+ module_function
79
+
80
+ def add_pool(pool)
81
+ POOL.unshift(pool)
82
+ POOL.uniq!
83
+ end
84
+
85
+ add_pool(Helper)
86
+
87
+ def add_path(path)
88
+ PATH.unshift(File.expand_path(path))
89
+ PATH.uniq!
90
+ end
91
+
92
+ add_path(File.dirname(__FILE__))
93
+ add_path('')
94
+
95
+ # Yield all the modules we can find for the given names of helpers, try to
96
+ # require them if not available.
97
+ #
98
+ # NOTE: Unlike usual #each, this will actually return an Array of the found
99
+ # modules instead of the given +*names+
100
+ #
101
+ #
102
+ # Usage:
103
+ #
104
+ # Innate::HelpersHelper.each(:cgi, :link, :aspect) do |mod|
105
+ # p mod
106
+ # end
107
+
108
+ def each(*names)
109
+ names.map do |name|
110
+ if name.class == Module
111
+ yield(name) if block_given?
112
+ name
113
+ elsif mod = get(name)
114
+ yield(mod) if block_given?
115
+ mod
116
+ elsif try_require(name)
117
+ redo
118
+ else
119
+ raise LoadError, "Helper #{name} not found"
120
+ end
121
+ end
122
+ end
123
+
124
+ # Shortcut to extend +into+ with Helper modules corresponding to +*names+.
125
+ # +into+ has to respond to #extend.
126
+ #
127
+ # Usage:
128
+ #
129
+ # class Hi
130
+ # Innate::HelpersHelper.each_extend(self, :cgi, :link, :aspect)
131
+ # end
132
+ def each_extend(into, *names, &block)
133
+ return if names.empty?
134
+ into.extend(*each(*names, &block))
135
+ end
136
+
137
+ # Shortcut to include Helper modules corresponding to +*names+ on +into+.
138
+ # +into+ has to respond to #include.
139
+ # #__send__(:include) is used in case #include raises due to being
140
+ # private/protected
141
+ #
142
+ # in case #include is a private/protected method.
143
+ #
144
+ # Usage:
145
+ #
146
+ # class Hi
147
+ # Innate::HelpersHelper.each_include(self, :cgi, :link, :aspect)
148
+ # end
149
+ def each_include(into, *names, &block)
150
+ return if names.empty?
151
+ mods = each(*names, &block)
152
+ into.include(*mods)
153
+ rescue NoMethodError
154
+ into.__send__(:include, *mods)
155
+ end
156
+
157
+ # Based on a simple set of rules we will first construct the most likely
158
+ # name for the helper and then grep the constants in the Innate::Helper
159
+ # module for any matches.
160
+ #
161
+ # helper :foo_bar # => FooBar
162
+ # helper :foo # => Foo
163
+ def get(name)
164
+ name = name.to_s.split('_').map{|e| e.capitalize}.join
165
+ POOL.each do |namespace|
166
+ if found = namespace.constants.grep(/^#{name}$/i).first
167
+ return namespace.const_get(found)
168
+ end
169
+ end
170
+
171
+ nil
172
+ end
173
+
174
+ # Figure out files that might have the helper we ask for and then require
175
+ # the first we find, if any.
176
+ def try_require(name)
177
+ if found = Dir[glob(name)].first
178
+ require File.expand_path(found)
179
+ else
180
+ raise LoadError, "Helper #{name} not found"
181
+ end
182
+ end
183
+
184
+ # Return a nice list of filenames in correct locations with correct
185
+ # filename-extensions.
186
+ def glob(name = '*')
187
+ "{#{paths.join(',')}}/helper/#{name}.{#{EXTS.join(',')}}"
188
+ end
189
+
190
+ # In case you want to do something better.
191
+ def paths
192
+ PATH
193
+ end
194
+ end
195
+ end
@@ -0,0 +1,62 @@
1
+ module Innate
2
+ module Helper
3
+
4
+ # Provides before/after wrappers for actions
5
+ module Aspect
6
+ AOP = Hash.new{|h,k| h[k] = Hash.new{|hh,kk| hh[kk] = {} }}
7
+
8
+ def self.included(into)
9
+ into.extend(SingletonMethods)
10
+ end
11
+
12
+ # Consider objects that have Aspect included
13
+ def self.ancestral_aop(from)
14
+ aop = {}
15
+ from.ancestors.reverse.map{|anc| aop.merge!(AOP[anc]) if anc < Aspect }
16
+ aop
17
+ end
18
+
19
+ def aspect_call(position, name)
20
+ return unless aop = Aspect.ancestral_aop(self.class)
21
+ return unless block = at_position = aop[position]
22
+
23
+ block = at_position[name.to_sym] unless at_position.is_a?(Proc)
24
+
25
+ instance_eval(&block) if block
26
+ end
27
+
28
+ def aspect_wrap(action)
29
+ return yield unless method = action.name
30
+
31
+ aspect_call(:before_all, method)
32
+ aspect_call(:before, method)
33
+ yield
34
+ aspect_call(:after, method)
35
+ aspect_call(:after_all, method)
36
+ end
37
+
38
+ module SingletonMethods
39
+ def before_all(&block)
40
+ AOP[self][:before_all] = block
41
+ end
42
+
43
+ def before(name, &block)
44
+ AOP[self][:before][name] = block
45
+ end
46
+
47
+ def after_all(&block)
48
+ AOP[self][:after_all] = block
49
+ end
50
+
51
+ def after(name, &block)
52
+ AOP[self][:after][name] = block
53
+ end
54
+
55
+ def wrap(name, &block)
56
+ before(name, &block)
57
+ after(name, &block)
58
+ end
59
+ end
60
+ end
61
+ end
62
+ end
@@ -0,0 +1,39 @@
1
+ autoload(:CGI, 'cgi') # in case you want to use html_unescape
2
+
3
+ module Innate
4
+
5
+ # Shortcuts to some CGI methods
6
+
7
+ module Helper
8
+ module CGI
9
+ # shortcut for Rack::Utils.escape
10
+ def url_encode(*args)
11
+ Rack::Utils.escape(*args.map{|a| a.to_s })
12
+ end
13
+
14
+ # shortcut for Rack::Utils.unescape
15
+ def url_decode(*args)
16
+ Rack::Utils.unescape(*args.map{|a| a.to_s })
17
+ end
18
+
19
+ # shortcut for Rack::Utils.escape_html
20
+ def html_escape(string)
21
+ Rack::Utils.escape_html(string)
22
+ end
23
+
24
+ # shortcut for CGI.unescapeHTML
25
+ def html_unescape(string)
26
+ ::CGI.unescapeHTML(string.to_s)
27
+ end
28
+
29
+ # safely escape all HTML and code
30
+ def h(string)
31
+ Rack::Utils.escape_html(string).gsub(/#([{@$]@?)/, '&#35;\1')
32
+ end
33
+
34
+ # one-letter versions help in case like #{h foo.inspect}
35
+ # ERb/ERuby/Rails compatible
36
+ alias u url_encode
37
+ end
38
+ end
39
+ end