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,47 @@
1
+ require 'thread'
2
+
3
+ module Innate
4
+ module State
5
+ # In case fibers are not available we fall back to this wrapper.
6
+ #
7
+ # It will raise errors happening inside the wrapping Thread even if
8
+ # Thread::abort_on_exception is false.
9
+ #
10
+ # For things that require a mutex in a threaded environment, use
11
+ # STATE#sync, if Fiber is available no mutex will be used.
12
+
13
+ class Thread
14
+ SEMAPHORE = Mutex.new
15
+
16
+ def [](key)
17
+ ::Thread.current[key]
18
+ end
19
+
20
+ def []=(key, value)
21
+ ::Thread.current[key] = value
22
+ end
23
+
24
+ # Execute given block in a new Thread and rescue any exceptions before
25
+ # they reach Thread::new, so in case Thread::raise_on_exception is false
26
+ # we can still reraise the error outside of the Thread.
27
+ #
28
+ # This is not meant to be concurrent, we only use Thread as a wrapping
29
+ # context so we can store objects in Thread::current and access them from
30
+ # anywhere within this thread.
31
+ def wrap
32
+ value = ::Thread.new{ begin; yield; rescue Exception => ex; ex; end }.value
33
+ raise(value) if Exception === value
34
+ return value
35
+ end
36
+
37
+ def sync(&block)
38
+ SEMAPHORE.synchronize(&block)
39
+ end
40
+
41
+ def defer
42
+ a = ::Thread.current
43
+ ::Thread.new{ b = ::Thread.current; a.keys.each{|k| b[k] = a[k] }; yield }
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,85 @@
1
+ module Innate
2
+ # Traited helps you doing configuration similar to class variables.
3
+ #
4
+ # It's built on a simple Hash, where keys are objects and the values the
5
+ # configuration.
6
+ # By using {Traited#ancestral_trait} you will get nicely inherited
7
+ # configuration, where keys later in the ancestors will take precedence.
8
+ #
9
+ # @example usage
10
+ #
11
+ # class Foo
12
+ # include Innate::Traited
13
+ # trait :hello => 'Hello'
14
+ #
15
+ # def initialize
16
+ # trait :hello => 'World!'
17
+ # end
18
+ #
19
+ # def show
20
+ # [class_trait[:hello], trait[:hello], ancestral_trait[:hello]]
21
+ # end
22
+ # end
23
+ #
24
+ # Foo.trait[:hello] # => "Hello"
25
+ # foo = Foo.new
26
+ # foo.trait[:hello] # => "World!"
27
+ # foo.show # => ["Hello", "World!", "World!"]
28
+ module Traited
29
+ TRAITS = {}
30
+
31
+ def self.included(into)
32
+ into.extend(self)
33
+ end
34
+
35
+ def trait(hash = nil)
36
+ if hash
37
+ TRAITS[self] ||= {}
38
+ TRAITS[self].merge!(hash)
39
+ else
40
+ TRAITS[self] || {}
41
+ end
42
+ end
43
+
44
+ # Builds a trait from all the ancestors, closer ancestors overwrite distant
45
+ # ancestors
46
+ #
47
+ # class Foo
48
+ # include Innate::Traited
49
+ # trait :one => :eins, :first => :erstes
50
+ # end
51
+ #
52
+ # class Bar < Foo
53
+ # trait :two => :zwei
54
+ # end
55
+ #
56
+ # class Foobar < Bar
57
+ # trait :three => :drei, :first => :overwritten
58
+ # end
59
+ #
60
+ # Foobar.ancestral_trait
61
+ # # => {:three => :drei, :two => :zwei, :one => :eins, :first => :overwritten}
62
+ def ancestral_trait
63
+ result = {}
64
+ each_ancestral_trait{|trait| result.merge!(trait) }
65
+ result
66
+ end
67
+
68
+ def ancestral_trait_values(key)
69
+ result = []
70
+ each_ancestral_trait{|trait| result << trait[key] if trait.key?(key) }
71
+ result
72
+ end
73
+
74
+ def each_ancestral_trait
75
+ ancs = respond_to?(:ancestors) ? ancestors : self.class.ancestors
76
+ ancs.unshift(self)
77
+ ancs.reverse_each{|anc| yield(TRAITS[anc]) if TRAITS.key?(anc) }
78
+ end
79
+
80
+ # trait for self.class if we are an instance
81
+ def class_trait
82
+ respond_to?(:ancestors) ? trait : self.class.trait
83
+ end
84
+ end
85
+ end
@@ -0,0 +1,18 @@
1
+ require 'innate/state/accessor'
2
+ require 'innate/request'
3
+
4
+ module Innate
5
+ # The module to be included into the Controller it basically just provides
6
+ # #request, #response and #session, each accessing Thread.current to
7
+ # retrieve the demanded object
8
+
9
+ module Trinity
10
+ extend StateAccessor
11
+
12
+ state_accessor :request, :response, :session, :actions
13
+
14
+ def action
15
+ actions.last
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,3 @@
1
+ module Innate
2
+ VERSION = "2009.04"
3
+ end
@@ -0,0 +1,60 @@
1
+ module Innate
2
+
3
+ # This is a container module for wrappers of templating engines and handles
4
+ # lazy requiring of needed engines.
5
+
6
+ module View
7
+ ENGINE, TEMP = {}, {}
8
+
9
+ module_function
10
+
11
+ def exts_of(engine)
12
+ name = engine.to_s
13
+ ENGINE.reject{|k,v| v != name }.keys
14
+ end
15
+
16
+ # Try to obtain given engine by its registered name.
17
+ def get(engine)
18
+ if klass = TEMP[engine]
19
+ return klass
20
+ elsif klass = ENGINE[engine]
21
+ TEMP[engine] = obtain(klass)
22
+ else
23
+ TEMP[engine] = obtain(engine, View)
24
+ end
25
+ end
26
+
27
+ # We need to put this in a Mutex because simultanous calls for the same
28
+ # class will cause race conditions and one call may return the wrong class
29
+ # on the first request (before TEMP is set).
30
+ # No mutex is used in Fiber environment, see Innate::State and subclasses.
31
+ def obtain(klass, root = Object)
32
+ STATE.sync do
33
+ klass.to_s.scan(/\w+/){|part| root = root.const_get(part) }
34
+ root
35
+ end
36
+ end
37
+
38
+ # Register given templating engine wrapper and extensions for later usage.
39
+ #
40
+ # +name+ : the class name of the templating engine wrapper
41
+ # +exts+ : any number of arguments will be turned into strings via #to_s
42
+ # that indicate which filename-extensions the templates may have.
43
+ def register(klass, *exts)
44
+ exts.each do |ext|
45
+ ext = ext.to_s
46
+ engine = ENGINE[ext]
47
+ Log.warn("overwriting %p, was set to %p" % [ext, engine]) if engine
48
+ ENGINE[ext] = klass
49
+ end
50
+ end
51
+
52
+ autoload :None, 'innate/view/none'
53
+ autoload :ERB, 'innate/view/erb'
54
+ autoload :Etanni, 'innate/view/etanni'
55
+
56
+ register 'Innate::View::None', :css, :html, :htm
57
+ register 'Innate::View::ERB', :erb
58
+ register 'Innate::View::Etanni', :xhtml
59
+ end
60
+ end
@@ -0,0 +1,15 @@
1
+ require 'erb'
2
+
3
+ module Innate
4
+ module View
5
+ module ERB
6
+ def self.call(action, string)
7
+ erb = ::ERB.new(string.to_s, nil, '%<>')
8
+ erb.filename = (action.view || action.method).to_s
9
+ html = erb.result(action.binding)
10
+
11
+ return html, 'text/html'
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,36 @@
1
+ module Innate
2
+ module View
3
+ module Etanni
4
+ def self.call(action, string)
5
+ template = Innate::Etanni.new(string.to_s)
6
+ html = template.result(action.binding, (action.view || action.method))
7
+ return html, 'text/html'
8
+ end
9
+ end
10
+ end
11
+
12
+ class Etanni
13
+ def initialize(template)
14
+ @template = template
15
+ compile
16
+ end
17
+
18
+ def compile
19
+ temp = @template.dup
20
+ start_heredoc = "T" << Digest::SHA1.hexdigest(temp)
21
+ start_heredoc, end_heredoc = "\n<<#{start_heredoc}.chomp\n", "\n#{start_heredoc}\n"
22
+ bufadd = "_out_ << "
23
+
24
+ temp.gsub!(/<\?r\s+(.*?)\s+\?>/m,
25
+ "#{end_heredoc} \\1; #{bufadd} #{start_heredoc}")
26
+
27
+ @compiled = "_out_ = ''
28
+ #{bufadd} #{start_heredoc} #{temp} #{end_heredoc}
29
+ _out_"
30
+ end
31
+
32
+ def result(binding, filename = '<Etanni>')
33
+ eval(@compiled, binding, filename).to_s.strip
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,9 @@
1
+ module Innate
2
+ module View
3
+ module None
4
+ def self.call(action, string)
5
+ return string.to_s, 'text/html'
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,30 @@
1
+ require 'spec/helper'
2
+ require 'example/app/retro_games'
3
+
4
+ describe 'Retro-games app' do
5
+ behaves_like :mock
6
+
7
+ it 'lists the first game' do
8
+ get '/'
9
+ last_response.should =~ /1 =&gt; Pacman/
10
+ end
11
+
12
+ it 'has a form to add another game' do
13
+ get '/'
14
+ last_response.should =~ /<form/
15
+ end
16
+
17
+ it 'allows you to add another game' do
18
+ post '/create', :name => 'Street Fighter II'
19
+ follow_redirect!
20
+ last_response.should =~ /0 =&gt; Street Fighter II/
21
+ end
22
+
23
+ it 'allows you to vote for a game' do
24
+ get '/vote/Street+Fighter+II'
25
+ follow_redirect!
26
+ last_response.should =~ /1 =&gt; Street Fighter II/
27
+ end
28
+
29
+ FileUtils.rm_f('games.yaml')
30
+ end
@@ -0,0 +1,13 @@
1
+ require 'spec/helper'
2
+ require 'example/hello'
3
+
4
+ describe 'example/hello' do
5
+ behaves_like :mock
6
+
7
+ should 'have index action' do
8
+ got = get('/')
9
+ got.status.should == 200
10
+ got['Content-Type'].should == 'text/html'
11
+ got.body.should == 'Hello, World!'
12
+ end
13
+ end
@@ -0,0 +1,25 @@
1
+ require 'spec/helper'
2
+ require 'example/link'
3
+
4
+ describe 'example/link' do
5
+ behaves_like :mock
6
+
7
+ should 'have index on Linking' do
8
+ get('/').body.should == 'Index links to <a href="/help">Help?</a>'
9
+ end
10
+
11
+ should 'have help on Linking' do
12
+ get('/help').body.should ==
13
+ "Help links to <a href=\"/link_to/another\">A Different Node</a>"
14
+ end
15
+
16
+ should 'have another on Different' do
17
+ get('/link_to/another').body.
18
+ should == "<a href=\"/link_to/and/deeper\">Another links even deeper</a>"
19
+ end
20
+
21
+ should 'have and__deeper on Different' do
22
+ get('/link_to/and/deeper').body.
23
+ should == "<a href=\"/index\">Back to Linking Node</a>"
24
+ end
25
+ end
@@ -0,0 +1,16 @@
1
+ require 'spec/helper'
2
+ require 'example/provides'
3
+
4
+ describe 'examples/provide' do
5
+ behaves_like :mock
6
+
7
+ it 'provides YAML representation' do
8
+ get '/list.yaml'
9
+ last_response.body.should == ARTICLES.to_yaml
10
+ last_response['Content-Type'].should == 'text/yaml'
11
+
12
+ get '/list'
13
+ last_response.body.should == ''
14
+ last_response['Content-Type'].should == 'text/html'
15
+ end
16
+ end
@@ -0,0 +1,22 @@
1
+ require 'spec/helper'
2
+ require 'example/session'
3
+
4
+ describe 'example/session' do
5
+ behaves_like :mock
6
+
7
+ it 'starts at 0' do
8
+ get('/').body.should =~ /Value is: 0/
9
+ end
10
+
11
+ it 'increments the counter' do
12
+ get('/increment').body.should =~ /Value is: 1/
13
+ get('/increment').body.should =~ /Value is: 2/
14
+ get('/increment').body.should =~ /Value is: 3/
15
+ end
16
+
17
+ it 'decrements the counter' do
18
+ get('/decrement').body.should =~ /Value is: 2/
19
+ get('/decrement').body.should =~ /Value is: 1/
20
+ get('/decrement').body.should =~ /Value is: 0/
21
+ end
22
+ end
@@ -0,0 +1,10 @@
1
+ if caller_line = caller.grep(%r!spec/innate/!).first
2
+ caller_file = caller_line.split(':', 2).first
3
+ caller_root = File.dirname(caller_file)
4
+ $0 = caller_file
5
+ end
6
+
7
+ require File.expand_path(File.join(File.dirname(__FILE__), '../lib/innate'))
8
+ require 'innate/spec'
9
+
10
+ Innate.options.roots = [caller_root] if caller_root
@@ -0,0 +1,107 @@
1
+ # Copyright (c) 2009 Michael Fellinger m.fellinger@gmail.com
2
+ # All files in this distribution are subject to the terms of the Ruby license.
3
+
4
+ require 'spec/helper'
5
+
6
+ class SpecActionLayout
7
+ include Innate::Node
8
+ map_layouts '/'
9
+ end
10
+
11
+ class SpecActionLayoutMethod < SpecActionLayout
12
+ Innate.node('/from_method', self)
13
+ layout('method_layout')
14
+
15
+ def method_layout
16
+ '<pre>#{ @content }</pre>'
17
+ end
18
+
19
+ def index
20
+ 'Method Layout'
21
+ end
22
+
23
+ def foo
24
+ "bar"
25
+ end
26
+ end
27
+
28
+ class SpecActionLayoutFile < SpecActionLayout
29
+ Innate.node('/from_file', self)
30
+ layout('file_layout')
31
+
32
+ def index
33
+ "File Layout"
34
+ end
35
+ end
36
+
37
+ class SpecActionLayoutSpecific < SpecActionLayout
38
+ Innate.node('/specific', self)
39
+ layout('file_layout'){|name, wish| name == 'index' }
40
+
41
+ def index
42
+ 'Specific Layout'
43
+ end
44
+
45
+ def without
46
+ "Without wrapper"
47
+ end
48
+ end
49
+
50
+ class SpecActionLayoutDeny < SpecActionLayout
51
+ Innate.node('/deny', self)
52
+ layout('file_layout'){|name, wish| name != 'without' }
53
+
54
+ def index
55
+ "Deny Layout"
56
+ end
57
+
58
+ def without
59
+ "Without wrapper"
60
+ end
61
+ end
62
+
63
+ class SpecActionLayoutMulti < SpecActionLayout
64
+ Innate.node('/multi', self)
65
+ layout('file_layout'){|name, wish| name =~ /index|second/ }
66
+
67
+ def index
68
+ "Multi Layout Index"
69
+ end
70
+
71
+ def second
72
+ "Multi Layout Second"
73
+ end
74
+
75
+ def without
76
+ "Without wrapper"
77
+ end
78
+ end
79
+
80
+ describe 'Innate::Action#layout' do
81
+ behaves_like :mock
82
+
83
+ it 'uses a layout method' do
84
+ get('/from_method').body.should == '<pre>Method Layout</pre>'
85
+ get('/from_method/foo').body.should == '<pre>bar</pre>'
86
+ end
87
+
88
+ it 'uses a layout file' do
89
+ get('/from_file').body.strip.should == '<p>File Layout</p>'
90
+ end
91
+
92
+ it 'denies layout to some actions' do
93
+ get('/deny').body.strip.should == '<p>Deny Layout</p>'
94
+ get('/deny/without').body.strip.should == 'Without wrapper'
95
+ end
96
+
97
+ it 'uses layout only for specific action' do
98
+ get('/specific').body.strip.should == '<p>Specific Layout</p>'
99
+ get('/specific/without').body.strip.should == 'Without wrapper'
100
+ end
101
+
102
+ it 'uses layout only for specific actions' do
103
+ get('/multi').body.strip.should == '<p>Multi Layout Index</p>'
104
+ get('/multi/second').body.strip.should == '<p>Multi Layout Second</p>'
105
+ get('/multi/without').body.strip.should == 'Without wrapper'
106
+ end
107
+ end