innate 2009.04.12 → 2009.05

Sign up to get free protection for your applications and to get access to all the features.
Files changed (55) hide show
  1. data/AUTHORS +10 -0
  2. data/CHANGELOG +360 -0
  3. data/MANIFEST +8 -8
  4. data/README.md +1 -9
  5. data/Rakefile +7 -5
  6. data/example/app/whywiki_erb/start.rb +1 -1
  7. data/innate.gemspec +9 -5
  8. data/lib/innate.rb +6 -13
  9. data/lib/innate/action.rb +29 -33
  10. data/lib/innate/cache.rb +1 -1
  11. data/lib/innate/cache/drb.rb +5 -5
  12. data/lib/innate/cache/file_based.rb +3 -0
  13. data/lib/innate/cache/marshal.rb +4 -1
  14. data/lib/innate/cache/memory.rb +1 -2
  15. data/lib/innate/cache/yaml.rb +4 -1
  16. data/lib/innate/current.rb +11 -13
  17. data/lib/innate/dynamap.rb +5 -0
  18. data/lib/innate/helper.rb +11 -9
  19. data/lib/innate/helper/aspect.rb +9 -9
  20. data/lib/innate/helper/cgi.rb +3 -0
  21. data/lib/innate/helper/link.rb +2 -2
  22. data/lib/innate/helper/redirect.rb +1 -1
  23. data/lib/innate/helper/render.rb +70 -7
  24. data/lib/innate/helper/send_file.rb +9 -1
  25. data/lib/innate/log/color_formatter.rb +19 -13
  26. data/lib/innate/node.rb +38 -35
  27. data/lib/innate/options/dsl.rb +5 -2
  28. data/lib/innate/request.rb +1 -1
  29. data/lib/innate/response.rb +1 -7
  30. data/lib/innate/route.rb +4 -0
  31. data/lib/innate/session.rb +16 -14
  32. data/lib/innate/state.rb +10 -11
  33. data/lib/innate/state/accessor.rb +8 -8
  34. data/lib/innate/traited.rb +15 -10
  35. data/lib/innate/version.rb +1 -1
  36. data/lib/innate/view.rb +41 -4
  37. data/lib/innate/view/erb.rb +1 -2
  38. data/lib/innate/view/etanni.rb +9 -12
  39. data/spec/innate/action/layout.rb +0 -3
  40. data/spec/innate/helper/flash.rb +0 -3
  41. data/spec/innate/helper/link.rb +8 -0
  42. data/spec/innate/helper/redirect.rb +11 -0
  43. data/spec/innate/helper/render.rb +32 -0
  44. data/spec/innate/node/node.rb +1 -0
  45. data/spec/innate/options.rb +5 -1
  46. data/tasks/authors.rake +30 -0
  47. data/tasks/release.rake +48 -9
  48. data/tasks/ycov.rake +84 -0
  49. metadata +21 -13
  50. data/lib/innate/core_compatibility/basic_object.rb +0 -10
  51. data/lib/innate/core_compatibility/string.rb +0 -3
  52. data/lib/innate/state/fiber.rb +0 -74
  53. data/lib/innate/state/thread.rb +0 -47
  54. data/spec/innate/state/fiber.rb +0 -58
  55. data/spec/innate/state/thread.rb +0 -40
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.08"
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
@@ -56,6 +56,14 @@ describe Innate::Helper::Link do
56
56
  Two.route(:foo, :bar).should == URI('/two/foo/bar')
57
57
  end
58
58
 
59
+ should 'respond with URI for node with path /foo/bar+baz' do
60
+ One.route('/foo/bar+baz').should == URI('/foo/bar+baz')
61
+ One.route(:foo, 'bar baz').should == URI('/foo/bar+baz')
62
+
63
+ Two.route('/foo/bar+baz').should == URI('/two/foo/bar+baz')
64
+ Two.route(:foo, 'bar baz').should == URI('/two/foo/bar+baz')
65
+ end
66
+
59
67
  should 'respond with URI for node with GET params' do
60
68
  One.route('/', :a => :b).should == URI('/?a=b')
61
69
 
@@ -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
@@ -49,6 +49,7 @@ class SpecNodeWithLayoutView < SpecNodeProvide
49
49
  layout 'another_layout'
50
50
 
51
51
  map_views 'node/another_layout'
52
+ map_layouts 'another_layout'
52
53
  end
53
54
 
54
55
  class SpecNodeWithLayoutMethod < SpecNodeProvide
@@ -46,7 +46,7 @@ describe Options do
46
46
 
47
47
  should 'get sub-sub option' do
48
48
  @options.get(:deep, :down, :me).
49
- should == {:value => :too, :doc => 'deep down', :trigger=> nil}
49
+ should == {:value => :too, :doc => 'deep down'}
50
50
  end
51
51
 
52
52
  should 'respond with nil on getting missing option' do
@@ -98,6 +98,10 @@ describe Options do
98
98
  lambda{ @options[:foo] = :bar }.should.raise(ArgumentError)
99
99
  end
100
100
 
101
+ should "raise when trying to assign to an option that doesn't exist" do
102
+ lambda{ @options.merge!(:foo => :bar) }.should.raise(IndexError)
103
+ end
104
+
101
105
  should 'pretty_print' do
102
106
  require 'pp'
103
107
  p = PP.new
@@ -0,0 +1,30 @@
1
+ # Once git has a fix for the glibc in handling .mailmap and another fix for
2
+ # allowing empty mail address to be mapped in .mailmap we won't have to handle
3
+ # them manually.
4
+
5
+ desc 'Update AUTHORS'
6
+ task :authors do
7
+ authors = Hash.new(0)
8
+
9
+ `git shortlog -nse`.scan(/(\d+)\s(.+)\s<(.*)>$/) do |count, name, email|
10
+ case name
11
+ when "ahoward"
12
+ name, email = "Ara T. Howard", "ara.t.howard@gmail.com"
13
+ when "Martin Hilbig blueonyx@dev-area.net"
14
+ name, email = "Martin Hilbig", "blueonyx@dev-area.net"
15
+ when "Michael Fellinger m.fellinger@gmail.com"
16
+ name, email = "Michael Fellinger", "m.fellinger@gmail.com"
17
+ end
18
+
19
+ authors[[name, email]] += count.to_i
20
+ end
21
+
22
+ File.open('AUTHORS', 'w+') do |io|
23
+ io.puts "Following persons have contributed to #{GEMSPEC.name}."
24
+ io.puts '(Sorted by number of submitted patches, then alphabetically)'
25
+ io.puts ''
26
+ authors.sort_by{|(n,e),c| [-c, n.downcase] }.each do |(name, email), count|
27
+ io.puts("%6d %s <%s>" % [count, name, email])
28
+ end
29
+ end
30
+ end
data/tasks/release.rake CHANGED
@@ -1,12 +1,51 @@
1
- desc 'publish to github'
2
- task :release => [:reversion, :gemspec] do
3
- name, version = GEMSPEC.name, GEMSPEC.version
1
+ namespace :release do
2
+ task :all => [:release_github, :release_rubyforge]
4
3
 
5
- sh("git add MANIFEST CHANGELOG #{name}.gemspec lib/#{name}/version.rb")
4
+ desc 'Display instructions to release on github'
5
+ task :github => [:reversion, :authors, :gemspec] do
6
+ name, version = GEMSPEC.name, GEMSPEC.version
6
7
 
7
- puts "I added the relevant files, you can now run:", ''
8
- puts "git commit -m 'Version #{version}'"
9
- puts "git tag -a -m '#{version}' '#{version}'"
10
- puts "git push"
11
- puts
8
+ puts <<INSTRUCTIONS
9
+ First add the relevant files:
10
+
11
+ git add AUTHORS MANIFEST CHANGELOG #{name}.gemspec lib/#{name}/version.rb
12
+
13
+ Then commit them, tag the commit, and push:
14
+
15
+ git commit -m 'Version #{version}'
16
+ git tag -a -m '#{version}' '#{version}'
17
+ git push
18
+
19
+ INSTRUCTIONS
20
+
21
+ end
22
+
23
+ # TODO: Not tested
24
+ desc 'Display instructions to release on rubyforge'
25
+ task :rubyforge => [:reversion, :authors, :gemspec, :package] do
26
+ name, version = GEMSPEC.name, GEMSPEC.version
27
+
28
+ puts <<INSTRUCTIONS
29
+ To publish to rubyforge do following:
30
+
31
+ rubyforge login
32
+ rubyforge add_release #{name} '#{version}' pkg/#{name}-#{version}.gem
33
+
34
+ After you have done these steps, see:
35
+
36
+ rake release:rubyforge_archives
37
+
38
+ INSTRUCTIONS
39
+ end
40
+
41
+ desc 'Display instructions to add archives after release:rubyforge'
42
+ task :rubyforge_archives do
43
+ puts "Adding archives for distro packagers is:", ""
44
+
45
+ Dir["pkg/#{name}-#{version}.{gz,zip}"].each do |file|
46
+ puts "rubyforge add_file #{name} #{name} '#{version}' '#{file}'"
47
+ end
48
+
49
+ puts
50
+ end
12
51
  end