提交 3075ae06 编写于 作者: J José Valim

Solving merge conflicts.

......@@ -17,6 +17,15 @@ def initialize(name, serializable)
@value = compute_value
end
# There is a significant speed improvement if the value
# does not need to be escaped, as <tt>tag!</tt> escapes all values
# to ensure that valid XML is generated. For known binary
# values, it is at least an order of magnitude faster to
# Base64 encode binary values and directly put them in the
# output XML than to pass the original value or the Base64
# encoded value to the <tt>tag!</tt> method. It definitely makes
# no sense to Base64 encode the value and then give it to
# <tt>tag!</tt>, since that just adds additional overhead.
def needs_encoding?
![ :binary, :date, :datetime, :boolean, :float, :integer ].include?(type)
end
......@@ -105,28 +114,6 @@ def serializable_method_attributes
end
end
def add_attributes
(serializable_attributes + serializable_method_attributes).each do |attribute|
add_tag(attribute)
end
end
def add_procs
if procs = options.delete(:procs)
[ *procs ].each do |proc|
proc.call(options)
end
end
end
def add_tag(attribute)
builder.tag!(
reformat_name(attribute.name),
attribute.value.to_s,
attribute.decorations(!options[:skip_types])
)
end
def serialize
args = [root]
......@@ -152,6 +139,24 @@ def reformat_name(name)
name = name.camelize if camelize?
dasherize? ? name.dasherize : name
end
def add_attributes
(serializable_attributes + serializable_method_attributes).each do |attribute|
builder.tag!(
reformat_name(attribute.name),
attribute.value.to_s,
attribute.decorations(!options[:skip_types])
)
end
end
def add_procs
if procs = options.delete(:procs)
[ *procs ].each do |proc|
proc.call(options)
end
end
end
end
def to_xml(options = {}, &block)
......
......@@ -133,6 +133,7 @@ def cache_attribute?(attr_name)
end
private
# Suffixes a, ?, c become regexp /(a|\?|c)$/
def rebuild_attribute_method_regexp
suffixes = attribute_method_suffixes.map { |s| Regexp.escape(s) }
......@@ -238,19 +239,17 @@ def evaluate_attribute_method(attr_name, method_definition, method_name=attr_nam
def method_missing(method_id, *args, &block)
method_name = method_id.to_s
if self.class.private_method_defined?(method_name)
raise NoMethodError.new("Attempt to call private method", method_name, args)
end
# If we haven't generated any methods yet, generate them, then
# see if we've created the method we're looking for.
if !self.class.generated_methods?
self.class.define_attribute_methods
guard_private_attribute_method!(method_name, args)
if self.class.generated_methods.include?(method_name)
return self.send(method_id, *args, &block)
end
end
guard_private_attribute_method!(method_name, args)
if self.class.primary_key.to_s == method_name
id
elsif md = self.class.match_attribute_method?(method_name)
......@@ -371,6 +370,12 @@ def respond_to?(method, include_private_methods = false)
end
private
# prevent method_missing from calling private methods with #send
def guard_private_attribute_method!(method_name, args)
if self.class.private_method_defined?(method_name)
raise NoMethodError.new("Attempt to call private method", method_name, args)
end
end
def missing_attribute(attr_name, stack)
raise ActiveRecord::MissingAttributeError, "missing attribute: #{attr_name}", stack
......
......@@ -164,42 +164,9 @@ def from_xml(xml)
end
end
class XmlSerializer < ActiveModel::Serializer #:nodoc:
class XmlSerializer < ActiveModel::Serializers::Xml::Serializer #:nodoc:
include Serialization::RecordSerializer
def builder
@builder ||= begin
require 'builder' unless defined? ::Builder
options[:indent] ||= 2
builder = options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent])
unless options[:skip_instruct]
builder.instruct!
options[:skip_instruct] = true
end
builder
end
end
def root
root = (options[:root] || @serializable.class.to_s.underscore).to_s
reformat_name(root)
end
def dasherize?
!options.has_key?(:dasherize) || options[:dasherize]
end
def camelize?
options.has_key?(:camelize) && options[:camelize]
end
def reformat_name(name)
name = name.camelize if camelize?
dasherize? ? name.dasherize : name
end
def serializable_attributes
serializable_attribute_names.collect { |name| Attribute.new(name, @serializable) }
end
......@@ -211,28 +178,6 @@ def serializable_method_attributes
end
end
def add_attributes
(serializable_attributes + serializable_method_attributes).each do |attribute|
add_tag(attribute)
end
end
def add_procs
if procs = options.delete(:procs)
[ *procs ].each do |proc|
proc.call(options)
end
end
end
def add_tag(attribute)
builder.tag!(
reformat_name(attribute.name),
attribute.value.to_s,
attribute.decorations(!options[:skip_types])
)
end
def add_associations(association, records, opts)
if records.is_a?(Enumerable)
tag = reformat_name(association.to_s)
......@@ -282,50 +227,10 @@ def serialize
end
end
class Attribute #:nodoc:
attr_reader :name, :value, :type
def initialize(name, record)
@name, @record = name, record
@type = compute_type
@value = compute_value
end
# There is a significant speed improvement if the value
# does not need to be escaped, as <tt>tag!</tt> escapes all values
# to ensure that valid XML is generated. For known binary
# values, it is at least an order of magnitude faster to
# Base64 encode binary values and directly put them in the
# output XML than to pass the original value or the Base64
# encoded value to the <tt>tag!</tt> method. It definitely makes
# no sense to Base64 encode the value and then give it to
# <tt>tag!</tt>, since that just adds additional overhead.
def needs_encoding?
![ :binary, :date, :datetime, :boolean, :float, :integer ].include?(type)
end
def decorations(include_types = true)
decorations = {}
if type == :binary
decorations[:encoding] = 'base64'
end
if include_types && type != :string
decorations[:type] = type
end
if value.nil?
decorations[:nil] = true
end
decorations
end
class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc:
protected
def compute_type
type = @record.class.serialized_attributes.has_key?(name) ? :yaml : @record.class.columns_hash[name].type
type = @serializable.class.serialized_attributes.has_key?(name) ? :yaml : @serializable.class.columns_hash[name].type
case type
when :text
......@@ -336,22 +241,12 @@ def compute_type
type
end
end
def compute_value
value = @record.send(name)
if formatter = Hash::XML_FORMATTING[type.to_s]
value ? formatter.call(value) : nil
else
value
end
end
end
class MethodAttribute < Attribute #:nodoc:
protected
def compute_type
Hash::XML_TYPE_NAMES[@record.send(name).class.name] || :string
Hash::XML_TYPE_NAMES[@serializable.send(name).class.name] || :string
end
end
end
......
......@@ -277,6 +277,22 @@ def test_bulk_update_respects_access_control
assert_raise(ActiveRecord::UnknownAttributeError) { @target.new.attributes = { :title => "Ants in pants" } }
end
def test_read_attribute_overwrites_private_method_not_considered_implemented
# simulate a model with a db column that shares its name an inherited
# private method (e.g. Object#system)
#
Object.class_eval do
private
def title; "private!"; end
end
assert !@target.instance_method_already_implemented?(:title)
topic = @target.new
assert_equal nil, topic.title
Object.send(:undef_method, :title) # remove test method from object
end
private
def time_related_columns_on_topic
Topic.columns.select{|c| [:time, :date, :datetime, :timestamp].include?(c.type)}.map(&:name)
......
......@@ -804,7 +804,8 @@ def dup
# my_company.size = 10
# my_company.save # sends PUT /companies/1 (update)
def save
new? ? create : update
notify(:before_save)
(new? ? create : update).tap { notify(:after_save) }
end
# Deletes the resource from the remote service.
......@@ -820,7 +821,8 @@ def save
# new_person.destroy
# Person.find(new_id) # 404 (Resource Not Found)
def destroy
connection.delete(element_path, self.class.headers)
notify(:before_destroy)
connection.delete(element_path, self.class.headers).tap { notify(:after_destroy) }
end
# Evaluates to <tt>true</tt> if this resource is not <tt>new?</tt> and is
......@@ -995,16 +997,20 @@ def connection(refresh = false)
# 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
......@@ -1088,5 +1094,6 @@ def method_missing(method_symbol, *arguments) #:nodoc:
class Base
extend ActiveModel::Naming
include CustomMethods, Validations
include ActiveModel::Observing
end
end
require 'abstract_unit'
class ObservingTest < Test::Unit::TestCase
cattr_accessor :history
class PersonObserver < ActiveModel::Observer
observe :person
%w( after_create after_destroy after_save after_update
before_create before_destroy before_save before_update).each do |method|
define_method(method) { log method }
end
private
def log(method)
(ObservingTest.history ||= []) << method.to_sym
end
end
def setup
@matz = { :id => 1, :name => 'Matz' }.to_xml(:root => 'person')
ActiveResource::HttpMock.respond_to do |mock|
mock.get "/people/1.xml", {}, @matz
mock.post "/people.xml", {}, @matz, 201, 'Location' => '/people/1.xml'
mock.put "/people/1.xml", {}, nil, 204
mock.delete "/people/1.xml", {}, nil, 200
end
PersonObserver.instance
end
def teardown
self.history = nil
end
def test_create_fires_save_and_create_notifications
rick = Person.create(:name => 'Rick')
assert_equal [:before_save, :before_create, :after_create, :after_save], self.history
end
def test_update_fires_save_and_update_notifications
person = Person.find(1)
person.save
assert_equal [:before_save, :before_update, :after_update, :after_save], self.history
end
def test_destroy_fires_destroy_notifications
person = Person.find(1)
person.destroy
assert_equal [:before_destroy, :after_destroy], self.history
end
end
......@@ -5,6 +5,9 @@
<% for attribute in attributes -%>
<th><%= attribute.human_name %></th>
<% end -%>
<th></th>
<th></th>
<th></th>
</tr>
<%% @<%= plural_name %>.each do |<%= singular_name %>| %>
......
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html;charset=UTF-8" />
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<title><%= controller_class_name %>: <%%= controller.action_name %></title>
<%%= stylesheet_link_tag 'scaffold' %>
</head>
......
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<title>The page you were looking for doesn't exist (404)</title>
<style type="text/css">
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog {
width: 25em;
padding: 0 4em;
margin: 4em auto 0 auto;
border: 1px solid #ccc;
border-right-color: #999;
border-bottom-color: #999;
}
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
</style>
<style type="text/css">
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog {
width: 25em;
padding: 0 4em;
margin: 4em auto 0 auto;
border: 1px solid #ccc;
border-right-color: #999;
border-bottom-color: #999;
}
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
</style>
</head>
<body>
......@@ -27,4 +24,4 @@
<p>You may have mistyped the address or the page may have moved.</p>
</div>
</body>
</html>
\ No newline at end of file
</html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<title>The change you wanted was rejected (422)</title>
<style type="text/css">
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog {
width: 25em;
padding: 0 4em;
margin: 4em auto 0 auto;
border: 1px solid #ccc;
border-right-color: #999;
border-bottom-color: #999;
}
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
</style>
<style type="text/css">
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog {
width: 25em;
padding: 0 4em;
margin: 4em auto 0 auto;
border: 1px solid #ccc;
border-right-color: #999;
border-bottom-color: #999;
}
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
</style>
</head>
<body>
......@@ -27,4 +24,4 @@
<p>Maybe you tried to change something you didn't have access to.</p>
</div>
</body>
</html>
\ No newline at end of file
</html>
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en" lang="en">
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="content-type" content="text/html; charset=UTF-8" />
<title>We're sorry, but something went wrong (500)</title>
<style type="text/css">
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog {
width: 25em;
padding: 0 4em;
margin: 4em auto 0 auto;
border: 1px solid #ccc;
border-right-color: #999;
border-bottom-color: #999;
}
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
</style>
<style type="text/css">
body { background-color: #fff; color: #666; text-align: center; font-family: arial, sans-serif; }
div.dialog {
width: 25em;
padding: 0 4em;
margin: 4em auto 0 auto;
border: 1px solid #ccc;
border-right-color: #999;
border-bottom-color: #999;
}
h1 { font-size: 100%; color: #f00; line-height: 1.5em; }
</style>
</head>
<body>
......
<!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.0 Transitional//EN"
"http://www.w3.org/TR/xhtml1/DTD/xhtml1-transitional.dtd">
<!DOCTYPE html>
<html>
<head>
<meta http-equiv="Content-type" content="text/html; charset=utf-8" />
......@@ -14,20 +13,20 @@
font-size: 13px;
color: #333;
}
h1 {
font-size: 28px;
color: #000;
}
a {color: #03c}
a:hover {
background-color: #03c;
color: white;
text-decoration: none;
}
#page {
background-color: #f0f0f0;
width: 750px;
......@@ -35,7 +34,7 @@
margin-left: auto;
margin-right: auto;
}
#content {
float: left;
background-color: white;
......@@ -44,7 +43,7 @@
padding: 25px;
width: 500px;
}
#sidebar {
float: right;
width: 175px;
......@@ -53,7 +52,7 @@
#footer {
clear: both;
}
#header, #about, #getting-started {
padding-left: 75px;
......@@ -73,14 +72,14 @@
font-weight: normal;
font-size: 16px;
}
#about h3 {
margin: 0;
margin-bottom: 10px;
font-size: 14px;
}
#about-content {
background-color: #ffd;
border: 1px solid #fc0;
......@@ -113,8 +112,8 @@
margin: 0;
padding: 10px;
}
#getting-started {
border-top: 1px solid #ccc;
margin-top: 25px;
......@@ -150,8 +149,8 @@
color: #555;
font-size: 13px;
}
#search {
margin: 0;
padding-top: 10px;
......@@ -163,8 +162,8 @@
margin: 2px;
}
#search-text {width: 170px}
#sidebar ul {
margin-left: 0;
padding-left: 0;
......@@ -181,7 +180,7 @@
#sidebar ul.links li {
margin-bottom: 5px;
}
</style>
<script type="text/javascript" src="javascripts/prototype.js"></script>
<script type="text/javascript" src="javascripts/effects.js"></script>
......@@ -194,11 +193,11 @@
onComplete: function() {new Effect.BlindDown('about-content', {duration: 0.25})}
});
} else {
new Effect[Element.visible('about-content') ?
new Effect[Element.visible('about-content') ?
'BlindUp' : 'BlindDown']('about-content', {duration: 0.25});
}
}
window.onload = function() {
$('search-text').value = '';
$('search').onsubmit = function() {
......@@ -218,7 +217,7 @@
<input type="submit" value="Search" /> the Rails site
</form>
</li>
<li>
<h3>Join the community</h3>
<ul class="links">
......@@ -227,7 +226,7 @@
<li><a href="http://wiki.rubyonrails.org/">Wiki</a></li>
</ul>
</li>
<li>
<h3>Browse the documentation</h3>
<ul class="links">
......@@ -250,17 +249,17 @@
<h3><a href="rails/info/properties" onclick="about(); return false">About your application&rsquo;s environment</a></h3>
<div id="about-content" style="display: none"></div>
</div>
<div id="getting-started">
<h1>Getting started</h1>
<h2>Here&rsquo;s how to get rolling:</h2>
<ol>
<ol>
<li>
<h2>Use <tt>script/generate</tt> to create your models and controllers</h2>
<h2>Use <code>script/generate</code> to create your models and controllers</h2>
<p>To see all available options, run it without parameters.</p>
</li>
<li>
<h2>Set up a default route and remove or rename this file</h2>
<p>Routes are set up in config/routes.rb.</p>
......@@ -268,13 +267,13 @@
<li>
<h2>Create your database</h2>
<p>Run <tt>rake db:migrate</tt> to create your database. If you're not using SQLite (the default), edit <tt>config/database.yml</tt> with your username and password.</p>
<p>Run <code>rake db:migrate</code> to create your database. If you're not using SQLite (the default), edit <code>config/database.yml</code> with your username and password.</p>
</li>
</ol>
</div>
</div>
<div id="footer">&nbsp;</div>
</div>
</body>
</html>
\ No newline at end of file
</html>
Markdown is supported
0% .
You are about to add 0 people to the discussion. Proceed with caution.
先完成此消息的编辑!
想要评论请 注册