manveru-innate 2009.02.06

Sign up to get free protection for your applications and to get access to all the features.
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