manveru-innate 2009.04.18 → 2009.05

Sign up to get free protection for your applications and to get access to all the features.
Files changed (48) hide show
  1. data/CHANGELOG +230 -0
  2. data/MANIFEST +2 -4
  3. data/Rakefile +6 -2
  4. data/example/app/whywiki_erb/start.rb +1 -1
  5. data/innate.gemspec +7 -3
  6. data/lib/innate.rb +5 -4
  7. data/lib/innate/action.rb +17 -25
  8. data/lib/innate/cache.rb +1 -1
  9. data/lib/innate/cache/drb.rb +5 -5
  10. data/lib/innate/cache/file_based.rb +3 -0
  11. data/lib/innate/cache/marshal.rb +4 -1
  12. data/lib/innate/cache/memory.rb +1 -2
  13. data/lib/innate/cache/yaml.rb +4 -1
  14. data/lib/innate/current.rb +11 -13
  15. data/lib/innate/dynamap.rb +5 -0
  16. data/lib/innate/helper.rb +9 -7
  17. data/lib/innate/helper/aspect.rb +9 -9
  18. data/lib/innate/helper/cgi.rb +3 -0
  19. data/lib/innate/helper/redirect.rb +1 -1
  20. data/lib/innate/helper/render.rb +68 -7
  21. data/lib/innate/log/color_formatter.rb +19 -13
  22. data/lib/innate/node.rb +38 -35
  23. data/lib/innate/options/dsl.rb +5 -2
  24. data/lib/innate/request.rb +1 -1
  25. data/lib/innate/response.rb +1 -0
  26. data/lib/innate/route.rb +4 -0
  27. data/lib/innate/session.rb +16 -14
  28. data/lib/innate/state.rb +10 -11
  29. data/lib/innate/state/accessor.rb +8 -8
  30. data/lib/innate/traited.rb +15 -10
  31. data/lib/innate/version.rb +1 -1
  32. data/lib/innate/view.rb +41 -4
  33. data/lib/innate/view/erb.rb +1 -2
  34. data/lib/innate/view/etanni.rb +9 -12
  35. data/spec/innate/action/layout.rb +0 -3
  36. data/spec/innate/helper/flash.rb +0 -3
  37. data/spec/innate/helper/redirect.rb +11 -0
  38. data/spec/innate/helper/render.rb +32 -0
  39. data/spec/innate/node/node.rb +1 -0
  40. data/spec/innate/options.rb +5 -1
  41. data/tasks/authors.rake +30 -0
  42. data/tasks/release.rake +3 -3
  43. data/tasks/ycov.rake +84 -0
  44. metadata +16 -9
  45. data/lib/innate/state/fiber.rb +0 -74
  46. data/lib/innate/state/thread.rb +0 -47
  47. data/spec/innate/state/fiber.rb +0 -58
  48. data/spec/innate/state/thread.rb +0 -40
@@ -78,7 +78,8 @@ module Innate
78
78
  # :doc, :value keys will be ignored
79
79
  def option(doc, key, value, other = {}, &block)
80
80
  trigger = block || other[:trigger]
81
- convert = {:doc => doc.to_s, :value => value, :trigger => trigger}
81
+ convert = {:doc => doc.to_s, :value => value}
82
+ convert[:trigger] = trigger if trigger
82
83
  @hash[key.to_sym] = other.merge(convert)
83
84
  end
84
85
  alias o option
@@ -115,7 +116,9 @@ module Innate
115
116
  # @param [Array] keys
116
117
  # @param [Object] value
117
118
  def set_value(keys, value)
118
- get(*keys)[:value] = value
119
+ got = get(*keys)
120
+ return got[:value] = value if got
121
+ raise(IndexError, "There is no option available for %p" % [keys])
119
122
  end
120
123
 
121
124
  # Retrieve only the :value from the value hash if found via +keys+.
@@ -56,7 +56,7 @@ module Innate
56
56
  # * values_at
57
57
 
58
58
  class Request < Rack::Request
59
- # Currently handled request from Innate::STATE[:request]
59
+ # Currently handled request from Thread.current[:request]
60
60
  # Call it from anywhere via Innate::Request.current
61
61
  def self.current
62
62
  Current.request
@@ -17,6 +17,7 @@ module Innate
17
17
 
18
18
  def finish
19
19
  options.headers.each{|key, value| self[key] ||= value }
20
+ Current.session.flush(self)
20
21
  super
21
22
  end
22
23
  end
data/lib/innate/route.rb CHANGED
@@ -5,6 +5,10 @@ module Innate
5
5
  #
6
6
  # This middleware should wrap Innate::DynaMap.
7
7
  #
8
+ # Please note that Rack::File is put before Route and Rewrite, that means
9
+ # that you cannot apply routes to static files unless you add your own route
10
+ # middleware before.
11
+ #
8
12
  # String routers are the simplest way to route in Innate. One path is
9
13
  # translated into another:
10
14
  #
@@ -9,12 +9,12 @@ module Innate
9
9
  # You may store anything in here that you may also store in the corresponding
10
10
  # store, usually it's best to keep it to things that are safe to Marshal.
11
11
  #
12
- # The default time of expiration is *
13
- #
12
+ # The default time of expiration is:
14
13
  # Time.at(2147483647) # => Tue Jan 19 12:14:07 +0900 2038
15
14
  #
16
15
  # Hopefully we all have 64bit systems by then.
17
-
16
+ #
17
+ # The Session instance is compatible with the specification of rack.session.
18
18
  class Session
19
19
  include Optioned
20
20
 
@@ -40,13 +40,17 @@ module Innate
40
40
  @flash = Flash.new(self)
41
41
  end
42
42
 
43
- def []=(key, value)
43
+ # Rack interface
44
+
45
+ def store(key, value)
44
46
  cache_sid[key] = value
45
47
  end
48
+ alias []= store
46
49
 
47
- def [](key)
50
+ def fetch(key, value = nil)
48
51
  cache_sid[key]
49
52
  end
53
+ alias [] fetch
50
54
 
51
55
  def delete(key)
52
56
  cache_sid.delete(key)
@@ -57,9 +61,7 @@ module Innate
57
61
  @cache_sid = nil
58
62
  end
59
63
 
60
- def cache_sid
61
- @cache_sid ||= cache[sid] || {}
62
- end
64
+ # Additional interface
63
65
 
64
66
  def flush(response = @response)
65
67
  return if !@cache_sid or @cache_sid.empty?
@@ -70,6 +72,12 @@ module Innate
70
72
  set_cookie(response)
71
73
  end
72
74
 
75
+ private
76
+
77
+ def cache_sid
78
+ @cache_sid ||= cache[sid] || {}
79
+ end
80
+
73
81
  def sid
74
82
  @sid ||= cookie || generate_sid
75
83
  end
@@ -78,12 +86,6 @@ module Innate
78
86
  @request.cookies[options.key]
79
87
  end
80
88
 
81
- def inspect
82
- cache.inspect
83
- end
84
-
85
- private
86
-
87
89
  def cache
88
90
  Innate::Cache.session
89
91
  end
data/lib/innate/state.rb CHANGED
@@ -1,12 +1,6 @@
1
- module Innate
2
- begin
3
- require 'innate/state/fiber'
4
- STATE = State::Fiber.new
5
- rescue LoadError
6
- require 'innate/state/thread'
7
- STATE = State::Thread.new
8
- end
1
+ require 'thread'
9
2
 
3
+ module Innate
10
4
  module SingletonMethods
11
5
  # Use this method to achieve thread-safety for sensitive operations.
12
6
  #
@@ -17,11 +11,16 @@ module Innate
17
11
  # @param [Proc] block the things you want to execute
18
12
  # @see State::Thread#sync State::Fiber#sync
19
13
  def sync(&block)
20
- STATE.sync(&block)
14
+ Thread.exclusive(&block)
21
15
  end
22
16
 
23
- def defer(&block)
24
- STATE.defer(&block)
17
+ def defer
18
+ outer = ::Thread.current
19
+ ::Thread.new{
20
+ inner = ::Thread.current
21
+ outer.keys.each{|k| inner[k] = outer[k] }
22
+ yield
23
+ }
25
24
  end
26
25
  end
27
26
  end
@@ -1,5 +1,5 @@
1
1
  module Innate
2
- # Simplify accessing STATE variables.
2
+ # Simplify accessing Thread.current variables.
3
3
  #
4
4
  # Example:
5
5
  #
@@ -54,7 +54,7 @@ module Innate
54
54
  state_reader(*names, &initializer)
55
55
  end
56
56
 
57
- # Writer accessor to STATE[key]=
57
+ # Writer accessor to Thread.current[key]=
58
58
  #
59
59
  # Example:
60
60
  #
@@ -81,11 +81,11 @@ module Innate
81
81
 
82
82
  def state_writer(*names)
83
83
  StateAccessor.each(*names) do |key, meth|
84
- class_eval("def %s=(obj) STATE[%p] = obj; end" % [meth, key])
84
+ class_eval("def %s=(obj) Thread.current[%p] = obj; end" % [meth, key])
85
85
  end
86
86
  end
87
87
 
88
- # Reader accessor for STATE[key]
88
+ # Reader accessor for Thread.current[key]
89
89
  #
90
90
  # Example:
91
91
  #
@@ -115,14 +115,14 @@ module Innate
115
115
  StateAccessor.each(*names) do |key, meth|
116
116
  if initializer
117
117
  define_method(meth) do
118
- unless STATE.key?(key)
119
- STATE[key] = instance_eval(&initializer)
118
+ unless Thread.current.key?(key)
119
+ Thread.current[key] = instance_eval(&initializer)
120
120
  else
121
- STATE[key]
121
+ Thread.current[key]
122
122
  end
123
123
  end
124
124
  else
125
- class_eval("def %s; STATE[%p]; end" % [meth, key])
125
+ class_eval("def %s; Thread.current[%p]; end" % [meth, key])
126
126
  end
127
127
  end
128
128
  end
@@ -26,7 +26,7 @@ module Innate
26
26
  # foo.trait[:hello] # => "World!"
27
27
  # foo.show # => ["Hello", "World!", "World!"]
28
28
  module Traited
29
- TRAITS = {}
29
+ TRAITS, ANCESTRAL_TRAITS, ANCESTRAL_VALUES = {}, {}, {}
30
30
 
31
31
  def self.included(into)
32
32
  into.extend(self)
@@ -35,7 +35,10 @@ module Innate
35
35
  def trait(hash = nil)
36
36
  if hash
37
37
  TRAITS[self] ||= {}
38
- TRAITS[self].merge!(hash)
38
+ result = TRAITS[self].merge!(hash)
39
+ ANCESTRAL_VALUES.clear
40
+ ANCESTRAL_TRAITS.clear
41
+ result
39
42
  else
40
43
  TRAITS[self] || {}
41
44
  end
@@ -60,21 +63,23 @@ module Innate
60
63
  # Foobar.ancestral_trait
61
64
  # # => {:three => :drei, :two => :zwei, :one => :eins, :first => :overwritten}
62
65
  def ancestral_trait
63
- result = {}
64
- each_ancestral_trait{|trait| result.merge!(trait) }
65
- result
66
+ klass = self.kind_of?(Module) ? self : self.class
67
+ ANCESTRAL_TRAITS[klass] ||=
68
+ each_ancestral_trait({}){|hash, trait| hash.update(trait) }
66
69
  end
67
70
 
68
71
  def ancestral_trait_values(key)
69
- result = []
70
- each_ancestral_trait{|trait| result << trait[key] if trait.key?(key) }
71
- result
72
+ klass = self.kind_of?(Module) ? self : self.class
73
+ cache = ANCESTRAL_VALUES[klass] ||= {}
74
+ cache[key] ||= each_ancestral_trait([]){|array, trait|
75
+ array << trait[key] if trait.key?(key) }
72
76
  end
73
77
 
74
- def each_ancestral_trait
78
+ def each_ancestral_trait(obj)
75
79
  ancs = respond_to?(:ancestors) ? ancestors : self.class.ancestors
76
80
  ancs.unshift(self)
77
- ancs.reverse_each{|anc| yield(TRAITS[anc]) if TRAITS.key?(anc) }
81
+ ancs.reverse_each{|anc| yield(obj, TRAITS[anc]) if TRAITS.key?(anc) }
82
+ obj
78
83
  end
79
84
 
80
85
  # trait for self.class if we are an instance
@@ -1,3 +1,3 @@
1
1
  module Innate
2
- VERSION = "2009.04.18"
2
+ VERSION = "2009.05"
3
3
  end
data/lib/innate/view.rb CHANGED
@@ -2,12 +2,32 @@ module Innate
2
2
 
3
3
  # This is a container module for wrappers of templating engines and handles
4
4
  # lazy requiring of needed engines.
5
-
6
5
  module View
6
+ include Optioned
7
+
7
8
  ENGINE, TEMP = {}, {}
8
9
 
10
+ options.dsl do
11
+ o "Cache compiled templates",
12
+ :cache, true
13
+
14
+ o "Cache template files after they're read to prevent additional filesystem calls",
15
+ :read_cache, false
16
+ end
17
+
18
+ # In order to be able to render actions without running
19
+ # Innate::setup_dependencies we have to add the cache here already.
20
+ Cache.add(:view)
21
+
9
22
  module_function
10
23
 
24
+ def compile(string)
25
+ return yield(string.to_s) unless View.options.cache
26
+ string = string.to_s
27
+ checksum = Digest::MD5.hexdigest(string)
28
+ Cache.view[checksum] ||= yield(string)
29
+ end
30
+
11
31
  def exts_of(engine)
12
32
  name = engine.to_s
13
33
  ENGINE.reject{|k,v| v != name }.keys
@@ -29,10 +49,27 @@ module Innate
29
49
  # on the first request (before TEMP is set).
30
50
  # No mutex is used in Fiber environment, see Innate::State and subclasses.
31
51
  def obtain(klass, root = Object)
32
- STATE.sync do
52
+ Thread.exclusive{
33
53
  klass.to_s.scan(/\w+/){|part| root = root.const_get(part) }
34
- root
35
- end
54
+ return root
55
+ }
56
+ end
57
+
58
+ # Reads the specified view template from the filesystem. When the read_cache
59
+ # option is enabled, templates will be cached to prevent unnecessary
60
+ # filesystem reads in the future.
61
+ #
62
+ # @example usage
63
+ #
64
+ # View.read('some/file.xhtml')
65
+ #
66
+ # @param [#to_str] view
67
+ #
68
+ # @api private
69
+ # @see Action#render
70
+ def read(view)
71
+ return Cache.view[view] ||= ::File.read(view) if View.options.read_cache
72
+ ::File.read(view)
36
73
  end
37
74
 
38
75
  # Register given templating engine wrapper and extensions for later usage.
@@ -4,10 +4,9 @@ module Innate
4
4
  module View
5
5
  module ERB
6
6
  def self.call(action, string)
7
- erb = ::ERB.new(string.to_s, nil, '%<>')
7
+ erb = View.compile(string){|s| ::ERB.new(s, nil, '%<>') }
8
8
  erb.filename = (action.view || action.method).to_s
9
9
  html = erb.result(action.binding)
10
-
11
10
  return html, 'text/html'
12
11
  end
13
12
  end
@@ -2,14 +2,19 @@ module Innate
2
2
  module View
3
3
  module Etanni
4
4
  def self.call(action, string)
5
- template = Innate::Etanni.new(string.to_s)
6
- html = template.result(action.binding, (action.view || action.method))
5
+ etanni = View.compile(string){|s| Innate::Etanni.new(s) }
6
+ html = etanni.result(action.binding, (action.view || action.method))
7
7
  return html, 'text/html'
8
8
  end
9
9
  end
10
10
  end
11
11
 
12
12
  class Etanni
13
+ SEPARATOR = "E69t116A65n110N78i105S83e101P80a97R82a97T84o111R82"
14
+ START = "\n<<#{SEPARATOR}.chomp\n"
15
+ STOP = "\n#{SEPARATOR}\n"
16
+ ADD = "_out_ << "
17
+
13
18
  def initialize(template)
14
19
  @template = template
15
20
  compile
@@ -17,16 +22,8 @@ module Innate
17
22
 
18
23
  def compile
19
24
  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_"
25
+ temp.gsub!(/<\?r\s+(.*?)\s+\?>/m, "#{STOP} \\1; #{ADD} #{START}")
26
+ @compiled = "_out_ = #{START} #{temp} #{STOP} _out_"
30
27
  end
31
28
 
32
29
  def result(binding, filename = '<Etanni>')
@@ -1,6 +1,3 @@
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
1
  require 'spec/helper'
5
2
 
6
3
  class SpecActionLayout
@@ -1,6 +1,3 @@
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
1
  require 'spec/helper'
5
2
 
6
3
  class SpecFlash
@@ -57,6 +57,11 @@ class SpecRedirectHelper
57
57
  def redirect_unmodified
58
58
  raw_redirect '/noop'
59
59
  end
60
+
61
+ def redirect_with_cookie
62
+ response.set_cookie('user', :value => 'manveru')
63
+ redirect r(:noop)
64
+ end
60
65
  end
61
66
 
62
67
  describe Innate::Helper::Redirect do
@@ -157,4 +162,10 @@ describe Innate::Helper::Redirect do
157
162
  last_response.status.should == 200
158
163
  last_response.body.should == 'no actual double redirect'
159
164
  end
165
+
166
+ should 'set cookie before redirect' do
167
+ get("#@uri/redirect_with_cookie")
168
+ follow_redirect!
169
+ last_request.cookies.should == {'user' => 'manveru'}
170
+ end
160
171
  end
@@ -74,6 +74,26 @@ class SpecHelperRenderMisc
74
74
  end
75
75
  end
76
76
 
77
+ class SpecHelperRenderFile
78
+ Innate.node '/render_file'
79
+
80
+ layout :layout
81
+
82
+ def layout
83
+ '{ #{@content} }'
84
+ end
85
+
86
+ FILE = File.expand_path('../view/aspect_hello.xhtml', __FILE__)
87
+
88
+ def absolute
89
+ render_file(FILE)
90
+ end
91
+
92
+ def absolute_with(foo, bar)
93
+ render_file(FILE, :foo => foo, :bar => bar)
94
+ end
95
+ end
96
+
77
97
  describe Innate::Helper::Render do
78
98
  describe '#render_full' do
79
99
  behaves_like :mock
@@ -130,4 +150,16 @@ describe Innate::Helper::Render do
130
150
  get('/misc/recursive').body.scan(/\S/).join.should == '{1{2{3{44}3}2}1}'
131
151
  end
132
152
  end
153
+
154
+ describe '#render_file' do
155
+ behaves_like :mock
156
+
157
+ it 'renders file from absolute path' do
158
+ get('/render_file/absolute').body.should == '{ ! }'
159
+ end
160
+
161
+ it 'renders file from absolute path with variables' do
162
+ get('/render_file/absolute_with/one/two').body.should == '{ one two! }'
163
+ end
164
+ end
133
165
  end