提交 9a28bd78 编写于 作者: E Emilio Tagua

Merge commit 'rails/master'

$:.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
+ <%= yield %> +
\ No newline at end of file
Hello <%= yield %> Goodbye
\ No newline at end of file
Hello <%= @name %>
\ No newline at end of file
......@@ -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
require 'active_support/core_ext/module/attr_internal'
module AbstractController
class Base
......
require "active_support/new_callbacks"
module AbstractController
module Callbacks
extend ActiveSupport::Concern
......
......@@ -8,12 +8,6 @@ module Helpers
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
......
......@@ -6,6 +6,7 @@ module Layouts
included do
extlib_inheritable_accessor(:_layout_conditions) { Hash.new }
_write_layout_method
end
module ClassMethods
......@@ -99,7 +100,7 @@ def _layout(details) end
# 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 @@ def _layout_for_name(name, details = {:formats => formats})
#
# ==== 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)
......
require 'active_support/core_ext/class/attribute_accessors'
require 'active_support/core_ext/logger'
module AbstractController
......
......@@ -17,25 +17,22 @@ module Renderer
# 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.
......
......@@ -114,7 +114,7 @@ def method_for_action(action_name)
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 @@ def _find_by_parts(name, details)
def _default_layout(details, require_layout = false)
super
rescue ActionView::MissingTemplate
_find_by_parts(_layout({}), {})
_find_layout(_layout({}), {})
nil
end
......
require 'active_support/core_ext/load_error'
require 'active_support/core_ext/name_error'
require 'active_support/dependencies'
module ActionController
......
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.
......
......@@ -77,6 +77,7 @@ def polymorphic_url(record_or_hash_or_array, options = {})
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
......
require 'action_dispatch'
require 'rack/session/abstract/id'
require 'active_support/core_ext/object/conversions'
......
......@@ -30,12 +30,14 @@
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'
......
......@@ -172,8 +172,6 @@ class Base
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 @@ def self.cache_template_loading?
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 @@ def include(*args)
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) }
......
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
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 ClassMethods
include SanitizeHelper::ClassMethods
end
include ActiveRecordHelper
include ActiveModelHelper
include AssetTagHelper
include AtomFeedHelper
include BenchmarkHelper
......
......@@ -15,7 +15,7 @@ module Helpers
# 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 @@ def input(record_name, method, options = {})
# * <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 @@ def error_message_on(object, method, *args)
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 @@ def error_messages_for(*params)
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 @@ def default_input_block
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 @@ def to_tag(options = {})
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 @@ def column_type
object.send(:column_for_attribute, @method_name).type
end
end
class InstanceTag
include ActiveRecordInstanceTag
end
end
end
......@@ -288,6 +288,8 @@ def form_for(record_or_name_or_array, *args, &proc)
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 @@ def fields_for(record_or_name_or_array, *args, &block)
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 @@ def text_area(object_name, method, options = {})
# 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 @@ def radio_button(object_name, method, tag_value, options = {})
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 @@ def value_before_type_cast(object)
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 @@ def sanitized_method_name
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 @@ def nested_attributes_association?(association_name)
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 @@ def nested_child_index(name)
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
......@@ -259,7 +259,7 @@ def _render_partial_object(template, options, object = nil)
_set_locals(object, locals, template, options)
self._partial = template
options[:_template] = template
_render_template(template, locals)
end
......@@ -278,7 +278,7 @@ def _render_partial_collection(collection, options = {}, passed_template = nil)
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 @@ def _render_partial_collection(collection, options = {}, passed_template = nil)
index += 1
self._partial = template
_render_template(template, locals)
end.join(spacer)
end
......
......@@ -12,9 +12,9 @@ def render(view, locals)
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 @@ def method_name(local_assigns)
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 @@ def #{render_symbol}(local_assigns)
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}"
......
......@@ -75,7 +75,7 @@ def #{method_name}(local_assigns)
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)
......
......@@ -23,6 +23,7 @@ def _render_template(template, local_assigns = {})
class TestCase < ActiveSupport::TestCase
include ActionDispatch::Assertions
include ActionController::TestProcess
include ActionView::Context
class_inheritable_accessor :helper_class
@@helper_class = nil
......
......@@ -20,6 +20,7 @@
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 @@ def assert_template(options = {}, message = nil)
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)
......
......@@ -126,7 +126,7 @@ def render_with_record_collection
end
class Game < Struct.new(:name, :id)
extend ActiveModel::Naming
extend ActiveModel::APICompliant
def to_param
id.to_s
end
......
require 'abstract_unit'
class Comment
extend ActiveModel::Naming
extend ActiveModel::APICompliant
attr_reader :id
def save; @id = 1 end
......
......@@ -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)
......
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 @@ class GoodCustomer < Customer
module Quiz
class Question < Struct.new(:name, :id)
extend ActiveModel::Naming
extend ActiveModel::APICompliant
def to_param
id.to_s
......
require 'abstract_unit'
class ActiveRecordHelperI18nTest < Test::Unit::TestCase
include ActionView::Helpers::ActiveRecordHelper
include ActionView::Context
include ActionView::Helpers::ActiveModelHelper
attr_reader :request
......
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
......
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 = {}
......
......@@ -3,7 +3,7 @@
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
......
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 @@ def tags_attributes=(attributes); end
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 @@ def relevances_attributes=(attributes); end
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 @@ def relevances_attributes=(attributes); end
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 @@ def value
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 @@ def test_form_for_with_labelled_builder
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 @@ def test_default_form_builder
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
......
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 @@ def name
end
class Article
extend ActiveModel::Naming
extend ActiveModel::APICompliant
attr_reader :id
attr_reader :author_id
def save; @id = 1; @author_id = 1 end
......
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
......
......@@ -41,7 +41,7 @@ def test_homepage_url
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
......
......@@ -494,7 +494,7 @@ def with_restful_routing
end
class Workshop
extend ActiveModel::Naming
extend ActiveModel::APICompliant
attr_accessor :id, :new_record
def initialize(id, new_record)
......@@ -511,7 +511,7 @@ def to_s
end
class Session
extend ActiveModel::Naming
extend ActiveModel::APICompliant
attr_accessor :id, :workshop_id, :new_record
def initialize(id, new_record)
......
$:.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
......@@ -26,6 +26,7 @@
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'
......
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
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
......
......@@ -2528,6 +2528,13 @@ def to_param
(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
......
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
......@@ -311,8 +311,8 @@ def test_sprintf_percent_in_replacement
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 @@ def test_sprintf_old_style
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
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册