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,21 @@
1
+ module Innate
2
+ class Cache
3
+ # Memory cache is simply a Hash with the Cache::API, it's the reference
4
+ # implementation for every other cache and the default cache.
5
+ class Memory < Hash
6
+ include Cache::API
7
+
8
+ def cache_store(*args)
9
+ super{|key, value| self[key] = value }
10
+ end
11
+
12
+ def cache_fetch(*args)
13
+ super{|key| self[key] }
14
+ end
15
+
16
+ def cache_delete(*args)
17
+ super{|key| delete(key) }
18
+ end
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,20 @@
1
+ require 'yaml/store'
2
+
3
+ module Innate
4
+ class Cache
5
+ # Keeps every cache in a separate file like this:
6
+ # /tmp/innate-cache-yaml/delta-manveru-session.yaml
7
+ #
8
+ # The YAML cache is not safe for use between multiple processes, it is also
9
+ # very slow compared to other caches, so generally the use of it is
10
+ # discouraged.
11
+ class YAML
12
+ include Cache::API
13
+ include Cache::FileBased
14
+
15
+ STORE = ::YAML::Store
16
+ DIR = 'innate-cache-yaml'
17
+ EXT = '.yaml'
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,35 @@
1
+ require 'innate/request'
2
+ require 'innate/response'
3
+
4
+ module Innate
5
+ # We track the current request/response/session (Trinity) in Thread.current
6
+ # so we can reach them from anywhere in the code without passing around the
7
+ # objects 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
+ # Run setup and call the app
20
+ def call(env)
21
+ setup(env)
22
+ @app.call(env)
23
+ end
24
+
25
+ # Setup new Request/Response/Session for this request/response cycle.
26
+ # The parameters are here to allow Ramaze to inject its own classes.
27
+ def setup(env, request = Request, response = Response, session = Session)
28
+ current = Thread.current
29
+ req = current[:request] = request.new(env)
30
+ res = current[:response] = response.new
31
+ current[:actions] = []
32
+ current[:session] = Session.new(req, res)
33
+ end
34
+ end
35
+ end
@@ -0,0 +1,96 @@
1
+ module Innate
2
+ class URLMap < Rack::URLMap
3
+ def initialize(map = {})
4
+ @originals = map
5
+ super
6
+ end
7
+
8
+ # super may raise when given invalid locations, so we only replace the
9
+ # `@originals` if we are sure the new map is valid
10
+ def remap(map)
11
+ value = super
12
+ @originals = map
13
+ value
14
+ end
15
+
16
+ def map(location, object)
17
+ return unless location and object
18
+ remap(@originals.merge(location.to_s => object))
19
+ end
20
+
21
+ def delete(location)
22
+ @originals.delete(location)
23
+ remap(@originals)
24
+ end
25
+
26
+ def at(location)
27
+ @originals[location]
28
+ end
29
+
30
+ def to(object)
31
+ @originals.invert[object]
32
+ end
33
+
34
+ def to_hash
35
+ @originals.dup
36
+ end
37
+
38
+ def call(env)
39
+ raise "Nothing mapped yet" if @originals.empty?
40
+ super
41
+ end
42
+ end
43
+
44
+ DynaMap = URLMap.new
45
+
46
+ # script_name, path_info = env['SCRIPT_NAME'], env['PATH_INFO']
47
+ # answer = app.call(env)
48
+ # env.merge!('SCRIPT_NAME' => script_name, 'PATH_INFO' => path_info)
49
+ # answer
50
+
51
+ module SingletonMethods
52
+ # Maps the given +object+ or +block+ to +location+, +object+ must respond to
53
+ # #call in order to be of any use.
54
+ #
55
+ # @example with passed +object+
56
+ #
57
+ # Innate.map('/', lambda{|env| [200, {}, "Hello, World"] })
58
+ # Innate.at('/').call({}) # => [200, {}, "Hello, World"]
59
+ #
60
+ # @example with passed +block+
61
+ #
62
+ # Innate.map('/'){|env| [200, {}, ['Hello, World!']] }
63
+ # Innate.at('/').call({})
64
+ def map(location, object = nil, &block)
65
+ DynaMap.map(location, object || block)
66
+ end
67
+
68
+ # Answer with object at +location+.
69
+ #
70
+ # @example
71
+ #
72
+ # class Hello
73
+ # include Innate::Node
74
+ # map '/'
75
+ # end
76
+ #
77
+ # Innate.at('/') # => Hello
78
+ def at(location)
79
+ DynaMap.at(location)
80
+ end
81
+
82
+ # Returns one of the paths the given +object+ is mapped to.
83
+ #
84
+ # @example
85
+ #
86
+ # class Hello
87
+ # include Innate::Node
88
+ # map '/'
89
+ # end
90
+ #
91
+ # Innate.to(Hello) # => '/'
92
+ def to(object)
93
+ DynaMap.to(object)
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,185 @@
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
+ into.helper(*HelpersHelper.options[:default])
15
+ end
16
+ end
17
+
18
+ # Provides access to #helper method without polluting the name-space any
19
+ # further.
20
+ module HelperAccess
21
+
22
+ # Convenience method used by Innate::Node.
23
+ #
24
+ # Usage:
25
+ #
26
+ # class Hi
27
+ # extend Innate::HelperAccess
28
+ # helper :cgi, :link, :aspect
29
+ # end
30
+ #
31
+ # This will require the helpers and call:
32
+ #
33
+ # Hi.include(Innate::Helper::CGI)
34
+ # Hi.include(Innate::Helper::Link)
35
+ # Hi.include(Innate::Helper::Aspect)
36
+ def helper(*helpers)
37
+ HelpersHelper.each_include(self, *helpers)
38
+ end
39
+ end
40
+
41
+ # Here come the utility methods used from the HelperAccess#helper method, we
42
+ # do this to keep method count at a minimum and because HelpersHelper is such
43
+ # an awesome name that just can't be wasted.
44
+ #
45
+ # Usage if you want to only extend with helpers:
46
+ #
47
+ # class Hi
48
+ # Innate::HelpersHelper.each_extend(self, :cgi, :link, :aspect)
49
+ # end
50
+ #
51
+ # Usage if you only want to include helpers:
52
+ #
53
+ # class Hi
54
+ # Innate::HelpersHelper.each_include(self, :cgi, :link, :aspect)
55
+ # end
56
+ #
57
+ # Usage for iteration:
58
+ #
59
+ # Innate::HelpersHelper.each(:cgi, :link, :aspect) do |mod|
60
+ # p mod
61
+ # end
62
+ #
63
+ # Usage for translating helpers to modules:
64
+ #
65
+ # p Innate::HelpersHelper.each(:cgi, :link, :aspect)
66
+ module HelpersHelper
67
+ include Optioned
68
+
69
+ options.dsl do
70
+ o "Paths that will be searched for helper files",
71
+ :paths, [Dir.pwd, File.dirname(__FILE__)]
72
+
73
+ o "Namespaces that will be searched for helper modules",
74
+ :namespaces, [Helper]
75
+
76
+ o "Filename extensions considered for helper files",
77
+ :exts, %w[rb so bundle]
78
+
79
+ o "Default helpers, added on inclusion of the Helper module",
80
+ :default, [:aspect, :cgi, :flash, :link, :render, :redirect, :send_file]
81
+ end
82
+
83
+ EXTS = %w[rb so bundle]
84
+
85
+ # all of the following are singleton methods
86
+
87
+ module_function
88
+
89
+ # Yield all the modules we can find for the given names of helpers, try to
90
+ # require them if not available.
91
+ #
92
+ # NOTE: Unlike usual #each, this will actually return an Array of the found
93
+ # modules instead of the given +*names+
94
+ #
95
+ #
96
+ # Usage:
97
+ #
98
+ # Innate::HelpersHelper.each(:cgi, :link, :aspect) do |mod|
99
+ # p mod
100
+ # end
101
+
102
+ def each(*names)
103
+ names.map do |name|
104
+ if name.class == Module
105
+ yield(name) if block_given?
106
+ name
107
+ elsif mod = get(name)
108
+ yield(mod) if block_given?
109
+ mod
110
+ elsif try_require(name)
111
+ redo
112
+ else
113
+ raise LoadError, "Helper #{name} not found"
114
+ end
115
+ end
116
+ end
117
+
118
+ # Shortcut to extend +into+ with Helper modules corresponding to +*names+.
119
+ # +into+ has to respond to #extend.
120
+ #
121
+ # Usage:
122
+ #
123
+ # class Hi
124
+ # Innate::HelpersHelper.each_extend(self, :cgi, :link, :aspect)
125
+ # end
126
+ def each_extend(into, *names, &block)
127
+ return if names.empty?
128
+ into.extend(*each(*names, &block))
129
+ end
130
+
131
+ # Shortcut to include Helper modules corresponding to +*names+ on +into+.
132
+ # +into+ has to respond to #include.
133
+ # #__send__(:include) is used in case #include raises due to being
134
+ # private/protected
135
+ #
136
+ # in case #include is a private/protected method.
137
+ #
138
+ # Usage:
139
+ #
140
+ # class Hi
141
+ # Innate::HelpersHelper.each_include(self, :cgi, :link, :aspect)
142
+ # end
143
+ def each_include(into, *names, &block)
144
+ return if names.compact.empty?
145
+ into.__send__(:include, *each(*names, &block))
146
+ end
147
+
148
+ # Based on a simple set of rules we will first construct the most likely
149
+ # name for the helper and then grep the constants in the Innate::Helper
150
+ # module for any matches.
151
+ #
152
+ # helper :foo_bar # => FooBar
153
+ # helper :foo # => Foo
154
+ def get(name)
155
+ module_name = /^#{name.to_s.dup.delete('_')}$/i
156
+
157
+ options.namespaces.each do |namespace|
158
+ found = namespace.constants.grep(module_name).first
159
+ return namespace.const_get(found) if found
160
+ end
161
+
162
+ nil
163
+ end
164
+
165
+ # Figure out files that might have the helper we ask for and then require
166
+ # the first we find, if any.
167
+ def try_require(name)
168
+ if found = find_helper(name.to_s)
169
+ require(found) || true
170
+ else
171
+ raise(LoadError, "Helper #{name} not found")
172
+ end
173
+ end
174
+
175
+ def find_helper(name)
176
+ options.paths.uniq.find do |path|
177
+ base = ::File.join(path, 'helper', name)
178
+ options.exts.find do |ext|
179
+ full = "#{base}.#{ext}"
180
+ return full if ::File.file?(full)
181
+ end
182
+ end
183
+ end
184
+ end
185
+ end
@@ -0,0 +1,124 @@
1
+ module Innate
2
+ module Helper
3
+
4
+ # Provides before/after wrappers for actions
5
+ #
6
+ # This helper is essential for proper working of {Action#render}.
7
+ module Aspect
8
+ AOP = Hash.new{|h,k| h[k] = Hash.new{|hh,kk| hh[kk] = {} }}
9
+
10
+ def self.included(into)
11
+ into.extend(SingletonMethods)
12
+ into.add_action_wrapper(5.0, :aspect_wrap)
13
+ end
14
+
15
+ # Consider objects that have Aspect included
16
+ def self.ancestral_aop(from)
17
+ aop = {}
18
+ from.ancestors.reverse.map{|anc| aop.merge!(AOP[anc]) if anc < Aspect }
19
+ aop
20
+ end
21
+
22
+ def aspect_call(position, name)
23
+ return unless aop = Aspect.ancestral_aop(self.class)
24
+ return unless block = at_position = aop[position]
25
+
26
+ block = at_position[name.to_sym] unless at_position.is_a?(Proc)
27
+
28
+ instance_eval(&block) if block
29
+ end
30
+
31
+ def aspect_wrap(action)
32
+ return yield unless method = action.name
33
+
34
+ aspect_call(:before_all, method)
35
+ aspect_call(:before, method)
36
+ result = yield
37
+ aspect_call(:after, method)
38
+ aspect_call(:after_all, method)
39
+
40
+ result
41
+ end
42
+
43
+ # This awesome piece of hackery implements action AOP.
44
+ #
45
+ # The so-called aspects are simply methods that may yield the next aspect
46
+ # in the chain, this is similar to racks concept of middleware, but instead
47
+ # of initializing with an app we simply pass a block that may be yielded
48
+ # with the action being processed.
49
+ #
50
+ # This gives us things like logging, caching, aspects, authentication, etc.
51
+ #
52
+ # Add the name of your method to the trait[:wrap] to add your own method to
53
+ # the wrap_action_call chain.
54
+ #
55
+ # @example adding your method
56
+ #
57
+ # class MyNode
58
+ # Innate.node '/'
59
+ #
60
+ # private
61
+ #
62
+ # def wrap_logging(action)
63
+ # Innate::Log.info("Executing #{action.name}")
64
+ # yield
65
+ # end
66
+ #
67
+ # trait[:wrap]
68
+ # end
69
+ #
70
+ #
71
+ # methods may register
72
+ # themself in the trait[:wrap] and will be called in left-to-right order,
73
+ # each being passed the action instance and a block that they have to yield
74
+ # to continue the chain.
75
+ #
76
+ # @param [Action] action instance that is being passed to every registered method
77
+ # @param [Proc] block contains the instructions to call the action method if any
78
+ #
79
+ # @see Action#render
80
+ # @author manveru
81
+ def wrap_action_call(action, &block)
82
+ wrap = SortedSet.new
83
+ action.node.ancestral_trait_values(:wrap).each{|sset| wrap.merge(sset) }
84
+ head, *tail = wrap.map{|k,v| v }
85
+ tail.reverse!
86
+ combined = tail.inject(block){|s,v| lambda{ __send__(v, action, &s) } }
87
+ __send__(head, action, &combined)
88
+ end
89
+
90
+ module SingletonMethods
91
+ include Traited
92
+
93
+ def before_all(&block)
94
+ AOP[self][:before_all] = block
95
+ end
96
+
97
+ def before(*names, &block)
98
+ names.each{|name| AOP[self][:before][name] = block }
99
+ end
100
+
101
+ def after_all(&block)
102
+ AOP[self][:after_all] = block
103
+ end
104
+
105
+ def after(*names, &block)
106
+ names.each{|name| AOP[self][:after][name] = block }
107
+ end
108
+
109
+ def wrap(*names, &block)
110
+ before(*names, &block)
111
+ after(*names, &block)
112
+ end
113
+
114
+ def add_action_wrapper(order, method_name)
115
+ if wrap = trait[:wrap]
116
+ wrap.merge(SortedSet[[order, method_name.to_s]])
117
+ else
118
+ trait :wrap => SortedSet[[order, method_name.to_s]]
119
+ end
120
+ end
121
+ end
122
+ end
123
+ end
124
+ end