diff options
author | Emilio Tagua <miloops@gmail.com> | 2009-07-20 16:57:23 -0300 |
---|---|---|
committer | Emilio Tagua <miloops@gmail.com> | 2009-07-20 16:57:23 -0300 |
commit | 9a28bd787660b08aae36155066e61d3608d0b4dd (patch) | |
tree | 2d10dd32ad28cc070c3d142c45d6a35fcd5ae43e | |
parent | b326faef0936e5a845d1f6eb9ed2200babfd05f8 (diff) | |
parent | 37658f15bb88e054635a496327a4a82bb50fd5d5 (diff) | |
download | rails-9a28bd787660b08aae36155066e61d3608d0b4dd.tar.gz rails-9a28bd787660b08aae36155066e61d3608d0b4dd.tar.bz2 rails-9a28bd787660b08aae36155066e61d3608d0b4dd.zip |
Merge commit 'rails/master'
47 files changed, 401 insertions, 176 deletions
diff --git a/actionpack/examples/very_simple.rb b/actionpack/examples/very_simple.rb new file mode 100644 index 0000000000..8d01cb39bd --- /dev/null +++ b/actionpack/examples/very_simple.rb @@ -0,0 +1,50 @@ +$:.push "rails/activesupport/lib" +$:.push "rails/actionpack/lib" + +require "action_controller" + +class Kaigi < ActionController::Http + include AbstractController::Callbacks + include ActionController::RackConvenience + include ActionController::Renderer + include ActionController::Layouts + include ActionView::Context + + before_filter :set_name + append_view_path "views" + + def _action_view + self + end + + def controller + self + end + + DEFAULT_LAYOUT = Object.new.tap {|l| def l.render(*) yield end } + + def _render_template_from_controller(template, layout = DEFAULT_LAYOUT, options = {}, partial = false) + ret = template.render(self, {}) + layout.render(self, {}) { ret } + end + + def index + render :template => "template" + end + + def alt + render :template => "template", :layout => "alt" + end + + private + def set_name + @name = params[:name] + end +end + +app = Rack::Builder.new do + map("/kaigi") { run Kaigi.action(:index) } + map("/kaigi/alt") { run Kaigi.action(:alt) } +end.to_app + +Rack::Handler::Mongrel.run app, :Port => 3000
\ No newline at end of file diff --git a/actionpack/examples/views/layouts/alt.html.erb b/actionpack/examples/views/layouts/alt.html.erb new file mode 100644 index 0000000000..c4816337a6 --- /dev/null +++ b/actionpack/examples/views/layouts/alt.html.erb @@ -0,0 +1 @@ ++ <%= yield %> +
\ No newline at end of file diff --git a/actionpack/examples/views/layouts/kaigi.html.erb b/actionpack/examples/views/layouts/kaigi.html.erb new file mode 100644 index 0000000000..274607a96a --- /dev/null +++ b/actionpack/examples/views/layouts/kaigi.html.erb @@ -0,0 +1 @@ +Hello <%= yield %> Goodbye
\ No newline at end of file diff --git a/actionpack/examples/views/template.html.erb b/actionpack/examples/views/template.html.erb new file mode 100644 index 0000000000..3108e9ad70 --- /dev/null +++ b/actionpack/examples/views/template.html.erb @@ -0,0 +1 @@ +Hello <%= @name %>
\ No newline at end of file diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index e822a11d14..32572c93c0 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -52,11 +52,21 @@ module ActionController autoload :SessionOverflowError, 'action_controller/base/exceptions' autoload :UnknownHttpMethod, 'action_controller/base/exceptions' - require 'action_controller/routing' + autoload :Routing, 'action_controller/routing' end autoload :HTML, 'action_controller/vendor/html-scanner' autoload :AbstractController, 'action_controller/abstract' -require 'action_dispatch' -require 'action_view' +autoload :Rack, 'action_dispatch' +autoload :ActionDispatch, 'action_dispatch' +autoload :ActionView, 'action_view' + +# Common ActiveSupport usage in ActionController +require "active_support/concern" +require 'active_support/core_ext/class/attribute_accessors' +require 'active_support/core_ext/load_error' +require 'active_support/core_ext/module/attr_internal' +require 'active_support/core_ext/module/delegation' +require 'active_support/core_ext/name_error' +require 'active_support/inflector'
\ No newline at end of file diff --git a/actionpack/lib/action_controller/abstract/base.rb b/actionpack/lib/action_controller/abstract/base.rb index a19a236ef7..ca00e66349 100644 --- a/actionpack/lib/action_controller/abstract/base.rb +++ b/actionpack/lib/action_controller/abstract/base.rb @@ -1,5 +1,3 @@ -require 'active_support/core_ext/module/attr_internal' - module AbstractController class Base diff --git a/actionpack/lib/action_controller/abstract/callbacks.rb b/actionpack/lib/action_controller/abstract/callbacks.rb index 0d5161c80e..ea4b59466e 100644 --- a/actionpack/lib/action_controller/abstract/callbacks.rb +++ b/actionpack/lib/action_controller/abstract/callbacks.rb @@ -1,3 +1,5 @@ +require "active_support/new_callbacks" + module AbstractController module Callbacks extend ActiveSupport::Concern diff --git a/actionpack/lib/action_controller/abstract/helpers.rb b/actionpack/lib/action_controller/abstract/helpers.rb index 6b73f887c1..5efa37fde3 100644 --- a/actionpack/lib/action_controller/abstract/helpers.rb +++ b/actionpack/lib/action_controller/abstract/helpers.rb @@ -8,12 +8,6 @@ module AbstractController extlib_inheritable_accessor(:_helpers) { Module.new } end - # Override AbstractController::Renderer's _action_view to include the - # helper module for this class into its helpers module. - def _action_view - @_action_view ||= super.tap { |av| av.helpers.include(_helpers) } - end - module ClassMethods # When a class is inherited, wrap its helper module in a new module. # This ensures that the parent class's module can be changed diff --git a/actionpack/lib/action_controller/abstract/layouts.rb b/actionpack/lib/action_controller/abstract/layouts.rb index 2ac4e6068a..f021dd8b62 100644 --- a/actionpack/lib/action_controller/abstract/layouts.rb +++ b/actionpack/lib/action_controller/abstract/layouts.rb @@ -6,6 +6,7 @@ module AbstractController included do extlib_inheritable_accessor(:_layout_conditions) { Hash.new } + _write_layout_method end module ClassMethods @@ -99,7 +100,7 @@ module AbstractController # the lookup to. By default, layout lookup is limited to the # formats specified for the current request. def _layout_for_name(name, details = {:formats => formats}) - name && _find_by_parts(name, details) + name && _find_layout(name, details) end # Take in the name and details and find a Template. @@ -111,7 +112,7 @@ module AbstractController # # ==== Returns # Template:: A template object matching the name and details - def _find_by_parts(name, details) + def _find_layout(name, details) # TODO: Make prefix actually part of details in ViewPath#find_by_parts prefix = details.key?(:prefix) ? details.delete(:prefix) : "layouts" view_paths.find_by_parts(name, details, prefix) diff --git a/actionpack/lib/action_controller/abstract/logger.rb b/actionpack/lib/action_controller/abstract/logger.rb index b960e152e3..fd33bd2ddd 100644 --- a/actionpack/lib/action_controller/abstract/logger.rb +++ b/actionpack/lib/action_controller/abstract/logger.rb @@ -1,4 +1,3 @@ -require 'active_support/core_ext/class/attribute_accessors' require 'active_support/core_ext/logger' module AbstractController diff --git a/actionpack/lib/action_controller/abstract/renderer.rb b/actionpack/lib/action_controller/abstract/renderer.rb index 611d3a16ce..41b7d47458 100644 --- a/actionpack/lib/action_controller/abstract/renderer.rb +++ b/actionpack/lib/action_controller/abstract/renderer.rb @@ -17,25 +17,22 @@ module AbstractController # An instance of a view class. The default view class is ActionView::Base # # The view class must have the following methods: - # initialize[paths, assigns_for_first_render, controller] - # paths<Array[ViewPath]>:: A list of resolvers to look for templates in - # controller<AbstractController::Base> A controller - # _render_partial_from_controller[options] + # View.for_controller[controller] Create a new ActionView instance for a + # controller + # View#_render_partial_from_controller[options] + # - responsible for setting options[:_template] + # - Returns String with the rendered partial # options<Hash>:: see _render_partial in ActionView::Base - # _render_template_from_controller[template, layout, options, partial] + # View#_render_template_from_controller[template, layout, options, partial] + # - Returns String with the rendered template # template<ActionView::Template>:: The template to render # layout<ActionView::Template>:: The layout to render around the template # options<Hash>:: See _render_template_with_layout in ActionView::Base # partial<Boolean>:: Whether or not the template to render is a partial - # _partial:: If a partial, rather than a template, was rendered, return - # the partial. - # helpers:: A module containing the helpers to be used in the view. This - # module should respond_to include. - # controller:: The controller that initialized the ActionView # # Override this method in a to change the default behavior. def _action_view - @_action_view ||= ActionView::Base.new(self.class.view_paths, {}, self) + @_action_view ||= ActionView::Base.for_controller(self) end # Mostly abstracts the fact that calling render twice is a DoubleRenderError. diff --git a/actionpack/lib/action_controller/base/compatibility.rb b/actionpack/lib/action_controller/base/compatibility.rb index cd4b72b1c6..13813ffd17 100644 --- a/actionpack/lib/action_controller/base/compatibility.rb +++ b/actionpack/lib/action_controller/base/compatibility.rb @@ -114,7 +114,7 @@ module ActionController super || (respond_to?(:method_missing) && "_handle_method_missing") end - def _find_by_parts(name, details) + def _find_layout(name, details) details[:prefix] = nil if name =~ /\blayouts/ super end @@ -123,7 +123,7 @@ module ActionController def _default_layout(details, require_layout = false) super rescue ActionView::MissingTemplate - _find_by_parts(_layout({}), {}) + _find_layout(_layout({}), {}) nil end diff --git a/actionpack/lib/action_controller/base/helpers.rb b/actionpack/lib/action_controller/base/helpers.rb index 2fa5ea6519..7c52779064 100644 --- a/actionpack/lib/action_controller/base/helpers.rb +++ b/actionpack/lib/action_controller/base/helpers.rb @@ -1,5 +1,3 @@ -require 'active_support/core_ext/load_error' -require 'active_support/core_ext/name_error' require 'active_support/dependencies' module ActionController diff --git a/actionpack/lib/action_controller/base/http.rb b/actionpack/lib/action_controller/base/http.rb index ec78bc15a8..3efd1b656f 100644 --- a/actionpack/lib/action_controller/base/http.rb +++ b/actionpack/lib/action_controller/base/http.rb @@ -1,5 +1,4 @@ require 'action_controller/abstract' -require 'active_support/core_ext/module/delegation' module ActionController # ActionController::Http provides a way to get a valid Rack application from a controller. diff --git a/actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb b/actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb index c6f7de17bd..159d869a54 100644 --- a/actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb +++ b/actionpack/lib/action_controller/routing/generation/polymorphic_routes.rb @@ -77,6 +77,7 @@ module ActionController end record = extract_record(record_or_hash_or_array) + record = record.to_model if record.respond_to?(:to_model) namespace = extract_namespace(record_or_hash_or_array) args = case record_or_hash_or_array diff --git a/actionpack/lib/action_controller/testing/process.rb b/actionpack/lib/action_controller/testing/process.rb index 7634290ea1..e7c64d0942 100644 --- a/actionpack/lib/action_controller/testing/process.rb +++ b/actionpack/lib/action_controller/testing/process.rb @@ -1,3 +1,4 @@ +require 'action_dispatch' require 'rack/session/abstract/id' require 'active_support/core_ext/object/conversions' diff --git a/actionpack/lib/action_view.rb b/actionpack/lib/action_view.rb index 27a06db5bb..70176a0ea4 100644 --- a/actionpack/lib/action_view.rb +++ b/actionpack/lib/action_view.rb @@ -30,12 +30,14 @@ require File.join(File.dirname(__FILE__), "action_pack") module ActionView def self.load_all! - [Base, InlineTemplate, TemplateError] + [Context, Base, InlineTemplate, TemplateError] end autoload :Base, 'action_view/base' + autoload :Context, 'action_view/context' autoload :Helpers, 'action_view/helpers' autoload :InlineTemplate, 'action_view/template/inline' + autoload :MissingTemplate, 'action_view/base' autoload :Partials, 'action_view/render/partials' autoload :Resolver, 'action_view/template/resolver' autoload :PathSet, 'action_view/paths' diff --git a/actionpack/lib/action_view/base.rb b/actionpack/lib/action_view/base.rb index 45184f58fb..9e696af83b 100644 --- a/actionpack/lib/action_view/base.rb +++ b/actionpack/lib/action_view/base.rb @@ -172,8 +172,6 @@ module ActionView #:nodoc: attr_accessor :controller attr_internal :captures - attr_accessor :output_buffer - class << self delegate :erb_trim_mode=, :to => 'ActionView::TemplateHandlers::ERB' delegate :logger, :to => 'ActionController::Base', :allow_nil => true @@ -206,10 +204,7 @@ module ActionView #:nodoc: delegate :find_by_parts, :to => :view_paths - module CompiledTemplates #:nodoc: - # holds compiled template code - end - include CompiledTemplates + include Context def self.process_view_paths(value) ActionView::PathSet.new(Array(value)) @@ -228,6 +223,12 @@ module ActionView #:nodoc: end end + def self.for_controller(controller) + new(controller.class.view_paths, {}, controller).tap do |view| + view.helpers.include(controller._helpers) if controller.respond_to?(:_helpers) + end + end + def initialize(view_paths = [], assigns_for_first_render = {}, controller = nil, formats = nil)#:nodoc: @formats = formats || [:html] @assigns = assigns_for_first_render.each { |key, value| instance_variable_set("@#{key}", value) } diff --git a/actionpack/lib/action_view/context.rb b/actionpack/lib/action_view/context.rb new file mode 100644 index 0000000000..f212fe25eb --- /dev/null +++ b/actionpack/lib/action_view/context.rb @@ -0,0 +1,44 @@ +module ActionView + module CompiledTemplates #:nodoc: + # holds compiled template code + end + + # ActionView contexts are supplied to ActionController + # to render template. The default ActionView context + # is ActionView::Base. + # + # In order to work with ActionController, a Context + # must implement: + # + # Context.for_controller[controller] Create a new ActionView instance for a + # controller + # Context#_render_partial_from_controller[options] + # - responsible for setting options[:_template] + # - Returns String with the rendered partial + # options<Hash>:: see _render_partial in ActionView::Base + # Context#_render_template_from_controller[template, layout, options, partial] + # - Returns String with the rendered template + # template<ActionView::Template>:: The template to render + # layout<ActionView::Template>:: The layout to render around the template + # options<Hash>:: See _render_template_with_layout in ActionView::Base + # partial<Boolean>:: Whether or not the template to render is a partial + # + # An ActionView context can also mix in ActionView's + # helpers. In order to mix in helpers, a context must + # implement: + # + # Context#controller + # - Returns an instance of AbstractController + # + # In any case, a context must mix in ActionView::Context, + # which stores compiled template and provides the output + # buffer. + module Context + include CompiledTemplates + attr_accessor :output_buffer + + def convert_to_model(object) + object.respond_to?(:to_model) ? object.to_model : object + end + end +end
\ No newline at end of file diff --git a/actionpack/lib/action_view/helpers.rb b/actionpack/lib/action_view/helpers.rb index 97fa2d80e9..c1c0eb59ae 100644 --- a/actionpack/lib/action_view/helpers.rb +++ b/actionpack/lib/action_view/helpers.rb @@ -1,6 +1,6 @@ module ActionView #:nodoc: module Helpers #:nodoc: - autoload :ActiveRecordHelper, 'action_view/helpers/active_record_helper' + autoload :ActiveModelHelper, 'action_view/helpers/active_model_helper' autoload :AssetTagHelper, 'action_view/helpers/asset_tag_helper' autoload :AtomFeedHelper, 'action_view/helpers/atom_feed_helper' autoload :BenchmarkHelper, 'action_view/helpers/benchmark_helper' @@ -31,7 +31,7 @@ module ActionView #:nodoc: include SanitizeHelper::ClassMethods end - include ActiveRecordHelper + include ActiveModelHelper include AssetTagHelper include AtomFeedHelper include BenchmarkHelper diff --git a/actionpack/lib/action_view/helpers/active_record_helper.rb b/actionpack/lib/action_view/helpers/active_model_helper.rb index 75cc651968..0f122d9232 100644 --- a/actionpack/lib/action_view/helpers/active_record_helper.rb +++ b/actionpack/lib/action_view/helpers/active_model_helper.rb @@ -15,7 +15,7 @@ module ActionView # method that creates a complete form for all the basic content types of the record (not associations or aggregations, though). This # is a great way of making the record quickly available for editing, but likely to prove lackluster for a complicated real-world form. # In that case, it's better to use the +input+ method and the specialized +form+ methods in link:classes/ActionView/Helpers/FormHelper.html - module ActiveRecordHelper + module ActiveModelHelper # Returns a default input tag for the type of object returned by the method. For example, if <tt>@post</tt> # has an attribute +title+ mapped to a +VARCHAR+ column that holds "Hello World": # @@ -77,6 +77,7 @@ module ActionView # * <tt>:submit_value</tt> - The text of the submit button (default: "Create" if a new record, otherwise "Update"). def form(record_name, options = {}) record = instance_variable_get("@#{record_name}") + record = convert_to_model(record) options = options.symbolize_keys options[:action] ||= record.new_record? ? "create" : "update" @@ -121,6 +122,8 @@ module ActionView end options.reverse_merge!(:prepend_text => '', :append_text => '', :css_class => 'formError') + object = convert_to_model(object) + if (obj = (object.respond_to?(:errors) ? object : instance_variable_get("@#{object}"))) && (errors = obj.errors[method]) content_tag("div", @@ -179,6 +182,8 @@ module ActionView objects = params.collect {|object_name| instance_variable_get("@#{object_name}") }.compact end + objects.map! {|o| convert_to_model(o) } + count = objects.inject(0) {|sum, object| sum + object.errors.count } unless count.zero? html = {} @@ -226,7 +231,14 @@ module ActionView end end - class InstanceTag #:nodoc: + module ActiveRecordInstanceTag + def object + @active_model_object ||= begin + object = super + object.respond_to?(:to_model) ? object.to_model : object + end + end + def to_tag(options = {}) case column_type when :string @@ -248,11 +260,9 @@ module ActionView end %w(tag content_tag to_date_select_tag to_datetime_select_tag to_time_select_tag).each do |meth| - without = "#{meth}_without_error_wrapping" - define_method "#{meth}_with_error_wrapping" do |*args| - error_wrapping(send(without, *args)) + define_method meth do |*| + error_wrapping(super) end - alias_method_chain meth, :error_wrapping end def error_wrapping(html_tag) @@ -267,5 +277,9 @@ module ActionView object.send(:column_for_attribute, @method_name).type end end + + class InstanceTag + include ActiveRecordInstanceTag + end end end diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index 6d6d623938..2d1d19d5f3 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -288,6 +288,8 @@ module ActionView def apply_form_for_options!(object_or_array, options) #:nodoc: object = object_or_array.is_a?(Array) ? object_or_array.last : object_or_array + object = convert_to_model(object) + html_options = if object.respond_to?(:new_record?) && object.new_record? { :class => dom_class(object, :new), :id => dom_id(object), :method => :post } @@ -488,7 +490,7 @@ module ActionView object_name = ActionController::RecordIdentifier.singular_class_name(object) end - builder = options[:builder] || ActionView::Base.default_form_builder + builder = options[:builder] || ActionView.default_form_builder yield builder.new(object_name, object, self, options, block) end @@ -626,8 +628,8 @@ module ActionView # Returns a checkbox tag tailored for accessing a specified attribute (identified by +method+) on an object # assigned to the template (identified by +object+). This object must be an instance object (@object) and not a local object. - # It's intended that +method+ returns an integer and if that integer is above zero, then the checkbox is checked. - # Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1 + # It's intended that +method+ returns an integer and if that integer is above zero, then the checkbox is checked. + # Additional options on the input tag can be passed as a hash with +options+. The +checked_value+ defaults to 1 # while the default +unchecked_value+ is set to 0 which is convenient for boolean values. # # ==== Gotcha @@ -709,7 +711,8 @@ module ActionView end end - class InstanceTag #:nodoc: + module InstanceTagMethods #:nodoc: + extend ActiveSupport::Concern include Helpers::TagHelper, Helpers::FormTagHelper attr_reader :method_name, :object_name @@ -832,7 +835,7 @@ module ActionView self.class.value_before_type_cast(object, @method_name) end - class << self + module ClassMethods def value(object, method_name) object.send method_name unless object.nil? end @@ -918,6 +921,10 @@ module ActionView end end + class InstanceTag + include InstanceTagMethods + end + class FormBuilder #:nodoc: # The methods which wrap a form helper call. class_inheritable_accessor :field_helpers @@ -1022,7 +1029,7 @@ module ActionView def fields_for_with_nested_attributes(association_name, args, block) name = "#{object_name}[#{association_name}_attributes]" association = @object.send(association_name) - explicit_object = args.first if args.first.respond_to?(:new_record?) + explicit_object = args.first.to_model if args.first.respond_to?(:to_model) if association.is_a?(Array) children = explicit_object ? [explicit_object] : association @@ -1054,9 +1061,21 @@ module ActionView end end - class << Base + class << ActionView attr_accessor :default_form_builder end - Base.default_form_builder = ::ActionView::Helpers::FormBuilder + self.default_form_builder = ::ActionView::Helpers::FormBuilder + + # 2.3 compatibility + class << Base + def default_form_builder=(builder) + ActionView.default_form_builder = builder + end + + def default_form_builder + ActionView.default_form_builder + end + end + end diff --git a/actionpack/lib/action_view/render/partials.rb b/actionpack/lib/action_view/render/partials.rb index a80ffe3c20..ccb14d513a 100644 --- a/actionpack/lib/action_view/render/partials.rb +++ b/actionpack/lib/action_view/render/partials.rb @@ -259,7 +259,7 @@ module ActionView _set_locals(object, locals, template, options) - self._partial = template + options[:_template] = template _render_template(template, locals) end @@ -278,7 +278,7 @@ module ActionView locals = (options[:locals] ||= {}) index, @_partial_path = 0, nil collection.map do |object| - template = passed_template || begin + options[:_template] = template = passed_template || begin _partial_path = ActionController::RecordIdentifier.partial_path(object, controller_path) template = _pick_partial_template(_partial_path) @@ -289,8 +289,6 @@ module ActionView index += 1 - self._partial = template - _render_template(template, locals) end.join(spacer) end diff --git a/actionpack/lib/action_view/template/renderable.rb b/actionpack/lib/action_view/template/renderable.rb index 54857516ab..7687578165 100644 --- a/actionpack/lib/action_view/template/renderable.rb +++ b/actionpack/lib/action_view/template/renderable.rb @@ -12,9 +12,9 @@ module ActionView end def load! - names = Base::CompiledTemplates.instance_methods.grep(/#{method_name_without_locals}/) + names = CompiledTemplates.instance_methods.grep(/#{method_name_without_locals}/) names.each do |name| - Base::CompiledTemplates.class_eval do + CompiledTemplates.class_eval do remove_method(name) end end @@ -56,7 +56,7 @@ module ActionView def compile(local_assigns) render_symbol = method_name(local_assigns) - if !Base::CompiledTemplates.method_defined?(render_symbol) || recompile? + if !CompiledTemplates.method_defined?(render_symbol) || recompile? compile!(render_symbol, local_assigns) end end @@ -74,7 +74,7 @@ module ActionView end_src begin - ActionView::Base::CompiledTemplates.module_eval(source, filename.to_s, 0) + ActionView::CompiledTemplates.module_eval(source, filename.to_s, 0) rescue Exception => e # errors from template code if logger = defined?(ActionController) && Base.logger logger.debug "ERROR: compiling #{render_symbol} RAISED #{e}" diff --git a/actionpack/lib/action_view/template/template.rb b/actionpack/lib/action_view/template/template.rb index fac50cd692..4145045e2d 100644 --- a/actionpack/lib/action_view/template/template.rb +++ b/actionpack/lib/action_view/template/template.rb @@ -75,7 +75,7 @@ module ActionView end begin - ActionView::Base::CompiledTemplates.module_eval(source, identifier, line) + ActionView::CompiledTemplates.module_eval(source, identifier, line) method_name rescue Exception => e # errors from template code if logger = (view && view.logger) diff --git a/actionpack/lib/action_view/test_case.rb b/actionpack/lib/action_view/test_case.rb index 7355af4192..3f3951509a 100644 --- a/actionpack/lib/action_view/test_case.rb +++ b/actionpack/lib/action_view/test_case.rb @@ -23,6 +23,7 @@ module ActionView class TestCase < ActiveSupport::TestCase include ActionDispatch::Assertions include ActionController::TestProcess + include ActionView::Context class_inheritable_accessor :helper_class @@helper_class = nil diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 30e795a7a2..6e71b85645 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -20,6 +20,7 @@ require 'action_controller/testing/process' require 'action_view/test_case' require 'action_controller/testing/integration' require 'active_support/dependencies' +require 'active_model' $tags[:new_base] = true @@ -97,7 +98,7 @@ module ActionController partials = hax[:partials] if expected_count = options[:count] found = partials.detect { |p, _| p.identifier.match(expected_partial) } - actual_count = found.nil? ? 0 : found.second + actual_count = found.nil? ? 0 : found[1] msg = build_message(message, "expecting ? to be rendered ? time(s) but rendered ? time(s)", expected_partial, expected_count, actual_count) diff --git a/actionpack/test/activerecord/render_partial_with_record_identification_test.rb b/actionpack/test/activerecord/render_partial_with_record_identification_test.rb index 2a31f3be44..0122bc7d8f 100644 --- a/actionpack/test/activerecord/render_partial_with_record_identification_test.rb +++ b/actionpack/test/activerecord/render_partial_with_record_identification_test.rb @@ -126,7 +126,7 @@ class RenderPartialWithRecordIdentificationController < ActionController::Base end class Game < Struct.new(:name, :id) - extend ActiveModel::Naming + extend ActiveModel::APICompliant def to_param id.to_s end diff --git a/actionpack/test/controller/record_identifier_test.rb b/actionpack/test/controller/record_identifier_test.rb index 28bc608d47..10f51639cf 100644 --- a/actionpack/test/controller/record_identifier_test.rb +++ b/actionpack/test/controller/record_identifier_test.rb @@ -1,7 +1,7 @@ require 'abstract_unit' class Comment - extend ActiveModel::Naming + extend ActiveModel::APICompliant attr_reader :id def save; @id = 1 end diff --git a/actionpack/test/controller/redirect_test.rb b/actionpack/test/controller/redirect_test.rb index 453a77e7bc..a71c39e504 100644 --- a/actionpack/test/controller/redirect_test.rb +++ b/actionpack/test/controller/redirect_test.rb @@ -4,7 +4,7 @@ class WorkshopsController < ActionController::Base end class Workshop - extend ActiveModel::Naming + extend ActiveModel::APICompliant attr_accessor :id, :new_record def initialize(id, new_record) diff --git a/actionpack/test/lib/controller/fake_models.rb b/actionpack/test/lib/controller/fake_models.rb index 9e6f14d373..07f01e1c47 100644 --- a/actionpack/test/lib/controller/fake_models.rb +++ b/actionpack/test/lib/controller/fake_models.rb @@ -1,7 +1,7 @@ require "active_model" class Customer < Struct.new(:name, :id) - extend ActiveModel::Naming + extend ActiveModel::APICompliant def to_param id.to_s @@ -16,7 +16,7 @@ end module Quiz class Question < Struct.new(:name, :id) - extend ActiveModel::Naming + extend ActiveModel::APICompliant def to_param id.to_s diff --git a/actionpack/test/template/active_record_helper_i18n_test.rb b/actionpack/test/template/active_record_helper_i18n_test.rb index 9d04c882c8..63032e4e5c 100644 --- a/actionpack/test/template/active_record_helper_i18n_test.rb +++ b/actionpack/test/template/active_record_helper_i18n_test.rb @@ -1,7 +1,8 @@ require 'abstract_unit' class ActiveRecordHelperI18nTest < Test::Unit::TestCase - include ActionView::Helpers::ActiveRecordHelper + include ActionView::Context + include ActionView::Helpers::ActiveModelHelper attr_reader :request diff --git a/actionpack/test/template/active_record_helper_test.rb b/actionpack/test/template/active_record_helper_test.rb index e1be048838..c1bbdae8fb 100644 --- a/actionpack/test/template/active_record_helper_test.rb +++ b/actionpack/test/template/active_record_helper_test.rb @@ -1,22 +1,20 @@ require 'abstract_unit' class ActiveRecordHelperTest < ActionView::TestCase - tests ActionView::Helpers::ActiveRecordHelper + tests ActionView::Helpers::ActiveModelHelper silence_warnings do - Post = Struct.new("Post", :title, :author_name, :body, :secret, :written_on) - Post.class_eval do - alias_method :title_before_type_cast, :title unless respond_to?(:title_before_type_cast) - alias_method :body_before_type_cast, :body unless respond_to?(:body_before_type_cast) - alias_method :author_name_before_type_cast, :author_name unless respond_to?(:author_name_before_type_cast) + class Post < Struct.new(:title, :author_name, :body, :secret, :written_on) + extend ActiveModel::APICompliant end - User = Struct.new("User", :email) - User.class_eval do - alias_method :email_before_type_cast, :email unless respond_to?(:email_before_type_cast) + class User < Struct.new(:email) + extend ActiveModel::APICompliant end - Column = Struct.new("Column", :type, :name, :human_name) + class Column < Struct.new(:type, :name, :human_name) + extend ActiveModel::APICompliant + end end class DirtyPost diff --git a/actionpack/test/template/atom_feed_helper_test.rb b/actionpack/test/template/atom_feed_helper_test.rb index 6f1179f359..89ff01ab0e 100644 --- a/actionpack/test/template/atom_feed_helper_test.rb +++ b/actionpack/test/template/atom_feed_helper_test.rb @@ -1,7 +1,12 @@ require 'abstract_unit' -Scroll = Struct.new(:id, :to_param, :title, :body, :updated_at, :created_at) -Scroll.extend ActiveModel::Naming +class Scroll < Struct.new(:id, :to_param, :title, :body, :updated_at, :created_at) + extend ActiveModel::APICompliant + + def new_record? + true + end +end class ScrollsController < ActionController::Base FEEDS = {} diff --git a/actionpack/test/template/compiled_templates_test.rb b/actionpack/test/template/compiled_templates_test.rb index 9c268aef27..7734e6da73 100644 --- a/actionpack/test/template/compiled_templates_test.rb +++ b/actionpack/test/template/compiled_templates_test.rb @@ -3,7 +3,7 @@ require 'controller/fake_models' class CompiledTemplatesTest < Test::Unit::TestCase def setup - @compiled_templates = ActionView::Base::CompiledTemplates + @compiled_templates = ActionView::CompiledTemplates @compiled_templates.instance_methods.each do |m| @compiled_templates.send(:remove_method, m) if m =~ /^_render_template_/ end diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 515f73c339..883afd985b 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -1,11 +1,9 @@ require 'abstract_unit' silence_warnings do - Post = Struct.new(:title, :author_name, :body, :secret, :written_on, :cost) - Post.class_eval do - alias_method :title_before_type_cast, :title unless respond_to?(:title_before_type_cast) - alias_method :body_before_type_cast, :body unless respond_to?(:body_before_type_cast) - alias_method :author_name_before_type_cast, :author_name unless respond_to?(:author_name_before_type_cast) + class Post < Struct.new(:title, :author_name, :body, :secret, :written_on, :cost) + extend ActiveModel::APICompliant + alias_method :secret?, :secret def new_record=(boolean) @@ -27,6 +25,8 @@ silence_warnings do end class Comment + extend ActiveModel::APICompliant + attr_reader :id attr_reader :post_id def initialize(id = nil, post_id = nil); @id, @post_id = id, post_id end @@ -43,6 +43,8 @@ silence_warnings do end class Tag + extend ActiveModel::APICompliant + attr_reader :id attr_reader :post_id def initialize(id = nil, post_id = nil); @id, @post_id = id, post_id end @@ -59,6 +61,8 @@ silence_warnings do end class CommentRelevance + extend ActiveModel::APICompliant + attr_reader :id attr_reader :comment_id def initialize(id = nil, comment_id = nil); @id, @comment_id = id, comment_id end @@ -71,6 +75,8 @@ silence_warnings do end class TagRelevance + extend ActiveModel::APICompliant + attr_reader :id attr_reader :tag_id def initialize(id = nil, tag_id = nil); @id, @tag_id = id, tag_id end @@ -1024,8 +1030,8 @@ class FormHelperTest < ActionView::TestCase end def test_default_form_builder - old_default_form_builder, ActionView::Base.default_form_builder = - ActionView::Base.default_form_builder, LabelledFormBuilder + old_default_form_builder, ActionView.default_form_builder = + ActionView.default_form_builder, LabelledFormBuilder form_for(:post, @post) do |f| concat f.text_field(:title) @@ -1042,7 +1048,7 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer ensure - ActionView::Base.default_form_builder = old_default_form_builder + ActionView.default_form_builder = old_default_form_builder end def test_default_form_builder_with_active_record_helpers diff --git a/actionpack/test/template/prototype_helper_test.rb b/actionpack/test/template/prototype_helper_test.rb index a7a1bc99f3..301a110076 100644 --- a/actionpack/test/template/prototype_helper_test.rb +++ b/actionpack/test/template/prototype_helper_test.rb @@ -1,10 +1,12 @@ require 'abstract_unit' +require 'active_model' Bunny = Struct.new(:Bunny, :id) -Bunny.extend ActiveModel::Naming +Bunny.extend ActiveModel::APICompliant class Author - extend ActiveModel::Naming + extend ActiveModel::APICompliant + attr_reader :id def save; @id = 1 end def new_record?; @id.nil? end @@ -14,7 +16,7 @@ class Author end class Article - extend ActiveModel::Naming + extend ActiveModel::APICompliant attr_reader :id attr_reader :author_id def save; @id = 1; @author_id = 1 end diff --git a/actionpack/test/template/record_tag_helper_test.rb b/actionpack/test/template/record_tag_helper_test.rb index 5b840d123b..bae26f555d 100644 --- a/actionpack/test/template/record_tag_helper_test.rb +++ b/actionpack/test/template/record_tag_helper_test.rb @@ -1,12 +1,12 @@ require 'abstract_unit' class Post - extend ActiveModel::Naming + extend ActiveModel::APICompliant def id 45 end def body - "What a wonderful world!" + super || "What a wonderful world!" end end diff --git a/actionpack/test/template/test_test.rb b/actionpack/test/template/test_test.rb index f32d0b3d42..98307fbae4 100644 --- a/actionpack/test/template/test_test.rb +++ b/actionpack/test/template/test_test.rb @@ -41,7 +41,7 @@ class PeopleHelperTest < ActionView::TestCase def test_link_to_person person = mock(:name => "David") - person.class.extend ActiveModel::Naming + person.class.extend ActiveModel::APICompliant expects(:mocha_mock_path).with(person).returns("/people/1") assert_equal '<a href="/people/1">David</a>', link_to_person(person) end diff --git a/actionpack/test/template/url_helper_test.rb b/actionpack/test/template/url_helper_test.rb index f0364fd660..4569689534 100644 --- a/actionpack/test/template/url_helper_test.rb +++ b/actionpack/test/template/url_helper_test.rb @@ -494,7 +494,7 @@ class LinkToUnlessCurrentWithControllerTest < ActionView::TestCase end class Workshop - extend ActiveModel::Naming + extend ActiveModel::APICompliant attr_accessor :id, :new_record def initialize(id, new_record) @@ -511,7 +511,7 @@ class Workshop end class Session - extend ActiveModel::Naming + extend ActiveModel::APICompliant attr_accessor :id, :workshop_id, :new_record def initialize(id, new_record) diff --git a/activemodel/examples/amo_ap_example.rb b/activemodel/examples/amo_ap_example.rb new file mode 100644 index 0000000000..cef718d0d4 --- /dev/null +++ b/activemodel/examples/amo_ap_example.rb @@ -0,0 +1,36 @@ +$:.push "activesupport/lib" +$:.push "activemodel/lib" + +require "active_model/validations" +require "active_model/deprecated_error_methods" +require "active_model/errors" +require "active_model/naming" + +class Person + include ActiveModel::Validations + extend ActiveModel::Naming + + validates_presence_of :name + + attr_accessor :name + def initialize(attributes = {}) + @name = attributes[:name] + end + + def persist + @persisted = true + end + + def new_record? + @persisted + end + + def to_model() self end +end + +person1 = Person.new +p person1.valid? +person1.errors + +person2 = Person.new(:name => "matz") +p person2.valid?
\ No newline at end of file diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index f988cd71b8..c6f63d2fdc 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -26,6 +26,7 @@ $:.unshift(activesupport_path) if File.directory?(activesupport_path) require 'active_support' module ActiveModel + autoload :APICompliant, 'active_model/api_compliant' autoload :Attributes, 'active_model/attributes' autoload :Base, 'active_model/base' autoload :DeprecatedErrorMethods, 'active_model/deprecated_error_methods' diff --git a/activemodel/lib/active_model/api_compliant.rb b/activemodel/lib/active_model/api_compliant.rb new file mode 100644 index 0000000000..26f83feb6b --- /dev/null +++ b/activemodel/lib/active_model/api_compliant.rb @@ -0,0 +1,25 @@ +module ActiveModel + module APICompliant + include Naming + + def self.extended(klass) + klass.class_eval do + include Validations + include InstanceMethods + end + end + + module InstanceMethods + def to_model + if respond_to?(:new_record?) + self.class.class_eval { def to_model() self end } + to_model + else + raise "In order to be ActiveModel API compliant, you need to define " \ + "a new_record? method, which should return true if it has not " \ + "yet been persisted." + end + end + end + end +end
\ No newline at end of file diff --git a/activemodel/lib/active_model/validations.rb b/activemodel/lib/active_model/validations.rb index 5223cea135..54a869396d 100644 --- a/activemodel/lib/active_model/validations.rb +++ b/activemodel/lib/active_model/validations.rb @@ -1,5 +1,7 @@ require 'active_support/core_ext/array/extract_options' require 'active_support/core_ext/hash/keys' +require 'active_support/concern' +require 'active_support/callbacks' module ActiveModel module Validations diff --git a/activerecord/lib/active_record/base.rb b/activerecord/lib/active_record/base.rb index 60bd38e74c..f6954813a4 100755 --- a/activerecord/lib/active_record/base.rb +++ b/activerecord/lib/active_record/base.rb @@ -2528,6 +2528,13 @@ module ActiveRecord #:nodoc: (id = self.id) ? id.to_s : nil # Be sure to stringify the id for routes end + # Returns the ActiveRecord object when asked for its + # ActiveModel-compliant representation, because ActiveRecord is + # ActiveModel-compliant. + def to_model + self + end + # Returns a cache key that can be used to identify this record. # # ==== Examples diff --git a/activesupport/lib/active_support/core_ext/string/interpolation.rb b/activesupport/lib/active_support/core_ext/string/interpolation.rb index b21977ecc1..d459c03d39 100644 --- a/activesupport/lib/active_support/core_ext/string/interpolation.rb +++ b/activesupport/lib/active_support/core_ext/string/interpolation.rb @@ -1,87 +1,93 @@ -if RUBY_VERSION < '1.9' - =begin - string.rb - Extension for String. - + heavily based on Masao Mutoh's gettext String interpolation extension + http://github.com/mutoh/gettext/blob/f6566738b981fe0952548c421042ad1e0cdfb31e/lib/gettext/core_ext/string.rb Copyright (C) 2005-2009 Masao Mutoh - - You may redistribute it and/or modify it under the same - license terms as Ruby. + You may redistribute it and/or modify it under the same license terms as Ruby. =end -# This feature is included in Ruby 1.9 or later but not occur TypeError. -# -# String#% method which accepts named arguments. Particularly useful if the -# string is to be used by a translator because named arguments mean more -# than %s/%d style. -class String +if RUBY_VERSION < '1.9' - unless instance_methods.find {|m| m.to_s == 'bytesize'} - # For older ruby (such as ruby-1.8.5) - alias :bytesize :size - end + # KeyError is raised by String#% when the string contains a named placeholder + # that is not contained in the given arguments hash. Ruby 1.9 includes and + # raises this exception natively. We define it to mimic Ruby 1.9's behaviour + # in Ruby 1.8.x - alias :_old_format_m :% # :nodoc: + class KeyError < IndexError + def initialize(message = nil) + super(message || "key not found") + end + end unless defined?(KeyError) - PERCENT_MATCH_RE = Regexp.union( + # Extension for String class. This feature is included in Ruby 1.9 or later but not occur TypeError. + # + # String#% method which accept "named argument". The translator can know + # the meaning of the msgids using "named argument" instead of %s/%d style. + + class String + # For older ruby versions, such as ruby-1.8.5 + alias :bytesize :size unless instance_methods.find {|m| m.to_s == 'bytesize'} + alias :interpolate_without_ruby_19_syntax :% # :nodoc: + + INTERPOLATION_PATTERN = Regexp.union( /%%/, - /%\{(\w+)\}/, - /%<(\w+)>(.*?\d*\.?\d*[bBdiouxXeEfgGcps])/ - ) + /%\{(\w+)\}/, # matches placeholders like "%{foo}" + /%<(\w+)>(.*?\d*\.?\d*[bBdiouxXeEfgGcps])/ # matches placeholders like "%<foo>.d" + ) - # call-seq: - # %(arg) - # %(hash) - # - # Format - Uses str as a format specification, and returns the result of applying it to arg. - # If the format specification contains more than one substitution, then arg must be - # an Array containing the values to be substituted. See Kernel::sprintf for details of the - # format string. This is the default behavior of the String class. - # * arg: an Array or other class except Hash. - # * Returns: formatted String - # Example: - # "%s, %s" % ["Masao", "Mutoh"] - # - # Also you can use a Hash as the "named argument". This is recommended way so translators - # can understand the meanings of the msgids easily. - # * hash: {:key1 => value1, :key2 => value2, ... } - # * Returns: formatted String - # Example: - # For strings. - # "%{firstname}, %{familyname}" % {:firstname => "Masao", :familyname => "Mutoh"} - # - # With field type to specify format such as d(decimal), f(float),... - # "%<age>d, %<weight>.1f" % {:age => 10, :weight => 43.4} - def %(args) - if args.kind_of?(Hash) - ret = dup - ret.gsub!(PERCENT_MATCH_RE) {|match| - if match == '%%' - '%' - elsif $1 - key = $1.to_sym - args.has_key?(key) ? args[key] : match - elsif $2 - key = $2.to_sym - args.has_key?(key) ? sprintf("%#{$3}", args[key]) : match - end - } - ret - else - ret = gsub(/%([{<])/, '%%\1') - begin - ret._old_format_m(args) - rescue ArgumentError => e - if $DEBUG - $stderr.puts " The string:#{ret}" - $stderr.puts " args:#{args.inspect}" - puts e.backtrace - else - raise ArgumentError, e.message + # % uses self (i.e. the String) as a format specification and returns the + # result of applying it to the given arguments. In other words it interpolates + # the given arguments to the string according to the formats the string + # defines. + # + # There are three ways to use it: + # + # * Using a single argument or Array of arguments. + # + # This is the default behaviour of the String class. See Kernel#sprintf for + # more details about the format string. + # + # Example: + # + # "%d %s" % [1, "message"] + # # => "1 message" + # + # * Using a Hash as an argument and unformatted, named placeholders. + # + # When you pass a Hash as an argument and specify placeholders with %{foo} + # it will interpret the hash values as named arguments. + # + # Example: + # + # "%{firstname}, %{lastname}" % {:firstname => "Masao", :lastname => "Mutoh"} + # # => "Masao Mutoh" + # + # * Using a Hash as an argument and formatted, named placeholders. + # + # When you pass a Hash as an argument and specify placeholders with %<foo>d + # it will interpret the hash values as named arguments and format the value + # according to the formatting instruction appended to the closing >. + # + # Example: + # + # "%<integer>d, %<float>.1f" % { :integer => 10, :float => 43.4 } + # # => "10, 43.3" + def %(args) + if args.kind_of?(Hash) + dup.gsub(INTERPOLATION_PATTERN) do |match| + if match == '%%' + '%' + else + key = ($1 || $2).to_sym + raise KeyError unless args.has_key?(key) + $3 ? sprintf("%#{$3}", args[key]) : args[key] + end end + elsif self =~ INTERPOLATION_PATTERN + raise ArgumentError.new('one hash required') + else + result = gsub(/%([{<])/, '%%\1') + result.send :'interpolate_without_ruby_19_syntax', args end end end -end - end
\ No newline at end of file diff --git a/activesupport/test/core_ext/string_ext_test.rb b/activesupport/test/core_ext/string_ext_test.rb index f77ad5236e..a23d3f6fef 100644 --- a/activesupport/test/core_ext/string_ext_test.rb +++ b/activesupport/test/core_ext/string_ext_test.rb @@ -311,8 +311,8 @@ class TestGetTextString < Test::Unit::TestCase end def test_sprintf_lack_argument - assert_equal("%{num}, test", "%{num}, %{record}" % {:record => "test"}) - assert_equal("%{record}", "%{record}" % {:num => 1}) + assert_raises(KeyError) { "%{num}, %{record}" % {:record => "test"} } + assert_raises(KeyError) { "%{record}" % {:num => 1} } end def test_no_placeholder @@ -336,9 +336,12 @@ class TestGetTextString < Test::Unit::TestCase assert_equal("foo 1.000000", "%s %f" % ["foo", 1.0]) end - def test_sprintf_mix + def test_sprintf_mix_unformatted_and_formatted_named_placeholders assert_equal("foo 1.000000", "%{name} %<num>f" % {:name => "foo", :num => 1.0}) - assert_equal("%{name} 1.000000", "%{name} %f" % [1.0]) - assert_equal("%{name} 1.000000", "%{name} %f" % [1.0, 2.0]) + end + + def test_string_interpolation_raises_an_argument_error_when_mixing_named_and_unnamed_placeholders + assert_raises(ArgumentError) { "%{name} %f" % [1.0] } + assert_raises(ArgumentError) { "%{name} %f" % [1.0, 2.0] } end end |