diff options
author | José Valim <jose.valim@gmail.com> | 2009-07-12 16:29:15 +0200 |
---|---|---|
committer | José Valim <jose.valim@gmail.com> | 2009-07-12 16:29:15 +0200 |
commit | 3075ae0611625657de052a2ad93cf083d24bb803 (patch) | |
tree | f421dda228ef1bbe79a31c9c5b95d502b041adf9 | |
parent | cca16a015d52a8fde0d4f66970ee333900ca21cc (diff) | |
parent | c863388039ff6e97447be86184d41fbf30f0f389 (diff) | |
download | rails-3075ae0611625657de052a2ad93cf083d24bb803.tar.gz rails-3075ae0611625657de052a2ad93cf083d24bb803.tar.bz2 rails-3075ae0611625657de052a2ad93cf083d24bb803.zip |
Solving merge conflicts.
12 files changed, 198 insertions, 226 deletions
diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb index d187859b44..7cdd281223 100644 --- a/activemodel/lib/active_model/serializers/xml.rb +++ b/activemodel/lib/active_model/serializers/xml.rb @@ -17,6 +17,15 @@ module ActiveModel @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 @@ module ActiveModel 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 @@ module ActiveModel 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) diff --git a/activerecord/lib/active_record/attribute_methods.rb b/activerecord/lib/active_record/attribute_methods.rb index 15358979c2..ecd2d57a5a 100644 --- a/activerecord/lib/active_record/attribute_methods.rb +++ b/activerecord/lib/active_record/attribute_methods.rb @@ -133,6 +133,7 @@ module ActiveRecord 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 @@ module ActiveRecord 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 @@ module ActiveRecord 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 diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb index c3811caa53..253fa03785 100644 --- a/activerecord/lib/active_record/serializers/xml_serializer.rb +++ b/activerecord/lib/active_record/serializers/xml_serializer.rb @@ -164,42 +164,9 @@ module ActiveRecord #:nodoc: 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 @@ module ActiveRecord #:nodoc: 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 @@ module ActiveRecord #:nodoc: 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 @@ module ActiveRecord #:nodoc: 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 diff --git a/activerecord/test/cases/attribute_methods_test.rb b/activerecord/test/cases/attribute_methods_test.rb index 17ed302465..183be1e2f9 100644 --- a/activerecord/test/cases/attribute_methods_test.rb +++ b/activerecord/test/cases/attribute_methods_test.rb @@ -277,6 +277,22 @@ class AttributeMethodsTest < ActiveRecord::TestCase 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) diff --git a/activeresource/lib/active_resource/base.rb b/activeresource/lib/active_resource/base.rb index f919f911e4..88a431a6d9 100644 --- a/activeresource/lib/active_resource/base.rb +++ b/activeresource/lib/active_resource/base.rb @@ -804,7 +804,8 @@ module ActiveResource # 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 @@ module ActiveResource # 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 @@ 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 @@ -1088,5 +1094,6 @@ module ActiveResource class Base extend ActiveModel::Naming include CustomMethods, Validations + include ActiveModel::Observing end end diff --git a/activeresource/test/observing_test.rb b/activeresource/test/observing_test.rb new file mode 100644 index 0000000000..334b256772 --- /dev/null +++ b/activeresource/test/observing_test.rb @@ -0,0 +1,53 @@ +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 diff --git a/railties/lib/generators/erb/scaffold/templates/index.html.erb b/railties/lib/generators/erb/scaffold/templates/index.html.erb index 427ff81e03..5e6a4af9e0 100644 --- a/railties/lib/generators/erb/scaffold/templates/index.html.erb +++ b/railties/lib/generators/erb/scaffold/templates/index.html.erb @@ -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 %>| %> diff --git a/railties/lib/generators/erb/scaffold/templates/layout.html.erb b/railties/lib/generators/erb/scaffold/templates/layout.html.erb index ebc97f8130..aacfbe4a8f 100644 --- a/railties/lib/generators/erb/scaffold/templates/layout.html.erb +++ b/railties/lib/generators/erb/scaffold/templates/layout.html.erb @@ -1,9 +1,7 @@ -<!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> diff --git a/railties/lib/generators/rails/app/templates/public/404.html b/railties/lib/generators/rails/app/templates/public/404.html index eff660b90c..88ee108e90 100644 --- a/railties/lib/generators/rails/app/templates/public/404.html +++ b/railties/lib/generators/rails/app/templates/public/404.html @@ -1,23 +1,20 @@ -<!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> diff --git a/railties/lib/generators/rails/app/templates/public/422.html b/railties/lib/generators/rails/app/templates/public/422.html index b54e4a3cad..9c3c96670b 100644 --- a/railties/lib/generators/rails/app/templates/public/422.html +++ b/railties/lib/generators/rails/app/templates/public/422.html @@ -1,23 +1,20 @@ -<!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> diff --git a/railties/lib/generators/rails/app/templates/public/500.html b/railties/lib/generators/rails/app/templates/public/500.html index ec3bbf02c4..f71c86e652 100644 --- a/railties/lib/generators/rails/app/templates/public/500.html +++ b/railties/lib/generators/rails/app/templates/public/500.html @@ -1,23 +1,20 @@ -<!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> diff --git a/railties/lib/generators/rails/app/templates/public/index.html b/railties/lib/generators/rails/app/templates/public/index.html index cd337dc74c..ff2dfd3193 100644 --- a/railties/lib/generators/rails/app/templates/public/index.html +++ b/railties/lib/generators/rails/app/templates/public/index.html @@ -1,5 +1,4 @@ -<!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’s environment</a></h3> <div id="about-content" style="display: none"></div> </div> - + <div id="getting-started"> <h1>Getting started</h1> <h2>Here’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"> </div> </div> </body> -</html>
\ No newline at end of file +</html> |