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
@@ -2,6 +2,9 @@ module Innate
2
2
  class Cache
3
3
 
4
4
  # Used by caches that serialize their contents to the filesystem.
5
+ # Right now we do not lock around write access to the file outside of the
6
+ # process, that means that all FileBased caches are not safe for use if you
7
+ # need more than one instance of your application.
5
8
  module FileBased
6
9
  attr_reader :filename
7
10
 
@@ -3,8 +3,11 @@ require 'pstore'
3
3
  module Innate
4
4
  class Cache
5
5
  # Keeps every cache in a separate file like this:
6
- #
7
6
  # /tmp/innate-cache-marshal/delta-manveru-session.marshal
7
+ #
8
+ # The Marshal cache is not safe for use between multiple processes, it is
9
+ # also slow compared to other caches, so generally the use of it is
10
+ # discouraged.
8
11
  class Marshal
9
12
  include Cache::API
10
13
  include Cache::FileBased
@@ -1,8 +1,7 @@
1
1
  module Innate
2
2
  class Cache
3
3
  # Memory cache is simply a Hash with the Cache::API, it's the reference
4
- # implementation for every other cache.
5
-
4
+ # implementation for every other cache and the default cache.
6
5
  class Memory < Hash
7
6
  include Cache::API
8
7
 
@@ -3,8 +3,11 @@ require 'yaml/store'
3
3
  module Innate
4
4
  class Cache
5
5
  # Keeps every cache in a separate file like this:
6
- #
7
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.
8
11
  class YAML
9
12
  include Cache::API
10
13
  include Cache::FileBased
@@ -2,9 +2,9 @@ require 'innate/request'
2
2
  require 'innate/response'
3
3
 
4
4
  module Innate
5
- # Uses STATE to scope request/response/session per Fiber/Thread so we can
6
- # reach them from anywhere in the code without passing around the objects
7
- # directly.
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
8
  class Current
9
9
  extend Trinity
10
10
 
@@ -16,22 +16,20 @@ module Innate
16
16
  end
17
17
  end
18
18
 
19
- # Wrap into STATE, run setup and call the app inside STATE.
20
-
19
+ # Run setup and call the app
21
20
  def call(env)
22
- STATE.wrap do
23
- setup(env)
24
- @app.call(env)
25
- end
21
+ setup(env)
22
+ @app.call(env)
26
23
  end
27
24
 
28
25
  # Setup new Request/Response/Session for this request/response cycle.
29
26
  # The parameters are here to allow Ramaze to inject its own classes.
30
27
  def setup(env, request = Request, response = Response, session = Session)
31
- req = STATE[:request] = request.new(env)
32
- res = STATE[:response] = response.new
33
- STATE[:actions] = []
34
- STATE[:session] = Session.new(req, res)
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)
35
33
  end
36
34
  end
37
35
  end
@@ -18,6 +18,11 @@ module Innate
18
18
  remap(@originals.merge(location.to_s => object))
19
19
  end
20
20
 
21
+ def delete(location)
22
+ @originals.delete(location)
23
+ remap(@originals)
24
+ end
25
+
21
26
  def at(location)
22
27
  @originals[location]
23
28
  end
data/lib/innate/helper.rb CHANGED
@@ -165,19 +165,21 @@ module Innate
165
165
  # Figure out files that might have the helper we ask for and then require
166
166
  # the first we find, if any.
167
167
  def try_require(name)
168
- if found = Dir[glob(name)].first
168
+ if found = find_helper(name.to_s)
169
169
  require(found) || true
170
170
  else
171
171
  raise(LoadError, "Helper #{name} not found")
172
172
  end
173
173
  end
174
174
 
175
- # Return a nice list of filenames in correct locations with correct
176
- # filename-extensions.
177
- def glob(name = '*')
178
- exts, paths = options.exts, options.paths
179
- paths.uniq!
180
- "{#{paths.join(',')}}/helper/#{name}.{#{exts.join(',')}}"
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
181
183
  end
182
184
  end
183
185
  end
@@ -94,28 +94,28 @@ module Innate
94
94
  AOP[self][:before_all] = block
95
95
  end
96
96
 
97
- def before(name, &block)
98
- AOP[self][:before][name] = block
97
+ def before(*names, &block)
98
+ names.each{|name| AOP[self][:before][name] = block }
99
99
  end
100
100
 
101
101
  def after_all(&block)
102
102
  AOP[self][:after_all] = block
103
103
  end
104
104
 
105
- def after(name, &block)
106
- AOP[self][:after][name] = block
105
+ def after(*names, &block)
106
+ names.each{|name| AOP[self][:after][name] = block }
107
107
  end
108
108
 
109
- def wrap(name, &block)
110
- before(name, &block)
111
- after(name, &block)
109
+ def wrap(*names, &block)
110
+ before(*names, &block)
111
+ after(*names, &block)
112
112
  end
113
113
 
114
114
  def add_action_wrapper(order, method_name)
115
115
  if wrap = trait[:wrap]
116
- wrap.merge(SortedSet[[order, method_name]])
116
+ wrap.merge(SortedSet[[order, method_name.to_s]])
117
117
  else
118
- trait :wrap => SortedSet[[order, method_name]]
118
+ trait :wrap => SortedSet[[order, method_name.to_s]]
119
119
  end
120
120
  end
121
121
  end
@@ -46,6 +46,9 @@ module Innate
46
46
  Rack::Utils.escape_html(input.to_s).gsub(/#([{@$]@?)/, '&#35;\1')
47
47
  end
48
48
  alias h html_and_code_escape
49
+
50
+ # aliases are ignored by module_function...
51
+ module_function :u, :h
49
52
  end
50
53
  end
51
54
  end
@@ -57,7 +57,7 @@ module Innate
57
57
  end
58
58
 
59
59
  def raw_redirect(target, options = {}, &block)
60
- header = {'Location' => target.to_s}
60
+ header = response.header.merge('Location' => target.to_s)
61
61
  status = options[:status] || 302
62
62
  body = options[:body] || redirect_body(target)
63
63
 
@@ -5,7 +5,6 @@ module Innate
5
5
  #
6
6
  # @example of added functionality
7
7
  # YourController.render_partial(:foo, :x => 42)
8
- #
9
8
  def self.included(into)
10
9
  into.extend(self)
11
10
  end
@@ -20,6 +19,14 @@ module Innate
20
19
  #
21
20
  # As usual, patches welcome.
22
21
  #
22
+ # @example usage
23
+ #
24
+ # render_full('/blog/article/1')
25
+ # render_full('/blog/article/1', :lang => :de)
26
+ #
27
+ # Please note that you have to give the full path in the same way you'd
28
+ # do in a direct request with curl or a browser.
29
+ #
23
30
  # @api external
24
31
  # @see Mock.session
25
32
  # @author manveru
@@ -38,17 +45,44 @@ module Innate
38
45
  end
39
46
 
40
47
  # Renders an action without any layout.
48
+ # You can further tweak the action to be rendered by passing a block.
49
+ #
50
+ # @example usage
51
+ #
52
+ # render_partial(:index)
53
+ # render_partial(:index, :title => :foo)
54
+ #
55
+ # Please note that you only have to supply the action name, if your
56
+ # action requires arguments then you have to pass a name suitable for
57
+ # that.
58
+ #
59
+ # @example usage with action that requires arguments
60
+ #
61
+ # # requires two arguments
62
+ # def foo(a, b)
63
+ # end
64
+ #
65
+ # # pass two suitable arguments
66
+ # render_partial('foo/1/2')
67
+ #
41
68
  # @api external
42
69
  # @see render_custom
43
70
  # @author manveru
44
71
  def render_partial(action_name, variables = {})
45
72
  render_custom(action_name, variables) do |action|
46
73
  action.layout = nil
74
+ yield(action) if block_given?
47
75
  end
48
76
  end
49
77
 
50
78
  # Renders an action view, doesn't execute any methods and won't wrap it
51
79
  # into a layout.
80
+ # You can further tweak the action to be rendered by passing a block.
81
+ #
82
+ # @example usage
83
+ #
84
+ # render_view(:index)
85
+ # render_view(:index, :title => :foo)
52
86
  #
53
87
  # @api external
54
88
  # @see render_custom
@@ -57,12 +91,41 @@ module Innate
57
91
  render_custom(action_name, variables) do |action|
58
92
  action.layout = nil
59
93
  action.method = nil
94
+ yield(action) if block_given?
95
+ end
96
+ end
97
+
98
+ # Use the given file as a template and render it in the same scope as
99
+ # the current action.
100
+ # The +filename+ may be an absolute path or relative to the process
101
+ # working directory.
102
+ #
103
+ # @example usage
104
+ #
105
+ # path = '/home/manveru/example/app/todo/view/index.xhtml'
106
+ # render_file(path)
107
+ # render_file(path, :title => :foo)
108
+ #
109
+ # Ramaze will emit a warning if you try to render an Action without a
110
+ # method or view template, but will still try to render it.
111
+ # The usual {Action#valid?} doesn't apply here, as sometimes you just
112
+ # cannot have a method associated with a template.
113
+ #
114
+ # @api external
115
+ # @see render_custom
116
+ # @author manveru
117
+ def render_file(filename, variables = {})
118
+ render_custom(action.path, variables) do |action|
119
+ action.layout = nil
120
+ action.method = nil
121
+ action.view = filename
122
+ yield(action) if block_given?
60
123
  end
61
124
  end
62
125
 
63
126
  def render_custom(action_name, variables = {})
64
127
  unless action = resolve(action_name.to_s)
65
- raise(ArgumentError, "No Action %p on #{self}" % action_name)
128
+ raise(ArgumentError, "No Action %p on #{self}" % [action_name])
66
129
  end
67
130
 
68
131
  action.sync_variables(self.action)
@@ -71,11 +134,9 @@ module Innate
71
134
 
72
135
  yield(action) if block_given?
73
136
 
74
- if action.valid?
75
- action.render
76
- else
77
- Log.warn("Invalid action: %p" % action)
78
- end
137
+ valid_action = action.view || action.method
138
+ Log.warn("Empty action: %p" % [action]) unless valid_action
139
+ action.render
79
140
  end
80
141
  end
81
142
  end
@@ -1,19 +1,25 @@
1
1
  class Logger
2
2
  # Extended Formatter that supports ANSI colors.
3
+ #
4
+ # The basic mapping of ANSI colors is as follows:
5
+ #
6
+ # | reset | bold | dark | underline | blink | negative
7
+ # MOD | 0 | 1 | 2 | 4 | 5 | 7
8
+ #
9
+ # | black | red | green | yellow | blue | magenta | cyan | white
10
+ # FG | 30 | 31 | 32 | 33 | 34 | 35 | 36 | 37
11
+ # BG | 40 | 41 | 42 | 43 | 44 | 45 | 46 | 47
12
+ #
13
+ # The output is done by: "\e[#{mod};#{fg};#{bg}m#{string}\e[0m"
14
+ # The suffix is to reset the terminal to the original state again.
3
15
  class ColorFormatter < Formatter
4
16
  LEVEL_COLOR = {
5
- 'DEBUG' => :blue,
6
- 'INFO' => :white,
7
- 'WARN' => :yellow,
8
- 'ERROR' => :red,
9
- 'FATAL' => :red,
10
- 'UNKNOWN' => :green,
11
- }
12
-
13
- COLOR_CODE = {
14
- :reset => 0, :bold => 1, :dark => 2, :underline => 4, :blink => 5,
15
- :negative => 7, :black => 30, :red => 31, :green => 32, :yellow => 33,
16
- :blue => 34, :magenta => 35, :cyan => 36, :white => 37,
17
+ 'DEBUG' => "\e[0;34;40m%s\e[0m", # blue on black
18
+ 'INFO' => "\e[0;37;40m%s\e[0m", # white on black
19
+ 'WARN' => "\e[0;33;40m%s\e[0m", # yellow on black
20
+ 'ERROR' => "\e[0;31;40m%s\e[0m", # red on black
21
+ 'FATAL' => "\e[0;35;40m%s\e[0m", # red on black
22
+ 'UNKNOWN' => "\e[0;32;40m%s\e[0m", # green on black
17
23
  }
18
24
 
19
25
  FORMAT_TIME = "%Y-%m-%d %H:%M:%S"
@@ -33,7 +39,7 @@ class Logger
33
39
  end
34
40
 
35
41
  def colorize(string, severity)
36
- "\e[#{COLOR_CODE[LEVEL_COLOR[severity]]}m#{string}\e[0m"
42
+ LEVEL_COLOR[severity] % string
37
43
  end
38
44
 
39
45
  def self.color?(logdev)
data/lib/innate/node.rb CHANGED
@@ -244,7 +244,7 @@ module Innate
244
244
  # with feedback in your logs.
245
245
  #
246
246
  # A lot of functionality in here relies on the fact that call is executed
247
- # within Innate::STATE.wrap which populates the variables used by Trinity.
247
+ # within Current#call which populates the variables used by Trinity.
248
248
  # So if you use the Node directly as a middleware make sure that you #use
249
249
  # Innate::Current as a middleware before it.
250
250
  #
@@ -261,11 +261,7 @@ module Innate
261
261
  path << '/' if path.empty?
262
262
 
263
263
  response.reset
264
- response = try_resolve(path)
265
-
266
- Current.session.flush(response)
267
-
268
- response.finish
264
+ try_resolve(path).finish
269
265
  end
270
266
 
271
267
  # Let's try to find some valid action for given +path+.
@@ -364,7 +360,7 @@ module Innate
364
360
  def resolve(path)
365
361
  name, wish, engine = find_provide(path)
366
362
  node = (respond_to?(:ancestors) && respond_to?(:new)) ? self : self.class
367
- action = Action.create(:node => node, :wish => wish, :engine => engine)
363
+ action = Action.create(:node => node, :wish => wish, :engine => engine, :path => path)
368
364
 
369
365
  if content_type = node.ancestral_trait["#{wish}_content_type"]
370
366
  action.options = {:content_type => content_type}
@@ -742,8 +738,6 @@ module Innate
742
738
  #
743
739
  # Since Innate supports multiple paths to templates the +path+ has to be an
744
740
  # Array that may be nested one level.
745
- # The +path+ is then translated by {Node#path_glob} and the +wish+ by
746
- # {Node#ext_glob}.
747
741
  #
748
742
  # @example Usage to find available templates
749
743
  #
@@ -779,7 +773,6 @@ module Innate
779
773
  #
780
774
  # @api external
781
775
  # @see Node#find_view Node#to_layout Node#find_aliased_view
782
- # Node#path_glob Node#ext_glob
783
776
  # @author manveru
784
777
  def to_template(path, wish)
785
778
  to_view(path, wish) || to_layout(path, wish)
@@ -802,22 +795,27 @@ module Innate
802
795
 
803
796
  def update_mapping_shared(paths)
804
797
  mapping = {}
798
+ paths.reject!{|path| !File.directory?(path) }
805
799
 
806
800
  provides.each do |wish_key, engine|
807
801
  wish = wish_key[/(.*)_handler/, 1]
808
- ext_glob = ext_glob(wish)
802
+ exts = possible_exts_for(wish)
809
803
 
810
804
  paths.reverse_each do |path|
811
- ::Dir.glob(::File.join(path, "/**/*.#{ext_glob}")) do |file|
812
- case file.sub(path, '').gsub('/', '__')
813
- when /^(.*)\.(.*)\.(.*)$/
814
- action_name, wish_ext, engine_ext = $1, $2, $3
815
- when /^(.*)\.(.*)$/
816
- action_name, wish_ext, engine_ext = $1, wish, $2
805
+ Find.find(path) do |file|
806
+ exts.each do |ext|
807
+ next unless file =~ ext
808
+
809
+ case file.sub(path, '').gsub('/', '__')
810
+ when /^(.*)\.(.*)\.(.*)$/
811
+ action_name, wish_ext, engine_ext = $1, $2, $3
812
+ when /^(.*)\.(.*)$/
813
+ action_name, wish_ext, engine_ext = $1, wish, $2
814
+ end
815
+
816
+ mapping[wish_ext] ||= {}
817
+ mapping[wish_ext][action_name] = file
817
818
  end
818
-
819
- mapping[wish_ext] ||= {}
820
- mapping[wish_ext][action_name] = file
821
819
  end
822
820
  end
823
821
  end
@@ -825,32 +823,37 @@ module Innate
825
823
  return mapping
826
824
  end
827
825
 
826
+ # Answer with an array of possible paths in order of significance for
827
+ # template lookup of the given +mappings+.
828
+ #
829
+ # @param [#map] An array two Arrays of inner and outer directories.
830
+ #
831
+ # @return [Array]
832
+ # @see update_view_mappings update_layout_mappings update_template_mappings
833
+ # @author manveru
828
834
  def possible_paths_for(mappings)
829
- root_mappings.map{|root_mapping|
830
- mappings.first.map{|outer_mapping|
831
- mappings.last.map{|inner_mapping|
832
- File.join(root_mapping, outer_mapping, inner_mapping, '/')
833
- }
834
- }
835
- }.flatten
835
+ root_mappings.map{|root|
836
+ mappings.first.map{|inner|
837
+ mappings.last.map{|outer|
838
+ ::File.join(root, inner, outer, '/') }}}.flatten
836
839
  end
837
840
 
838
- # Produce a glob that can be processed by Dir::[] matching the extensions
839
- # associated with the given +wish+.
841
+ # Answer with an array of possible extensions in order of significance for
842
+ # the given +wish+.
840
843
  #
841
844
  # @param [#to_s] wish the extension (no leading '.')
842
845
  #
843
- # @return [String] glob matching the valid exts for the given +wish+
846
+ # @return [Array] list of exts valid for this +wish+
844
847
  #
845
848
  # @api internal
846
849
  # @see Node#to_template View::exts_of Node#provides
847
850
  # @author manveru
848
- def ext_glob(wish)
851
+ def possible_exts_for(wish)
849
852
  pr = provides
850
853
  return unless engine = pr["#{wish}_handler"]
851
- engine_exts = View.exts_of(engine).join(',')
852
- represented = [*wish].map{|k| "#{k}." }.join(',')
853
- "{%s,}{%s}" % [represented, engine_exts]
854
+ View.exts_of(engine).map{|e_ext|
855
+ [[*wish].map{|w_ext| /#{w_ext}\.#{e_ext}$/ }, /#{e_ext}$/]
856
+ }.flatten
854
857
  end
855
858
 
856
859
  # For compatibility with new Kernel#binding behaviour in 1.9
@@ -929,7 +932,7 @@ module Innate
929
932
  # @author manveru
930
933
  def layout_mappings
931
934
  paths = [*ancestral_trait[:layouts]]
932
- paths = [mapping] if paths.empty?
935
+ paths = ['/'] if paths.empty?
933
936
 
934
937
  [[*options.layouts].flatten, [*paths].flatten]
935
938
  end