mirror of
https://github.com/github/rails.git
synced 2026-04-26 03:00:59 -04:00
Solving merge conflicts.
This commit is contained in:
50
actionpack/examples/very_simple.rb
Normal file
50
actionpack/examples/very_simple.rb
Normal file
@@ -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
|
||||
1
actionpack/examples/views/layouts/alt.html.erb
Normal file
1
actionpack/examples/views/layouts/alt.html.erb
Normal file
@@ -0,0 +1 @@
|
||||
+ <%= yield %> +
|
||||
1
actionpack/examples/views/layouts/kaigi.html.erb
Normal file
1
actionpack/examples/views/layouts/kaigi.html.erb
Normal file
@@ -0,0 +1 @@
|
||||
Hello <%= yield %> Goodbye
|
||||
1
actionpack/examples/views/template.html.erb
Normal file
1
actionpack/examples/views/template.html.erb
Normal file
@@ -0,0 +1 @@
|
||||
Hello <%= @name %>
|
||||
@@ -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'
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'active_support/core_ext/module/attr_internal'
|
||||
|
||||
module AbstractController
|
||||
|
||||
class Base
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
require "active_support/new_callbacks"
|
||||
|
||||
module AbstractController
|
||||
module Callbacks
|
||||
extend ActiveSupport::Concern
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -1,4 +1,3 @@
|
||||
require 'active_support/core_ext/class/attribute_accessors'
|
||||
require 'active_support/core_ext/logger'
|
||||
|
||||
module AbstractController
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,5 +1,3 @@
|
||||
require 'active_support/core_ext/load_error'
|
||||
require 'active_support/core_ext/name_error'
|
||||
require 'active_support/dependencies'
|
||||
|
||||
module ActionController
|
||||
|
||||
@@ -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.
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,3 +1,4 @@
|
||||
require 'action_dispatch'
|
||||
require 'rack/session/abstract/id'
|
||||
require 'active_support/core_ext/object/conversions'
|
||||
|
||||
|
||||
@@ -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'
|
||||
|
||||
@@ -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) }
|
||||
|
||||
44
actionpack/lib/action_view/context.rb
Normal file
44
actionpack/lib/action_view/context.rb
Normal file
@@ -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
|
||||
@@ -1,6 +1,7 @@
|
||||
module ActionView #:nodoc:
|
||||
module Helpers #:nodoc:
|
||||
autoload :ActiveRecordHelper, 'action_view/helpers/active_record_helper'
|
||||
autoload :ActiveModelHelper, 'action_view/helpers/active_model_helper'
|
||||
autoload :AjaxHelper, 'action_view/helpers/ajax_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 +32,7 @@ module ActionView #:nodoc:
|
||||
include SanitizeHelper::ClassMethods
|
||||
end
|
||||
|
||||
include ActiveRecordHelper
|
||||
include ActiveModelHelper
|
||||
include AssetTagHelper
|
||||
include AtomFeedHelper
|
||||
include BenchmarkHelper
|
||||
|
||||
@@ -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
|
||||
68
actionpack/lib/action_view/helpers/ajax_helper.rb
Normal file
68
actionpack/lib/action_view/helpers/ajax_helper.rb
Normal file
@@ -0,0 +1,68 @@
|
||||
module ActionView
|
||||
module Helpers
|
||||
module AjaxHelper
|
||||
include UrlHelper
|
||||
|
||||
def link_to_remote(name, url, options = {})
|
||||
html = options.delete(:html) || {}
|
||||
|
||||
update = options.delete(:update)
|
||||
if update.is_a?(Hash)
|
||||
html["data-update-success"] = update[:success]
|
||||
html["data-update-failure"] = update[:failure]
|
||||
else
|
||||
html["data-update-success"] = update
|
||||
end
|
||||
|
||||
html["data-update-position"] = options.delete(:position)
|
||||
html["data-method"] = options.delete(:method)
|
||||
html["data-remote"] = "true"
|
||||
|
||||
html.merge!(options)
|
||||
|
||||
url = url_for(url) if url.is_a?(Hash)
|
||||
link_to(name, url, html)
|
||||
end
|
||||
|
||||
def button_to_remote(name, options = {}, html_options = {})
|
||||
url = options.delete(:url)
|
||||
url = url_for(url) if url.is_a?(Hash)
|
||||
|
||||
html_options.merge!(:type => "button", :value => name,
|
||||
:"data-url" => url)
|
||||
|
||||
tag(:input, html_options)
|
||||
end
|
||||
|
||||
module Rails2Compatibility
|
||||
def set_callbacks(options, html)
|
||||
[:complete, :failure, :success, :interactive, :loaded, :loading].each do |type|
|
||||
html["data-#{type}-code"] = options.delete(type.to_sym)
|
||||
end
|
||||
|
||||
options.each do |option, value|
|
||||
if option.is_a?(Integer)
|
||||
html["data-#{option}-code"] = options.delete(option)
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def link_to_remote(name, url, options = nil)
|
||||
if !options && url.is_a?(Hash) && url.key?(:url)
|
||||
url, options = url.delete(:url), url
|
||||
end
|
||||
|
||||
set_callbacks(options, options[:html] ||= {})
|
||||
|
||||
super
|
||||
end
|
||||
|
||||
def button_to_remote(name, options = {}, html_options = {})
|
||||
set_callbacks(options, html_options)
|
||||
super
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -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
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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}"
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -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)
|
||||
|
||||
@@ -127,6 +127,7 @@ end
|
||||
|
||||
class Game < Struct.new(:name, :id)
|
||||
extend ActiveModel::Naming
|
||||
include ActiveModel::Conversion
|
||||
def to_param
|
||||
id.to_s
|
||||
end
|
||||
|
||||
@@ -2,6 +2,7 @@ require 'abstract_unit'
|
||||
|
||||
class Comment
|
||||
extend ActiveModel::Naming
|
||||
include ActiveModel::Conversion
|
||||
|
||||
attr_reader :id
|
||||
def save; @id = 1 end
|
||||
|
||||
@@ -5,6 +5,7 @@ end
|
||||
|
||||
class Workshop
|
||||
extend ActiveModel::Naming
|
||||
include ActiveModel::Conversion
|
||||
attr_accessor :id, :new_record
|
||||
|
||||
def initialize(id, new_record)
|
||||
|
||||
115
actionpack/test/javascript/ajax_test.rb
Normal file
115
actionpack/test/javascript/ajax_test.rb
Normal file
@@ -0,0 +1,115 @@
|
||||
require "abstract_unit"
|
||||
|
||||
class AjaxTestCase < ActiveSupport::TestCase
|
||||
include ActionView::Helpers::AjaxHelper
|
||||
include ActionView::Helpers::TagHelper
|
||||
|
||||
def assert_html(html, matches)
|
||||
matches.each do |match|
|
||||
assert_match Regexp.new(Regexp.escape(match)), html
|
||||
end
|
||||
end
|
||||
|
||||
def self.assert_callbacks_work(&blk)
|
||||
define_method(:assert_callbacks_work, &blk)
|
||||
|
||||
[:complete, :failure, :success, :interactive, :loaded, :loading, 404].each do |callback|
|
||||
test "#{callback} callback" do
|
||||
markup = assert_callbacks_work(callback)
|
||||
assert_html markup, %W(data-#{callback}-code="undoRequestCompleted\(request\)")
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class LinkToRemoteTest < AjaxTestCase
|
||||
def url_for(hash)
|
||||
"/blog/destroy/4"
|
||||
end
|
||||
|
||||
def link(options = {})
|
||||
link_to_remote("Delete this post", "/blog/destroy/3", options)
|
||||
end
|
||||
|
||||
test "with no update" do
|
||||
assert_html link, %w(href="/blog/destroy/3" Delete\ this\ post data-remote="true")
|
||||
end
|
||||
|
||||
test "basic" do
|
||||
assert_html link(:update => "#posts"),
|
||||
%w(data-update-success="#posts")
|
||||
end
|
||||
|
||||
test "using a url hash" do
|
||||
link = link_to_remote("Delete this post", {:controller => :blog}, :update => "#posts")
|
||||
assert_html link, %w(href="/blog/destroy/4" data-update-success="#posts")
|
||||
end
|
||||
|
||||
test "with :html options" do
|
||||
expected = %{<a href="/blog/destroy/3" data-custom="me" data-update-success="#posts">Delete this post</a>}
|
||||
assert_equal expected, link(:update => "#posts", :html => {"data-custom" => "me"})
|
||||
end
|
||||
|
||||
test "with a hash for :update" do
|
||||
link = link(:update => {:success => "#posts", :failure => "#error"})
|
||||
assert_match /data-update-success="#posts"/, link
|
||||
assert_match /data-update-failure="#error"/, link
|
||||
end
|
||||
|
||||
test "with positional parameters" do
|
||||
link = link(:position => :top, :update => "#posts")
|
||||
assert_match /data\-update\-position="top"/, link
|
||||
end
|
||||
|
||||
test "with an optional method" do
|
||||
link = link(:method => "delete")
|
||||
assert_match /data-method="delete"/, link
|
||||
end
|
||||
|
||||
class LegacyLinkToRemoteTest < AjaxTestCase
|
||||
include ActionView::Helpers::AjaxHelper::Rails2Compatibility
|
||||
|
||||
def link(options)
|
||||
link_to_remote("Delete this post", "/blog/destroy/3", options)
|
||||
end
|
||||
|
||||
test "basic link_to_remote with :url =>" do
|
||||
expected = %{<a href="/blog/destroy/3" data-update-success="#posts">Delete this post</a>}
|
||||
assert_equal expected,
|
||||
link_to_remote("Delete this post", :url => "/blog/destroy/3", :update => "#posts")
|
||||
end
|
||||
|
||||
assert_callbacks_work do |callback|
|
||||
link(callback => "undoRequestCompleted(request)")
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class ButtonToRemoteTest < AjaxTestCase
|
||||
def button(options, html = {})
|
||||
button_to_remote("Remote outpost", options, html)
|
||||
end
|
||||
|
||||
def url_for(*)
|
||||
"/whatnot"
|
||||
end
|
||||
|
||||
class StandardTest < ButtonToRemoteTest
|
||||
test "basic" do
|
||||
button = button({:url => {:action => "whatnot"}}, {:class => "fine"})
|
||||
[/input/, /class="fine"/, /type="button"/, /value="Remote outpost"/,
|
||||
/data-url="\/whatnot"/].each do |match|
|
||||
assert_match match, button
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
class LegacyButtonToRemoteTest < ButtonToRemoteTest
|
||||
include ActionView::Helpers::AjaxHelper::Rails2Compatibility
|
||||
|
||||
assert_callbacks_work do |callback|
|
||||
button(callback => "undoRequestCompleted(request)")
|
||||
end
|
||||
end
|
||||
|
||||
end
|
||||
@@ -2,6 +2,7 @@ require "active_model"
|
||||
|
||||
class Customer < Struct.new(:name, :id)
|
||||
extend ActiveModel::Naming
|
||||
include ActiveModel::Conversion
|
||||
|
||||
def to_param
|
||||
id.to_s
|
||||
@@ -17,6 +18,7 @@ end
|
||||
module Quiz
|
||||
class Question < Struct.new(:name, :id)
|
||||
extend ActiveModel::Naming
|
||||
include ActiveModel::Conversion
|
||||
|
||||
def to_param
|
||||
id.to_s
|
||||
|
||||
@@ -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
|
||||
|
||||
|
||||
@@ -1,22 +1,23 @@
|
||||
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::Naming
|
||||
include ActiveModel::Conversion
|
||||
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::Naming
|
||||
include ActiveModel::Conversion
|
||||
end
|
||||
|
||||
Column = Struct.new("Column", :type, :name, :human_name)
|
||||
class Column < Struct.new(:type, :name, :human_name)
|
||||
extend ActiveModel::Naming
|
||||
include ActiveModel::Conversion
|
||||
end
|
||||
end
|
||||
|
||||
class DirtyPost
|
||||
|
||||
@@ -1,7 +1,13 @@
|
||||
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::Naming
|
||||
include ActiveModel::Conversion
|
||||
|
||||
def new_record?
|
||||
true
|
||||
end
|
||||
end
|
||||
|
||||
class ScrollsController < ActionController::Base
|
||||
FEEDS = {}
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,11 +1,10 @@
|
||||
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::Naming
|
||||
include ActiveModel::Conversion
|
||||
|
||||
alias_method :secret?, :secret
|
||||
|
||||
def new_record=(boolean)
|
||||
@@ -27,6 +26,9 @@ silence_warnings do
|
||||
end
|
||||
|
||||
class Comment
|
||||
extend ActiveModel::Naming
|
||||
include ActiveModel::Conversion
|
||||
|
||||
attr_reader :id
|
||||
attr_reader :post_id
|
||||
def initialize(id = nil, post_id = nil); @id, @post_id = id, post_id end
|
||||
@@ -43,6 +45,9 @@ silence_warnings do
|
||||
end
|
||||
|
||||
class Tag
|
||||
extend ActiveModel::Naming
|
||||
include ActiveModel::Conversion
|
||||
|
||||
attr_reader :id
|
||||
attr_reader :post_id
|
||||
def initialize(id = nil, post_id = nil); @id, @post_id = id, post_id end
|
||||
@@ -59,6 +64,9 @@ silence_warnings do
|
||||
end
|
||||
|
||||
class CommentRelevance
|
||||
extend ActiveModel::Naming
|
||||
include ActiveModel::Conversion
|
||||
|
||||
attr_reader :id
|
||||
attr_reader :comment_id
|
||||
def initialize(id = nil, comment_id = nil); @id, @comment_id = id, comment_id end
|
||||
@@ -71,6 +79,9 @@ silence_warnings do
|
||||
end
|
||||
|
||||
class TagRelevance
|
||||
extend ActiveModel::Naming
|
||||
include ActiveModel::Conversion
|
||||
|
||||
attr_reader :id
|
||||
attr_reader :tag_id
|
||||
def initialize(id = nil, tag_id = nil); @id, @tag_id = id, tag_id end
|
||||
@@ -1024,8 +1035,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 +1053,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
|
||||
|
||||
@@ -1,10 +1,15 @@
|
||||
require 'abstract_unit'
|
||||
require 'active_model'
|
||||
|
||||
Bunny = Struct.new(:Bunny, :id)
|
||||
Bunny.extend ActiveModel::Naming
|
||||
class Bunny < Struct.new(:Bunny, :id)
|
||||
extend ActiveModel::Naming
|
||||
include ActiveModel::Conversion
|
||||
end
|
||||
|
||||
class Author
|
||||
extend ActiveModel::Naming
|
||||
include ActiveModel::Conversion
|
||||
|
||||
attr_reader :id
|
||||
def save; @id = 1 end
|
||||
def new_record?; @id.nil? end
|
||||
@@ -15,6 +20,7 @@ end
|
||||
|
||||
class Article
|
||||
extend ActiveModel::Naming
|
||||
include ActiveModel::Conversion
|
||||
attr_reader :id
|
||||
attr_reader :author_id
|
||||
def save; @id = 1; @author_id = 1 end
|
||||
|
||||
@@ -2,11 +2,12 @@ require 'abstract_unit'
|
||||
|
||||
class Post
|
||||
extend ActiveModel::Naming
|
||||
include ActiveModel::Conversion
|
||||
def id
|
||||
45
|
||||
end
|
||||
def body
|
||||
"What a wonderful world!"
|
||||
super || "What a wonderful world!"
|
||||
end
|
||||
end
|
||||
|
||||
|
||||
@@ -495,6 +495,7 @@ end
|
||||
|
||||
class Workshop
|
||||
extend ActiveModel::Naming
|
||||
include ActiveModel::Conversion
|
||||
attr_accessor :id, :new_record
|
||||
|
||||
def initialize(id, new_record)
|
||||
@@ -512,6 +513,7 @@ end
|
||||
|
||||
class Session
|
||||
extend ActiveModel::Naming
|
||||
include ActiveModel::Conversion
|
||||
attr_accessor :id, :workshop_id, :new_record
|
||||
|
||||
def initialize(id, new_record)
|
||||
|
||||
29
activemodel/examples/validations.rb
Normal file
29
activemodel/examples/validations.rb
Normal file
@@ -0,0 +1,29 @@
|
||||
require 'activemodel'
|
||||
|
||||
class Person
|
||||
include ActiveModel::Conversion
|
||||
include ActiveModel::Validations
|
||||
|
||||
validates_presence_of :name
|
||||
|
||||
attr_accessor :name
|
||||
|
||||
def initialize(attributes = {})
|
||||
@name = attributes[:name]
|
||||
end
|
||||
|
||||
def persist
|
||||
@persisted = true
|
||||
end
|
||||
|
||||
def new_record?
|
||||
@persisted
|
||||
end
|
||||
end
|
||||
|
||||
person1 = Person.new
|
||||
p person1.valid?
|
||||
person1.errors
|
||||
|
||||
person2 = Person.new(:name => "matz")
|
||||
p person2.valid?
|
||||
@@ -26,8 +26,7 @@ $:.unshift(activesupport_path) if File.directory?(activesupport_path)
|
||||
require 'active_support'
|
||||
|
||||
module ActiveModel
|
||||
autoload :Attributes, 'active_model/attributes'
|
||||
autoload :Base, 'active_model/base'
|
||||
autoload :Conversion, 'active_model/conversion'
|
||||
autoload :DeprecatedErrorMethods, 'active_model/deprecated_error_methods'
|
||||
autoload :Errors, 'active_model/errors'
|
||||
autoload :Name, 'active_model/naming'
|
||||
|
||||
@@ -1,25 +0,0 @@
|
||||
require 'active_support/core_ext/object/instance_variables'
|
||||
|
||||
module ActiveModel
|
||||
module Attributes
|
||||
def self.append_features(base)
|
||||
unless base.instance_methods.include?('attributes')
|
||||
super
|
||||
else
|
||||
false
|
||||
end
|
||||
end
|
||||
|
||||
def attributes
|
||||
instance_values
|
||||
end
|
||||
|
||||
def read_attribute(attr_name)
|
||||
instance_variable_get(:"@#{attr_name}")
|
||||
end
|
||||
|
||||
def write_attribute(attr_name, value)
|
||||
instance_variable_set(:"@#{attr_name}", value)
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,8 +0,0 @@
|
||||
module ActiveModel
|
||||
class Base
|
||||
include Observing
|
||||
# disabled, until they're tested
|
||||
# include Callbacks
|
||||
# include Validations
|
||||
end
|
||||
end
|
||||
8
activemodel/lib/active_model/conversion.rb
Normal file
8
activemodel/lib/active_model/conversion.rb
Normal file
@@ -0,0 +1,8 @@
|
||||
module ActiveModel
|
||||
# Include ActiveModel::Conversion if your object "acts like an ActiveModel model".
|
||||
module Conversion
|
||||
def to_model
|
||||
self
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -1,7 +1,8 @@
|
||||
require 'observer'
|
||||
require 'singleton'
|
||||
require 'active_support/core_ext/string/inflections'
|
||||
require 'active_support/core_ext/array/wrap'
|
||||
require 'active_support/core_ext/module/aliasing'
|
||||
require 'active_support/core_ext/string/inflections'
|
||||
|
||||
module ActiveModel
|
||||
module Observing
|
||||
@@ -39,8 +40,25 @@ module ActiveModel
|
||||
observers.each { |o| instantiate_observer(o) }
|
||||
end
|
||||
|
||||
# Wraps methods with before and after notifications.
|
||||
#
|
||||
# wrap_with_notifications :create, :save, :update, :destroy
|
||||
def wrap_with_notifications(*methods)
|
||||
methods.each do |method|
|
||||
class_eval(<<-EOS, __FILE__, __LINE__ + 1)
|
||||
def #{method}_with_notifications(*args, &block)
|
||||
notify_observers(:before_#{method})
|
||||
result = #{method}_without_notifications(*args, &block)
|
||||
notify_observers(:after_#{method})
|
||||
result
|
||||
end
|
||||
EOS
|
||||
alias_method_chain(method, :notifications)
|
||||
end
|
||||
end
|
||||
|
||||
protected
|
||||
def instantiate_observer(observer)
|
||||
def instantiate_observer(observer) #:nodoc:
|
||||
# string/symbol
|
||||
if observer.respond_to?(:to_sym)
|
||||
observer = observer.to_s.camelize.constantize.instance
|
||||
@@ -60,12 +78,72 @@ module ActiveModel
|
||||
end
|
||||
|
||||
private
|
||||
def notify(method) #:nodoc:
|
||||
# Fires notifications to model's observers
|
||||
#
|
||||
# def save
|
||||
# notify_observers(:before_save)
|
||||
# ...
|
||||
# notify_observers(:after_save)
|
||||
# end
|
||||
def notify_observers(method)
|
||||
self.class.changed
|
||||
self.class.notify_observers(method, self)
|
||||
end
|
||||
end
|
||||
|
||||
# Observer classes respond to lifecycle callbacks to implement trigger-like
|
||||
# behavior outside the original class. This is a great way to reduce the
|
||||
# clutter that normally comes when the model class is burdened with
|
||||
# functionality that doesn't pertain to the core responsibility of the
|
||||
# class. Example:
|
||||
#
|
||||
# class CommentObserver < ActiveModel::Observer
|
||||
# def after_save(comment)
|
||||
# Notifications.deliver_comment("admin@do.com", "New comment was posted", comment)
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# This Observer sends an email when a Comment#save is finished.
|
||||
#
|
||||
# class ContactObserver < ActiveModel::Observer
|
||||
# def after_create(contact)
|
||||
# contact.logger.info('New contact added!')
|
||||
# end
|
||||
#
|
||||
# def after_destroy(contact)
|
||||
# contact.logger.warn("Contact with an id of #{contact.id} was destroyed!")
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# This Observer uses logger to log when specific callbacks are triggered.
|
||||
#
|
||||
# == Observing a class that can't be inferred
|
||||
#
|
||||
# Observers will by default be mapped to the class with which they share a name. So CommentObserver will
|
||||
# be tied to observing Comment, ProductManagerObserver to ProductManager, and so on. If you want to name your observer
|
||||
# differently than the class you're interested in observing, you can use the Observer.observe class method which takes
|
||||
# either the concrete class (Product) or a symbol for that class (:product):
|
||||
#
|
||||
# class AuditObserver < ActiveModel::Observer
|
||||
# observe :account
|
||||
#
|
||||
# def after_update(account)
|
||||
# AuditTrail.new(account, "UPDATED")
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# If the audit observer needs to watch more than one kind of object, this can be specified with multiple arguments:
|
||||
#
|
||||
# class AuditObserver < ActiveModel::Observer
|
||||
# observe :account, :balance
|
||||
#
|
||||
# def after_update(record)
|
||||
# AuditTrail.new(record, "UPDATED")
|
||||
# end
|
||||
# end
|
||||
#
|
||||
# The AuditObserver will now act on both updates to Account and Balance by treating them both as records.
|
||||
#
|
||||
class Observer
|
||||
include Singleton
|
||||
|
||||
@@ -77,6 +155,15 @@ module ActiveModel
|
||||
define_method(:observed_classes) { models }
|
||||
end
|
||||
|
||||
# Returns an array of Classes to observe.
|
||||
#
|
||||
# You can override this instead of using the +observe+ helper.
|
||||
#
|
||||
# class AuditObserver < ActiveModel::Observer
|
||||
# def self.observed_classes
|
||||
# [AccountObserver, BalanceObserver]
|
||||
# end
|
||||
# end
|
||||
def observed_classes
|
||||
Array.wrap(observed_class)
|
||||
end
|
||||
@@ -97,7 +184,7 @@ module ActiveModel
|
||||
observed_classes.each { |klass| add_observer!(klass) }
|
||||
end
|
||||
|
||||
def observed_classes
|
||||
def observed_classes #:nodoc:
|
||||
self.class.observed_classes
|
||||
end
|
||||
|
||||
@@ -114,7 +201,7 @@ module ActiveModel
|
||||
end
|
||||
|
||||
protected
|
||||
def add_observer!(klass)
|
||||
def add_observer!(klass) #:nodoc:
|
||||
klass.add_observer(self)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -5,7 +5,6 @@ module ActiveModel
|
||||
module Serializers
|
||||
module JSON
|
||||
extend ActiveSupport::Concern
|
||||
include ActiveModel::Attributes
|
||||
|
||||
included do
|
||||
extend ActiveModel::Naming
|
||||
|
||||
@@ -5,7 +5,6 @@ module ActiveModel
|
||||
module Serializers
|
||||
module Xml
|
||||
extend ActiveSupport::Concern
|
||||
include ActiveModel::Attributes
|
||||
|
||||
class Serializer < ActiveModel::Serializer #:nodoc:
|
||||
class Attribute #:nodoc:
|
||||
|
||||
@@ -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
|
||||
|
||||
1
activemodel/lib/activemodel.rb
Normal file
1
activemodel/lib/activemodel.rb
Normal file
@@ -0,0 +1 @@
|
||||
require 'active_model'
|
||||
@@ -1,30 +0,0 @@
|
||||
require 'cases/helper'
|
||||
|
||||
class AttributesTest < ActiveModel::TestCase
|
||||
class Person
|
||||
include ActiveModel::Attributes
|
||||
attr_accessor :name
|
||||
end
|
||||
|
||||
test "reads attribute" do
|
||||
p = Person.new
|
||||
assert_equal nil, p.read_attribute(:name)
|
||||
|
||||
p.name = "Josh"
|
||||
assert_equal "Josh", p.read_attribute(:name)
|
||||
end
|
||||
|
||||
test "writes attribute" do
|
||||
p = Person.new
|
||||
assert_equal nil, p.name
|
||||
|
||||
p.write_attribute(:name, "Josh")
|
||||
assert_equal "Josh", p.name
|
||||
end
|
||||
|
||||
test "returns all attributes" do
|
||||
p = Person.new
|
||||
p.name = "Josh"
|
||||
assert_equal({"name" => "Josh"}, p.attributes)
|
||||
end
|
||||
end
|
||||
@@ -1,6 +1,8 @@
|
||||
require 'cases/helper'
|
||||
|
||||
class ObservedModel < ActiveModel::Base
|
||||
class ObservedModel
|
||||
include ActiveModel::Observing
|
||||
|
||||
class Observer
|
||||
end
|
||||
end
|
||||
@@ -17,7 +19,8 @@ class FooObserver < ActiveModel::Observer
|
||||
end
|
||||
end
|
||||
|
||||
class Foo < ActiveModel::Base
|
||||
class Foo
|
||||
include ActiveModel::Observing
|
||||
end
|
||||
|
||||
class ObservingTest < ActiveModel::TestCase
|
||||
|
||||
@@ -3,6 +3,10 @@ require 'models/contact'
|
||||
|
||||
class Contact
|
||||
include ActiveModel::Serializers::JSON
|
||||
|
||||
def attributes
|
||||
instance_values
|
||||
end
|
||||
end
|
||||
|
||||
class JsonSerializationTest < ActiveModel::TestCase
|
||||
|
||||
@@ -3,6 +3,10 @@ require 'models/contact'
|
||||
|
||||
class Contact
|
||||
include ActiveModel::Serializers::Xml
|
||||
|
||||
def attributes
|
||||
instance_values
|
||||
end
|
||||
end
|
||||
|
||||
class XmlSerializationTest < ActiveModel::TestCase
|
||||
|
||||
@@ -1,5 +1,9 @@
|
||||
*Edge*
|
||||
|
||||
* Added :primary_key option to belongs_to associations. #765 [Szymon Nowak, Philip Hallstrom, Noel Rocha]
|
||||
# employees.company_name references companies.name
|
||||
Employee.belongs_to :company, :primary_key => 'name', :foreign_key => 'company_name'
|
||||
|
||||
* Implement #many? for NamedScope and AssociationCollection using #size. #1500 [Chris Kampmeier]
|
||||
|
||||
* Added :touch option to belongs_to associations that will touch the parent record when the current record is saved or destroyed [DHH]
|
||||
|
||||
@@ -963,6 +963,8 @@ module ActiveRecord
|
||||
# of the association with an "_id" suffix. So a class that defines a <tt>belongs_to :person</tt> association will use
|
||||
# "person_id" as the default <tt>:foreign_key</tt>. Similarly, <tt>belongs_to :favorite_person, :class_name => "Person"</tt>
|
||||
# will use a foreign key of "favorite_person_id".
|
||||
# [:primary_key]
|
||||
# Specify the method that returns the primary key of associated object used for the association. By default this is id.
|
||||
# [:dependent]
|
||||
# If set to <tt>:destroy</tt>, the associated object is destroyed when this object is. If set to
|
||||
# <tt>:delete</tt>, the associated object is deleted *without* calling its destroy method. This option should not be specified when
|
||||
@@ -993,6 +995,7 @@ module ActiveRecord
|
||||
#
|
||||
# Option examples:
|
||||
# belongs_to :firm, :foreign_key => "client_of"
|
||||
# belongs_to :person, :primary_key => "name", :foreign_key => "person_name"
|
||||
# belongs_to :author, :class_name => "Person", :foreign_key => "author_id"
|
||||
# belongs_to :valid_coupon, :class_name => "Coupon", :foreign_key => "coupon_id",
|
||||
# :conditions => 'discounts > #{payments_count}'
|
||||
@@ -1328,14 +1331,14 @@ module ActiveRecord
|
||||
method_name = "belongs_to_counter_cache_after_create_for_#{reflection.name}".to_sym
|
||||
define_method(method_name) do
|
||||
association = send(reflection.name)
|
||||
association.class.increment_counter(cache_column, send(reflection.primary_key_name)) unless association.nil?
|
||||
association.class.increment_counter(cache_column, association.id) unless association.nil?
|
||||
end
|
||||
after_create(method_name)
|
||||
|
||||
method_name = "belongs_to_counter_cache_before_destroy_for_#{reflection.name}".to_sym
|
||||
define_method(method_name) do
|
||||
association = send(reflection.name)
|
||||
association.class.decrement_counter(cache_column, send(reflection.primary_key_name)) unless association.nil?
|
||||
association.class.decrement_counter(cache_column, association.id) unless association.nil?
|
||||
end
|
||||
before_destroy(method_name)
|
||||
|
||||
@@ -1527,7 +1530,7 @@ module ActiveRecord
|
||||
|
||||
mattr_accessor :valid_keys_for_belongs_to_association
|
||||
@@valid_keys_for_belongs_to_association = [
|
||||
:class_name, :foreign_key, :foreign_type, :remote, :select, :conditions,
|
||||
:class_name, :primary_key, :foreign_key, :foreign_type, :remote, :select, :conditions,
|
||||
:include, :dependent, :counter_cache, :extend, :polymorphic, :readonly,
|
||||
:validate, :touch, :inverse_of
|
||||
]
|
||||
|
||||
@@ -14,7 +14,7 @@ module ActiveRecord
|
||||
|
||||
if record.nil?
|
||||
if counter_cache_name && !@owner.new_record?
|
||||
@reflection.klass.decrement_counter(counter_cache_name, @owner[@reflection.primary_key_name]) if @owner[@reflection.primary_key_name]
|
||||
@reflection.klass.decrement_counter(counter_cache_name, previous_record_id) if @owner[@reflection.primary_key_name]
|
||||
end
|
||||
|
||||
@target = @owner[@reflection.primary_key_name] = nil
|
||||
@@ -27,7 +27,7 @@ module ActiveRecord
|
||||
end
|
||||
|
||||
@target = (AssociationProxy === record ? record.target : record)
|
||||
@owner[@reflection.primary_key_name] = record.id unless record.new_record?
|
||||
@owner[@reflection.primary_key_name] = record_id(record) unless record.new_record?
|
||||
@updated = true
|
||||
end
|
||||
|
||||
@@ -43,13 +43,18 @@ module ActiveRecord
|
||||
|
||||
private
|
||||
def find_target
|
||||
the_target = @reflection.klass.find(
|
||||
find_method = if @reflection.options[:primary_key]
|
||||
"find_by_#{@reflection.options[:primary_key]}"
|
||||
else
|
||||
"find"
|
||||
end
|
||||
the_target = @reflection.klass.send(find_method,
|
||||
@owner[@reflection.primary_key_name],
|
||||
:select => @reflection.options[:select],
|
||||
:conditions => conditions,
|
||||
:include => @reflection.options[:include],
|
||||
:readonly => @reflection.options[:readonly]
|
||||
)
|
||||
) if @owner[@reflection.primary_key_name]
|
||||
set_inverse_instance(the_target, @owner)
|
||||
the_target
|
||||
end
|
||||
@@ -63,6 +68,19 @@ module ActiveRecord
|
||||
def we_can_set_the_inverse_on_this?(record)
|
||||
@reflection.has_inverse? && @reflection.inverse_of.macro == :has_one
|
||||
end
|
||||
|
||||
def record_id(record)
|
||||
record.send(@reflection.options[:primary_key] || :id)
|
||||
end
|
||||
|
||||
def previous_record_id
|
||||
@previous_record_id ||= if @reflection.options[:primary_key]
|
||||
previous_record = @owner.send(@reflection.name)
|
||||
previous_record.nil? ? nil : previous_record.id
|
||||
else
|
||||
@owner[@reflection.primary_key_name]
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
@@ -7,7 +7,7 @@ module ActiveRecord
|
||||
else
|
||||
@target = (AssociationProxy === record ? record.target : record)
|
||||
|
||||
@owner[@reflection.primary_key_name] = record.id
|
||||
@owner[@reflection.primary_key_name] = record_id(record)
|
||||
@owner[@reflection.options[:foreign_type]] = record.class.base_class.name.to_s
|
||||
|
||||
@updated = true
|
||||
@@ -41,6 +41,10 @@ module ActiveRecord
|
||||
!@owner[@reflection.primary_key_name].nil?
|
||||
end
|
||||
|
||||
def record_id(record)
|
||||
record.send(@reflection.options[:primary_key] || :id)
|
||||
end
|
||||
|
||||
def association_class
|
||||
@owner[@reflection.options[:foreign_type]] ? @owner[@reflection.options[:foreign_type]].constantize : nil
|
||||
end
|
||||
|
||||
@@ -339,7 +339,8 @@ module ActiveRecord
|
||||
association.save(!autosave) if association.new_record? || autosave
|
||||
|
||||
if association.updated?
|
||||
self[reflection.primary_key_name] = association.id
|
||||
association_id = association.send(reflection.options[:primary_key] || :id)
|
||||
self[reflection.primary_key_name] = association_id
|
||||
# TODO: Removing this code doesn't seem to matter…
|
||||
if reflection.options[:polymorphic]
|
||||
self[reflection.options[:foreign_type]] = association.class.base_class.name.to_s
|
||||
|
||||
@@ -3179,6 +3179,7 @@ module ActiveRecord #:nodoc:
|
||||
include Dirty
|
||||
include Callbacks, ActiveModel::Observing, Timestamp
|
||||
include Associations, AssociationPreload, NamedScope
|
||||
include ActiveModel::Conversion
|
||||
|
||||
# AutosaveAssociation needs to be included before Transactions, because we want
|
||||
# #save_with_autosave_associations to be wrapped inside a transaction.
|
||||
|
||||
@@ -349,7 +349,7 @@ module ActiveRecord
|
||||
result = send(method)
|
||||
end
|
||||
|
||||
notify(method)
|
||||
notify_observers(method)
|
||||
|
||||
return result
|
||||
end
|
||||
|
||||
@@ -14,6 +14,7 @@ require 'models/tagging'
|
||||
require 'models/comment'
|
||||
require 'models/sponsor'
|
||||
require 'models/member'
|
||||
require 'models/essay'
|
||||
|
||||
class BelongsToAssociationsTest < ActiveRecord::TestCase
|
||||
fixtures :accounts, :companies, :developers, :projects, :topics,
|
||||
@@ -25,6 +26,11 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
||||
assert !Client.find(3).firm.nil?, "Microsoft should have a firm"
|
||||
end
|
||||
|
||||
def test_belongs_to_with_primary_key
|
||||
client = Client.create(:name => "Primary key client", :firm_name => companies(:first_firm).name)
|
||||
assert_equal companies(:first_firm).name, client.firm_with_primary_key.name
|
||||
end
|
||||
|
||||
def test_proxy_assignment
|
||||
account = Account.find(1)
|
||||
assert_nothing_raised { account.firm = account.firm }
|
||||
@@ -47,6 +53,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
||||
assert_equal apple.id, citibank.firm_id
|
||||
end
|
||||
|
||||
def test_natural_assignment_with_primary_key
|
||||
apple = Firm.create("name" => "Apple")
|
||||
citibank = Client.create("name" => "Primary key client")
|
||||
citibank.firm_with_primary_key = apple
|
||||
assert_equal apple.name, citibank.firm_name
|
||||
end
|
||||
|
||||
def test_no_unexpected_aliasing
|
||||
first_firm = companies(:first_firm)
|
||||
another_firm = companies(:another_firm)
|
||||
@@ -69,6 +82,15 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
||||
assert_equal apple, citibank.firm
|
||||
end
|
||||
|
||||
def test_creating_the_belonging_object_with_primary_key
|
||||
client = Client.create(:name => "Primary key client")
|
||||
apple = client.create_firm_with_primary_key("name" => "Apple")
|
||||
assert_equal apple, client.firm_with_primary_key
|
||||
client.save
|
||||
client.reload
|
||||
assert_equal apple, client.firm_with_primary_key
|
||||
end
|
||||
|
||||
def test_building_the_belonging_object
|
||||
citibank = Account.create("credit_limit" => 10)
|
||||
apple = citibank.build_firm("name" => "Apple")
|
||||
@@ -76,6 +98,13 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
||||
assert_equal apple.id, citibank.firm_id
|
||||
end
|
||||
|
||||
def test_building_the_belonging_object_with_primary_key
|
||||
client = Client.create(:name => "Primary key client")
|
||||
apple = client.build_firm_with_primary_key("name" => "Apple")
|
||||
client.save
|
||||
assert_equal apple.name, client.firm_name
|
||||
end
|
||||
|
||||
def test_natural_assignment_to_nil
|
||||
client = Client.find(3)
|
||||
client.firm = nil
|
||||
@@ -84,6 +113,14 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
||||
assert_nil client.client_of
|
||||
end
|
||||
|
||||
def test_natural_assignment_to_nil_with_primary_key
|
||||
client = Client.create(:name => "Primary key client", :firm_name => companies(:first_firm).name)
|
||||
client.firm_with_primary_key = nil
|
||||
client.save
|
||||
assert_nil client.firm_with_primary_key(true)
|
||||
assert_nil client.client_of
|
||||
end
|
||||
|
||||
def test_with_different_class_name
|
||||
assert_equal Company.find(1).name, Company.find(3).firm_with_other_name.name
|
||||
assert_not_nil Company.find(3).firm_with_other_name, "Microsoft should have a firm"
|
||||
@@ -110,6 +147,17 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
||||
assert_equal 0, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply deleted"
|
||||
end
|
||||
|
||||
def test_belongs_to_with_primary_key_counter
|
||||
debate = Topic.create("title" => "debate")
|
||||
assert_equal 0, debate.send(:read_attribute, "replies_count"), "No replies yet"
|
||||
|
||||
trash = debate.replies_with_primary_key.create("title" => "blah!", "content" => "world around!")
|
||||
assert_equal 1, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply created"
|
||||
|
||||
trash.destroy
|
||||
assert_equal 0, Topic.find(debate.id).send(:read_attribute, "replies_count"), "First reply deleted"
|
||||
end
|
||||
|
||||
def test_belongs_to_counter_with_assigning_nil
|
||||
p = Post.find(1)
|
||||
c = Comment.find(1)
|
||||
@@ -122,6 +170,18 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
||||
assert_equal 1, Post.find(p.id).comments.size
|
||||
end
|
||||
|
||||
def test_belongs_to_with_primary_key_counter_with_assigning_nil
|
||||
debate = Topic.create("title" => "debate")
|
||||
reply = Reply.create("title" => "blah!", "content" => "world around!", "parent_title" => "debate")
|
||||
|
||||
assert_equal debate.title, reply.parent_title
|
||||
assert_equal 1, Topic.find(debate.id).send(:read_attribute, "replies_count")
|
||||
|
||||
reply.topic_with_primary_key = nil
|
||||
|
||||
assert_equal 0, Topic.find(debate.id).send(:read_attribute, "replies_count")
|
||||
end
|
||||
|
||||
def test_belongs_to_counter_with_reassigning
|
||||
t1 = Topic.create("title" => "t1")
|
||||
t2 = Topic.create("title" => "t2")
|
||||
@@ -219,6 +279,18 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
||||
assert_equal firm, final_cut.firm(true)
|
||||
end
|
||||
|
||||
def test_assignment_before_child_saved_with_primary_key
|
||||
final_cut = Client.new("name" => "Final Cut")
|
||||
firm = Firm.find(1)
|
||||
final_cut.firm_with_primary_key = firm
|
||||
assert final_cut.new_record?
|
||||
assert final_cut.save
|
||||
assert !final_cut.new_record?
|
||||
assert !firm.new_record?
|
||||
assert_equal firm, final_cut.firm_with_primary_key
|
||||
assert_equal firm, final_cut.firm_with_primary_key(true)
|
||||
end
|
||||
|
||||
def test_new_record_with_foreign_key_but_no_object
|
||||
c = Client.new("firm_id" => 1)
|
||||
assert_equal Firm.find(:first), c.firm_with_basic_id
|
||||
@@ -304,6 +376,20 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
||||
sponsor.sponsorable = member
|
||||
assert_equal "Member", sponsor.sponsorable_type
|
||||
end
|
||||
|
||||
def test_polymorphic_assignment_with_primary_key_foreign_type_field_updating
|
||||
# should update when assigning a saved record
|
||||
essay = Essay.new
|
||||
writer = Author.create(:name => "David")
|
||||
essay.writer = writer
|
||||
assert_equal "Author", essay.writer_type
|
||||
|
||||
# should update when assigning a new record
|
||||
essay = Essay.new
|
||||
writer = Author.new
|
||||
essay.writer = writer
|
||||
assert_equal "Author", essay.writer_type
|
||||
end
|
||||
|
||||
def test_polymorphic_assignment_updates_foreign_id_field_for_new_and_saved_records
|
||||
sponsor = Sponsor.new
|
||||
@@ -317,6 +403,18 @@ class BelongsToAssociationsTest < ActiveRecord::TestCase
|
||||
assert_equal nil, sponsor.sponsorable_id
|
||||
end
|
||||
|
||||
def test_polymorphic_assignment_with_primary_key_updates_foreign_id_field_for_new_and_saved_records
|
||||
essay = Essay.new
|
||||
saved_writer = Author.create(:name => "David")
|
||||
new_writer = Author.new
|
||||
|
||||
essay.writer = saved_writer
|
||||
assert_equal saved_writer.name, essay.writer_id
|
||||
|
||||
essay.writer = new_writer
|
||||
assert_equal nil, essay.writer_id
|
||||
end
|
||||
|
||||
def test_belongs_to_proxy_should_not_respond_to_private_methods
|
||||
assert_raise(NoMethodError) { companies(:first_firm).private_method }
|
||||
assert_raise(NoMethodError) { companies(:second_client).firm.private_method }
|
||||
|
||||
@@ -2026,7 +2026,7 @@ class BasicsTest < ActiveRecord::TestCase
|
||||
|
||||
def test_inspect_instance
|
||||
topic = topics(:first)
|
||||
assert_equal %(#<Topic id: 1, title: "The First Topic", author_name: "David", author_email_address: "david@loudthinking.com", written_on: "#{topic.written_on.to_s(:db)}", bonus_time: "#{topic.bonus_time.to_s(:db)}", last_read: "#{topic.last_read.to_s(:db)}", content: "Have a nice day", approved: false, replies_count: 1, parent_id: nil, type: nil>), topic.inspect
|
||||
assert_equal %(#<Topic id: 1, title: "The First Topic", author_name: "David", author_email_address: "david@loudthinking.com", written_on: "#{topic.written_on.to_s(:db)}", bonus_time: "#{topic.bonus_time.to_s(:db)}", last_read: "#{topic.last_read.to_s(:db)}", content: "Have a nice day", approved: false, replies_count: 1, parent_id: nil, parent_title: nil, type: nil>), topic.inspect
|
||||
end
|
||||
|
||||
def test_inspect_new_instance
|
||||
|
||||
@@ -27,25 +27,25 @@ class ReflectionTest < ActiveRecord::TestCase
|
||||
|
||||
def test_read_attribute_names
|
||||
assert_equal(
|
||||
%w( id title author_name author_email_address bonus_time written_on last_read content approved replies_count parent_id type ).sort,
|
||||
%w( id title author_name author_email_address bonus_time written_on last_read content approved replies_count parent_id parent_title type ).sort,
|
||||
@first.attribute_names
|
||||
)
|
||||
end
|
||||
|
||||
def test_columns
|
||||
assert_equal 12, Topic.columns.length
|
||||
assert_equal 13, Topic.columns.length
|
||||
end
|
||||
|
||||
def test_columns_are_returned_in_the_order_they_were_declared
|
||||
column_names = Topic.columns.map { |column| column.name }
|
||||
assert_equal %w(id title author_name author_email_address written_on bonus_time last_read content approved replies_count parent_id type), column_names
|
||||
assert_equal %w(id title author_name author_email_address written_on bonus_time last_read content approved replies_count parent_id parent_title type), column_names
|
||||
end
|
||||
|
||||
def test_content_columns
|
||||
content_columns = Topic.content_columns
|
||||
content_column_names = content_columns.map {|column| column.name}
|
||||
assert_equal 8, content_columns.length
|
||||
assert_equal %w(title author_name author_email_address written_on bonus_time last_read content approved).sort, content_column_names.sort
|
||||
assert_equal 9, content_columns.length
|
||||
assert_equal %w(title author_name author_email_address written_on bonus_time last_read content approved parent_title).sort, content_column_names.sort
|
||||
end
|
||||
|
||||
def test_column_string_type_and_limit
|
||||
|
||||
@@ -87,6 +87,8 @@ class Author < ActiveRecord::Base
|
||||
has_many :tags, :through => :posts # through has_many :through
|
||||
has_many :post_categories, :through => :posts, :source => :categories
|
||||
|
||||
has_one :essay, :primary_key => :name, :as => :writer
|
||||
|
||||
belongs_to :author_address, :dependent => :destroy
|
||||
belongs_to :author_address_extra, :dependent => :delete, :class_name => "AuthorAddress"
|
||||
|
||||
|
||||
@@ -88,6 +88,7 @@ class Client < Company
|
||||
belongs_to :firm_with_select, :class_name => "Firm", :foreign_key => "firm_id", :select => "id"
|
||||
belongs_to :firm_with_other_name, :class_name => "Firm", :foreign_key => "client_of"
|
||||
belongs_to :firm_with_condition, :class_name => "Firm", :foreign_key => "client_of", :conditions => ["1 = ?", 1]
|
||||
belongs_to :firm_with_primary_key, :class_name => "Firm", :primary_key => "name", :foreign_key => "firm_name"
|
||||
belongs_to :readonly_firm, :class_name => "Firm", :foreign_key => "firm_id", :readonly => true
|
||||
|
||||
# Record destruction so we can test whether firm.clients.clear has
|
||||
|
||||
3
activerecord/test/models/essay.rb
Normal file
3
activerecord/test/models/essay.rb
Normal file
@@ -0,0 +1,3 @@
|
||||
class Essay < ActiveRecord::Base
|
||||
belongs_to :writer, :primary_key => :name, :polymorphic => true
|
||||
end
|
||||
@@ -4,12 +4,13 @@ class Reply < Topic
|
||||
named_scope :base
|
||||
|
||||
belongs_to :topic, :foreign_key => "parent_id", :counter_cache => true
|
||||
belongs_to :topic_with_primary_key, :class_name => "Topic", :primary_key => "title", :foreign_key => "parent_title", :counter_cache => "replies_count"
|
||||
has_many :replies, :class_name => "SillyReply", :dependent => :destroy, :foreign_key => "parent_id"
|
||||
|
||||
validate :errors_on_empty_content
|
||||
validate_on_create :title_is_wrong_create
|
||||
|
||||
attr_accessible :title, :author_name, :author_email_address, :written_on, :content, :last_read
|
||||
attr_accessible :title, :author_name, :author_email_address, :written_on, :content, :last_read, :parent_title
|
||||
|
||||
validate :check_empty_title
|
||||
validate_on_create :check_content_mismatch
|
||||
|
||||
@@ -39,6 +39,7 @@ class Topic < ActiveRecord::Base
|
||||
named_scope :by_rejected_ids, lambda {{ :conditions => { :id => all(:conditions => {:approved => false}).map(&:id) } }}
|
||||
|
||||
has_many :replies, :dependent => :destroy, :foreign_key => "parent_id"
|
||||
has_many :replies_with_primary_key, :class_name => "Reply", :dependent => :destroy, :primary_key => "title", :foreign_key => "parent_title"
|
||||
serialize :content
|
||||
|
||||
before_create :default_written_on
|
||||
|
||||
@@ -161,6 +161,12 @@ ActiveRecord::Schema.define do
|
||||
t.integer :course_id, :null => false
|
||||
end
|
||||
|
||||
create_table :essays, :force => true do |t|
|
||||
t.string :name
|
||||
t.string :writer_id
|
||||
t.string :writer_type
|
||||
end
|
||||
|
||||
create_table :events, :force => true do |t|
|
||||
t.string :title, :limit => 5
|
||||
end
|
||||
@@ -421,6 +427,7 @@ ActiveRecord::Schema.define do
|
||||
t.boolean :approved, :default => true
|
||||
t.integer :replies_count, :default => 0
|
||||
t.integer :parent_id
|
||||
t.string :parent_title
|
||||
t.string :type
|
||||
end
|
||||
|
||||
|
||||
@@ -34,6 +34,7 @@ module ActiveResource
|
||||
autoload :Connection, 'active_resource/connection'
|
||||
autoload :CustomMethods, 'active_resource/custom_methods'
|
||||
autoload :Formats, 'active_resource/formats'
|
||||
autoload :Observing, 'active_resource/observing'
|
||||
autoload :Validations, 'active_resource/validations'
|
||||
autoload :HttpMock, 'active_resource/http_mock'
|
||||
end
|
||||
|
||||
@@ -804,8 +804,7 @@ module ActiveResource
|
||||
# my_company.size = 10
|
||||
# my_company.save # sends PUT /companies/1 (update)
|
||||
def save
|
||||
notify(:before_save)
|
||||
(new? ? create : update).tap { notify(:after_save) }
|
||||
new? ? create : update
|
||||
end
|
||||
|
||||
# Deletes the resource from the remote service.
|
||||
@@ -821,8 +820,7 @@ module ActiveResource
|
||||
# new_person.destroy
|
||||
# Person.find(new_id) # 404 (Resource Not Found)
|
||||
def destroy
|
||||
notify(:before_destroy)
|
||||
connection.delete(element_path, self.class.headers).tap { notify(:after_destroy) }
|
||||
connection.delete(element_path, self.class.headers)
|
||||
end
|
||||
|
||||
# Evaluates to <tt>true</tt> if this resource is not <tt>new?</tt> and is
|
||||
@@ -997,20 +995,16 @@ module ActiveResource
|
||||
|
||||
# Update the resource on the remote service.
|
||||
def update
|
||||
notify(:before_update)
|
||||
connection.put(element_path(prefix_options), encode, self.class.headers).tap do |response|
|
||||
load_attributes_from_response(response)
|
||||
notify(:after_update)
|
||||
end
|
||||
end
|
||||
|
||||
# Create (i.e., \save to the remote service) the \new resource.
|
||||
def create
|
||||
notify(:before_create)
|
||||
connection.post(collection_path, encode, self.class.headers).tap do |response|
|
||||
self.id = id_from_response(response)
|
||||
load_attributes_from_response(response)
|
||||
notify(:after_create)
|
||||
end
|
||||
end
|
||||
|
||||
@@ -1093,7 +1087,6 @@ module ActiveResource
|
||||
|
||||
class Base
|
||||
extend ActiveModel::Naming
|
||||
include CustomMethods, Validations
|
||||
include ActiveModel::Observing
|
||||
include CustomMethods, Observing, Validations
|
||||
end
|
||||
end
|
||||
|
||||
10
activeresource/lib/active_resource/observing.rb
Normal file
10
activeresource/lib/active_resource/observing.rb
Normal file
@@ -0,0 +1,10 @@
|
||||
module ActiveResource
|
||||
module Observing
|
||||
extend ActiveSupport::Concern
|
||||
include ActiveModel::Observing
|
||||
|
||||
included do
|
||||
wrap_with_notifications :create, :save, :update, :destroy
|
||||
end
|
||||
end
|
||||
end
|
||||
@@ -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
|
||||
@@ -19,7 +19,8 @@ module ActiveSupport
|
||||
class TestCase < ::Test::Unit::TestCase
|
||||
if defined? MiniTest
|
||||
Assertion = MiniTest::Assertion
|
||||
alias_method :method_name, :name
|
||||
alias_method :method_name, :name if method_defined? :name
|
||||
alias_method :method_name, :__name__ if method_defined? :__name__
|
||||
else
|
||||
# TODO: Figure out how to get the Rails::BacktraceFilter into minitest/unit
|
||||
if defined?(Rails) && ENV['BACKTRACE'].nil?
|
||||
|
||||
@@ -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
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
<%= shebang %>
|
||||
require File.dirname(__FILE__) + '/../config/boot'
|
||||
require File.expand_path('../../config/boot', __FILE__)
|
||||
$LOAD_PATH.unshift "#{RAILTIES_PATH}/builtin/rails_info"
|
||||
require 'commands/about'
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<%= shebang %>
|
||||
require File.dirname(__FILE__) + '/../config/boot'
|
||||
require File.expand_path('../../config/boot', __FILE__)
|
||||
require 'commands/console'
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<%= shebang %>
|
||||
require File.dirname(__FILE__) + '/../config/boot'
|
||||
require File.expand_path('../../config/boot', __FILE__)
|
||||
require 'commands/dbconsole'
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<%= shebang %>
|
||||
require File.dirname(__FILE__) + '/../config/boot'
|
||||
require File.expand_path('../../config/boot', __FILE__)
|
||||
require 'commands/destroy'
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<%= shebang %>
|
||||
require File.dirname(__FILE__) + '/../config/boot'
|
||||
require File.expand_path('../../config/boot', __FILE__)
|
||||
require 'commands/generate'
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<%= shebang %>
|
||||
require File.dirname(__FILE__) + '/../../config/boot'
|
||||
require File.expand_path('../../../config/boot', __FILE__)
|
||||
require 'commands/performance/benchmarker'
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<%= shebang %>
|
||||
require File.dirname(__FILE__) + '/../../config/boot'
|
||||
require File.expand_path('../../../config/boot', __FILE__)
|
||||
require 'commands/performance/profiler'
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<%= shebang %>
|
||||
require File.dirname(__FILE__) + '/../config/boot'
|
||||
require File.expand_path('../../config/boot', __FILE__)
|
||||
require 'commands/plugin'
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<%= shebang %>
|
||||
require File.dirname(__FILE__) + '/../config/boot'
|
||||
require File.expand_path('../../config/boot', __FILE__)
|
||||
require 'commands/runner'
|
||||
|
||||
@@ -1,3 +1,3 @@
|
||||
<%= shebang %>
|
||||
require File.dirname(__FILE__) + '/../config/boot'
|
||||
require File.expand_path('../../config/boot', __FILE__)
|
||||
require 'commands/server'
|
||||
|
||||
20
railties/lib/vendor/bundler/LICENSE
vendored
Normal file
20
railties/lib/vendor/bundler/LICENSE
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
Copyright (c) 2009 Engine Yard
|
||||
|
||||
Permission is hereby granted, free of charge, to any person obtaining
|
||||
a copy of this software and associated documentation files (the
|
||||
"Software"), to deal in the Software without restriction, including
|
||||
without limitation the rights to use, copy, modify, merge, publish,
|
||||
distribute, sublicense, and/or sell copies of the Software, and to
|
||||
permit persons to whom the Software is furnished to do so, subject to
|
||||
the following conditions:
|
||||
|
||||
The above copyright notice and this permission notice shall be
|
||||
included in all copies or substantial portions of the Software.
|
||||
|
||||
THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
|
||||
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
||||
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
|
||||
NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
|
||||
LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
|
||||
OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
|
||||
WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
||||
52
railties/lib/vendor/bundler/Rakefile
vendored
Normal file
52
railties/lib/vendor/bundler/Rakefile
vendored
Normal file
@@ -0,0 +1,52 @@
|
||||
require 'rubygems' unless ENV['NO_RUBYGEMS']
|
||||
require 'rake/gempackagetask'
|
||||
require 'rubygems/specification'
|
||||
require 'date'
|
||||
require 'spec/rake/spectask'
|
||||
|
||||
spec = Gem::Specification.new do |s|
|
||||
s.name = "bundler"
|
||||
s.version = "0.0.1"
|
||||
s.author = "Your Name"
|
||||
s.email = "Your Email"
|
||||
s.homepage = "http://example.com"
|
||||
s.description = s.summary = "A gem that provides..."
|
||||
|
||||
s.platform = Gem::Platform::RUBY
|
||||
s.has_rdoc = true
|
||||
s.extra_rdoc_files = ["README", "LICENSE"]
|
||||
s.summary = ""
|
||||
|
||||
# Uncomment this to add a dependency
|
||||
# s.add_dependency "foo"
|
||||
|
||||
s.bindir = "bin"
|
||||
s.executables = %w( gem_bundler )
|
||||
s.require_path = 'lib'
|
||||
s.files = %w(LICENSE README Rakefile) + Dir.glob("{bin,lib,spec}/**/*")
|
||||
end
|
||||
|
||||
task :default => :spec
|
||||
|
||||
desc "Run specs"
|
||||
Spec::Rake::SpecTask.new do |t|
|
||||
t.spec_files = FileList['spec/**/*_spec.rb']
|
||||
t.spec_opts = %w(-fs --color)
|
||||
end
|
||||
|
||||
|
||||
Rake::GemPackageTask.new(spec) do |pkg|
|
||||
pkg.gem_spec = spec
|
||||
end
|
||||
|
||||
desc "install the gem locally"
|
||||
task :install => [:package] do
|
||||
sh %{sudo gem install pkg/#{GEM}-#{GEM_VERSION}}
|
||||
end
|
||||
|
||||
desc "create a gemspec file"
|
||||
task :make_spec do
|
||||
File.open("#{GEM}.gemspec", "w") do |file|
|
||||
file.puts spec.to_ruby
|
||||
end
|
||||
end
|
||||
40
railties/lib/vendor/bundler/bin/gem_bundler
vendored
Executable file
40
railties/lib/vendor/bundler/bin/gem_bundler
vendored
Executable file
@@ -0,0 +1,40 @@
|
||||
#!/usr/bin/env ruby
|
||||
require "optparse"
|
||||
require "bundler"
|
||||
|
||||
options = {}
|
||||
|
||||
parser = OptionParser.new do |op|
|
||||
op.banner = "Usage: gem_bundler [OPTIONS] [PATH]"
|
||||
|
||||
op.on("-m", "--manifest MANIFEST") do |manifest|
|
||||
options[:manifest] = manifest
|
||||
end
|
||||
|
||||
op.on_tail("-h", "--help", "Show this message") do
|
||||
puts op
|
||||
exit
|
||||
end
|
||||
end
|
||||
parser.parse!
|
||||
|
||||
options[:path] = ARGV.shift
|
||||
|
||||
unless options[:path]
|
||||
puts parser
|
||||
puts %(
|
||||
[PATH] must be specified
|
||||
)
|
||||
exit
|
||||
end
|
||||
|
||||
unless options[:manifest] && File.exist?(options[:manifest])
|
||||
puts parser
|
||||
puts %(
|
||||
MANIFEST must be a valid manifest file
|
||||
)
|
||||
exit
|
||||
end
|
||||
|
||||
|
||||
Bundler.run(options)
|
||||
24
railties/lib/vendor/bundler/lib/bundler.rb
vendored
Normal file
24
railties/lib/vendor/bundler/lib/bundler.rb
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
require 'logger'
|
||||
require 'set'
|
||||
# Required elements of rubygems
|
||||
require "rubygems/remote_fetcher"
|
||||
require "rubygems/installer"
|
||||
|
||||
require "bundler/gem_bundle"
|
||||
require "bundler/installer"
|
||||
require "bundler/finder"
|
||||
require "bundler/gem_specification"
|
||||
require "bundler/resolver"
|
||||
require "bundler/manifest"
|
||||
require "bundler/dependency"
|
||||
require "bundler/runtime"
|
||||
require "bundler/cli"
|
||||
|
||||
module Bundler
|
||||
VERSION = "0.5.0"
|
||||
|
||||
def self.run(options = {})
|
||||
manifest = ManifestBuilder.load(options[:path], options[:manifest])
|
||||
manifest.install
|
||||
end
|
||||
end
|
||||
24
railties/lib/vendor/bundler/lib/bundler/cli.rb
vendored
Normal file
24
railties/lib/vendor/bundler/lib/bundler/cli.rb
vendored
Normal file
@@ -0,0 +1,24 @@
|
||||
module Bundler
|
||||
module CLI
|
||||
|
||||
def default_manifest
|
||||
current = Pathname.new(Dir.pwd)
|
||||
|
||||
begin
|
||||
manifest = current.join("Gemfile")
|
||||
return manifest.to_s if File.exist?(manifest)
|
||||
current = current.parent
|
||||
end until current.root?
|
||||
nil
|
||||
end
|
||||
|
||||
module_function :default_manifest
|
||||
|
||||
def default_path
|
||||
Pathname.new(File.dirname(default_manifest)).join("vendor").join("gems").to_s
|
||||
end
|
||||
|
||||
module_function :default_path
|
||||
|
||||
end
|
||||
end
|
||||
35
railties/lib/vendor/bundler/lib/bundler/dependency.rb
vendored
Normal file
35
railties/lib/vendor/bundler/lib/bundler/dependency.rb
vendored
Normal file
@@ -0,0 +1,35 @@
|
||||
module Bundler
|
||||
class Dependency
|
||||
|
||||
attr_reader :name, :version, :require_as, :only, :except
|
||||
|
||||
def initialize(name, options = {})
|
||||
options.each do |k, v|
|
||||
options[k.to_s] = v
|
||||
end
|
||||
|
||||
@name = name
|
||||
@version = options["version"] || ">= 0"
|
||||
@require_as = Array(options["require_as"] || name)
|
||||
@only = Array(options["only"]).map {|e| e.to_s } if options["only"]
|
||||
@except = Array(options["except"]).map {|e| e.to_s } if options["except"]
|
||||
end
|
||||
|
||||
def in?(environment)
|
||||
environment = environment.to_s
|
||||
|
||||
return false unless !@only || @only.include?(environment)
|
||||
return false if @except && @except.include?(environment)
|
||||
true
|
||||
end
|
||||
|
||||
def to_s
|
||||
to_gem_dependency.to_s
|
||||
end
|
||||
|
||||
def to_gem_dependency
|
||||
@gem_dep ||= Gem::Dependency.new(name, version)
|
||||
end
|
||||
|
||||
end
|
||||
end
|
||||
42
railties/lib/vendor/bundler/lib/bundler/finder.rb
vendored
Normal file
42
railties/lib/vendor/bundler/lib/bundler/finder.rb
vendored
Normal file
@@ -0,0 +1,42 @@
|
||||
module Bundler
|
||||
class Finder
|
||||
def initialize(*sources)
|
||||
@results = {}
|
||||
@index = Hash.new { |h,k| h[k] = {} }
|
||||
|
||||
sources.each { |source| fetch(source) }
|
||||
end
|
||||
|
||||
def resolve(*dependencies)
|
||||
resolved = Resolver.resolve(dependencies, self)
|
||||
resolved && GemBundle.new(resolved.all_specs)
|
||||
end
|
||||
|
||||
def fetch(source)
|
||||
deflated = Gem::RemoteFetcher.fetcher.fetch_path("#{source}/Marshal.4.8.Z")
|
||||
inflated = Gem.inflate deflated
|
||||
|
||||
append(Marshal.load(inflated), source)
|
||||
rescue Gem::RemoteFetcher::FetchError => e
|
||||
raise ArgumentError, "#{source} is not a valid source: #{e.message}"
|
||||
end
|
||||
|
||||
def append(index, source)
|
||||
index.gems.values.each do |spec|
|
||||
next unless Gem::Platform.match(spec.platform)
|
||||
spec.source = source
|
||||
@index[spec.name][spec.version] ||= spec
|
||||
end
|
||||
self
|
||||
end
|
||||
|
||||
def search(dependency)
|
||||
@results[dependency.hash] ||= begin
|
||||
possibilities = @index[dependency.name].values
|
||||
possibilities.select do |spec|
|
||||
dependency =~ spec
|
||||
end.sort_by {|s| s.version }
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
23
railties/lib/vendor/bundler/lib/bundler/gem_bundle.rb
vendored
Normal file
23
railties/lib/vendor/bundler/lib/bundler/gem_bundle.rb
vendored
Normal file
@@ -0,0 +1,23 @@
|
||||
module Bundler
|
||||
class GemBundle < Array
|
||||
def download(directory)
|
||||
FileUtils.mkdir_p(directory)
|
||||
|
||||
current = Dir[File.join(directory, "cache", "*.gem*")]
|
||||
|
||||
each do |spec|
|
||||
cached = File.join(directory, "cache", "#{spec.full_name}.gem")
|
||||
|
||||
unless File.file?(cached)
|
||||
Gem::RemoteFetcher.fetcher.download(spec, spec.source, directory)
|
||||
end
|
||||
|
||||
current.delete(cached)
|
||||
end
|
||||
|
||||
current.each { |file| File.delete(file) }
|
||||
|
||||
self
|
||||
end
|
||||
end
|
||||
end
|
||||
10
railties/lib/vendor/bundler/lib/bundler/gem_specification.rb
vendored
Normal file
10
railties/lib/vendor/bundler/lib/bundler/gem_specification.rb
vendored
Normal file
@@ -0,0 +1,10 @@
|
||||
module Gem
|
||||
class Specification
|
||||
attribute :source
|
||||
|
||||
def source=(source)
|
||||
@source = source.is_a?(URI) ? source : URI.parse(source)
|
||||
raise ArgumentError, "The source must be an absolute URI" unless @source.absolute?
|
||||
end
|
||||
end
|
||||
end
|
||||
44
railties/lib/vendor/bundler/lib/bundler/installer.rb
vendored
Normal file
44
railties/lib/vendor/bundler/lib/bundler/installer.rb
vendored
Normal file
@@ -0,0 +1,44 @@
|
||||
module Bundler
|
||||
class Installer
|
||||
def initialize(path)
|
||||
if !File.directory?(path)
|
||||
raise ArgumentError, "#{path} is not a directory"
|
||||
elsif !File.directory?(File.join(path, "cache"))
|
||||
raise ArgumentError, "#{path} is not a valid environment (it does not contain a cache directory)"
|
||||
end
|
||||
|
||||
@path = path
|
||||
@gems = Dir[(File.join(path, "cache", "*.gem"))]
|
||||
end
|
||||
|
||||
def install(options = {})
|
||||
bin_dir = options[:bin_dir] ||= File.join(@path, "bin")
|
||||
|
||||
specs = Dir[File.join(@path, "specifications", "*.gemspec")]
|
||||
gems = Dir[File.join(@path, "gems", "*")]
|
||||
|
||||
@gems.each do |gem|
|
||||
name = File.basename(gem).gsub(/\.gem$/, '')
|
||||
installed = specs.any? { |g| File.basename(g) == "#{name}.gemspec" } &&
|
||||
gems.any? { |g| File.basename(g) == name }
|
||||
|
||||
unless installed
|
||||
installer = Gem::Installer.new(gem, :install_dir => @path,
|
||||
:ignore_dependencies => true,
|
||||
:env_shebang => true,
|
||||
:wrappers => true,
|
||||
:bin_dir => bin_dir)
|
||||
installer.install
|
||||
end
|
||||
|
||||
# remove this spec
|
||||
specs.delete_if { |g| File.basename(g) == "#{name}.gemspec"}
|
||||
gems.delete_if { |g| File.basename(g) == name }
|
||||
end
|
||||
|
||||
(specs + gems).each do |path|
|
||||
FileUtils.rm_rf(path)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
130
railties/lib/vendor/bundler/lib/bundler/manifest.rb
vendored
Normal file
130
railties/lib/vendor/bundler/lib/bundler/manifest.rb
vendored
Normal file
@@ -0,0 +1,130 @@
|
||||
require "rubygems/source_index"
|
||||
require "pathname"
|
||||
|
||||
module Bundler
|
||||
class VersionConflict < StandardError; end
|
||||
|
||||
class Manifest
|
||||
attr_reader :sources, :dependencies, :path
|
||||
|
||||
def initialize(sources, dependencies, path)
|
||||
sources.map! {|s| s.is_a?(URI) ? s : URI.parse(s) }
|
||||
@sources, @dependencies, @path = sources, dependencies, Pathname.new(path)
|
||||
end
|
||||
|
||||
def fetch
|
||||
return if all_gems_installed?
|
||||
|
||||
finder = Finder.new(*sources)
|
||||
unless bundle = finder.resolve(*gem_dependencies)
|
||||
gems = @dependencies.map {|d| " #{d.to_s}" }.join("\n")
|
||||
raise VersionConflict, "No compatible versions could be found for:\n#{gems}"
|
||||
end
|
||||
|
||||
bundle.download(@path)
|
||||
end
|
||||
|
||||
def install(options = {})
|
||||
fetch
|
||||
installer = Installer.new(@path)
|
||||
installer.install # options come here
|
||||
create_load_paths_files(File.join(@path, "environments"))
|
||||
create_fake_rubygems(File.join(@path, "environments"))
|
||||
end
|
||||
|
||||
def activate(environment = "default")
|
||||
require File.join(@path, "environments", "#{environment}.rb")
|
||||
end
|
||||
|
||||
def require_all
|
||||
dependencies.each do |dep|
|
||||
dep.require_as.each {|file| require file }
|
||||
end
|
||||
end
|
||||
|
||||
def gems_for(environment)
|
||||
deps = dependencies.select { |d| d.in?(environment) }
|
||||
deps.map! { |d| d.to_gem_dependency }
|
||||
index = Gem::SourceIndex.from_gems_in(File.join(@path, "specifications"))
|
||||
Resolver.resolve(deps, index).all_specs
|
||||
end
|
||||
|
||||
def environments
|
||||
envs = dependencies.map {|dep| Array(dep.only) + Array(dep.except) }.flatten
|
||||
envs << "default"
|
||||
end
|
||||
|
||||
private
|
||||
|
||||
def gem_dependencies
|
||||
@gem_dependencies ||= dependencies.map { |d| d.to_gem_dependency }
|
||||
end
|
||||
|
||||
def all_gems_installed?
|
||||
gem_versions = {}
|
||||
|
||||
Dir[File.join(@path, "cache", "*.gem")].each do |file|
|
||||
file =~ /\/([^\/]+)-([\d\.]+)\.gem$/
|
||||
name, version = $1, $2
|
||||
gem_versions[name] = Gem::Version.new(version)
|
||||
end
|
||||
|
||||
gem_dependencies.all? do |dep|
|
||||
gem_versions[dep.name] &&
|
||||
dep.version_requirements.satisfied_by?(gem_versions[dep.name])
|
||||
end
|
||||
end
|
||||
|
||||
def create_load_paths_files(path)
|
||||
FileUtils.mkdir_p(path)
|
||||
environments.each do |environment|
|
||||
gem_specs = gems_for(environment)
|
||||
File.open(File.join(path, "#{environment}.rb"), "w") do |file|
|
||||
file.puts <<-RUBY_EVAL
|
||||
module Bundler
|
||||
def self.rubygems_required
|
||||
#{create_gem_stubs(path, gem_specs)}
|
||||
end
|
||||
end
|
||||
RUBY_EVAL
|
||||
file.puts "$LOAD_PATH.unshift File.expand_path(File.dirname(__FILE__))"
|
||||
load_paths_for_specs(gem_specs).each do |load_path|
|
||||
file.puts "$LOAD_PATH.unshift #{load_path.inspect}"
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
|
||||
def create_gem_stubs(path, gem_specs)
|
||||
gem_specs.map do |spec|
|
||||
path = File.expand_path(File.join(path, '..', 'specifications', "#{spec.full_name}.gemspec"))
|
||||
%{
|
||||
Gem.loaded_specs["#{spec.name}"] = eval(File.read("#{path}"))
|
||||
}
|
||||
end.join("\n")
|
||||
end
|
||||
|
||||
def create_fake_rubygems(path)
|
||||
File.open(File.join(path, "rubygems.rb"), "w") do |file|
|
||||
file.puts <<-RUBY_EVAL
|
||||
$:.delete File.expand_path(File.dirname(__FILE__))
|
||||
load "rubygems.rb"
|
||||
if defined?(Bundler) && Bundler.respond_to?(:rubygems_required)
|
||||
Bundler.rubygems_required
|
||||
end
|
||||
RUBY_EVAL
|
||||
end
|
||||
end
|
||||
|
||||
def load_paths_for_specs(specs)
|
||||
load_paths = []
|
||||
specs.each do |spec|
|
||||
load_paths << File.join(spec.full_gem_path, spec.bindir) if spec.bindir
|
||||
spec.require_paths.each do |path|
|
||||
load_paths << File.join(spec.full_gem_path, path)
|
||||
end
|
||||
end
|
||||
load_paths
|
||||
end
|
||||
end
|
||||
end
|
||||
19
railties/lib/vendor/bundler/lib/bundler/resolver.rb
vendored
Normal file
19
railties/lib/vendor/bundler/lib/bundler/resolver.rb
vendored
Normal file
@@ -0,0 +1,19 @@
|
||||
require 'bundler/resolver/inspect'
|
||||
require 'bundler/resolver/search'
|
||||
require 'bundler/resolver/engine'
|
||||
require 'bundler/resolver/stack'
|
||||
require 'bundler/resolver/state'
|
||||
|
||||
module Bundler
|
||||
module Resolver
|
||||
def self.resolve(deps, source_index = Gem.source_index, logger = nil)
|
||||
unless logger
|
||||
logger = Logger.new($stderr)
|
||||
logger.datetime_format = ""
|
||||
logger.level = ENV["GEM_RESOLVER_DEBUG"] ? Logger::DEBUG : Logger::ERROR
|
||||
end
|
||||
|
||||
Engine.resolve(deps, source_index, logger)
|
||||
end
|
||||
end
|
||||
end
|
||||
61
railties/lib/vendor/bundler/lib/bundler/resolver/builders.rb
vendored
Normal file
61
railties/lib/vendor/bundler/lib/bundler/resolver/builders.rb
vendored
Normal file
@@ -0,0 +1,61 @@
|
||||
module Bundler
|
||||
module Resolver
|
||||
module Builders
|
||||
def build_index(&block)
|
||||
index = Gem::SourceIndex.new
|
||||
IndexBuilder.run(index, &block) if block_given?
|
||||
index
|
||||
end
|
||||
|
||||
def build_spec(name, version, &block)
|
||||
spec = Gem::Specification.new
|
||||
spec.instance_variable_set(:@name, name)
|
||||
spec.instance_variable_set(:@version, Gem::Version.new(version))
|
||||
DepBuilder.run(spec, &block) if block_given?
|
||||
spec
|
||||
end
|
||||
|
||||
def build_dep(name, requirements, type = :runtime)
|
||||
Gem::Dependency.new(name, requirements, type)
|
||||
end
|
||||
|
||||
class IndexBuilder
|
||||
include Builders
|
||||
|
||||
def self.run(index, &block)
|
||||
new(index).run(&block)
|
||||
end
|
||||
|
||||
def initialize(index)
|
||||
@index = index
|
||||
end
|
||||
|
||||
def run(&block)
|
||||
instance_eval(&block)
|
||||
end
|
||||
|
||||
def add_spec(*args, &block)
|
||||
@index.add_spec(build_spec(*args, &block))
|
||||
end
|
||||
end
|
||||
|
||||
class DepBuilder
|
||||
def self.run(spec, &block)
|
||||
new(spec).run(&block)
|
||||
end
|
||||
|
||||
def initialize(spec)
|
||||
@spec = spec
|
||||
end
|
||||
|
||||
def run(&block)
|
||||
instance_eval(&block)
|
||||
end
|
||||
|
||||
def runtime(name, requirements)
|
||||
@spec.add_runtime_dependency(name, requirements)
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
end
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user