fortitude 0.9.4 → 0.9.5
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +4 -4
- data/.fix_bundler_for_jruby_17 +26 -0
- data/.gitignore +1 -0
- data/.travis.yml +33 -40
- data/CHANGES.md +44 -0
- data/CONTRIBUTORS.md +26 -0
- data/Rakefile +1 -1
- data/ext/com/fortituderuby/ext/fortitude/FortitudeNativeLibrary.java +45 -33
- data/ext/fortitude_native_ext/fortitude_native_ext.c +23 -23
- data/fortitude.gemspec +25 -8
- data/lib/fortitude/erector.rb +26 -18
- data/lib/fortitude/errors.rb +15 -4
- data/lib/fortitude/extensions/fortitude_ruby_ext.rb +35 -10
- data/lib/fortitude/rails/helpers.rb +59 -2
- data/lib/fortitude/rails/railtie.rb +238 -157
- data/lib/fortitude/rails/renderer.rb +15 -0
- data/lib/fortitude/rails/rendering_methods.rb +46 -33
- data/lib/fortitude/rails/template_handler.rb +49 -18
- data/lib/fortitude/rails/yielded_object_outputter.rb +3 -2
- data/lib/fortitude/rendering_context.rb +14 -5
- data/lib/fortitude/support/method_overriding.rb +90 -0
- data/lib/fortitude/support/staticized_method.rb +12 -0
- data/lib/fortitude/version.rb +1 -1
- data/lib/fortitude/widget/content.rb +4 -2
- data/lib/fortitude/widget/files.rb +17 -11
- data/lib/fortitude/widget/helpers.rb +7 -1
- data/lib/fortitude/widget/integration.rb +4 -0
- data/lib/fortitude/widget/localization.rb +63 -4
- data/lib/fortitude/widget/rendering.rb +17 -10
- data/lib/fortitude_jruby_native_ext.jar +0 -0
- data/spec/helpers/fortitude_rails_helpers.rb +26 -4
- data/spec/rails/capture_system_spec.rb +1 -1
- data/spec/rails/class_loading_system_spec.rb +16 -2
- data/spec/rails/complex_helpers_system_spec.rb +29 -0
- data/spec/rails/data_passing_system_spec.rb +2 -2
- data/spec/rails/development_mode_system_spec.rb +1 -1
- data/spec/rails/erector_coexistence_system_spec.rb +1 -1
- data/spec/rails/helpers_system_spec.rb +20 -2
- data/spec/rails/layouts_system_spec.rb +1 -1
- data/spec/rails/rendering_system_spec.rb +4 -4
- data/spec/rails/rules_system_spec.rb +2 -2
- data/spec/rails/templates/class_loading_system_spec/app/views/some_namespace/some_other_namespace/placeholder.rb +5 -0
- data/spec/rails/templates/complex_helpers_system_spec/app/controllers/complex_helpers_system_spec_controller.rb +4 -0
- data/spec/rails/templates/complex_helpers_system_spec/app/views/complex_helpers_system_spec/label_block_test.rb +9 -0
- data/spec/rails/templates/helpers_system_spec/app/controllers/helpers_system_spec_controller.rb +4 -0
- data/spec/rails/templates/helpers_system_spec/app/controllers/home_controller.rb +9 -0
- data/spec/rails/templates/helpers_system_spec/app/views/helpers_system_spec/rails_helpers_without_automatic_helper_access.rb +37 -0
- data/spec/rails/templates/helpers_system_spec/app/views/helpers_system_spec/url_helpers_without_automatic_helper_access.rb +45 -0
- data/spec/rails/templates/helpers_system_spec/config/initializers/host.rb +1 -0
- data/spec/rails/templates/helpers_system_spec/config/routes.rb +7 -0
- data/spec/rails/templates/static_method_system_spec/app/views/static_method_system_spec/localization.rb +1 -1
- data/spec/rails/templates/view_paths_system_spec/app/controllers/view_paths_system_spec_controller.rb +15 -0
- data/spec/rails/templates/view_paths_system_spec/config/application.rb +30 -0
- data/spec/rails/templates/view_paths_system_spec/view_path_one/baseone/basetwo/base_class_one.rb +5 -0
- data/spec/rails/templates/view_paths_system_spec/view_path_one/view_paths_system_spec/added_view_path.html.rb +5 -0
- data/spec/rails/templates/view_paths_system_spec/view_path_one/view_paths_system_spec/autoloading_from_added_view_path.html.rb +5 -0
- data/spec/rails/templates/view_paths_system_spec/view_path_two/view_paths_system_spec/added_view_path_from_controller.html.rb +5 -0
- 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
- data/spec/rails/view_paths_system_spec.rb +19 -0
- data/spec/system/escaping_system_spec.rb +10 -2
- data/spec/system/helpers_system_spec.rb +37 -6
- data/spec/system/inline_system_spec.rb +19 -0
- data/spec/system/static_method_system_spec.rb +16 -0
- data/spec/system/tag_rendering_system_spec.rb +4 -4
- data/spec/system/widget_class_from_spec.rb +39 -0
- data/spec/system/yield_system_spec.rb +53 -1
- metadata +54 -22
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 =
|
29
|
-
|
30
|
-
|
31
|
-
|
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",
|
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.
|
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
|
data/lib/fortitude/erector.rb
CHANGED
@@ -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
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
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
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
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
|
data/lib/fortitude/errors.rb
CHANGED
@@ -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
|
-
|
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
|
-
|
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
|
+
'&' => '&',
|
10
|
+
'"' => '"'
|
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.
|
81
|
-
when Symbol then object.to_s.
|
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.
|
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
|
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
|
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
|
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
|
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
|
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
|
71
|
-
|
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
|
-
|
74
|
-
|
75
|
-
|
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
|
-
|
78
|
-
@@_fortitude_views_root
|
79
|
-
end
|
115
|
+
path = File.expand_path(p, root)
|
80
116
|
|
81
|
-
|
82
|
-
|
83
|
-
|
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
|
-
|
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
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
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
|
-
|
127
|
-
|
128
|
-
|
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
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
#
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
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
|
-
|
155
|
-
|
156
|
-
|
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
|
159
|
-
|
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
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
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
|
-
|
169
|
-
|
170
|
-
|
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
|
-
|
173
|
-
|
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
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
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
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
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
|
-
|
212
|
-
|
213
|
-
|
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
|
-
|
216
|
-
|
217
|
-
|
294
|
+
all_files.sort.each do |full_path|
|
295
|
+
filename = File.basename(full_path, ".rb")
|
296
|
+
directory = File.dirname(full_path)
|
218
297
|
|
219
|
-
|
220
|
-
|
298
|
+
longer_name_regex = /^#{Regexp.escape(filename)}\..+\.rb$/i
|
299
|
+
longer_name = Dir.entries(directory).detect { |e| e =~ longer_name_regex }
|
221
300
|
|
222
|
-
|
223
|
-
|
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
|
-
|
232
|
-
|
233
|
-
# app.config.eager_load_paths << views_root
|
309
|
+
::Fortitude::MethodOverriding.override_methods(
|
310
|
+
::Rails::Engine, RailsEngineOverrides, :fortitude, [ :eager_load! ])
|
234
311
|
|
235
|
-
#
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
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 =
|
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
|
-
::
|
266
|
-
::
|
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
|