manveru-innate 2009.04 → 2009.04.01

Sign up to get free protection for your applications and to get access to all the features.
Files changed (82) hide show
  1. data/CHANGELOG +0 -258
  2. data/MANIFEST +22 -23
  3. data/README.md +9 -1
  4. data/Rakefile +6 -6
  5. data/example/app/retro_games.rb +8 -8
  6. data/example/app/todo/layout/{default.xhtml → default.erb} +1 -1
  7. data/example/app/todo/view/{index.xhtml → index.erb} +11 -11
  8. data/example/app/whywiki_erb/start.rb +1 -2
  9. data/example/app/whywiki_erb/view/{edit.erb → edit.html.erb} +0 -0
  10. data/example/app/whywiki_erb/view/{index.erb → index.html.erb} +0 -0
  11. data/example/howto_spec.rb +1 -1
  12. data/example/provides.rb +6 -9
  13. data/example/session.rb +3 -3
  14. data/innate.gemspec +14 -6
  15. data/lib/innate.rb +18 -12
  16. data/lib/innate/action.rb +11 -22
  17. data/lib/innate/cache/file_based.rb +0 -2
  18. data/lib/innate/core_compatibility/basic_object.rb +10 -0
  19. data/lib/innate/core_compatibility/string.rb +3 -0
  20. data/lib/innate/dynamap.rb +0 -5
  21. data/lib/innate/helper.rb +29 -20
  22. data/lib/innate/helper/cgi.rb +19 -32
  23. data/lib/innate/helper/link.rb +2 -2
  24. data/lib/innate/helper/partial.rb +93 -0
  25. data/lib/innate/helper/redirect.rb +1 -1
  26. data/lib/innate/helper/send_file.rb +1 -9
  27. data/lib/innate/mock.rb +3 -2
  28. data/lib/innate/node.rb +51 -78
  29. data/lib/innate/options.rb +1 -1
  30. data/lib/innate/request.rb +23 -3
  31. data/lib/innate/response.rb +7 -0
  32. data/lib/innate/session.rb +2 -1
  33. data/lib/innate/spec.rb +50 -6
  34. data/lib/innate/version.rb +1 -1
  35. data/lib/innate/view/erb.rb +1 -1
  36. data/lib/innate/view/etanni.rb +2 -2
  37. data/lib/innate/view/none.rb +1 -1
  38. data/spec/example/session.rb +14 -8
  39. data/spec/innate/action/layout.rb +1 -1
  40. data/spec/innate/action/layout/file_layout.erb +1 -0
  41. data/spec/innate/helper/aspect.rb +6 -6
  42. data/spec/innate/helper/flash.rb +47 -28
  43. data/spec/innate/helper/link.rb +0 -8
  44. data/spec/innate/helper/partial.rb +101 -0
  45. data/spec/innate/helper/redirect.rb +43 -58
  46. data/spec/innate/helper/view/aspect_hello.erb +1 -0
  47. data/spec/innate/helper/view/locals.erb +1 -0
  48. data/spec/innate/helper/view/loop.erb +4 -0
  49. data/spec/innate/helper/view/num.erb +1 -0
  50. data/spec/innate/helper/view/partial.erb +1 -0
  51. data/spec/innate/helper/view/recursive.erb +8 -0
  52. data/spec/innate/node/node.rb +13 -5
  53. data/spec/innate/node/view/another_layout/{another_layout.xhtml → another_layout.erb} +1 -1
  54. data/spec/innate/node/view/{bar.xhtml → bar.erb} +0 -0
  55. data/spec/innate/node/view/foo.html.erb +1 -0
  56. data/spec/innate/node/view/{only_view.xhtml → only_view.erb} +0 -0
  57. data/spec/innate/node/view/with_layout.erb +1 -0
  58. data/spec/innate/provides.rb +2 -2
  59. data/spec/innate/provides/list.html.erb +1 -0
  60. data/spec/innate/provides/list.txt.erb +1 -0
  61. data/spec/innate/session.rb +15 -14
  62. data/spec/innate/state/fiber.rb +7 -8
  63. data/tasks/bacon.rake +21 -38
  64. data/tasks/install_dependencies.rake +4 -2
  65. data/tasks/release.rake +9 -48
  66. metadata +57 -29
  67. data/lib/innate/helper/render.rb +0 -87
  68. data/spec/example/app/retro_games.rb +0 -30
  69. data/spec/example/provides.rb +0 -16
  70. data/spec/innate/action/layout/file_layout.xhtml +0 -1
  71. data/spec/innate/helper/render.rb +0 -133
  72. data/spec/innate/helper/view/aspect_hello.xhtml +0 -1
  73. data/spec/innate/helper/view/locals.xhtml +0 -1
  74. data/spec/innate/helper/view/loop.xhtml +0 -4
  75. data/spec/innate/helper/view/num.xhtml +0 -1
  76. data/spec/innate/helper/view/partial.xhtml +0 -1
  77. data/spec/innate/helper/view/recursive.xhtml +0 -7
  78. data/spec/innate/node/view/foo.html.xhtml +0 -1
  79. data/spec/innate/node/view/with_layout.xhtml +0 -1
  80. data/spec/innate/provides/list.html.xhtml +0 -1
  81. data/spec/innate/provides/list.txt.xhtml +0 -1
  82. data/tasks/setup.rake +0 -28
@@ -6,49 +6,36 @@ module Innate
6
6
 
7
7
  module Helper
8
8
  module CGI
9
- module_function
10
-
11
- # Shortcut for Rack::Utils.escape
12
- #
13
- # @param [#to_s] input
14
- # @return [String] URI-encoded representation of +input+
15
- def url_encode(input)
16
- Rack::Utils.escape(input.to_s)
9
+ # shortcut for Rack::Utils.escape
10
+ def url_encode(*args)
11
+ Rack::Utils.escape(*args.map{|a| a.to_s })
17
12
  end
18
- alias u url_encode
19
13
 
20
- # Shortcut for Rack::Utils.unescape
21
- #
22
- # @param [#to_s] input
23
- # @return [String] URI-decoded representation of +input+
24
- def url_decode(input)
25
- Rack::Utils.unescape(input.to_s)
14
+ # shortcut for Rack::Utils.unescape
15
+ def url_decode(*args)
16
+ Rack::Utils.unescape(*args.map{|a| a.to_s })
26
17
  end
27
18
 
28
- # Shortcut for Rack::Utils.escape_html
29
- #
30
- # @param [#to_s] input
31
- # @return [String]
32
- def html_escape(input)
33
- Rack::Utils.escape_html(input.to_s)
19
+ # shortcut for Rack::Utils.escape_html
20
+ def html_escape(string)
21
+ Rack::Utils.escape_html(string)
34
22
  end
35
23
 
36
- # Shortcut for CGI.unescapeHTML
37
- #
38
- # @param [#to_s] input
39
- # @return [String]
40
- def html_unescape(input)
41
- ::CGI.unescapeHTML(input.to_s)
24
+ # shortcut for CGI.unescapeHTML
25
+ def html_unescape(string)
26
+ ::CGI.unescapeHTML(string.to_s)
42
27
  end
43
28
 
44
29
  # safely escape all HTML and code
45
- def html_and_code_escape(input)
46
- Rack::Utils.escape_html(input.to_s).gsub(/#([{@$]@?)/, '#\1')
30
+ def h(string)
31
+ Rack::Utils.escape_html(string).gsub(/#([{@$]@?)/, '#\1')
47
32
  end
48
- alias h html_and_code_escape
49
33
 
50
- # aliases are ignored by module_function...
51
- module_function :u, :h
34
+ # one-letter versions help in case like #{h foo.inspect}
35
+ # ERb/ERuby/Rails compatible
36
+ alias u url_encode
37
+
38
+ module_function(:url_encode, :url_decode, :html_escape, :html_unescape, :h, :u)
52
39
  end
53
40
  end
54
41
  end
@@ -28,12 +28,12 @@ module Innate
28
28
  hashes, names = args.partition{|arg| arg.respond_to?(:merge!) }
29
29
  hashes.each{|to_merge| hash.merge!(to_merge) }
30
30
 
31
- escape = Rack::Utils.method(:escape)
32
31
  location = route_location(self)
33
- front = Array[location, name, *names.map{|n| escape[n]}].join('/').squeeze('/')
32
+ front = Array[location, name, *names].join('/').squeeze('/')
34
33
 
35
34
  return URI(front) if hash.empty?
36
35
 
36
+ escape = Rack::Utils.method(:escape)
37
37
  query = hash.map{|k, v| "#{escape[k]}=#{escape[v]}" }.join(';')
38
38
  URI("#{front}?#{query}")
39
39
  end
@@ -0,0 +1,93 @@
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
+ module Innate
5
+ module Helper
6
+
7
+ # = Helper::Partial
8
+ #
9
+ # === Example Usage
10
+ #
11
+ # class MyController
12
+ # def index
13
+ # end
14
+ #
15
+ # def list
16
+ # plain = request['plain']
17
+ # "Hello World from List! Plain List == #{plain}"
18
+ # end
19
+ # end
20
+ #
21
+ #
22
+ # <html>
23
+ # <head><title>Partial Render Index</title></head>
24
+ # <body>
25
+ # #{render_partial(Rs(:list), 'plain' => true)}
26
+ # </body>
27
+ # </html>
28
+ module Partial
29
+ module_function
30
+
31
+ # Renders a url 'inline'.
32
+ #
33
+ # +url+ normal URL, like you'd use for redirecting.
34
+ # +options+ optional, will be used as request parameters.
35
+ #
36
+ # Issues a mock request to the given +url+ with +options+ turned into
37
+ # query arguments.
38
+ def render_partial(url, options = {})
39
+ uri = URI(url)
40
+ query = options # Innate::Current.request.params.merge(options)
41
+ uri.query = Rack::Utils.build_query(query)
42
+
43
+ body = nil
44
+
45
+ Innate::Mock.session do |session|
46
+ cookie = Innate::Current.session.cookie
47
+ session.cookie = cookie
48
+ body = session.get(uri.to_s, options).body
49
+ end
50
+
51
+ body
52
+ end
53
+
54
+ # Render the template file in view_root of the
55
+ # current controller.
56
+ #
57
+ # TODO:
58
+ # * Doesn't work for absolute paths, but there are no specs for that yet.
59
+ # * the local variable hack isn't working because innate allocates a new
60
+ # binding.
61
+ # For now one can simply use instance variables, which I prefer anyway.
62
+ #
63
+ # the local binding hack:
64
+ #
65
+ # variables.each do |key, value|
66
+ # value = "ObjectSpace._id2ref(#{value.object_id})"
67
+ # eval "#{key} = #{value}", action.binding
68
+ # end
69
+
70
+ def render_template(path, variables = {})
71
+ path = path.to_s
72
+
73
+ ext = File.extname(path)
74
+ basename = File.basename(path, ext)
75
+
76
+ action = Innate::Current.action.dup
77
+ action.layout = nil
78
+ action.view = action.node.find_view(basename, 'html')
79
+ action.method = action.node.find_method(basename, action.params)
80
+
81
+ action.variables = action.variables.merge(variables)
82
+ action.sync_variables(action)
83
+
84
+ return action.call if action.valid?
85
+ raise(ArgumentError, "cannot render %p" % path)
86
+ end
87
+
88
+ def render_action(method, *params)
89
+ render_partial(r(method), *params)
90
+ end
91
+ end
92
+ end
93
+ end
@@ -57,7 +57,7 @@ module Innate
57
57
  end
58
58
 
59
59
  def raw_redirect(target, options = {}, &block)
60
- header = response.header.merge('Location' => target.to_s)
60
+ header = {'Location' => target.to_s}
61
61
  status = options[:status] || 302
62
62
  body = options[:body] || redirect_body(target)
63
63
 
@@ -3,20 +3,12 @@ module Innate
3
3
  module SendFile
4
4
  # Not optimally performing but convenient way to send files by their
5
5
  # filename.
6
- #
7
- # I think we should remove this from the default helpers and move it into
8
- # Ramaze, the functionality is almost never used, the naming is ambigous,
9
- # and it doesn't use the send_file capabilities of frontend servers.
10
- #
11
- # So for now, I'll mark it for deprecation
12
- def send_file(filename, content_type = nil, content_disposition = nil)
6
+ def send_file(filename, content_type = nil)
13
7
  content_type ||= Rack::Mime.mime_type(::File.extname(filename))
14
- content_disposition ||= File.basename(filename)
15
8
 
16
9
  response.body = ::File.readlines(filename, 'rb')
17
10
  response['Content-Length'] = ::File.size(filename).to_s
18
11
  response['Content-Type'] = content_type
19
- response['Content-Disposition'] = content_disposition
20
12
  response.status = 200
21
13
 
22
14
  throw(:respond, response)
@@ -39,8 +39,9 @@ module Innate
39
39
  hash['HTTP_COOKIE'] ||= @cookie if @cookie
40
40
  response = Mock::mock(method, path, hash)
41
41
 
42
- cookie = response['Set-Cookie']
43
- @cookie = cookie if cookie
42
+ if cookie = response['Set-Cookie']
43
+ @cookie = cookie
44
+ end
44
45
 
45
46
  response
46
47
  end
@@ -28,8 +28,7 @@ module Innate
28
28
  module Node
29
29
  include Traited
30
30
 
31
- attr_reader :method_arities, :layout_templates, :view_templates
32
-
31
+ DEFAULT_HELPERS = %w[aspect cgi flash link partial redirect send_file]
33
32
  NODE_LIST = Set.new
34
33
 
35
34
  # These traits are inherited into ancestors, changing a trait in an
@@ -57,6 +56,8 @@ module Innate
57
56
  # Upon inclusion we make ourselves comfortable.
58
57
  def self.included(into)
59
58
  into.__send__(:include, Helper)
59
+ into.helper(*DEFAULT_HELPERS)
60
+
60
61
  into.extend(Trinity, self)
61
62
 
62
63
  NODE_LIST << into
@@ -364,14 +365,13 @@ module Innate
364
365
  def resolve(path)
365
366
  name, wish, engine = find_provide(path)
366
367
  node = (respond_to?(:ancestors) && respond_to?(:new)) ? self : self.class
367
- action = Action.create(:node => node, :wish => wish, :engine => engine, :path => path)
368
+ action = Action.create(:node => node, :wish => wish, :engine => engine)
368
369
 
369
370
  if content_type = node.ancestral_trait["#{wish}_content_type"]
370
371
  action.options = {:content_type => content_type}
371
372
  end
372
373
 
373
374
  node.update_method_arities
374
- node.update_template_mappings
375
375
  node.fill_action(action, name)
376
376
  end
377
377
 
@@ -537,10 +537,12 @@ module Innate
537
537
  @method_arities
538
538
  end
539
539
 
540
+ attr_reader :method_arities
541
+
540
542
  # Try to find the best template for the given basename and wish and respect
541
543
  # aliased views.
542
544
  #
543
- # @param [#to_s] action_name
545
+ # @param [#to_s] file
544
546
  # @param [#to_s] wish
545
547
  #
546
548
  # @return [String, nil] depending whether a template could be found
@@ -548,11 +550,11 @@ module Innate
548
550
  # @api external
549
551
  # @see Node#to_template Node#find_aliased_view
550
552
  # @author manveru
551
- def find_view(action_name, wish)
552
- aliased = find_aliased_view(action_name, wish)
553
+ def find_view(file, wish)
554
+ aliased = find_aliased_view(file, wish)
553
555
  return aliased if aliased
554
556
 
555
- to_view(action_name, wish)
557
+ to_view(file, wish)
556
558
  end
557
559
 
558
560
  # Try to find the best template for the given basename and wish.
@@ -560,7 +562,7 @@ module Innate
560
562
  # This method is mostly here for symetry with {to_layout} and to allow you
561
563
  # overriding the template lookup easily.
562
564
  #
563
- # @param [#to_s] action_name
565
+ # @param [#to_s] file
564
566
  # @param [#to_s] wish
565
567
  #
566
568
  # @return [String, nil] depending whether a template could be found
@@ -569,9 +571,9 @@ module Innate
569
571
  # @see {Node#find_view} {Node#to_template} {Node#root_mappings}
570
572
  # {Node#view_mappings} {Node#to_template}
571
573
  # @author manveru
572
- def to_view(action_name, wish)
573
- return unless files = view_templates[wish.to_s]
574
- files[action_name.to_s]
574
+ def to_view(file, wish)
575
+ path = [root_mappings].concat(view_mappings) << file
576
+ to_template(path, wish)
575
577
  end
576
578
 
577
579
  # Aliasing one view from another.
@@ -609,9 +611,9 @@ module Innate
609
611
  trait[:alias_view][to.to_s] = node ? [from.to_s, node] : from.to_s
610
612
  end
611
613
 
612
- # Resolve one level of aliasing for the given +action_name+ and +wish+.
614
+ # Resolve one level of aliasing for the given +file+ and +wish+.
613
615
  #
614
- # @param [String] action_name
616
+ # @param [String] file
615
617
  # @param [String] wish
616
618
  #
617
619
  # @return [nil, String] the absolute path to the aliased template or nil
@@ -619,21 +621,18 @@ module Innate
619
621
  # @api internal
620
622
  # @see Node::alias_view Node::find_view
621
623
  # @author manveru
622
- def find_aliased_view(action_name, wish)
623
- aliased_name, aliased_node = ancestral_trait[:alias_view][action_name]
624
- return unless aliased_name
625
-
624
+ def find_aliased_view(file, wish)
625
+ aliased_file, aliased_node = ancestral_trait[:alias_view][file]
626
626
  aliased_node ||= self
627
- aliased_node.update_view_mappings
628
- aliased_node.find_view(aliased_name, wish)
627
+ aliased_node.find_view(aliased_file, wish) if aliased_file
629
628
  end
630
629
 
631
- # Find the best matching action_name for the layout, if any.
630
+ # Find the best matching file for the layout, if any.
632
631
  #
633
632
  # This is mostly an abstract method that you might find handy if you want
634
633
  # to do vastly different layout lookup.
635
634
  #
636
- # @param [String] action_name
635
+ # @param [String] file
637
636
  # @param [String] wish
638
637
  #
639
638
  # @return [nil, String] the absolute path to the template or nil
@@ -641,9 +640,9 @@ module Innate
641
640
  # @api external
642
641
  # @see {Node#to_template} {Node#root_mappings} {Node#layout_mappings}
643
642
  # @author manveru
644
- def to_layout(action_name, wish)
645
- return unless files = layout_templates[wish.to_s]
646
- files[action_name.to_s]
643
+ def to_layout(file, wish)
644
+ path = [root_mappings].concat(layout_mappings) << file
645
+ to_template(path, wish)
647
646
  end
648
647
 
649
648
  # Define a layout to use on this Node.
@@ -728,11 +727,11 @@ module Innate
728
727
  result = nil
729
728
 
730
729
  atoms.size.downto(0) do |len|
731
- action_name = atoms[0...len].join('__')
730
+ action = atoms[0...len].join('__')
732
731
  params = atoms[len..-1]
733
- action_name = 'index' if action_name.empty? and params != ['index']
732
+ action = 'index' if action.empty? and params != ['index']
734
733
 
735
- return result if result = yield(action_name, params)
734
+ return result if result = yield(action, params)
736
735
  end
737
736
 
738
737
  return nil
@@ -782,57 +781,31 @@ module Innate
782
781
  # Node#path_glob Node#ext_glob
783
782
  # @author manveru
784
783
  def to_template(path, wish)
785
- to_view(path, wish) || to_layout(path, wish)
786
- end
787
-
788
- def update_template_mappings
789
- update_view_mappings
790
- update_layout_mappings
791
- end
792
-
793
- def update_view_mappings
794
- paths = possible_paths_for(view_mappings)
795
- @view_templates = update_mapping_shared(paths)
796
- end
784
+ return unless exts = ext_glob(wish)
785
+ glob = "#{path_glob(*path)}.#{exts}"
786
+ found = Dir[glob].uniq
797
787
 
798
- def update_layout_mappings
799
- paths = possible_paths_for(layout_mappings)
800
- @layout_templates = update_mapping_shared(paths)
801
- end
788
+ count = found.size
789
+ Log.warn("%d views found for %p" % [count, glob]) if count > 1
802
790
 
803
- def update_mapping_shared(paths)
804
- mapping = {}
805
-
806
- provides.each do |wish_key, engine|
807
- wish = wish_key[/(.*)_handler/, 1]
808
- ext_glob = ext_glob(wish)
809
-
810
- 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
817
- end
818
-
819
- mapping[wish_ext] ||= {}
820
- mapping[wish_ext][action_name] = file
821
- end
822
- end
823
- end
824
-
825
- return mapping
791
+ found.first
826
792
  end
827
793
 
828
- 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
794
+ # Produce a glob that can be processed by Dir::[] matching the possible
795
+ # paths to the given +elements+.
796
+ #
797
+ # The +elements+ are an Array that may be nested one level, take care to
798
+ # splat if you try to pass an existing Array.
799
+ #
800
+ # @return [String] glob matching possible paths to the given +elements+
801
+ #
802
+ # @api internal
803
+ # @see Node#to_template
804
+ # @author manveru
805
+ def path_glob(*elements)
806
+ File.join(elements.map{|element|
807
+ "{%s}" % [*element].map{|e| e.to_s.gsub('__', '/') }.join(',')
808
+ }).gsub(/\/\{\/?\}\//, '/')
836
809
  end
837
810
 
838
811
  # Produce a glob that can be processed by Dir::[] matching the extensions
@@ -869,7 +842,7 @@ module Innate
869
842
  # @api external
870
843
  # @author manveru
871
844
  def root_mappings
872
- [*options.roots].flatten
845
+ [*options.roots].dup
873
846
  end
874
847
 
875
848
  # Set the paths for lookup below the Innate.options.views paths.
@@ -900,7 +873,7 @@ module Innate
900
873
  paths = [*ancestral_trait[:views]]
901
874
  paths = [mapping] if paths.empty?
902
875
 
903
- [[*options.views].flatten, [*paths].flatten]
876
+ [*options.views] + paths
904
877
  end
905
878
 
906
879
  # Set the paths for lookup below the Innate.options.layouts paths.
@@ -931,7 +904,7 @@ module Innate
931
904
  paths = [*ancestral_trait[:layouts]]
932
905
  paths = [mapping] if paths.empty?
933
906
 
934
- [[*options.layouts].flatten, [*paths].flatten]
907
+ [*options.layouts] + paths
935
908
  end
936
909
 
937
910
  def options