fortitude 0.9.4-java → 0.9.5-java

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (67) hide show
  1. checksums.yaml +4 -4
  2. data/.fix_bundler_for_jruby_17 +26 -0
  3. data/.gitignore +1 -0
  4. data/.travis.yml +33 -40
  5. data/CHANGES.md +44 -0
  6. data/CONTRIBUTORS.md +26 -0
  7. data/Rakefile +1 -1
  8. data/ext/com/fortituderuby/ext/fortitude/FortitudeNativeLibrary.java +45 -33
  9. data/ext/fortitude_native_ext/fortitude_native_ext.c +23 -23
  10. data/fortitude.gemspec +25 -8
  11. data/lib/fortitude/erector.rb +26 -18
  12. data/lib/fortitude/errors.rb +15 -4
  13. data/lib/fortitude/extensions/fortitude_ruby_ext.rb +35 -10
  14. data/lib/fortitude/rails/helpers.rb +59 -2
  15. data/lib/fortitude/rails/railtie.rb +238 -157
  16. data/lib/fortitude/rails/renderer.rb +15 -0
  17. data/lib/fortitude/rails/rendering_methods.rb +46 -33
  18. data/lib/fortitude/rails/template_handler.rb +49 -18
  19. data/lib/fortitude/rails/yielded_object_outputter.rb +3 -2
  20. data/lib/fortitude/rendering_context.rb +14 -5
  21. data/lib/fortitude/support/method_overriding.rb +90 -0
  22. data/lib/fortitude/support/staticized_method.rb +12 -0
  23. data/lib/fortitude/version.rb +1 -1
  24. data/lib/fortitude/widget/content.rb +4 -2
  25. data/lib/fortitude/widget/files.rb +17 -11
  26. data/lib/fortitude/widget/helpers.rb +7 -1
  27. data/lib/fortitude/widget/integration.rb +4 -0
  28. data/lib/fortitude/widget/localization.rb +63 -4
  29. data/lib/fortitude/widget/rendering.rb +17 -10
  30. data/lib/fortitude_jruby_native_ext.jar +0 -0
  31. data/spec/helpers/fortitude_rails_helpers.rb +26 -4
  32. data/spec/rails/capture_system_spec.rb +1 -1
  33. data/spec/rails/class_loading_system_spec.rb +16 -2
  34. data/spec/rails/complex_helpers_system_spec.rb +29 -0
  35. data/spec/rails/data_passing_system_spec.rb +2 -2
  36. data/spec/rails/development_mode_system_spec.rb +1 -1
  37. data/spec/rails/erector_coexistence_system_spec.rb +1 -1
  38. data/spec/rails/helpers_system_spec.rb +20 -2
  39. data/spec/rails/layouts_system_spec.rb +1 -1
  40. data/spec/rails/rendering_system_spec.rb +4 -4
  41. data/spec/rails/rules_system_spec.rb +2 -2
  42. data/spec/rails/templates/class_loading_system_spec/app/views/some_namespace/some_other_namespace/placeholder.rb +5 -0
  43. data/spec/rails/templates/complex_helpers_system_spec/app/controllers/complex_helpers_system_spec_controller.rb +4 -0
  44. data/spec/rails/templates/complex_helpers_system_spec/app/views/complex_helpers_system_spec/label_block_test.rb +9 -0
  45. data/spec/rails/templates/helpers_system_spec/app/controllers/helpers_system_spec_controller.rb +4 -0
  46. data/spec/rails/templates/helpers_system_spec/app/controllers/home_controller.rb +9 -0
  47. data/spec/rails/templates/helpers_system_spec/app/views/helpers_system_spec/rails_helpers_without_automatic_helper_access.rb +37 -0
  48. data/spec/rails/templates/helpers_system_spec/app/views/helpers_system_spec/url_helpers_without_automatic_helper_access.rb +45 -0
  49. data/spec/rails/templates/helpers_system_spec/config/initializers/host.rb +1 -0
  50. data/spec/rails/templates/helpers_system_spec/config/routes.rb +7 -0
  51. data/spec/rails/templates/static_method_system_spec/app/views/static_method_system_spec/localization.rb +1 -1
  52. data/spec/rails/templates/view_paths_system_spec/app/controllers/view_paths_system_spec_controller.rb +15 -0
  53. data/spec/rails/templates/view_paths_system_spec/config/application.rb +30 -0
  54. data/spec/rails/templates/view_paths_system_spec/view_path_one/baseone/basetwo/base_class_one.rb +5 -0
  55. data/spec/rails/templates/view_paths_system_spec/view_path_one/view_paths_system_spec/added_view_path.html.rb +5 -0
  56. data/spec/rails/templates/view_paths_system_spec/view_path_one/view_paths_system_spec/autoloading_from_added_view_path.html.rb +5 -0
  57. data/spec/rails/templates/view_paths_system_spec/view_path_two/view_paths_system_spec/added_view_path_from_controller.html.rb +5 -0
  58. data/spec/rails/templates/view_paths_system_spec/view_path_two/view_paths_system_spec/added_view_path_from_controller_with_impossible_to_guess_name.html.rb +14 -0
  59. data/spec/rails/view_paths_system_spec.rb +19 -0
  60. data/spec/system/escaping_system_spec.rb +10 -2
  61. data/spec/system/helpers_system_spec.rb +37 -6
  62. data/spec/system/inline_system_spec.rb +19 -0
  63. data/spec/system/static_method_system_spec.rb +16 -0
  64. data/spec/system/tag_rendering_system_spec.rb +4 -4
  65. data/spec/system/widget_class_from_spec.rb +39 -0
  66. data/spec/system/yield_system_spec.rb +53 -1
  67. metadata +90 -58
data/fortitude.gemspec CHANGED
@@ -25,25 +25,42 @@ Gem::Specification.new do |s|
25
25
  s.extensions << "ext/fortitude_native_ext/extconf.rb"
26
26
  end
27
27
 
28
- activesupport_spec = if RUBY_VERSION =~ /^1\.8\./
29
- [ ">= 3.0", "< 4.0" ]
30
- else
31
- [ ">= 3.0" ]
28
+ activesupport_spec = [ ">= 3.0" ]
29
+ ref_spec = [ ">= 1.0.5" ]
30
+ rake_spec = [ ">= 1.0" ]
31
+ json_spec = [ ">= 1.0" ]
32
+
33
+ if RUBY_VERSION =~ /^1\.8\./
34
+ activesupport_spec << "< 4.0"
35
+ ref_spec << "< 2.0.0"
36
+ rake_spec << "< 11.0.0"
37
+ json_spec << "< 2.0.0"
38
+ elsif RUBY_VERSION =~ /^1\.9\./
39
+ activesupport_spec << "< 5.0"
40
+ json_spec << "< 2.0.0"
41
+ elsif RUBY_VERSION =~ /^2\.[01]\./
42
+ activesupport_spec << "< 5.0"
32
43
  end
33
44
 
34
45
  s.add_dependency "activesupport", *activesupport_spec
35
- s.add_dependency "ref", ">= 1.0.5"
46
+ s.add_dependency "ref", *ref_spec
47
+
48
+ s.add_development_dependency "rake", *rake_spec
49
+ s.add_development_dependency "json", *json_spec
36
50
 
37
51
  s.add_development_dependency "bundler", "~> 1.5"
38
- s.add_development_dependency "rake"
39
52
  s.add_development_dependency "rspec", "~> 2.99"
40
53
  s.add_development_dependency "rake-compiler"
41
- s.add_development_dependency "json"
42
54
  s.add_development_dependency "tilt", "~> 2.0"
43
- s.add_development_dependency "oop_rails_server", ">= 0.0.7"
55
+ s.add_development_dependency "oop_rails_server", ">= 0.0.22"
44
56
 
45
57
  # This is because i18n >= 0.7 is incompatible with Ruby 1.8.x.
46
58
  if RUBY_VERSION =~ /^1\.8\./
47
59
  s.add_development_dependency "i18n", "~> 0.6.0", "< 0.7.0"
48
60
  end
61
+
62
+ # This is because 'tins' >= 1.7.0 is incompatible with Ruby < 2.0.0.
63
+ if RUBY_VERSION =~ /^1\./
64
+ s.add_development_dependency "tins", "< 1.7.0"
65
+ end
49
66
  end
@@ -1,3 +1,5 @@
1
+ require 'fortitude/support/method_overriding'
2
+
1
3
  module Fortitude
2
4
  module Erector
3
5
  class << self
@@ -31,6 +33,10 @@ module Fortitude
31
33
  return is_erector_widget_class?(widget_class.superclass)
32
34
  end
33
35
 
36
+ def erector_widget_base_class_if_available
37
+ ::Erector::Widget if is_erector_available?
38
+ end
39
+
34
40
  def is_erector_widget?(widget)
35
41
  is_erector_widget_class?(widget.class)
36
42
  end
@@ -48,27 +54,29 @@ module Fortitude
48
54
  private
49
55
  attr_reader :erector_output
50
56
  end
51
- end
52
- end
53
57
 
54
- if ::Fortitude::Erector.is_erector_available?
55
- ::Erector::AbstractWidget.class_eval do
56
- def widget_with_fortitude(target, assigns = {}, options = {}, &block)
57
- if (target.kind_of?(::Class) && target < ::Fortitude::Widget)
58
- target = target.new(assigns)
59
- end
58
+ module ErectorAbstractWidgetOverrides
59
+ def widget_uniwith_fortitude(original_method, target, assigns = {}, options = {}, &block)
60
+ if (target.kind_of?(::Class) && target < ::Fortitude::Widget)
61
+ target = target.new(assigns)
62
+ end
60
63
 
61
- if target.kind_of?(::Fortitude::Widget)
62
- rendering_context = ::Fortitude::RenderingContext.new(
63
- :delegate_object => parent,
64
- :output_buffer_holder => ::Fortitude::Erector::ErectorOutputBufferHolder.new(output),
65
- :helpers_object => helpers)
66
- return target.render_to(rendering_context, &block)
67
- else
68
- return widget_without_fortitude(target, assigns, options, &block)
64
+ if target.kind_of?(::Fortitude::Widget)
65
+ rendering_context = ::Fortitude::RenderingContext.new(
66
+ :delegate_object => parent,
67
+ :output_buffer_holder => ::Fortitude::Erector::ErectorOutputBufferHolder.new(output),
68
+ :helpers_object => helpers)
69
+ return target.render_to(rendering_context, &block)
70
+ else
71
+ return original_method.call(target, assigns, options, &block)
72
+ end
69
73
  end
70
74
  end
71
-
72
- alias_method_chain :widget, :fortitude
73
75
  end
74
76
  end
77
+
78
+ if ::Fortitude::Erector.is_erector_available?
79
+ ::Fortitude::MethodOverriding.override_methods(
80
+ ::Erector::AbstractWidget, ::Fortitude::Erector::ErectorAbstractWidgetOverrides, :fortitude,
81
+ [ :widget ])
82
+ end
@@ -153,16 +153,27 @@ you simply need to convert these to "p :class => :some_class" or
153
153
  class NoBlockToYieldTo < Base
154
154
  attr_reader :widget
155
155
 
156
- def initialize(widget)
157
- super(%{You're trying to call 'yield' (or 'yield_from_widget', or the Erector-compatibility method 'call_block')
156
+ def initialize(widget, local_jump_exception = nil)
157
+ @local_jump_exception = local_jump_exception
158
+ @widget = widget
159
+
160
+ message = %{You're trying to call 'yield' (or 'yield_from_widget', or the Erector-compatibility method 'call_block')
158
161
  from the widget #{widget}; however, there is nothing to yield to. Fortitude
159
162
  looks for something to yield to in this order:
160
163
  1. A block passed to a yield at render time directly (usually via the 'widget' call);
161
164
  2. A block passed to the constructor of the widget;
162
165
  3. The layout the widget is being rendered in.
163
166
  None of these exist here, and so calling 'yield', 'yield_from_widget', or 'call_block' is an
164
- undefined operation.})
165
- @widget = widget
167
+ undefined operation.}
168
+
169
+ if @local_jump_exception
170
+ message += %{
171
+
172
+ This was caused by a #{@local_jump_exception.class.name}, which is: #{@local_jump_exception}:\n
173
+ #{@local_jump_exception.backtrace.join("\n ")}}
174
+ end
175
+
176
+ super(message)
166
177
  end
167
178
  end
168
179
  end
@@ -2,10 +2,35 @@ require 'erb'
2
2
 
3
3
  ::String.class_eval do
4
4
  def fortitude_append_escaped_string(output)
5
+ _fortitude_append_escaped_string(output, false)
6
+ end
7
+
8
+ TABLE_FOR_ESCAPE_ATTRIBUTE_VALUE__ = {
9
+ '&' => '&amp;',
10
+ '"' => '&quot;'
11
+ }
12
+
13
+ PROC_FOR_ESCAPE_ATTRIBUTE_VALUE__ = Proc.new do |match|
14
+ TABLE_FOR_ESCAPE_ATTRIBUTE_VALUE__[match]
15
+ end
16
+
17
+ if RUBY_VERSION =~ /^1\.8\./
18
+ def _fortitude_append_escaped_string_for_value(output)
19
+ output.original_concat(self.gsub(/[&\"]/, &PROC_FOR_ESCAPE_ATTRIBUTE_VALUE__))
20
+ end
21
+ else
22
+ def _fortitude_append_escaped_string_for_value(output)
23
+ output.original_concat(self.gsub(/[&\"]/, TABLE_FOR_ESCAPE_ATTRIBUTE_VALUE__))
24
+ end
25
+ end
26
+
27
+ def _fortitude_append_escaped_string(output, for_attribute_value)
5
28
  raise ArgumentError, "You can only append to a String" unless output.kind_of?(String)
6
29
 
7
30
  if html_safe?
8
31
  output.original_concat(self)
32
+ elsif for_attribute_value
33
+ _fortitude_append_escaped_string_for_value(output)
9
34
  else
10
35
  output.original_concat(ERB::Util.html_escape(self))
11
36
  end
@@ -33,8 +58,8 @@ end
33
58
  each do |key, value|
34
59
  if value.kind_of?(Hash)
35
60
  new_prefix = case prefix
36
- when String then fortitude_append_to(key, prefix.dup)
37
- when nil then fortitude_append_to(key, "".html_safe)
61
+ when String then fortitude_append_to(key, prefix.dup, false)
62
+ when nil then fortitude_append_to(key, "".html_safe, false)
38
63
  else raise ArgumentError, "You can only use a String as a prefix"
39
64
  end
40
65
 
@@ -52,19 +77,19 @@ end
52
77
  else raise ArgumentError, "You can only use a String as a prefix"
53
78
  end
54
79
 
55
- fortitude_append_to(key, target)
80
+ fortitude_append_to(key, target, false)
56
81
 
57
82
  if value == true
58
83
  if allows_bare_attributes
59
84
  # nothing here
60
85
  else
61
86
  target.original_concat(::Hash::FORTITUDE_EQUALS_QUOTE)
62
- fortitude_append_to(key, target)
87
+ fortitude_append_to(key, target, false)
63
88
  target.original_concat(::Hash::FORTITUDE_QUOTE)
64
89
  end
65
90
  else
66
91
  target.original_concat(::Hash::FORTITUDE_EQUALS_QUOTE)
67
- fortitude_append_to(value, target)
92
+ fortitude_append_to(value, target, true)
68
93
  target.original_concat(::Hash::FORTITUDE_QUOTE)
69
94
  end
70
95
  end
@@ -75,17 +100,17 @@ end
75
100
  end
76
101
 
77
102
  private
78
- def fortitude_append_to(object, output)
103
+ def fortitude_append_to(object, output, for_attribute_value)
79
104
  case object
80
- when String then object.fortitude_append_escaped_string(output)
81
- when Symbol then object.to_s.fortitude_append_escaped_string(output)
105
+ when String then object._fortitude_append_escaped_string(output, for_attribute_value)
106
+ when Symbol then object.to_s._fortitude_append_escaped_string(output, for_attribute_value)
82
107
  when Array then object.each_with_index do |o,i|
83
108
  output.original_concat(" ") if i > 0
84
- fortitude_append_to(o, output)
109
+ fortitude_append_to(o, output, for_attribute_value)
85
110
  end
86
111
  when nil then nil
87
112
  when Integer then output.original_concat(object.to_s)
88
- else object.to_s.fortitude_append_escaped_string(output)
113
+ else object.to_s._fortitude_append_escaped_string(output, for_attribute_value)
89
114
  end
90
115
  end
91
116
  end
@@ -4,20 +4,77 @@ module Fortitude
4
4
  class << self
5
5
  def helper(name, options = { })
6
6
  @helpers ||= { }
7
- @helpers[name] = options
7
+ @helpers[normalize_helper_name(name)] = options
8
8
  end
9
9
 
10
10
  def helper_options(name)
11
- @helpers[name.to_s.strip.downcase.to_sym]
11
+ @helpers[normalize_helper_name(name)]
12
12
  end
13
13
 
14
14
  def apply_refined_helpers_to!(o)
15
+ o.send(:include, ::Rails.application.routes.url_helpers)
15
16
  @helpers.each do |name, options|
16
17
  o.helper(name, options)
17
18
  end
18
19
  end
20
+
21
+ ALL_BUILTIN_HELPER_MODULES = {
22
+ ActionView::Helpers => %w{
23
+ ActiveModelHelper
24
+ ActiveModelInstanceTag
25
+ AssetTagHelper
26
+ AssetUrlHelper
27
+ AtomFeedHelper
28
+ CacheHelper
29
+ CaptureHelper
30
+ CsrfHelper
31
+ DateHelper
32
+ DebugHelper
33
+ FormHelper
34
+ FormOptionsHelper
35
+ FormTagHelper
36
+ JavaScriptHelper
37
+ NumberHelper
38
+ OutputSafetyHelper
39
+ RecordTagHelper
40
+ SanitizeHelper
41
+ TagHelper
42
+ TextHelper
43
+ TranslationHelper
44
+ UrlHelper
45
+ }
46
+ }
47
+
48
+ def declare_all_builtin_rails_helpers!
49
+ ALL_BUILTIN_HELPER_MODULES.each do |base_module, constant_names|
50
+ constant_names.each do |constant_name|
51
+ if base_module.const_defined?(constant_name)
52
+ helper_module = base_module.const_get(constant_name)
53
+ helper_module.public_instance_methods.each do |helper_method_name|
54
+ # This is because ActionView::Helpers::FormTagHelper exposes #embed_authenticity_token_in_remote_forms=
55
+ # as a public instance method. This seems like it should not be included as a helper.
56
+ # next if helper_method_name.to_s == 'embed_authenticity_token_in_remote_forms='
57
+ helper helper_method_name
58
+ end
59
+ end
60
+ end
61
+ end
62
+
63
+ helper :default_url_options
64
+ end
65
+
66
+ private
67
+ def normalize_helper_name(name)
68
+ name.to_s.strip.downcase.to_sym
69
+ end
19
70
  end
20
71
 
72
+ # This gives us all built-in Rails helpers, whether they're refined or not; our re-declarations of helpers,
73
+ # below, will override any of these. We need to grab all built-in Rails helpers because we want them all
74
+ # formally declared -- that is, even if +automatic_helper_access+ is set to +false+, built-in Rails helpers
75
+ # should still work properly.
76
+ declare_all_builtin_rails_helpers!
77
+
21
78
  # tags/
22
79
  # active_model_helper
23
80
  # asset_tag_helper
@@ -1,5 +1,7 @@
1
1
  require 'fortitude/rendering_context'
2
2
  require 'fortitude/rails/fortitude_rails_helpers'
3
+ require 'fortitude/support/method_overriding'
4
+ require 'thread'
3
5
 
4
6
  if defined?(ActiveSupport)
5
7
  ActiveSupport.on_load(:before_initialize) do
@@ -23,6 +25,33 @@ end
23
25
  module Fortitude
24
26
  module Rails
25
27
  class Railtie < ::Rails::Railtie
28
+ class << self
29
+ def _fortitude_view_roots
30
+ @_fortitude_view_roots_mutex.synchronize do
31
+ raise "@_fortitude_view_roots has not yet been set" unless @_fortitude_view_roots
32
+ @_fortitude_view_roots
33
+ end
34
+ end
35
+
36
+ def _fortitude_view_roots=(x)
37
+ @_fortitude_view_roots_mutex.synchronize do
38
+ if @_fortitude_view_roots_locked
39
+ raise "@_fortitude_view_roots was locked, and cannot be changed. It was locked at:\n #{@_fortitude_view_roots_locked}"
40
+ end
41
+
42
+ @_fortitude_view_roots = x
43
+ end
44
+ end
45
+
46
+ def _lock_fortitude_view_roots!
47
+ @_fortitude_view_roots_mutex.synchronize do
48
+ @_fortitude_view_roots_locked ||= caller.join("\n")
49
+ end
50
+ end
51
+ end
52
+
53
+ @_fortitude_view_roots_mutex = Mutex.new
54
+
26
55
  config.after_initialize do
27
56
  if Fortitude.refine_rails_helpers
28
57
  require 'fortitude/rails/helpers'
@@ -45,14 +74,14 @@ module Fortitude
45
74
  # Why so hard?
46
75
  #
47
76
  # We're trying to do something that ActiveSupport::Dependencies -- which is what Rails uses for
48
- # class autoloading -- doesn't really support. We want app/views to be on the autoload path,
77
+ # class autoloading -- doesn't really support. We want all view paths to be on the autoload path,
49
78
  # because there are now Ruby classes living there. (It usually isn't just because all that's there
50
79
  # are template source files, not actual Ruby code.) That isn't an issue, though -- adding it
51
80
  # is trivial (just do
52
- # <tt>ActiveSupport::Dependencies.autoload_paths << File.join(Rails.root, 'app/views')</tt>).
81
+ # <tt>ActiveSupport::Dependencies.autoload_paths += ::Rails.application.paths['app/views'].expanded</tt>).
53
82
  #
54
- # The real issue is that we want the class <tt>app/views/foo/bar.rb</tt> to define a class called
55
- # <tt>Views::Foo::Bar</tt>, not just plain <tt>Foo::Bar</tt>. This is what's different from what
83
+ # The real issue is that we want the class (<em>e.g.</em>) <tt>app/views/foo/bar.rb</tt> to define a class
84
+ # called <tt>Views::Foo::Bar</tt>, not just plain <tt>Foo::Bar</tt>. This is what's different from what
56
85
  # ActiveSupport::Dependencies normally supports; it expects the filesystem path underneath the
57
86
  # root to be exactly identical to the fully-qualified class name.
58
87
  #
@@ -62,208 +91,260 @@ module Fortitude
62
91
  # potential for conflicts is enormous.
63
92
  #
64
93
  # As such, we have this code. We'll walk through it step-by-step; note that at the end we *do*
65
- # add app/views/ to the autoload path, so all this code is doing is just dealing with the fact that
94
+ # add all view paths to the autoload path, so all this code is doing is just dealing with the fact that
66
95
  # the fully-qualified classname (<tt>Views::Foo::Bar</tt>) has one extra component on the front of it
67
96
  # (<tt>Views::</tt>) when compared to the subpath (<tt>foo/bar.rb</tt>) underneath what's on the autoload
68
97
  # path (<tt>app/views</tt>).
69
98
 
70
- # Go compute our views root.
71
- views_root = File.expand_path(File.join(::Rails.root, 'app', 'views'))
99
+ # Go compute our view roots.
100
+ #
101
+ # Rails 3.0.x doesn't define #expanded on ::Rails::Paths::Path; it also has a different way of getting at
102
+ # the view paths (<tt>::Rails.application.paths.app.views</tt>, rather than
103
+ # <tt>::Rails.application.paths['app/views']</tt>). So, if we're on Rails 3.0.x, we simply inline the
104
+ # equivalent code here.
105
+ view_roots = if ::Rails.version =~ /^3\.0\./
106
+ paths = ::Rails.application.paths.app.views
107
+
108
+ result = []
72
109
 
73
- # Now, do all this work inside ::ActiveSupport::Dependencies...
74
- ::ActiveSupport::Dependencies.module_eval do
75
- @@_fortitude_views_root = views_root
110
+ paths.each do |p|
111
+ root_path = p.instance_variable_get("@root")
112
+ root = if root_path then root_path.path else ::Rails.root end
113
+ glob = p.instance_variable_get("@glob")
76
114
 
77
- def self._fortitude_views_root
78
- @@_fortitude_views_root
79
- end
115
+ path = File.expand_path(p, root)
80
116
 
81
- # This is the method that gets called to auto-generate namespacing empty
82
- # modules (_e.g._, the toplevel <tt>Views::</tt> module) for directories
83
- # under an autoload path.
84
- #
85
- # The original method says:
86
- #
87
- # "Does the provided path_suffix correspond to an autoloadable module?
88
- # Instead of returning a boolean, the autoload base for this module is
89
- # returned."
90
- #
91
- # So, we just need to strip off the leading +views/+ from the +path_suffix+,
92
- # and see if that maps to a directory underneath <tt>app/views/</tt>; if so,
93
- # we'll return the path to <tt>.../app/views/</tt>. Otherwise, we just
94
- # delegate back to the superclass method.
95
- def autoloadable_module_with_fortitude?(path_suffix)
96
- if path_suffix =~ %r{^views(/.*)?$}i
97
- # If we got here, then we were passed a subpath of views/....
98
- subpath = $1
99
-
100
- if subpath.blank? || File.directory?(File.join(@@_fortitude_views_root, subpath))
101
- return @@_fortitude_views_root
117
+ if glob && File.directory?(path)
118
+ Dir.chdir(path) do
119
+ result.concat(Dir.glob(glob).map { |file| File.join path, file }.sort)
102
120
  end
103
- end
104
-
105
- with_fortitude_views_removed_from_autoload_path do
106
- autoloadable_module_without_fortitude?(path_suffix)
121
+ else
122
+ result << path
107
123
  end
108
124
  end
109
125
 
110
- alias_method_chain :autoloadable_module?, :fortitude
111
-
112
- # When we delegate back to original methods, we want them to act as if
113
- # <tt>app/views/</tt> is _not_ on the autoload path. In order to be thread-safe
114
- # about that, we couple this method with our override of the writer side of the
115
- # <tt>mattr_accessor :autoload_paths</tt>, which simply prefers the thread-local
116
- # that we set to the actual underlying variable.
117
- def with_fortitude_views_removed_from_autoload_path
118
- begin
119
- Thread.current[:_fortitude_autoload_paths_override] = autoload_paths - [ @@_fortitude_views_root ]
120
- yield
121
- ensure
122
- Thread.current[:_fortitude_autoload_paths_override] = nil
126
+ result.uniq!
127
+ result
128
+ else
129
+ ::Rails.application.paths['app/views'].expanded
130
+ end
131
+
132
+ ::Fortitude::Rails::Railtie._fortitude_view_roots = view_roots
133
+
134
+ module ActiveSupportDependenciesOverrides
135
+ class << self
136
+ # When we delegate back to original methods, we want them to act as if
137
+ # all view roots are _not_ on the autoload path. In order to be thread-safe
138
+ # about that, we couple this method with our override of the writer side of the
139
+ # <tt>mattr_accessor :autoload_paths</tt>, which simply prefers the thread-local
140
+ # that we set to the actual underlying variable.
141
+ def with_fortitude_views_removed_from_autoload_path
142
+ begin
143
+ Thread.current[:_fortitude_autoload_paths_override] =
144
+ ::ActiveSupport::Dependencies.autoload_paths -
145
+ ::Fortitude::Rails::Railtie._fortitude_view_roots
146
+
147
+ yield
148
+ ensure
149
+ Thread.current[:_fortitude_autoload_paths_override] = nil
150
+ end
123
151
  end
124
152
  end
125
153
 
126
- # The use of 'class_eval' here may seem funny, and I think it is, but, without it,
127
- # the +@@autoload_paths+ gets interpreted as a class variable for this *Railtie*,
128
- # rather than for ::ActiveSupport::Dependencies. (Why is that? Got me...)
129
- class_eval <<-EOS
130
- def self.autoload_paths
131
- Thread.current[:_fortitude_autoload_paths_override] || @@autoload_paths
154
+ module Common
155
+ def autoload_paths_uniwith_fortitude(original_method)
156
+ Thread.current[:_fortitude_autoload_paths_override] || original_method.call
132
157
  end
133
- EOS
134
158
 
135
- # The original method says:
136
- #
137
- # "Search for a file in autoload_paths matching the provided suffix."
138
- #
139
- # So, we just look to see if the given +path_suffix+ is specifying something like
140
- # <tt>views/foo/bar</tt> or the fully-qualified version thereof; if so, we glue it together properly,
141
- # removing the initial <tt>views/</tt> first. (Otherwise, the mechanism would expect
142
- # <tt>Views::Foo::Bar</tt> to show up in <tt>app/views/views/foo/bar</tt> (yes, a double
143
- # +views+), since <tt>app/views</tt> is on the autoload path.)
144
- def search_for_file_with_fortitude(path_suffix)
145
- # Remove any ".rb" extension, if present...
146
- new_path_suffix = path_suffix.sub(/(\.rb)?$/, "")
147
-
148
- found_subpath = if new_path_suffix =~ %r{^views(/.*)$}i
149
- $1
150
- elsif new_path_suffix =~ %r{^#{Regexp.escape(@@_fortitude_views_root)}(/.*)$}i
151
- $1
159
+ # This is the method that gets called to auto-generate namespacing empty
160
+ # modules (_e.g._, the toplevel <tt>Views::</tt> module) for directories
161
+ # under an autoload path.
162
+ #
163
+ # The original method says:
164
+ #
165
+ # "Does the provided path_suffix correspond to an autoloadable module?
166
+ # Instead of returning a boolean, the autoload base for this module is
167
+ # returned."
168
+ #
169
+ # So, we just need to strip off the leading +views/+ from the +path_suffix+,
170
+ # and see if that maps to a directory underneath one of our view roots; if so,
171
+ # we'll return the path to that view root. Otherwise, we just
172
+ # delegate back to the superclass method.
173
+ def autoloadable_module_uniwith_fortitude?(original_method, path_suffix)
174
+ if path_suffix =~ %r{^(views)(/.*)?$}i
175
+ # If we got here, then we were passed a subpath of views/....
176
+ prefix = $1
177
+ subpath = $2
178
+
179
+ if subpath.blank?
180
+ ::Fortitude::Rails::Railtie._fortitude_view_roots.each do |view_root|
181
+ return view_root if File.basename(view_root).strip.downcase == prefix.strip.downcase
182
+ end
183
+ else
184
+ ::Fortitude::Rails::Railtie._fortitude_view_roots.each do |view_root|
185
+ return view_root if File.directory?(File.join(view_root, subpath))
186
+ end
187
+ end
188
+ end
189
+
190
+ ActiveSupportDependenciesOverrides.with_fortitude_views_removed_from_autoload_path do
191
+ original_method.call(path_suffix)
192
+ end
152
193
  end
153
194
 
154
- if found_subpath
155
- full_path = File.join(@@_fortitude_views_root, "#{found_subpath}")
156
- directory = File.dirname(full_path)
195
+ # The original method says:
196
+ #
197
+ # "Search for a file in autoload_paths matching the provided suffix."
198
+ #
199
+ # So, we just look to see if the given +path_suffix+ is specifying something like
200
+ # <tt>views/foo/bar</tt> or the fully-qualified version thereof; if so, we glue it together properly,
201
+ # removing the initial <tt>views/</tt> first. (Otherwise, the mechanism would expect
202
+ # <tt>Views::Foo::Bar</tt> to show up in <tt>app/views/views/foo/bar</tt> (yes, a double
203
+ # +views+), since <tt>app/views</tt> is on the autoload path.)
204
+ def search_for_file_uniwith_fortitude(original_method, path_suffix)
205
+ # Remove any ".rb" extension, if present...
206
+ new_path_suffix = path_suffix.sub(/(\.rb)?$/, "")
207
+
208
+ found_subpath = nil
209
+ if new_path_suffix =~ %r{^views(/.*)$}i
210
+ found_subpath = $1
211
+ else
212
+ ::Fortitude::Rails::Railtie._fortitude_view_roots.each do |view_root|
213
+ if new_path_suffix =~ %r{^#{Regexp.escape(view_root)}(/.*)$}i
214
+ found_subpath = $1
215
+ break
216
+ end
217
+ end
218
+ end
157
219
 
158
- if File.directory?(directory)
159
- filename = File.basename(full_path)
220
+ if found_subpath
221
+ ::Fortitude::Rails::Railtie._fortitude_view_roots.each do |view_root|
222
+ full_path = File.join(view_root, "#{found_subpath}")
223
+ directory = File.dirname(full_path)
160
224
 
161
- regexp1 = /^_?#{Regexp.escape(filename)}\./
162
- regexp2 = /\.rb$/i
163
- applicable_entries = Dir.entries(directory).select do |entry|
164
- ((entry == filename) || (entry =~ regexp1 && entry =~ regexp2)) && File.file?(File.join(directory, entry))
165
- end
166
- return nil if applicable_entries.length == 0
225
+ if File.directory?(directory)
226
+ filename = File.basename(full_path)
227
+
228
+ regexp1 = /^_?#{Regexp.escape(filename)}\./
229
+ regexp2 = /\.rb$/i
230
+ applicable_entries = Dir.entries(directory).select do |entry|
231
+ ((entry == filename) || (entry =~ regexp1 && entry =~ regexp2)) && File.file?(File.join(directory, entry))
232
+ end
233
+
234
+ return nil if applicable_entries.length == 0
167
235
 
168
- # Prefer those without an underscore
169
- without_underscore = applicable_entries.select { |e| e !~ /^_/ }
170
- applicable_entries = without_underscore if without_underscore.length > 0
236
+ # Prefer those without an underscore
237
+ without_underscore = applicable_entries.select { |e| e !~ /^_/ }
238
+ applicable_entries = without_underscore if without_underscore.length > 0
171
239
 
172
- entry_to_use = applicable_entries.sort_by { |e| e.length }.reverse.first
173
- return File.join(directory, entry_to_use)
240
+ entry_to_use = applicable_entries.sort_by { |e| e.length }.reverse.first
241
+ return File.join(directory, entry_to_use)
242
+ end
243
+ end
174
244
  end
175
- end
176
245
 
177
- # Make sure that we remove the views autoload path before letting the rest of
178
- # the dependency mechanism go searching for files, or else <tt>app/views/foo/bar.rb</tt>
179
- # *will* be found when looking for just <tt>::Foo::Bar</tt>.
180
- with_fortitude_views_removed_from_autoload_path { search_for_file_without_fortitude(path_suffix) }
246
+ # Make sure that we remove the views autoload path before letting the rest of
247
+ # the dependency mechanism go searching for files, or else <tt>app/views/foo/bar.rb</tt>
248
+ # *will* be found when looking for just <tt>::Foo::Bar</tt>.
249
+ ActiveSupportDependenciesOverrides.with_fortitude_views_removed_from_autoload_path do
250
+ original_method.call(path_suffix)
251
+ end
252
+ end
181
253
  end
182
-
183
- alias_method_chain :search_for_file, :fortitude
184
254
  end
185
255
 
186
- # Two important comments here:
187
- #
188
- # 1: We also need to patch ::Rails::Engine.eager_load! so that it loads classes under app/views/. However, we
189
- # can't just add it to the normal eager load paths, because that will allow people to do "require 'foo/bar'"
190
- # and have it match app/views/foo/bar.rb, which we don't want. So, instead, we load these classes ourselves.
191
- # Note that we ALSO have to do things slightly differently than Rails does it, because we need to skip loading
192
- # 'foo.rb' if 'foo.html.rb' exists -- and because we have to require the fully-qualified pathname, since
193
- # app/views is not actually on the load path.
194
- #
195
- # 2: I (ageweke) added this very late in the path of Fortitude development, after trying to use Fortitude in a
196
- # deployment (production) environment in which widgets just weren't getting loaded at all. Yet there's something
197
- # I don't understand: clearly, without this code, widgets will not be eager-loaded (which is probably not a
198
- # great thing for performance reasons)...but I think they still should get auto-loaded and hence actually work
199
- # just fine. But they don't in that environment (you'll get errors like "uninitialized constant Views::Base").
200
- # Since I understand what's going on and have the fix for it here, that's fine...except that I can't seem to
201
- # write a spec for it, because I don't know how to actually *make* it fail. If anybody comes along later and
202
- # knows what would make it fail (and I double-checked, and we don't have autoloading disabled in production or
203
- # anything like that), let me know, so that I can write a spec for this. Thanks!
204
- ::Rails::Engine.class_eval do
205
- def eager_load_with_fortitude!
206
- eager_load_without_fortitude!
256
+ ::Fortitude::MethodOverriding.override_methods(
257
+ ::ActiveSupport::Dependencies, ActiveSupportDependenciesOverrides::Common, :fortitude,
258
+ [ :search_for_file, :autoloadable_module?, :autoload_paths ])
259
+
260
+ eigenclass = ::ActiveSupport::Dependencies.module_eval "class << self; self; end"
261
+ ::Fortitude::MethodOverriding.override_methods(
262
+ eigenclass, ActiveSupportDependenciesOverrides::Common, :fortitude,
263
+ [ :autoload_paths ])
264
+
265
+ module RailsEngineOverrides
266
+ # Two important comments here:
267
+ #
268
+ # 1: We also need to patch ::Rails::Engine.eager_load! so that it loads classes under all view roots. However, we
269
+ # can't just add them to the normal eager load paths, because that will allow people to do "require 'foo/bar'"
270
+ # and have it match app/views/foo/bar.rb, which we don't want. So, instead, we load these classes ourselves.
271
+ # Note that we ALSO have to do things slightly differently than Rails does it, because we need to skip loading
272
+ # 'foo.rb' if 'foo.html.rb' exists -- and because we have to require the fully-qualified pathname, since
273
+ # app/views is not actually on the load path.
274
+ #
275
+ # 2: I (ageweke) added this very late in the path of Fortitude development, after trying to use Fortitude in a
276
+ # deployment (production) environment in which widgets just weren't getting loaded at all. Yet there's something
277
+ # I don't understand: clearly, without this code, widgets will not be eager-loaded (which is probably not a
278
+ # great thing for performance reasons)...but I think they still should get auto-loaded and hence actually work
279
+ # just fine. But they don't in that environment (you'll get errors like "uninitialized constant Views::Base").
280
+ # Since I understand what's going on and have the fix for it here, that's fine...except that I can't seem to
281
+ # write a spec for it, because I don't know how to actually *make* it fail. If anybody comes along later and
282
+ # knows what would make it fail (and I double-checked, and we don't have autoloading disabled in production or
283
+ # anything like that), let me know, so that I can write a spec for this. Thanks!
284
+ def eager_load_uniwith_fortitude!(original_method)
285
+ original_method.call
207
286
  eager_load_fortitude_views!
208
287
  end
209
288
 
210
289
  def eager_load_fortitude_views!
211
- load_path = ::ActiveSupport::Dependencies._fortitude_views_root
212
- all_files = Dir.glob("#{load_path}/**/*.rb")
213
- matcher = /\A#{Regexp.escape(load_path.to_s)}\/(.*)\.rb\Z/
290
+ ::Fortitude::Rails::Railtie._fortitude_view_roots.each do |load_path|
291
+ all_files = Dir.glob("#{load_path}/**/*.rb")
292
+ matcher = /\A#{Regexp.escape(load_path.to_s)}\/(.*)\.rb\Z/
214
293
 
215
- all_files.sort.each do |full_path|
216
- filename = File.basename(full_path, ".rb")
217
- directory = File.dirname(full_path)
294
+ all_files.sort.each do |full_path|
295
+ filename = File.basename(full_path, ".rb")
296
+ directory = File.dirname(full_path)
218
297
 
219
- longer_name_regex = /^#{Regexp.escape(filename)}\..+\.rb$/i
220
- longer_name = Dir.entries(directory).detect { |e| e =~ longer_name_regex }
298
+ longer_name_regex = /^#{Regexp.escape(filename)}\..+\.rb$/i
299
+ longer_name = Dir.entries(directory).detect { |e| e =~ longer_name_regex }
221
300
 
222
- unless longer_name
223
- require_dependency File.join('views', full_path.sub(matcher, '\1'))
301
+ unless longer_name
302
+ require_dependency File.join('views', full_path.sub(matcher, '\1'))
303
+ end
224
304
  end
225
305
  end
226
306
  end
227
-
228
- alias_method_chain :eager_load!, :fortitude
229
307
  end
230
308
 
231
- # And, finally, this is where we add our root to the set of autoload paths.
232
- ::ActiveSupport::Dependencies.autoload_paths << views_root
233
- # app.config.eager_load_paths << views_root
309
+ ::Fortitude::MethodOverriding.override_methods(
310
+ ::Rails::Engine, RailsEngineOverrides, :fortitude, [ :eager_load! ])
234
311
 
235
- # This is our support for partials. Fortitude doesn't really have a distinction between
236
- # partials and "full" templates -- everything is just a widget, which is much more elegant --
237
- # but we still want you to be able to render a widget <tt>Views::Foo::Bar</tt> by saying
238
- # <tt>render :partial => 'foo/bar'</tt> (from ERb, although you can do it from Fortitude if
239
- # you want for some reason, too).
240
- #
241
- # Normally, ActionView only looks for partials in files starting with an underscore. We
242
- # do want to allow this, too (in the above case, if you define the widget in the file
243
- # <tt>app/views/foo/_bar.rb</tt>, it will still work fine); however, we also want to allow
244
- # you to define it in a file that does _not_ start with an underscore ('cause these are
245
- # Ruby classes, and that's just plain weird).
246
- #
247
- # So, we patch #find_templates: if it's looking for a partial, doesn't find one, and is
248
- # searching Fortitude templates (the +.rb+ handler), then we try again, turning off the
249
- # +partial+ flag, and return that instead.
250
- ::ActionView::PathResolver.class_eval do
251
- def find_templates_with_fortitude(name, prefix, partial, details)
252
- templates = find_templates_without_fortitude(name, prefix, partial, details)
312
+ # And, finally, this is where we add our view roots to the set of autoload paths.
313
+ ::ActiveSupport::Dependencies.autoload_paths += view_roots
314
+
315
+ module ActionViewPathResolverOverrides
316
+ # This is our support for partials. Fortitude doesn't really have a distinction between
317
+ # partials and "full" templates -- everything is just a widget, which is much more elegant --
318
+ # but we still want you to be able to render a widget <tt>Views::Foo::Bar</tt> by saying
319
+ # <tt>render :partial => 'foo/bar'</tt> (from ERb, although you can do it from Fortitude if
320
+ # you want for some reason, too).
321
+ #
322
+ # Normally, ActionView only looks for partials in files starting with an underscore. We
323
+ # do want to allow this, too (in the above case, if you define the widget in the file
324
+ # <tt>app/views/foo/_bar.rb</tt>, it will still work fine); however, we also want to allow
325
+ # you to define it in a file that does _not_ start with an underscore ('cause these are
326
+ # Ruby classes, and that's just plain weird).
327
+ #
328
+ # So, we patch #find_templates: if it's looking for a partial, doesn't find one, and is
329
+ # searching Fortitude templates (the +.rb+ handler), then we try again, turning off the
330
+ # +partial+ flag, and return that instead.
331
+ def find_templates_uniwith_fortitude(original_method, name, prefix, partial, details, *args)
332
+ templates = original_method.call(name, prefix, partial, details, *args)
253
333
  if partial && templates.empty? && details[:handlers] && details[:handlers].include?(:rb)
254
- templates = find_templates_without_fortitude(name, prefix, false, details.merge(:handlers => [ :rb ]))
334
+ templates = original_method.call(name, prefix, false, details.merge(:handlers => [ :rb ]), *args)
255
335
  end
256
336
  templates
257
337
  end
258
-
259
- alias_method_chain :find_templates, :fortitude
260
338
  end
261
339
 
340
+ ::Fortitude::MethodOverriding.override_methods(
341
+ ::ActionView::PathResolver, ActionViewPathResolverOverrides, :fortitude, [ :find_templates ])
342
+
262
343
  require "fortitude/rails/template_handler"
263
344
  require "fortitude/rails/rendering_methods"
264
345
 
265
- ::ActionController::Base.send(:include, ::Fortitude::Rails::RenderingMethods)
266
- ::ActionMailer::Base.send(:include, ::Fortitude::Rails::RenderingMethods)
346
+ ::Fortitude::Rails::RenderingMethods.include_into!(::ActionController::Base)
347
+ ::Fortitude::Rails::RenderingMethods.include_into!(::ActionMailer::Base)
267
348
  end
268
349
  end
269
350
  end