manveru-innate 2009.04.01 → 2009.04.08

Sign up to get free protection for your applications and to get access to all the features.
Files changed (61) hide show
  1. data/CHANGELOG +104 -0
  2. data/MANIFEST +18 -18
  3. data/Rakefile +3 -3
  4. data/example/app/retro_games.rb +6 -6
  5. data/example/app/todo/layout/default.erb +1 -1
  6. data/example/app/todo/view/index.erb +11 -11
  7. data/example/app/whywiki_erb/start.rb +1 -0
  8. data/example/app/whywiki_erb/view/{edit.html.erb → edit.erb} +0 -0
  9. data/example/app/whywiki_erb/view/{index.html.erb → index.erb} +0 -0
  10. data/example/howto_spec.rb +1 -1
  11. data/example/session.rb +3 -3
  12. data/innate.gemspec +3 -12
  13. data/lib/innate/action.rb +2 -1
  14. data/lib/innate/helper.rb +18 -27
  15. data/lib/innate/helper/cgi.rb +30 -20
  16. data/lib/innate/helper/render.rb +80 -0
  17. data/lib/innate/mock.rb +2 -3
  18. data/lib/innate/node.rb +79 -50
  19. data/lib/innate/options.rb +1 -1
  20. data/lib/innate/request.rb +3 -23
  21. data/lib/innate/spec.rb +3 -6
  22. data/lib/innate/version.rb +1 -1
  23. data/lib/innate/view/erb.rb +1 -1
  24. data/lib/innate/view/etanni.rb +2 -2
  25. data/lib/innate/view/none.rb +1 -1
  26. data/spec/innate/action/layout.rb +1 -1
  27. data/spec/innate/action/layout/file_layout.xhtml +1 -0
  28. data/spec/innate/helper/aspect.rb +6 -6
  29. data/spec/innate/helper/flash.rb +6 -6
  30. data/spec/innate/helper/render.rb +157 -0
  31. data/spec/innate/helper/view/aspect_hello.xhtml +1 -0
  32. data/spec/innate/helper/view/locals.xhtml +1 -0
  33. data/spec/innate/helper/view/loop.xhtml +4 -0
  34. data/spec/innate/helper/view/num.xhtml +1 -0
  35. data/spec/innate/helper/view/partial.xhtml +1 -0
  36. data/spec/innate/helper/view/recursive.xhtml +7 -0
  37. data/spec/innate/node/node.rb +5 -13
  38. data/spec/innate/node/view/another_layout/{another_layout.erb → another_layout.xhtml} +1 -1
  39. data/spec/innate/node/view/{bar.erb → bar.xhtml} +0 -0
  40. data/spec/innate/node/view/foo.html.xhtml +1 -0
  41. data/spec/innate/node/view/{only_view.erb → only_view.xhtml} +0 -0
  42. data/spec/innate/node/view/with_layout.xhtml +1 -0
  43. data/spec/innate/provides.rb +2 -2
  44. data/spec/innate/provides/list.html.xhtml +1 -0
  45. data/spec/innate/provides/list.txt.xhtml +1 -0
  46. data/spec/innate/state/fiber.rb +8 -7
  47. data/tasks/bacon.rake +38 -21
  48. metadata +22 -51
  49. data/lib/innate/helper/partial.rb +0 -93
  50. data/spec/innate/action/layout/file_layout.erb +0 -1
  51. data/spec/innate/helper/partial.rb +0 -101
  52. data/spec/innate/helper/view/aspect_hello.erb +0 -1
  53. data/spec/innate/helper/view/locals.erb +0 -1
  54. data/spec/innate/helper/view/loop.erb +0 -4
  55. data/spec/innate/helper/view/num.erb +0 -1
  56. data/spec/innate/helper/view/partial.erb +0 -1
  57. data/spec/innate/helper/view/recursive.erb +0 -8
  58. data/spec/innate/node/view/foo.html.erb +0 -1
  59. data/spec/innate/node/view/with_layout.erb +0 -1
  60. data/spec/innate/provides/list.html.erb +0 -1
  61. data/spec/innate/provides/list.txt.erb +0 -1
@@ -6,36 +6,46 @@ module Innate
6
6
 
7
7
  module Helper
8
8
  module CGI
9
- # shortcut for Rack::Utils.escape
10
- def url_encode(*args)
11
- Rack::Utils.escape(*args.map{|a| a.to_s })
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)
12
17
  end
18
+ alias u url_encode
13
19
 
14
- # shortcut for Rack::Utils.unescape
15
- def url_decode(*args)
16
- Rack::Utils.unescape(*args.map{|a| a.to_s })
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)
17
26
  end
18
27
 
19
- # shortcut for Rack::Utils.escape_html
20
- def html_escape(string)
21
- Rack::Utils.escape_html(string)
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)
22
34
  end
23
35
 
24
- # shortcut for CGI.unescapeHTML
25
- def html_unescape(string)
26
- ::CGI.unescapeHTML(string.to_s)
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)
27
42
  end
28
43
 
29
44
  # safely escape all HTML and code
30
- def h(string)
31
- Rack::Utils.escape_html(string).gsub(/#([{@$]@?)/, '#\1')
45
+ def html_and_code_escape(input)
46
+ Rack::Utils.escape_html(input.to_s).gsub(/#([{@$]@?)/, '#\1')
32
47
  end
33
-
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)
48
+ alias h html_and_code_escape
39
49
  end
40
50
  end
41
51
  end
@@ -0,0 +1,80 @@
1
+ module Innate
2
+ module Helper
3
+ module Render
4
+ # Enables you to simply call:
5
+ #
6
+ # @example of added functionality
7
+ # YourController.render_partial(:foo, :x => 42)
8
+ #
9
+ def self.included(into)
10
+ into.extend(self)
11
+ end
12
+
13
+ # Renders the full action in the way a real request would.
14
+ #
15
+ # Please be aware that, if this is the first request from a client, you
16
+ # will not have access to the session in the action being rendered, as no
17
+ # actual session has been put into place yet.
18
+ #
19
+ # It should work as expected on any subsequent requests.
20
+ #
21
+ # As usual, patches welcome.
22
+ #
23
+ # @api external
24
+ # @see Mock.session
25
+ # @author manveru
26
+ def render_full(path, query = {})
27
+ uri = URI(path.to_s)
28
+ uri.query = Rack::Utils.build_query(query)
29
+
30
+ if cookie = request.env['HTTP_COOKIE']
31
+ Mock.session do |mock|
32
+ mock.cookie = cookie
33
+ return mock.get(uri.to_s).body
34
+ end
35
+ else
36
+ Mock.get(uri.to_s).body
37
+ end
38
+ end
39
+
40
+ # Renders an action without any layout.
41
+ # @api external
42
+ # @see render_custom
43
+ # @author manveru
44
+ def render_partial(action_name, variables = {})
45
+ render_custom(action_name, variables) do |action|
46
+ action.layout = nil
47
+ end
48
+ end
49
+
50
+ # Renders an action view, doesn't execute any methods and won't wrap it
51
+ # into a layout.
52
+ #
53
+ # @api external
54
+ # @see render_custom
55
+ # @author manveru
56
+ def render_view(action_name, variables = {})
57
+ render_custom(action_name, variables) do |action|
58
+ action.layout = nil
59
+ action.method = nil
60
+ end
61
+ end
62
+
63
+ def render_custom(action_name, variables = {})
64
+ action = resolve(action_name.to_s)
65
+
66
+ action.sync_variables(self.action)
67
+ action.instance = action.node.new
68
+ action.variables = action.variables.merge(variables)
69
+
70
+ yield(action) if block_given?
71
+
72
+ if action.valid?
73
+ action.render
74
+ else
75
+ Log.warn("Invalid action: %p" % action)
76
+ end
77
+ end
78
+ end
79
+ end
80
+ end
@@ -39,9 +39,8 @@ module Innate
39
39
  hash['HTTP_COOKIE'] ||= @cookie if @cookie
40
40
  response = Mock::mock(method, path, hash)
41
41
 
42
- if cookie = response['Set-Cookie']
43
- @cookie = cookie
44
- end
42
+ cookie = response['Set-Cookie']
43
+ @cookie = cookie if cookie
45
44
 
46
45
  response
47
46
  end
@@ -28,7 +28,8 @@ module Innate
28
28
  module Node
29
29
  include Traited
30
30
 
31
- DEFAULT_HELPERS = %w[aspect cgi flash link partial redirect send_file]
31
+ attr_reader :method_arities, :layout_templates, :view_templates
32
+
32
33
  NODE_LIST = Set.new
33
34
 
34
35
  # These traits are inherited into ancestors, changing a trait in an
@@ -56,8 +57,6 @@ module Innate
56
57
  # Upon inclusion we make ourselves comfortable.
57
58
  def self.included(into)
58
59
  into.__send__(:include, Helper)
59
- into.helper(*DEFAULT_HELPERS)
60
-
61
60
  into.extend(Trinity, self)
62
61
 
63
62
  NODE_LIST << into
@@ -372,6 +371,7 @@ module Innate
372
371
  end
373
372
 
374
373
  node.update_method_arities
374
+ node.update_template_mappings
375
375
  node.fill_action(action, name)
376
376
  end
377
377
 
@@ -537,12 +537,10 @@ module Innate
537
537
  @method_arities
538
538
  end
539
539
 
540
- attr_reader :method_arities
541
-
542
540
  # Try to find the best template for the given basename and wish and respect
543
541
  # aliased views.
544
542
  #
545
- # @param [#to_s] file
543
+ # @param [#to_s] action_name
546
544
  # @param [#to_s] wish
547
545
  #
548
546
  # @return [String, nil] depending whether a template could be found
@@ -550,11 +548,11 @@ module Innate
550
548
  # @api external
551
549
  # @see Node#to_template Node#find_aliased_view
552
550
  # @author manveru
553
- def find_view(file, wish)
554
- aliased = find_aliased_view(file, wish)
551
+ def find_view(action_name, wish)
552
+ aliased = find_aliased_view(action_name, wish)
555
553
  return aliased if aliased
556
554
 
557
- to_view(file, wish)
555
+ to_view(action_name, wish)
558
556
  end
559
557
 
560
558
  # Try to find the best template for the given basename and wish.
@@ -562,7 +560,7 @@ module Innate
562
560
  # This method is mostly here for symetry with {to_layout} and to allow you
563
561
  # overriding the template lookup easily.
564
562
  #
565
- # @param [#to_s] file
563
+ # @param [#to_s] action_name
566
564
  # @param [#to_s] wish
567
565
  #
568
566
  # @return [String, nil] depending whether a template could be found
@@ -571,9 +569,9 @@ module Innate
571
569
  # @see {Node#find_view} {Node#to_template} {Node#root_mappings}
572
570
  # {Node#view_mappings} {Node#to_template}
573
571
  # @author manveru
574
- def to_view(file, wish)
575
- path = [root_mappings].concat(view_mappings) << file
576
- to_template(path, wish)
572
+ def to_view(action_name, wish)
573
+ return unless files = view_templates[wish.to_s]
574
+ files[action_name.to_s]
577
575
  end
578
576
 
579
577
  # Aliasing one view from another.
@@ -611,9 +609,9 @@ module Innate
611
609
  trait[:alias_view][to.to_s] = node ? [from.to_s, node] : from.to_s
612
610
  end
613
611
 
614
- # Resolve one level of aliasing for the given +file+ and +wish+.
612
+ # Resolve one level of aliasing for the given +action_name+ and +wish+.
615
613
  #
616
- # @param [String] file
614
+ # @param [String] action_name
617
615
  # @param [String] wish
618
616
  #
619
617
  # @return [nil, String] the absolute path to the aliased template or nil
@@ -621,18 +619,21 @@ module Innate
621
619
  # @api internal
622
620
  # @see Node::alias_view Node::find_view
623
621
  # @author manveru
624
- def find_aliased_view(file, wish)
625
- aliased_file, aliased_node = ancestral_trait[:alias_view][file]
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
+
626
626
  aliased_node ||= self
627
- aliased_node.find_view(aliased_file, wish) if aliased_file
627
+ aliased_node.update_view_mappings
628
+ aliased_node.find_view(aliased_name, wish)
628
629
  end
629
630
 
630
- # Find the best matching file for the layout, if any.
631
+ # Find the best matching action_name for the layout, if any.
631
632
  #
632
633
  # This is mostly an abstract method that you might find handy if you want
633
634
  # to do vastly different layout lookup.
634
635
  #
635
- # @param [String] file
636
+ # @param [String] action_name
636
637
  # @param [String] wish
637
638
  #
638
639
  # @return [nil, String] the absolute path to the template or nil
@@ -640,9 +641,9 @@ module Innate
640
641
  # @api external
641
642
  # @see {Node#to_template} {Node#root_mappings} {Node#layout_mappings}
642
643
  # @author manveru
643
- def to_layout(file, wish)
644
- path = [root_mappings].concat(layout_mappings) << file
645
- to_template(path, wish)
644
+ def to_layout(action_name, wish)
645
+ return unless files = layout_templates[wish.to_s]
646
+ files[action_name.to_s]
646
647
  end
647
648
 
648
649
  # Define a layout to use on this Node.
@@ -727,11 +728,11 @@ module Innate
727
728
  result = nil
728
729
 
729
730
  atoms.size.downto(0) do |len|
730
- action = atoms[0...len].join('__')
731
+ action_name = atoms[0...len].join('__')
731
732
  params = atoms[len..-1]
732
- action = 'index' if action.empty? and params != ['index']
733
+ action_name = 'index' if action_name.empty? and params != ['index']
733
734
 
734
- return result if result = yield(action, params)
735
+ return result if result = yield(action_name, params)
735
736
  end
736
737
 
737
738
  return nil
@@ -781,31 +782,59 @@ module Innate
781
782
  # Node#path_glob Node#ext_glob
782
783
  # @author manveru
783
784
  def to_template(path, wish)
784
- return unless exts = ext_glob(wish)
785
- glob = "#{path_glob(*path)}.#{exts}"
786
- found = Dir[glob].uniq
785
+ to_view(path, wish) || to_layout(path, wish)
786
+ end
787
787
 
788
- count = found.size
789
- Log.warn("%d views found for %p" % [count, glob]) if count > 1
788
+ def update_template_mappings
789
+ update_view_mappings
790
+ update_layout_mappings
791
+ end
790
792
 
791
- found.first
793
+ def update_view_mappings
794
+ paths = possible_paths_for(view_mappings)
795
+ @view_templates = update_mapping_shared(paths)
792
796
  end
793
797
 
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(/\/\{\/?\}\//, '/')
798
+ def update_layout_mappings
799
+ paths = possible_paths_for(layout_mappings)
800
+ @layout_templates = update_mapping_shared(paths)
801
+ end
802
+
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, 'html', $2
817
+ when /.*/
818
+ p $1
819
+ end
820
+
821
+ mapping[wish_ext] ||= {}
822
+ mapping[wish_ext][action_name] = file
823
+ end
824
+ end
825
+ end
826
+
827
+ return mapping
828
+ end
829
+
830
+ def possible_paths_for(mappings)
831
+ root_mappings.map{|root_mapping|
832
+ mappings.first.map{|outer_mapping|
833
+ mappings.last.map{|inner_mapping|
834
+ File.join(root_mapping, outer_mapping, inner_mapping, '/')
835
+ }
836
+ }
837
+ }.flatten
809
838
  end
810
839
 
811
840
  # Produce a glob that can be processed by Dir::[] matching the extensions
@@ -842,7 +871,7 @@ module Innate
842
871
  # @api external
843
872
  # @author manveru
844
873
  def root_mappings
845
- [*options.roots].dup
874
+ [*options.roots].flatten
846
875
  end
847
876
 
848
877
  # Set the paths for lookup below the Innate.options.views paths.
@@ -873,7 +902,7 @@ module Innate
873
902
  paths = [*ancestral_trait[:views]]
874
903
  paths = [mapping] if paths.empty?
875
904
 
876
- [*options.views] + paths
905
+ [[*options.views].flatten, [*paths].flatten]
877
906
  end
878
907
 
879
908
  # Set the paths for lookup below the Innate.options.layouts paths.
@@ -904,7 +933,7 @@ module Innate
904
933
  paths = [*ancestral_trait[:layouts]]
905
934
  paths = [mapping] if paths.empty?
906
935
 
907
- [*options.layouts] + paths
936
+ [[*options.layouts].flatten, [*paths].flatten]
908
937
  end
909
938
 
910
939
  def options
@@ -2,7 +2,7 @@ module Innate
2
2
  # this has to be run after a couple of other files have been required
3
3
 
4
4
  options.dsl do
5
- o "Indicate that calls Innate::start will be ignored",
5
+ o "Innate::start will not start an adapter if true",
6
6
  :started, false
7
7
 
8
8
  o "Will send ::setup to each element during Innate::start",
@@ -71,7 +71,7 @@ module Innate
71
71
  # Both +value+ and the elements of +keys+ will be turned into String by #to_s.
72
72
  def [](value, *keys)
73
73
  return super(value) if keys.empty?
74
- [value, *keys].map{|k| super(k) }
74
+ [value, *keys].map{|key| super(key) }
75
75
  end
76
76
 
77
77
  # the full request URI provided by Rack::Request
@@ -94,8 +94,8 @@ module Innate
94
94
  # # => {'name' => 'jason', 'job' => 'lumberjack'}
95
95
 
96
96
  def subset(*keys)
97
- keys = keys.map{|k| k.to_s }
98
- params.reject{|k,v| not keys.include?(k) }
97
+ keys = keys.map{|key| key.to_s }
98
+ params.reject{|key, value| not keys.include?(key) }
99
99
  end
100
100
 
101
101
  # Try to figure out the domain we are running on, this might work for some
@@ -137,25 +137,5 @@ module Innate
137
137
  rescue ArgumentError => ex
138
138
  raise ArgumentError, ex unless ex.message == 'invalid address'
139
139
  end
140
-
141
- REQUEST_STRING_FORMAT = "#<%s params=%p cookies=%p env=%p>"
142
-
143
- def to_s
144
- REQUEST_STRING_FORMAT % [self.class, params, cookies, http_variables]
145
- end
146
- alias inspect to_s
147
-
148
- # Pretty prints current action with parameters, cookies and enviroment
149
- # variables.
150
- def pretty_print(pp)
151
- pp.object_group(self){
152
- group = { 'params' => params, 'cookies' => cookies, 'env' => http_vars }
153
- group.each do |name, hash|
154
- pp.breakable
155
- pp.text " @#{name}="
156
- pp.nest(name.size + 3){ pp.pp_hash(hash) }
157
- end
158
- }
159
- end
160
140
  end
161
141
  end