innate 2009.04

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