From 8ff7693a8dc61f43fc4eaf72ed24d3b8699191fe Mon Sep 17 00:00:00 2001 From: Jose and Yehuda Date: Mon, 26 Sep 2011 18:48:19 -0400 Subject: Initial commit of serializer support --- actionpack/lib/action_controller.rb | 1 + actionpack/lib/action_controller/base.rb | 1 + .../lib/action_controller/metal/serialization.rb | 26 + actionpack/test/controller/render_json_test.rb | 34 ++ railties/guides/source/serializers.textile | 600 +++++++++++++++++++++ 5 files changed, 662 insertions(+) create mode 100644 actionpack/lib/action_controller/metal/serialization.rb create mode 100644 railties/guides/source/serializers.textile diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index f4eaa2fd1b..3829e60bb0 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -31,6 +31,7 @@ module ActionController autoload :RequestForgeryProtection autoload :Rescue autoload :Responder + autoload :Serialization autoload :SessionManagement autoload :Streaming autoload :Testing diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index 98bfe72fef..cfb9cf5e6e 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -190,6 +190,7 @@ module ActionController Redirecting, Rendering, Renderers::All, + Serialization, ConditionalGet, RackDelegation, SessionManagement, diff --git a/actionpack/lib/action_controller/metal/serialization.rb b/actionpack/lib/action_controller/metal/serialization.rb new file mode 100644 index 0000000000..9bb665a9ae --- /dev/null +++ b/actionpack/lib/action_controller/metal/serialization.rb @@ -0,0 +1,26 @@ +module ActionController + module Serialization + extend ActiveSupport::Concern + + include ActionController::Renderers + + included do + class_attribute :_serialization_scope + end + + def serialization_scope + send(_serialization_scope) + end + + def _render_option_json(json, options) + json = json.active_model_serializer.new(json, serialization_scope) if json.respond_to?(:active_model_serializer) + super + end + + module ClassMethods + def serialization_scope(scope) + self._serialization_scope = scope + end + end + end +end diff --git a/actionpack/test/controller/render_json_test.rb b/actionpack/test/controller/render_json_test.rb index fc604a2db3..f886af1a95 100644 --- a/actionpack/test/controller/render_json_test.rb +++ b/actionpack/test/controller/render_json_test.rb @@ -15,9 +15,32 @@ class RenderJsonTest < ActionController::TestCase end end + class JsonSerializer + def initialize(object, scope) + @object, @scope = object, scope + end + + def as_json(*) + { :object => @object.as_json, :scope => @scope.as_json } + end + end + + class JsonSerializable + def active_model_serializer + JsonSerializer + end + + def as_json(*) + { :serializable_object => true } + end + end + class TestController < ActionController::Base protect_from_forgery + serialization_scope :current_user + attr_reader :current_user + def self.controller_path 'test' end @@ -61,6 +84,11 @@ class RenderJsonTest < ActionController::TestCase def render_json_without_options render :json => JsonRenderable.new end + + def render_json_with_serializer + @current_user = Struct.new(:as_json).new(:current_user => true) + render :json => JsonSerializable.new + end end tests TestController @@ -132,4 +160,10 @@ class RenderJsonTest < ActionController::TestCase get :render_json_without_options assert_equal '{"a":"b"}', @response.body end + + def test_render_json_with_serializer + get :render_json_with_serializer + assert_match '"scope":{"current_user":true}', @response.body + assert_match '"object":{"serializable_object":true}', @response.body + end end diff --git a/railties/guides/source/serializers.textile b/railties/guides/source/serializers.textile new file mode 100644 index 0000000000..a631a14694 --- /dev/null +++ b/railties/guides/source/serializers.textile @@ -0,0 +1,600 @@ +h2. Rails Serializers + +This guide describes how to use Active Model serializers to build non-trivial JSON services in Rails. By reading this guide, you will learn: + +* When to use the built-in Active Model serialization +* When to use a custom serializer for your models +* How to use serializers to encapsulate authorization concerns +* How to create serializer templates to describe the application-wide structure of your serialized JSON +* How to build resources not backed by a single database table for use with JSON services + +This guide covers an intermediate topic and assumes familiarity with Rails conventions. It is suitable for applications that expose a +JSON API that may return different results based on the authorization status of the user. + +endprologue. + +h3. Serialization + +By default, Active Record objects can serialize themselves into JSON by using the `to_json` method. This method takes a series of additional +parameter to control which properties and associations Rails should include in the serialized output. + +When building a web application that uses JavaScript to retrieve JSON data from the server, this mechanism has historically been the primary +way that Rails developers prepared their responses. This works great for simple cases, as the logic for serializing an Active Record object +is neatly encapsulated in Active Record itself. + +However, this solution quickly falls apart in the face of serialization requirements based on authorization. For instance, a web service +may choose to expose additional information about a resource only if the user is entitled to access it. In addition, a JavaScript front-end +may want information that is not neatly described in terms of serializing a single Active Record object, or in a different format than. + +In addition, neither the controller nor the model seems like the correct place for logic that describes how to serialize an model object +*for the current user*. + +Serializers solve these problems by encapsulating serialization in an object designed for this purpose. If the default +to_json+ semantics, +with at most a few configuration options serve your needs, by all means continue to use the built-in +to_json+. If you find yourself doing +hash-driven-development in your controllers, juggling authorization logic and other concerns, serializers are for you! + +h3. The Most Basic Serializer + +A basic serializer is a simple Ruby object named after the model class it is serializing. + + +class PostSerializer + def initialize(post, scope) + @post, @scope = post, scope + end + + def as_json + { post: { title: @post.name, body: @post.body } } + end +end + + +A serializer is initialized with two parameters: the model object it should serialize and an authorization scope. By default, the +authorization scope is the current user (+current_user+) but you can use a different object if you want. The serializer also +implements an +as_json+ method, which returns a Hash that will be sent to the JSON encoder. + +Rails will transparently use your serializer when you use +render :json+ in your controller. + + +class PostsController < ApplicationController + def show + @post = Post.find(params[:id]) + render json: @post + end +end + + +Because +respond_with+ uses +render :json+ under the hood for JSON requests, Rails will automatically use your serializer when +you use +respond_with+ as well. + +h4. +serializable_hash+ + +In general, you will want to implement +serializable_hash+ and +as_json+ to allow serializers to embed associated content +directly. The easiest way to implement these two methods is to have +as_json+ call +serializable_hash+ and insert the root. + + +class PostSerializer + def initialize(post, scope) + @post, @scope = post, scope + end + + def serializable_hash + { title: @post.name, body: @post.body } + end + + def as_json + { post: serializable_hash } + end +end + + +h4. Authorization + +Let's update our serializer to include the email address of the author of the post, but only if the current user has superuser +access. + + +class PostSerializer + def initialize(post, scope) + @post, @scope = post, scope + end + + def as_json + { post: serializable_hash } + end + + def serializable_hash + hash = post + hash.merge!(super_data) if super? + hash + end + +private + def post + { title: @post.name, body: @post.body } + end + + def super_data + { email: @post.email } + end + + def super? + @scope.superuser? + end +end + + +h4. Testing + +One benefit of encapsulating our objects this way is that it becomes extremely straight-forward to test the serialization +logic in isolation. + + +require "ostruct" + +class PostSerializerTest < ActiveSupport::TestCase + # For now, we use a very simple authorization structure. These tests will need + # refactoring if we change that. + plebe = OpenStruct.new(super?: false) + god = OpenStruct.new(super?: true) + + post = OpenStruct.new(title: "Welcome to my blog!", body: "Blah blah blah", email: "tenderlove@gmail.com") + + test "a regular user sees just the title and body" do + json = PostSerializer.new(post, plebe).to_json + hash = JSON.parse(json) + + assert_equal post.title, hash.delete("title") + assert_equal post.body, hash.delete("body") + assert_empty hash + end + + test "a superuser sees the title, body and email" do + json = PostSerializer.new(post, god).to_json + hash = JSON.parse(json) + + assert_equal post.title, hash["title"] + assert_equal post.body, hash["body"] + assert_equal post.email, hash["email"] + assert_empty hash + end +end + + +It's important to note that serializer objects define a clear interface specifically for serializing an existing object. +In this case, the serializer expects to receive a post object with +name+, +body+ and +email+ attributes and an authorization +scope with a +super?+ method. + +By defining a clear interface, it's must easier to ensure that your authorization logic is behaving correctly. In this case, +the serializer doesn't need to concern itself with how the authorization scope decides whether to set the +super?+ flag, just +whether it is set. In general, you should document these requirements in your serializer files and programatically via tests. +The documentation library +YARD+ provides excellent tools for describing this kind of requirement: + + +class PostSerializer + # @param [~body, ~title, ~email] post the post to serialize + # @param [~super] scope the authorization scope for this serializer + def initialize(post, scope) + @post, @scope = post, scope + end + + # ... +end + + +h3. Attribute Sugar + +To simplify this process for a number of common cases, Rails provides a default superclass named +ActiveModel::Serializer+ +that you can use to implement your serializers. + +For example, you will sometimes want to simply include a number of existing attributes from the source model into the outputted +JSON. In the above example, the +title+ and +body+ attributes were always included in the JSON. Let's see how to use ++ActiveModel::Serializer+ to simplify our post serializer. + + +class PostSerializer < ActiveModel::Serializer + attributes :title, :body + + def initialize(post, scope) + @post, @scope = post, scope + end + + def serializable_hash + hash = attributes + hash.merge!(super_data) if super? + hash + end + +private + def super_data + { email: @post.email } + end + + def super? + @scope.superuser? + end +end + + +First, we specified the list of included attributes at the top of the class. This will create an instance method called ++attributes+ that extracts those attributes from the post model. + +NOTE: Internally, +ActiveModel::Serializer+ uses +read_attribute_for_serialization+, which defaults to +read_attribute+, which defaults to +send+. So if you're rolling your own models for use with the serializer, you can use simple Ruby accessors for your attributes if you like. + +Next, we use the attributes methood in our +serializable_hash+ method, which allowed us to eliminate the +post+ method we hand-rolled +earlier. We could also eliminate the +as_json+ method, as +ActiveModel::Serializer+ provides a default +as_json+ method for +us that calls our +serializable_hash+ method and inserts a root. But we can go a step further! + + +class PostSerializer < ActiveModel::Serializer + attributes :title, :body + +private + def attributes + hash = super + hash.merge!(email: post.email) if super? + hash + end + + def super? + @scope.superuser? + end +end + + +The superclass provides a default +initialize+ method as well as a default +serializable_hash+ method, which uses ++attributes+. We can call +super+ to get the hash based on the attributes we declared, and then add in any additional +attributes we want to use. + +NOTE: +ActiveModel::Serializer+ will create an accessor matching the name of the current class for the resource you pass in. In this case, because we have defined a PostSerializer, we can access the resource with the +post+ accessor. + +h3. Associations + +In most JSON APIs, you will want to include associated objects with your serialized object. In this case, let's include +the comments with the current post. + + +class PostSerializer < ActiveModel::Serializer + attributes :title. :body + has_many :comments + +private + def attributes + hash = super + hash.merge!(email: post.email) if super? + hash + end + + def super? + @scope.superuser? + end +end + + +The default +serializable_hash+ method will include the comments as embedded objects inside the post. + + +{ + post: { + title: "Hello Blog!", + body: "This is my first post. Isn't it fabulous!", + comments: [ + { + title: "Awesome", + body: "Your first post is great" + } + ] + } +} + + +Rails uses the same logic to generate embedded serializations as it does when you use +render :json+. In this case, +because you didn't define a +CommentSerializer+, Rails used the default +as_json+ on your comment object. + +If you define a serializer, Rails will automatically instantiate it with the existing authorization scope. + + +class CommentSerializer + def initialize(comment, scope) + @comment, @scope = comment, scope + end + + def serializable_hash + { title: @comment.title } + end + + def as_json + { comment: serializable_hash } + end +end + + +If we define the above comment serializer, the outputted JSON will change to: + + +{ + post: { + title: "Hello Blog!", + body: "This is my first post. Isn't it fabulous!", + comments: [{ title: "Awesome" }] + } +} + + +Let's imagine that our comment system allows an administrator to kill a comment, and we only want to allow +users to see the comments they're entitled to see. By default, +has_many :comments+ will simply use the ++comments+ accessor on the post object. We can override the +comments+ accessor to limit the comments used +to just the comments we want to allow for the current user. + + +class PostSerializer < ActiveModel::Serializer + attributes :title. :body + has_many :comments + +private + def attributes + hash = super + hash.merge!(email: post.email) if super? + hash + end + + def comments + post.comments_for(scope) + end + + def super? + @scope.superuser? + end +end + + ++ActiveModel::Serializer+ will still embed the comments, but this time it will use just the comments +for the current user. + +NOTE: The logic for deciding which comments a user should see still belongs in the model layer. In general, you should encapsulate concerns that require making direct Active Record queries in scopes or public methods on your models. + +h3. Customizing Associations + +Not all front-ends expect embedded documents in the same form. In these cases, you can override the +default +serializable_hash+, and use conveniences provided by +ActiveModel::Serializer+ to avoid having to +build up the hash manually. + +For example, let's say our front-end expects the posts and comments in the following format: + + +{ + post: { + id: 1 + title: "Hello Blog!", + body: "This is my first post. Isn't it fabulous!", + comments: [1,2] + }, + comments: [ + { + id: 1 + title: "Awesome", + body: "Your first post is great" + }, + { + id: 2 + title: "Not so awesome", + body: "Why is it so short!" + } + ] +} + + +We could achieve this with a custom +as_json+ method. We will also need to define a serializer for comments. + + +class CommentSerializer < ActiveModel::Serializer + attributes :id, :title, :body + + # define any logic for dealing with authorization-based attributes here +end + +class PostSerializer < ActiveModel::Serializer + attributes :title. :body + has_many :comments + + def as_json + { post: serializable_hash }.merge!(associations) + end + + def serializable_hash + post_hash = attributes + post_hash.merge!(association_ids) + post_hash + end + +private + def attributes + hash = super + hash.merge!(email: post.email) if super? + hash + end + + def comments + post.comments_for(scope) + end + + def super? + @scope.superuser? + end +end + + +Here, we used two convenience methods: +associations+ and +association_ids+. The first, ++associations+, creates a hash of all of the define associations, using their defined +serializers. The second, +association_ids+, generates a hash whose key is the association +name and whose value is an Array of the association's keys. + +The +association_ids+ helper will use the overridden version of the association, so in +this case, +association_ids+ will only include the ids of the comments provided by the ++comments+ method. + +h3. Special Association Serializers + +So far, associations defined in serializers use either the +as_json+ method on the model +or the defined serializer for the association type. Sometimes, you may want to serialize +associated models differently when they are requested as part of another resource than +when they are requested on their own. + +For instance, we might want to provide the full comment when it is requested directly, +but only its title when requested as part of the post. To achieve this, you can define +a serializer for associated objects nested inside the main serializer. + + +class PostSerializer < ActiveModel::Serializer + class CommentSerializer < ActiveModel::Serializer + attributes :id, :title + end + + # same as before + # ... +end + + +In other words, if a +PostSerializer+ is trying to serialize comments, it will first +look for +PostSerializer::CommentSerializer+ before falling back to +CommentSerializer+ +and finally +comment.as_json+. + +h3. Optional Associations + +In some cases, you will want to allow a front-end to decide whether to include associated +content or not. You can achieve this easily by making an association *optional*. + + +class PostSerializer < ActiveModel::Serializer + attributes :title. :body + has_many :comments, :optional => true + + # ... +end + + +If an association is optional, it will not be included unless the request asks for it +with an +including+ parameter. The +including+ parameter is a comma-separated list of +optional associations to include. If the +including+ parameter includes an association +you did not specify in your serializer, it will receive a +401 Forbidden+ response. + +h3. Overriding the Defaults + +h4. Authorization Scope + +By default, the authorization scope for serializers is +:current_user+. This means +that when you call +render json: @post+, the controller will automatically call +its +current_user+ method and pass that along to the serializer's initializer. + +If you want to change that behavior, simply use the +serialization_scope+ class +method. + + +class PostsController < ApplicationController + serialization_scope :current_app +end + + +You can also implement an instance method called (no surprise) +serialization_scope+, +which allows you to define a dynamic authorization scope based on the current request. + +WARNING: If you use different objects as authorization scopes, make sure that they all implement whatever interface you use in your serializers to control what the outputted JSON looks like. + +h4. Parameter to Specify Included Optional Associations + +In most cases, you should be able to use the default +including+ parameter to specify +which optional associations to include. If you are already using that parameter name or +want to reserve it for some reason, you can specify a different name by using the ++serialization_includes_param+ class method. + + +class PostsController < ApplicationController + serialization_includes_param :associations_to_include +end + + +You can also implement a +serialization_includes+ instance method, which should return an +Array of optional includes. + +WARNING: If you implement +serialization_includes+ and return an invalid association, your user will receive a +401 Forbidden+ exception. + +h3. Using Serializers Outside of a Request + +The serialization API encapsulates the concern of generating a JSON representation of +a particular model for a particular user. As a result, you should be able to easily use +serializers, whether you define them yourself or whether you use +ActiveModel::Serializer+ +outside a request. + +For instance, if you want to generate the JSON representation of a post for a user outside +of a request: + + +user = get_user # some logic to get the user in question +PostSerializer.new(post, user).to_json # reliably generate JSON output + + +If you want to generate JSON for an anonymous user, you should be able to use whatever +technique you use in your application to generate anonymous users outside of a request. +Typically, that means creating a new user and not saving it to the database: + + +user = User.new # create a new anonymous user +PostSerializer.new(post, user).to_json + + +In general, the better you encapsulate your authorization logic, the more easily you +will be able to use the serializer outside of the context of a request. For instance, +if you use an authorization library like Cancan, which uses a uniform +user.can?(action, model)+, +the authorization interface can very easily be replaced by a plain Ruby object for +testing or usage outside the context of a request. + +h3. Collections + +So far, we've talked about serializing individual model objects. By default, Rails +will serialize collections, including when using the +associations+ helper, by +looping over each element of the collection, calling +serializable_hash+ on the element, +and then grouping them by their type (using the plural version of their class name +as the root). + +For example, an Array of post objects would serialize as: + + +{ + posts: [ + { + title: "FIRST POST!", + body: "It's my first pooooost" + }, + { title: "Second post!", + body: "Zomg I made it to my second post" + } + ] +} + + +If you want to change the behavior of serialized Arrays, you need to create +a custom Array serializer. + + +class ArraySerializer < ActiveModel::ArraySerializer + def serializable_array + serializers.map do |serializer| + serializer.serializable_hash + end + end + + def as_json + hash = { root => serializable_array } + hash.merge!(associations) + hash + end +end + + +When generating embedded associations using the +associations+ helper inside a +regular serializer, it will create a new ArraySerializer with the +associated content and call its +serializable_array+ method. In this case, those +embedded associations will not recursively include associations. + +When generating an Array using +render json: posts+, the controller will invoke +the +as_json+ method, which will include its associations and its root. -- cgit v1.2.3 From a4c04a43cc96d9c94b7a5ecd5c7458fdc2f8617f Mon Sep 17 00:00:00 2001 From: Jose and Yehuda Date: Mon, 26 Sep 2011 18:57:49 -0400 Subject: Refactor to make renderers a Set --- actionpack/lib/action_controller/metal/renderers.rb | 20 +++++++++----------- 1 file changed, 9 insertions(+), 11 deletions(-) diff --git a/actionpack/lib/action_controller/metal/renderers.rb b/actionpack/lib/action_controller/metal/renderers.rb index 0ad9dbeda9..6e9ce450ac 100644 --- a/actionpack/lib/action_controller/metal/renderers.rb +++ b/actionpack/lib/action_controller/metal/renderers.rb @@ -1,5 +1,6 @@ require 'active_support/core_ext/class/attribute' require 'active_support/core_ext/object/blank' +require 'set' module ActionController # See Renderers.add @@ -12,16 +13,13 @@ module ActionController included do class_attribute :_renderers - self._renderers = {}.freeze + self._renderers = Set.new.freeze end module ClassMethods def use_renderers(*args) - new = _renderers.dup - args.each do |key| - new[key] = RENDERERS[key] - end - self._renderers = new.freeze + renderers = _renderers + args + self._renderers = renderers.freeze end alias use_renderer use_renderers end @@ -31,10 +29,10 @@ module ActionController end def _handle_render_options(options) - _renderers.each do |name, value| - if options.key?(name.to_sym) + _renderers.each do |name| + if options.key?(name) _process_options(options) - return send("_render_option_#{name}", options.delete(name.to_sym), options) + return send("_render_option_#{name}", options.delete(name), options) end end nil @@ -42,7 +40,7 @@ module ActionController # Hash of available renderers, mapping a renderer name to its proc. # Default keys are :json, :js, :xml. - RENDERERS = {} + RENDERERS = Set.new # Adds a new renderer to call within controller actions. # A renderer is invoked by passing its name as an option to @@ -79,7 +77,7 @@ module ActionController # ActionController::MimeResponds#respond_with def self.add(key, &block) define_method("_render_option_#{key}", &block) - RENDERERS[key] = block + RENDERERS << key.to_sym end module All -- cgit v1.2.3 From c3de52d7ed470433e9ecd44226518797c0a9f389 Mon Sep 17 00:00:00 2001 From: Jose and Yehuda Date: Mon, 26 Sep 2011 19:29:37 -0400 Subject: Initial implementation of ActiveModel::Serializer --- activemodel/lib/active_model.rb | 1 + activemodel/lib/active_model/serializer.rb | 46 ++++++++++++++++++ activemodel/test/cases/serializer_test.rb | 75 ++++++++++++++++++++++++++++++ 3 files changed, 122 insertions(+) create mode 100644 activemodel/lib/active_model/serializer.rb create mode 100644 activemodel/test/cases/serializer_test.rb diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index d0e2a6f39c..28765b00bb 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -43,6 +43,7 @@ module ActiveModel autoload :Observer, 'active_model/observing' autoload :Observing autoload :SecurePassword + autoload :Serializer autoload :Serialization autoload :TestCase autoload :Translation diff --git a/activemodel/lib/active_model/serializer.rb b/activemodel/lib/active_model/serializer.rb new file mode 100644 index 0000000000..2eca3ebc5e --- /dev/null +++ b/activemodel/lib/active_model/serializer.rb @@ -0,0 +1,46 @@ +require "active_support/core_ext/class/attribute" +require "active_support/core_ext/string/inflections" +require "set" + +module ActiveModel + class Serializer + class_attribute :_attributes + self._attributes = Set.new + + def self.attributes(*attrs) + self._attributes += attrs + end + + attr_reader :object, :scope + + def self.inherited(klass) + name = klass.name.demodulize.underscore.sub(/_serializer$/, '') + + klass.class_eval do + alias_method name.to_sym, :object + end + end + + def initialize(object, scope) + @object, @scope = object, scope + end + + def as_json(*) + serializable_hash + end + + def serializable_hash(*) + attributes + end + + def attributes + hash = {} + + _attributes.each do |name| + hash[name] = @object.read_attribute_for_serialization(name) + end + + hash + end + end +end diff --git a/activemodel/test/cases/serializer_test.rb b/activemodel/test/cases/serializer_test.rb new file mode 100644 index 0000000000..91ed6987a1 --- /dev/null +++ b/activemodel/test/cases/serializer_test.rb @@ -0,0 +1,75 @@ +require "cases/helper" + +class SerializerTest < ActiveModel::TestCase + class User + attr_accessor :superuser + + def super_user? + @superuser + end + + def read_attribute_for_serialization(name) + hash = { :first_name => "Jose", :last_name => "Valim", :password => "oh noes yugive my password" } + hash[name] + end + end + + class UserSerializer < ActiveModel::Serializer + attributes :first_name, :last_name + end + + class User2Serializer < ActiveModel::Serializer + attributes :first_name, :last_name + + def serializable_hash(*) + attributes.merge(:ok => true).merge(scope) + end + end + + class MyUserSerializer < ActiveModel::Serializer + attributes :first_name, :last_name + + def serializable_hash(*) + hash = attributes + hash = hash.merge(:super_user => true) if my_user.super_user? + hash + end + end + + def test_attributes + user = User.new + user_serializer = UserSerializer.new(user, nil) + + hash = user_serializer.as_json + + assert_equal({ :first_name => "Jose", :last_name => "Valim" }, hash) + end + + def test_attributes_method + user = User.new + user_serializer = User2Serializer.new(user, {}) + + hash = user_serializer.as_json + + assert_equal({ :first_name => "Jose", :last_name => "Valim", :ok => true }, hash) + end + + def test_serializer_receives_scope + user = User.new + user_serializer = User2Serializer.new(user, {:scope => true}) + + hash = user_serializer.as_json + + assert_equal({ :first_name => "Jose", :last_name => "Valim", :ok => true, :scope => true }, hash) + end + + def test_pretty_accessors + user = User.new + user.superuser = true + user_serializer = MyUserSerializer.new(user, nil) + + hash = user_serializer.as_json + + assert_equal({ :first_name => "Jose", :last_name => "Valim", :super_user => true }, hash) + end +end -- cgit v1.2.3 From f5836543c3cd16f7789c936a4181f4a7204141dc Mon Sep 17 00:00:00 2001 From: Vijay Dev Date: Wed, 28 Sep 2011 17:05:03 +0530 Subject: minor edits in serializers guide --- railties/guides/source/serializers.textile | 18 +++++++++--------- 1 file changed, 9 insertions(+), 9 deletions(-) diff --git a/railties/guides/source/serializers.textile b/railties/guides/source/serializers.textile index a631a14694..86a5e5ac8d 100644 --- a/railties/guides/source/serializers.textile +++ b/railties/guides/source/serializers.textile @@ -153,9 +153,9 @@ class PostSerializerTest < ActiveSupport::TestCase json = PostSerializer.new(post, god).to_json hash = JSON.parse(json) - assert_equal post.title, hash["title"] - assert_equal post.body, hash["body"] - assert_equal post.email, hash["email"] + assert_equal post.title, hash.delete("title") + assert_equal post.body, hash.delete("body") + assert_equal post.email, hash.delete("email") assert_empty hash end end @@ -255,7 +255,7 @@ the comments with the current post. class PostSerializer < ActiveModel::Serializer - attributes :title. :body + attributes :title, :body has_many :comments private @@ -361,7 +361,7 @@ build up the hash manually. For example, let's say our front-end expects the posts and comments in the following format: - + { post: { id: 1 @@ -382,7 +382,7 @@ For example, let's say our front-end expects the posts and comments in the follo } ] } - + We could achieve this with a custom +as_json+ method. We will also need to define a serializer for comments. @@ -394,7 +394,7 @@ class CommentSerializer < ActiveModel::Serializer end class PostSerializer < ActiveModel::Serializer - attributes :title. :body + attributes :title, :body has_many :comments def as_json @@ -558,7 +558,7 @@ as the root). For example, an Array of post objects would serialize as: - + { posts: [ { @@ -570,7 +570,7 @@ For example, an Array of post objects would serialize as: } ] } - + If you want to change the behavior of serialized Arrays, you need to create a custom Array serializer. -- cgit v1.2.3 From e407dfb9bf6ee3b12d699511ef05e6f260c5edf1 Mon Sep 17 00:00:00 2001 From: Jose and Yehuda Date: Tue, 27 Sep 2011 17:34:47 -0400 Subject: Don't require serializable_hash to take options. --- activemodel/lib/active_model/serializer.rb | 2 +- activemodel/test/cases/serializer_test.rb | 4 ++-- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/activemodel/lib/active_model/serializer.rb b/activemodel/lib/active_model/serializer.rb index 2eca3ebc5e..a2035ae2cb 100644 --- a/activemodel/lib/active_model/serializer.rb +++ b/activemodel/lib/active_model/serializer.rb @@ -29,7 +29,7 @@ module ActiveModel serializable_hash end - def serializable_hash(*) + def serializable_hash attributes end diff --git a/activemodel/test/cases/serializer_test.rb b/activemodel/test/cases/serializer_test.rb index 91ed6987a1..1c1da588f7 100644 --- a/activemodel/test/cases/serializer_test.rb +++ b/activemodel/test/cases/serializer_test.rb @@ -21,7 +21,7 @@ class SerializerTest < ActiveModel::TestCase class User2Serializer < ActiveModel::Serializer attributes :first_name, :last_name - def serializable_hash(*) + def serializable_hash attributes.merge(:ok => true).merge(scope) end end @@ -29,7 +29,7 @@ class SerializerTest < ActiveModel::TestCase class MyUserSerializer < ActiveModel::Serializer attributes :first_name, :last_name - def serializable_hash(*) + def serializable_hash hash = attributes hash = hash.merge(:super_user => true) if my_user.super_user? hash -- cgit v1.2.3 From 598fc578fbd38907288e14053bfc6eb8d4da1e3b Mon Sep 17 00:00:00 2001 From: Jose and Yehuda Date: Tue, 27 Sep 2011 17:35:11 -0400 Subject: Don't unnecessarily use String eval. --- activesupport/lib/active_support/core_ext/object/to_json.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/object/to_json.rb b/activesupport/lib/active_support/core_ext/object/to_json.rb index 14ef27340e..e7dc60a612 100644 --- a/activesupport/lib/active_support/core_ext/object/to_json.rb +++ b/activesupport/lib/active_support/core_ext/object/to_json.rb @@ -10,10 +10,10 @@ end # several cases (for instance, the JSON implementation for Hash does not work) with inheritance # and consequently classes as ActiveSupport::OrderedHash cannot be serialized to json. [Object, Array, FalseClass, Float, Hash, Integer, NilClass, String, TrueClass].each do |klass| - klass.class_eval <<-RUBY, __FILE__, __LINE__ + klass.class_eval do # Dumps object in JSON (JavaScript Object Notation). See www.json.org for more info. def to_json(options = nil) ActiveSupport::JSON.encode(self, options) end - RUBY + end end -- cgit v1.2.3 From 2a4aaae72af037715db81fda332190df62f3ec44 Mon Sep 17 00:00:00 2001 From: Jose and Yehuda Date: Sat, 15 Oct 2011 16:20:45 +0200 Subject: Added has_one and has_many --- activemodel/lib/active_model/serializer.rb | 56 +++++++++++++++++---- activemodel/test/cases/serializer_test.rb | 81 ++++++++++++++++++++++++++++-- 2 files changed, 123 insertions(+), 14 deletions(-) diff --git a/activemodel/lib/active_model/serializer.rb b/activemodel/lib/active_model/serializer.rb index a2035ae2cb..dc5e2aadb3 100644 --- a/activemodel/lib/active_model/serializer.rb +++ b/activemodel/lib/active_model/serializer.rb @@ -7,20 +7,41 @@ module ActiveModel class_attribute :_attributes self._attributes = Set.new - def self.attributes(*attrs) - self._attributes += attrs - end + class_attribute :_associations + self._associations = {} - attr_reader :object, :scope + class << self + def attributes(*attrs) + self._attributes += attrs + end + + def has_many(*attrs) + options = attrs.extract_options! + options[:has_many] = true + hash = {} + attrs.each { |attr| hash[attr] = options } + self._associations = _associations.merge(hash) + end + + def has_one(*attrs) + options = attrs.extract_options! + options[:has_one] = true + hash = {} + attrs.each { |attr| hash[attr] = options } + self._associations = _associations.merge(hash) + end - def self.inherited(klass) - name = klass.name.demodulize.underscore.sub(/_serializer$/, '') + def inherited(klass) + name = klass.name.demodulize.underscore.sub(/_serializer$/, '') - klass.class_eval do - alias_method name.to_sym, :object + klass.class_eval do + alias_method name.to_sym, :object + end end end + attr_reader :object, :scope + def initialize(object, scope) @object, @scope = object, scope end @@ -30,7 +51,24 @@ module ActiveModel end def serializable_hash - attributes + hash = attributes + + _associations.each do |association, options| + associated_object = object.send(association) + serializer = options[:serializer] + + if options[:has_many] + serialized_array = associated_object.map do |item| + serializer.new(item, scope).serializable_hash + end + + hash[association] = serialized_array + elsif options[:has_one] + hash[association] = serializer.new(associated_object, scope).serializable_hash + end + end + + hash end def attributes diff --git a/activemodel/test/cases/serializer_test.rb b/activemodel/test/cases/serializer_test.rb index 1c1da588f7..24de81d48f 100644 --- a/activemodel/test/cases/serializer_test.rb +++ b/activemodel/test/cases/serializer_test.rb @@ -1,17 +1,33 @@ require "cases/helper" class SerializerTest < ActiveModel::TestCase - class User + class Model + def initialize(hash) + @attributes = hash + end + + def read_attribute_for_serialization(name) + @attributes[name] + end + end + + class User < Model attr_accessor :superuser + def initialize(hash={}) + super hash.merge(:first_name => "Jose", :last_name => "Valim", :password => "oh noes yugive my password") + end + def super_user? @superuser end + end - def read_attribute_for_serialization(name) - hash = { :first_name => "Jose", :last_name => "Valim", :password => "oh noes yugive my password" } - hash[name] - end + class Post < Model + attr_accessor :comments + end + + class Comment < Model end class UserSerializer < ActiveModel::Serializer @@ -36,6 +52,25 @@ class SerializerTest < ActiveModel::TestCase end end + class CommentSerializer + def initialize(comment, scope) + @comment, @scope = comment, scope + end + + def serializable_hash + { title: @comment.read_attribute_for_serialization(:title) } + end + + def as_json + { comment: serializable_hash } + end + end + + class PostSerializer < ActiveModel::Serializer + attributes :title, :body + has_many :comments, :serializer => CommentSerializer + end + def test_attributes user = User.new user_serializer = UserSerializer.new(user, nil) @@ -72,4 +107,40 @@ class SerializerTest < ActiveModel::TestCase assert_equal({ :first_name => "Jose", :last_name => "Valim", :super_user => true }, hash) end + + def test_has_many + user = User.new + + post = Post.new(:title => "New Post", :body => "Body of new post", :email => "tenderlove@tenderlove.com") + comments = [Comment.new(:title => "Comment1"), Comment.new(:title => "Comment2")] + post.comments = comments + + post_serializer = PostSerializer.new(post, user) + + assert_equal({ + :title => "New Post", + :body => "Body of new post", + :comments => [ + { :title => "Comment1" }, + { :title => "Comment2" } + ] + }, post_serializer.as_json) + end + + class Blog < Model + attr_accessor :author + end + + class AuthorSerializer < ActiveModel::Serializer + attributes :first_name, :last_name + end + + class BlogSerializer < ActiveModel::Serializer + has_one :author, :serializer => AuthorSerializer + end + + def test_has_one + user = User.new + blog = Blog.new(:author => user) + end end -- cgit v1.2.3 From 776da539d72c98d077f97789a1265dd23a79711f Mon Sep 17 00:00:00 2001 From: Jose and Yehuda Date: Sat, 15 Oct 2011 16:53:22 +0200 Subject: Add support for implicit serializers --- activemodel/lib/active_model/serializer.rb | 63 ++++++++++++++++++------------ activemodel/test/cases/serializer_test.rb | 35 ++++++++++++++++- 2 files changed, 72 insertions(+), 26 deletions(-) diff --git a/activemodel/lib/active_model/serializer.rb b/activemodel/lib/active_model/serializer.rb index dc5e2aadb3..c5b433df51 100644 --- a/activemodel/lib/active_model/serializer.rb +++ b/activemodel/lib/active_model/serializer.rb @@ -1,37 +1,62 @@ require "active_support/core_ext/class/attribute" require "active_support/core_ext/string/inflections" +require "active_support/core_ext/module/anonymous" require "set" module ActiveModel class Serializer + module Associations + class Config < Struct.new(:name, :options) + def serializer + options[:serializer] + end + end + + class HasMany < Config + def serialize(collection, scope) + collection.map do |item| + serializer.new(item, scope).serializable_hash + end + end + end + + class HasOne < Config + def serialize(object, scope) + serializer.new(object, scope).serializable_hash + end + end + end + class_attribute :_attributes self._attributes = Set.new class_attribute :_associations - self._associations = {} + self._associations = [] class << self def attributes(*attrs) self._attributes += attrs end - def has_many(*attrs) + def associate(klass, attrs) options = attrs.extract_options! - options[:has_many] = true - hash = {} - attrs.each { |attr| hash[attr] = options } - self._associations = _associations.merge(hash) + self._associations += attrs.map do |attr| + options[:serializer] ||= const_get("#{attr.to_s.camelize}Serializer") + klass.new(attr, options) + end + end + + def has_many(*attrs) + associate(Associations::HasMany, attrs) end def has_one(*attrs) - options = attrs.extract_options! - options[:has_one] = true - hash = {} - attrs.each { |attr| hash[attr] = options } - self._associations = _associations.merge(hash) + associate(Associations::HasOne, attrs) end def inherited(klass) + return if klass.anonymous? + name = klass.name.demodulize.underscore.sub(/_serializer$/, '') klass.class_eval do @@ -53,19 +78,9 @@ module ActiveModel def serializable_hash hash = attributes - _associations.each do |association, options| - associated_object = object.send(association) - serializer = options[:serializer] - - if options[:has_many] - serialized_array = associated_object.map do |item| - serializer.new(item, scope).serializable_hash - end - - hash[association] = serialized_array - elsif options[:has_one] - hash[association] = serializer.new(associated_object, scope).serializable_hash - end + _associations.each do |association| + associated_object = object.send(association.name) + hash[association.name] = association.serialize(associated_object, scope) end hash diff --git a/activemodel/test/cases/serializer_test.rb b/activemodel/test/cases/serializer_test.rb index 24de81d48f..37a675bc20 100644 --- a/activemodel/test/cases/serializer_test.rb +++ b/activemodel/test/cases/serializer_test.rb @@ -2,7 +2,7 @@ require "cases/helper" class SerializerTest < ActiveModel::TestCase class Model - def initialize(hash) + def initialize(hash={}) @attributes = hash end @@ -141,6 +141,37 @@ class SerializerTest < ActiveModel::TestCase def test_has_one user = User.new - blog = Blog.new(:author => user) + blog = Blog.new + blog.author = user + + json = BlogSerializer.new(blog, user).as_json + assert_equal({ + :author => { + :first_name => "Jose", + :last_name => "Valim" + } + }, json) + end + + def test_implicit_serializer + author_serializer = Class.new(ActiveModel::Serializer) do + attributes :first_name + end + + blog_serializer = Class.new(ActiveModel::Serializer) do + const_set(:AuthorSerializer, author_serializer) + has_one :author + end + + user = User.new + blog = Blog.new + blog.author = user + + json = blog_serializer.new(blog, user).as_json + assert_equal({ + :author => { + :first_name => "Jose" + } + }, json) end end -- cgit v1.2.3 From 22c322f056f42d95b0421e6608f404134463de13 Mon Sep 17 00:00:00 2001 From: Jose and Yehuda Date: Sat, 15 Oct 2011 17:01:08 +0200 Subject: Add support for overriding associations, mostly used for authorization --- activemodel/lib/active_model/serializer.rb | 6 +++++- activemodel/test/cases/serializer_test.rb | 26 ++++++++++++++++++++++++++ 2 files changed, 31 insertions(+), 1 deletion(-) diff --git a/activemodel/lib/active_model/serializer.rb b/activemodel/lib/active_model/serializer.rb index c5b433df51..6e89eec09c 100644 --- a/activemodel/lib/active_model/serializer.rb +++ b/activemodel/lib/active_model/serializer.rb @@ -41,6 +41,10 @@ module ActiveModel def associate(klass, attrs) options = attrs.extract_options! self._associations += attrs.map do |attr| + unless method_defined?(attr) + class_eval "def #{attr}() object.#{attr} end", __FILE__, __LINE__ + end + options[:serializer] ||= const_get("#{attr.to_s.camelize}Serializer") klass.new(attr, options) end @@ -79,7 +83,7 @@ module ActiveModel hash = attributes _associations.each do |association| - associated_object = object.send(association.name) + associated_object = send(association.name) hash[association.name] = association.serialize(associated_object, scope) end diff --git a/activemodel/test/cases/serializer_test.rb b/activemodel/test/cases/serializer_test.rb index 37a675bc20..08dd73e03b 100644 --- a/activemodel/test/cases/serializer_test.rb +++ b/activemodel/test/cases/serializer_test.rb @@ -174,4 +174,30 @@ class SerializerTest < ActiveModel::TestCase } }, json) end + + def test_overridden_associations + author_serializer = Class.new(ActiveModel::Serializer) do + attributes :first_name + end + + blog_serializer = Class.new(ActiveModel::Serializer) do + const_set(:PersonSerializer, author_serializer) + has_one :person + + def person + object.author + end + end + + user = User.new + blog = Blog.new + blog.author = user + + json = blog_serializer.new(blog, user).as_json + assert_equal({ + :person => { + :first_name => "Jose" + } + }, json) + end end -- cgit v1.2.3 From 322f47898e80af3fcdc3cb3db35e177d8216a2d2 Mon Sep 17 00:00:00 2001 From: Jose and Yehuda Date: Sat, 15 Oct 2011 18:27:56 +0200 Subject: Add association_ids --- activemodel/lib/active_model/serializer.rb | 30 ++++++++++++++- activemodel/test/cases/serializer_test.rb | 61 +++++++++++++++++++++++++++++- 2 files changed, 89 insertions(+), 2 deletions(-) diff --git a/activemodel/lib/active_model/serializer.rb b/activemodel/lib/active_model/serializer.rb index 6e89eec09c..99a007de31 100644 --- a/activemodel/lib/active_model/serializer.rb +++ b/activemodel/lib/active_model/serializer.rb @@ -18,12 +18,25 @@ module ActiveModel serializer.new(item, scope).serializable_hash end end + + def serialize_ids(collection, scope) + # use named scopes if they are present + return collection.ids if collection.respond_to?(:ids) + + collection.map do |item| + item.read_attribute_for_serialization(:id) + end + end end class HasOne < Config def serialize(object, scope) serializer.new(object, scope).serializable_hash end + + def serialize_ids(object, scope) + object.read_attribute_for_serialization(:id) + end end end @@ -80,7 +93,11 @@ module ActiveModel end def serializable_hash - hash = attributes + attributes.merge(associations) + end + + def associations + hash = {} _associations.each do |association| associated_object = send(association.name) @@ -90,6 +107,17 @@ module ActiveModel hash end + def association_ids + hash = {} + + _associations.each do |association| + associated_object = send(association.name) + hash[association.name] = association.serialize_ids(associated_object, scope) + end + + hash + end + def attributes hash = {} diff --git a/activemodel/test/cases/serializer_test.rb b/activemodel/test/cases/serializer_test.rb index 08dd73e03b..168a77838f 100644 --- a/activemodel/test/cases/serializer_test.rb +++ b/activemodel/test/cases/serializer_test.rb @@ -182,11 +182,12 @@ class SerializerTest < ActiveModel::TestCase blog_serializer = Class.new(ActiveModel::Serializer) do const_set(:PersonSerializer, author_serializer) - has_one :person def person object.author end + + has_one :person end user = User.new @@ -200,4 +201,62 @@ class SerializerTest < ActiveModel::TestCase } }, json) end + + def post_serializer(type) + Class.new(ActiveModel::Serializer) do + attributes :title, :body + has_many :comments, :serializer => CommentSerializer + + define_method :serializable_hash do + post_hash = attributes + post_hash.merge!(send(type)) + post_hash + end + end + end + + def test_associations + post = Post.new(:title => "New Post", :body => "Body of new post", :email => "tenderlove@tenderlove.com") + comments = [Comment.new(:title => "Comment1"), Comment.new(:title => "Comment2")] + post.comments = comments + + serializer = post_serializer(:associations).new(post, nil) + + assert_equal({ + :title => "New Post", + :body => "Body of new post", + :comments => [ + { :title => "Comment1" }, + { :title => "Comment2" } + ] + }, serializer.as_json) + end + + def test_association_ids + serializer = post_serializer(:association_ids) + + serializer.class_eval do + def as_json(*) + { post: serializable_hash }.merge(associations) + end + end + + post = Post.new(:title => "New Post", :body => "Body of new post", :email => "tenderlove@tenderlove.com") + comments = [Comment.new(:title => "Comment1", :id => 1), Comment.new(:title => "Comment2", :id => 2)] + post.comments = comments + + serializer = serializer.new(post, nil) + + assert_equal({ + :post => { + :title => "New Post", + :body => "Body of new post", + :comments => [1, 2] + }, + :comments => [ + { :title => "Comment1" }, + { :title => "Comment2" } + ] + }, serializer.as_json) + end end -- cgit v1.2.3 From 7a28498b55913aa0fd1d3529909ab57eaf64af0e Mon Sep 17 00:00:00 2001 From: Jose and Yehuda Date: Sat, 15 Oct 2011 18:37:12 +0200 Subject: Fix nil has_one association --- activemodel/lib/active_model/serializer.rb | 4 ++-- activemodel/test/cases/serializer_test.rb | 18 ++++++++++++++++++ 2 files changed, 20 insertions(+), 2 deletions(-) diff --git a/activemodel/lib/active_model/serializer.rb b/activemodel/lib/active_model/serializer.rb index 99a007de31..1d11870bb4 100644 --- a/activemodel/lib/active_model/serializer.rb +++ b/activemodel/lib/active_model/serializer.rb @@ -31,11 +31,11 @@ module ActiveModel class HasOne < Config def serialize(object, scope) - serializer.new(object, scope).serializable_hash + object && serializer.new(object, scope).serializable_hash end def serialize_ids(object, scope) - object.read_attribute_for_serialization(:id) + object && object.read_attribute_for_serialization(:id) end end end diff --git a/activemodel/test/cases/serializer_test.rb b/activemodel/test/cases/serializer_test.rb index 168a77838f..165c1d2490 100644 --- a/activemodel/test/cases/serializer_test.rb +++ b/activemodel/test/cases/serializer_test.rb @@ -259,4 +259,22 @@ class SerializerTest < ActiveModel::TestCase ] }, serializer.as_json) end + + def test_associations_with_nil_association + user = User.new + blog = Blog.new + + json = BlogSerializer.new(blog, user).as_json + assert_equal({ + :author => nil + }, json) + + serializer = Class.new(BlogSerializer) do + def serializable_hash + attributes.merge(association_ids) + end + end + + assert_equal({ :author => nil }, serializer.new(blog, user).as_json) + end end -- cgit v1.2.3 From a230f040ff61f069d46a9b86417a8e251016d5db Mon Sep 17 00:00:00 2001 From: Jose and Yehuda Date: Sat, 15 Oct 2011 18:54:20 +0200 Subject: Add support for the root attribute --- activemodel/lib/active_model/serializer.rb | 13 ++++- activemodel/test/cases/serializer_test.rb | 76 ++++++++++++++++++++++++------ 2 files changed, 74 insertions(+), 15 deletions(-) diff --git a/activemodel/lib/active_model/serializer.rb b/activemodel/lib/active_model/serializer.rb index 1d11870bb4..98801ae633 100644 --- a/activemodel/lib/active_model/serializer.rb +++ b/activemodel/lib/active_model/serializer.rb @@ -46,6 +46,8 @@ module ActiveModel class_attribute :_associations self._associations = [] + class_attribute :_root + class << self def attributes(*attrs) self._attributes += attrs @@ -71,6 +73,10 @@ module ActiveModel associate(Associations::HasOne, attrs) end + def root(name) + self._root = name + end + def inherited(klass) return if klass.anonymous? @@ -78,6 +84,7 @@ module ActiveModel klass.class_eval do alias_method name.to_sym, :object + root name.to_sym unless self._root == false end end end @@ -89,7 +96,11 @@ module ActiveModel end def as_json(*) - serializable_hash + if _root + { _root => serializable_hash } + else + serializable_hash + end end def serializable_hash diff --git a/activemodel/test/cases/serializer_test.rb b/activemodel/test/cases/serializer_test.rb index 165c1d2490..044c184829 100644 --- a/activemodel/test/cases/serializer_test.rb +++ b/activemodel/test/cases/serializer_test.rb @@ -77,7 +77,9 @@ class SerializerTest < ActiveModel::TestCase hash = user_serializer.as_json - assert_equal({ :first_name => "Jose", :last_name => "Valim" }, hash) + assert_equal({ + :user => { :first_name => "Jose", :last_name => "Valim" } + }, hash) end def test_attributes_method @@ -86,7 +88,9 @@ class SerializerTest < ActiveModel::TestCase hash = user_serializer.as_json - assert_equal({ :first_name => "Jose", :last_name => "Valim", :ok => true }, hash) + assert_equal({ + :user2 => { :first_name => "Jose", :last_name => "Valim", :ok => true } + }, hash) end def test_serializer_receives_scope @@ -95,7 +99,14 @@ class SerializerTest < ActiveModel::TestCase hash = user_serializer.as_json - assert_equal({ :first_name => "Jose", :last_name => "Valim", :ok => true, :scope => true }, hash) + assert_equal({ + :user2 => { + :first_name => "Jose", + :last_name => "Valim", + :ok => true, + :scope => true + } + }, hash) end def test_pretty_accessors @@ -105,7 +116,11 @@ class SerializerTest < ActiveModel::TestCase hash = user_serializer.as_json - assert_equal({ :first_name => "Jose", :last_name => "Valim", :super_user => true }, hash) + assert_equal({ + :my_user => { + :first_name => "Jose", :last_name => "Valim", :super_user => true + } + }, hash) end def test_has_many @@ -118,12 +133,14 @@ class SerializerTest < ActiveModel::TestCase post_serializer = PostSerializer.new(post, user) assert_equal({ - :title => "New Post", - :body => "Body of new post", - :comments => [ - { :title => "Comment1" }, - { :title => "Comment2" } - ] + :post => { + :title => "New Post", + :body => "Body of new post", + :comments => [ + { :title => "Comment1" }, + { :title => "Comment2" } + ] + } }, post_serializer.as_json) end @@ -146,9 +163,11 @@ class SerializerTest < ActiveModel::TestCase json = BlogSerializer.new(blog, user).as_json assert_equal({ - :author => { - :first_name => "Jose", - :last_name => "Valim" + :blog => { + :author => { + :first_name => "Jose", + :last_name => "Valim" + } } }, json) end @@ -266,15 +285,44 @@ class SerializerTest < ActiveModel::TestCase json = BlogSerializer.new(blog, user).as_json assert_equal({ - :author => nil + :blog => { :author => nil } }, json) serializer = Class.new(BlogSerializer) do + root :blog + def serializable_hash attributes.merge(association_ids) end end + json = serializer.new(blog, user).as_json + assert_equal({ :blog => { :author => nil } }, json) + end + + def test_custom_root + user = User.new + blog = Blog.new + + serializer = Class.new(BlogSerializer) do + root :my_blog + end + + assert_equal({ :my_blog => { :author => nil } }, serializer.new(blog, user).as_json) + end + + def test_false_root + user = User.new + blog = Blog.new + + serializer = Class.new(BlogSerializer) do + root false + end + + assert_equal({ :author => nil }, serializer.new(blog, user).as_json) + + # test inherited false root + serializer = Class.new(serializer) assert_equal({ :author => nil }, serializer.new(blog, user).as_json) end end -- cgit v1.2.3 From 2abb2e617af8e3353d4411a8bd51d03256e0274a Mon Sep 17 00:00:00 2001 From: Jose and Yehuda Date: Sat, 15 Oct 2011 19:22:16 +0200 Subject: Add initial support for embed API --- activemodel/lib/active_model/serializer.rb | 22 ++++++-- activemodel/test/cases/serializer_test.rb | 86 ++++++++++++++++++++++++++++-- 2 files changed, 101 insertions(+), 7 deletions(-) diff --git a/activemodel/lib/active_model/serializer.rb b/activemodel/lib/active_model/serializer.rb index 98801ae633..6d0746a3e8 100644 --- a/activemodel/lib/active_model/serializer.rb +++ b/activemodel/lib/active_model/serializer.rb @@ -21,7 +21,7 @@ module ActiveModel def serialize_ids(collection, scope) # use named scopes if they are present - return collection.ids if collection.respond_to?(:ids) + #return collection.ids if collection.respond_to?(:ids) collection.map do |item| item.read_attribute_for_serialization(:id) @@ -47,6 +47,9 @@ module ActiveModel self._associations = [] class_attribute :_root + class_attribute :_embed + self._embed = :objects + class_attribute :_root_embed class << self def attributes(*attrs) @@ -73,6 +76,11 @@ module ActiveModel associate(Associations::HasOne, attrs) end + def embed(type, options={}) + self._embed = type + self._root_embed = true if options[:include] + end + def root(name) self._root = name end @@ -97,14 +105,22 @@ module ActiveModel def as_json(*) if _root - { _root => serializable_hash } + hash = { _root => serializable_hash } + hash.merge!(associations) if _root_embed + hash else serializable_hash end end def serializable_hash - attributes.merge(associations) + if _embed == :ids + attributes.merge(association_ids) + elsif _embed == :objects + attributes.merge(associations) + else + attributes + end end def associations diff --git a/activemodel/test/cases/serializer_test.rb b/activemodel/test/cases/serializer_test.rb index 044c184829..e6a0d0cdcf 100644 --- a/activemodel/test/cases/serializer_test.rb +++ b/activemodel/test/cases/serializer_test.rb @@ -226,10 +226,12 @@ class SerializerTest < ActiveModel::TestCase attributes :title, :body has_many :comments, :serializer => CommentSerializer - define_method :serializable_hash do - post_hash = attributes - post_hash.merge!(send(type)) - post_hash + if type != :super + define_method :serializable_hash do + post_hash = attributes + post_hash.merge!(send(type)) + post_hash + end end end end @@ -325,4 +327,80 @@ class SerializerTest < ActiveModel::TestCase serializer = Class.new(serializer) assert_equal({ :author => nil }, serializer.new(blog, user).as_json) end + + def test_embed_ids + serializer = post_serializer(:super) + + serializer.class_eval do + root :post + embed :ids + end + + post = Post.new(:title => "New Post", :body => "Body of new post", :email => "tenderlove@tenderlove.com") + comments = [Comment.new(:title => "Comment1", :id => 1), Comment.new(:title => "Comment2", :id => 2)] + post.comments = comments + + serializer = serializer.new(post, nil) + + assert_equal({ + :post => { + :title => "New Post", + :body => "Body of new post", + :comments => [1, 2] + } + }, serializer.as_json) + end + + def test_embed_ids_include_true + serializer = post_serializer(:super) + + serializer.class_eval do + root :post + embed :ids, :include => true + end + + post = Post.new(:title => "New Post", :body => "Body of new post", :email => "tenderlove@tenderlove.com") + comments = [Comment.new(:title => "Comment1", :id => 1), Comment.new(:title => "Comment2", :id => 2)] + post.comments = comments + + serializer = serializer.new(post, nil) + + assert_equal({ + :post => { + :title => "New Post", + :body => "Body of new post", + :comments => [1, 2] + }, + :comments => [ + { :title => "Comment1" }, + { :title => "Comment2" } + ] + }, serializer.as_json) + end + + def test_embed_objects + serializer = post_serializer(:super) + + serializer.class_eval do + root :post + embed :objects + end + + post = Post.new(:title => "New Post", :body => "Body of new post", :email => "tenderlove@tenderlove.com") + comments = [Comment.new(:title => "Comment1", :id => 1), Comment.new(:title => "Comment2", :id => 2)] + post.comments = comments + + serializer = serializer.new(post, nil) + + assert_equal({ + :post => { + :title => "New Post", + :body => "Body of new post", + :comments => [ + { :title => "Comment1" }, + { :title => "Comment2" } + ] + } + }, serializer.as_json) + end end -- cgit v1.2.3 From afd7140b66e7cb32e1be58d9e44489e6bcbde0dc Mon Sep 17 00:00:00 2001 From: Jose and Yehuda Date: Sat, 15 Oct 2011 22:33:58 +0200 Subject: Remove 1.9 Hash syntax - tests passing on 1.8.7 --- activemodel/test/cases/serializer_test.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/activemodel/test/cases/serializer_test.rb b/activemodel/test/cases/serializer_test.rb index e6a0d0cdcf..00d519dc1a 100644 --- a/activemodel/test/cases/serializer_test.rb +++ b/activemodel/test/cases/serializer_test.rb @@ -58,11 +58,11 @@ class SerializerTest < ActiveModel::TestCase end def serializable_hash - { title: @comment.read_attribute_for_serialization(:title) } + { :title => @comment.read_attribute_for_serialization(:title) } end def as_json - { comment: serializable_hash } + { :comment => serializable_hash } end end @@ -258,7 +258,7 @@ class SerializerTest < ActiveModel::TestCase serializer.class_eval do def as_json(*) - { post: serializable_hash }.merge(associations) + { :post => serializable_hash }.merge(associations) end end -- cgit v1.2.3 From 5bc23e4fb503e038c8e24a3a58d4eb1c323e94c8 Mon Sep 17 00:00:00 2001 From: Yasuo Honda Date: Wed, 19 Oct 2011 15:56:25 -0400 Subject: Fix ORA-00932 error when trying to insert 0 to DATE type columns. --- activerecord/test/cases/migration_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index b3f1785f44..47ab5d5f9d 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -471,7 +471,7 @@ if ActiveRecord::Base.connection.supports_migrations? # Do a manual insertion if current_adapter?(:OracleAdapter) - Person.connection.execute "insert into people (id, wealth, created_at, updated_at) values (people_seq.nextval, 12345678901234567890.0123456789, 0, 0)" + Person.connection.execute "insert into people (id, wealth, created_at, updated_at) values (people_seq.nextval, 12345678901234567890.0123456789, sysdate, sysdate)" elsif current_adapter?(:OpenBaseAdapter) || (current_adapter?(:MysqlAdapter) && Mysql.client_version < 50003) #before mysql 5.0.3 decimals stored as strings Person.connection.execute "insert into people (wealth, created_at, updated_at) values ('12345678901234567890.0123456789', 0, 0)" elsif current_adapter?(:PostgreSQLAdapter) -- cgit v1.2.3 From a50f659e081785479b068b311862703584a589ca Mon Sep 17 00:00:00 2001 From: Olivier Lacan Date: Thu, 3 Nov 2011 10:01:32 -0400 Subject: CSS fix to prevent error output from being breaking out of body element. Using the white-space: pre-wrap adds extra line breaks to prevent the text from breaking out of the element's box. In this case single line output can be extremely long, breaking out the element. See for reference: http://www.quirksmode.org/css/whitespace.html Before: http://link.olivierlacan.com/BVU4 After: http://link.olivierlacan.com/BUfM --- actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb | 1 + 1 file changed, 1 insertion(+) diff --git a/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb index 6e71fd7ddc..1a308707d1 100644 --- a/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb +++ b/actionpack/lib/action_dispatch/middleware/templates/rescues/layout.erb @@ -16,6 +16,7 @@ background-color: #eee; padding: 10px; font-size: 11px; + white-space: pre-wrap; } a { color: #000; } -- cgit v1.2.3 From 15fb4302b6ff16e641b6279a3530eb8ed97f2899 Mon Sep 17 00:00:00 2001 From: Alex Tambellini Date: Thu, 8 Sep 2011 16:07:04 -0400 Subject: schema_format :sql should behave like schema_format :ruby This commit adds a db:structure:load task that is run instead of db:schema:load when schema_format is set to :sql. This patch also removes the prefixing of the structure.sql files to mimic the use of a single schema.rb file. The patch originates from github issue #715. --- .../lib/active_record/railties/databases.rake | 104 +++++++++++++-------- 1 file changed, 66 insertions(+), 38 deletions(-) diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 44848b3391..aea928b443 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -151,6 +151,7 @@ db_namespace = namespace :db do ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) db_namespace["schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby + db_namespace["structure:dump"].invoke if ActiveRecord::Base.schema_format == :sql end namespace :migrate do @@ -174,6 +175,7 @@ db_namespace = namespace :db do raise 'VERSION is required' unless version ActiveRecord::Migrator.run(:up, ActiveRecord::Migrator.migrations_paths, version) db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby + db_namespace['structure:dump'].invoke if ActiveRecord::Base.schema_format == :sql end # desc 'Runs the "down" for a given migration VERSION.' @@ -182,6 +184,7 @@ db_namespace = namespace :db do raise 'VERSION is required' unless version ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_paths, version) db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby + db_namespace['structure:dump'].invoke if ActiveRecord::Base.schema_format == :sql end desc 'Display status of migrations' @@ -222,6 +225,7 @@ db_namespace = namespace :db do step = ENV['STEP'] ? ENV['STEP'].to_i : 1 ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step) db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby + db_namespace['structure:dump'].invoke if ActiveRecord::Base.schema_format == :sql end # desc 'Pushes the schema to the next version (specify steps w/ STEP=n).' @@ -229,10 +233,14 @@ db_namespace = namespace :db do step = ENV['STEP'] ? ENV['STEP'].to_i : 1 ActiveRecord::Migrator.forward(ActiveRecord::Migrator.migrations_paths, step) db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby + db_namespace['structure:dump'].invoke if ActiveRecord::Base.schema_format == :sql end # desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.' - task :reset => [ 'db:drop', 'db:setup' ] + task :reset => :environment do + db_namespace["drop"].invoke + db_namespace["setup"].invoke + end # desc "Retrieves the charset for the current environment's database" task :charset => :environment do @@ -285,7 +293,12 @@ db_namespace = namespace :db do end desc 'Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the db first)' - task :setup => [ 'db:create', 'db:schema:load', 'db:seed' ] + task :setup => :environment do + db_namespace["create"].invoke + db_namespace["schema:load"].invoke if ActiveRecord::Base.schema_format == :ruby + db_namespace["structure:load"].invoke if ActiveRecord::Base.schema_format == :sql + db_namespace["seed"].invoke + end desc 'Load the seed data from db/seeds.rb' task :seed => 'db:abort_if_pending_migrations' do @@ -360,81 +373,96 @@ db_namespace = namespace :db do case abcs[Rails.env]['adapter'] when /mysql/, 'oci', 'oracle' ActiveRecord::Base.establish_connection(abcs[Rails.env]) - File.open("#{Rails.root}/db/#{Rails.env}_structure.sql", "w+") { |f| f << ActiveRecord::Base.connection.structure_dump } + File.open("#{Rails.root}/db/structure.sql", "w:utf-8") { |f| f << ActiveRecord::Base.connection.structure_dump } when /postgresql/ ENV['PGHOST'] = abcs[Rails.env]['host'] if abcs[Rails.env]['host'] - ENV['PGPORT'] = abcs[Rails.env]["port"].to_s if abcs[Rails.env]['port'] + ENV['PGPORT'] = abcs[Rails.env]['port'].to_s if abcs[Rails.env]['port'] ENV['PGPASSWORD'] = abcs[Rails.env]['password'].to_s if abcs[Rails.env]['password'] + ENV['PGUSER'] = abcs[Rails.env]['username'].to_s if abcs[Rails.env]['username'] search_path = abcs[Rails.env]['schema_search_path'] unless search_path.blank? search_path = search_path.split(",").map{|search_path_part| "--schema=#{search_path_part.strip}" }.join(" ") end - `pg_dump -i -U "#{abcs[Rails.env]['username']}" -s -x -O -f db/#{Rails.env}_structure.sql #{search_path} #{abcs[Rails.env]['database']}` + `pg_dump -i -s -x -O -f db/structure.sql #{search_path} #{abcs[Rails.env]['database']}` raise 'Error dumping database' if $?.exitstatus == 1 when /sqlite/ dbfile = abcs[Rails.env]['database'] || abcs[Rails.env]['dbfile'] - `sqlite3 #{dbfile} .schema > db/#{Rails.env}_structure.sql` + `sqlite3 #{dbfile} .schema > db/structure.sql` when 'sqlserver' - `smoscript -s #{abcs[Rails.env]['host']} -d #{abcs[Rails.env]['database']} -u #{abcs[Rails.env]['username']} -p #{abcs[Rails.env]['password']} -f db\\#{Rails.env}_structure.sql -A -U` + `smoscript -s #{abcs[Rails.env]['host']} -d #{abcs[Rails.env]['database']} -u #{abcs[Rails.env]['username']} -p #{abcs[Rails.env]['password']} -f db\\structure.sql -A -U` when "firebird" set_firebird_env(abcs[Rails.env]) db_string = firebird_db_string(abcs[Rails.env]) - sh "isql -a #{db_string} > #{Rails.root}/db/#{Rails.env}_structure.sql" + sh "isql -a #{db_string} > #{Rails.root}/db/structure.sql" else raise "Task not supported by '#{abcs[Rails.env]["adapter"]}'" end if ActiveRecord::Base.connection.supports_migrations? - File.open("#{Rails.root}/db/#{Rails.env}_structure.sql", "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information } + File.open("#{Rails.root}/db/structure.sql", "a") { |f| f << ActiveRecord::Base.connection.dump_schema_information } end end - end - - namespace :test do - # desc "Recreate the test database from the current schema.rb" - task :load => 'db:test:purge' do - ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test']) - ActiveRecord::Schema.verbose = false - db_namespace['schema:load'].invoke - end - # desc "Recreate the test database from the current environment's database schema" - task :clone => %w(db:schema:dump db:test:load) + # desc "Recreate the databases from the structure.sql file" + task :load => [:environment, :load_config] do + env = ENV['RAILS_ENV'] || 'test' - # desc "Recreate the test databases from the development structure" - task :clone_structure => [ 'db:structure:dump', 'db:test:purge' ] do abcs = ActiveRecord::Base.configurations - case abcs['test']['adapter'] + case abcs[env]['adapter'] when /mysql/ - ActiveRecord::Base.establish_connection(:test) + ActiveRecord::Base.establish_connection(abcs[env]) ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0') - IO.readlines("#{Rails.root}/db/#{Rails.env}_structure.sql").join.split("\n\n").each do |table| + IO.readlines("#{Rails.root}/db/structure.sql").join.split("\n\n").each do |table| ActiveRecord::Base.connection.execute(table) end when /postgresql/ - ENV['PGHOST'] = abcs['test']['host'] if abcs['test']['host'] - ENV['PGPORT'] = abcs['test']['port'].to_s if abcs['test']['port'] - ENV['PGPASSWORD'] = abcs['test']['password'].to_s if abcs['test']['password'] - `psql -U "#{abcs['test']['username']}" -f "#{Rails.root}/db/#{Rails.env}_structure.sql" #{abcs['test']['database']} #{abcs['test']['template']}` + ENV['PGHOST'] = abcs[env]['host'] if abcs[env]['host'] + ENV['PGPORT'] = abcs[env]['port'].to_s if abcs[env]['port'] + ENV['PGPASSWORD'] = abcs[env]['password'].to_s if abcs[env]['password'] + ENV['PGUSER'] = abcs[env]['username'].to_s if abcs[env]['username'] + `psql -f "#{Rails.root}/db/structure.sql" #{abcs[env]['database']} #{abcs[env]['template']}` when /sqlite/ - dbfile = abcs['test']['database'] || abcs['test']['dbfile'] - `sqlite3 #{dbfile} < "#{Rails.root}/db/#{Rails.env}_structure.sql"` + dbfile = abcs[env]['database'] || abcs[env]['dbfile'] + `sqlite3 #{dbfile} < "#{Rails.root}/db/structure.sql"` when 'sqlserver' - `sqlcmd -S #{abcs['test']['host']} -d #{abcs['test']['database']} -U #{abcs['test']['username']} -P #{abcs['test']['password']} -i db\\#{Rails.env}_structure.sql` + `sqlcmd -S #{abcs[env]['host']} -d #{abcs[env]['database']} -U #{abcs[env]['username']} -P #{abcs[env]['password']} -i db\\structure.sql` when 'oci', 'oracle' - ActiveRecord::Base.establish_connection(:test) - IO.readlines("#{Rails.root}/db/#{Rails.env}_structure.sql").join.split(";\n\n").each do |ddl| + ActiveRecord::Base.establish_connection(abcs[env]) + IO.readlines("#{Rails.root}/db/structure.sql").join.split(";\n\n").each do |ddl| ActiveRecord::Base.connection.execute(ddl) end when 'firebird' - set_firebird_env(abcs['test']) - db_string = firebird_db_string(abcs['test']) - sh "isql -i #{Rails.root}/db/#{Rails.env}_structure.sql #{db_string}" + set_firebird_env(abcs[env]) + db_string = firebird_db_string(abcs[env]) + sh "isql -i #{Rails.root}/db/structure.sql #{db_string}" else - raise "Task not supported by '#{abcs['test']['adapter']}'" + raise "Task not supported by '#{abcs[env]['adapter']}'" + end + end + end + + namespace :test do + # desc "Recreate the test database from the current schema.rb" + task :load => 'db:test:purge' do + ActiveRecord::Base.establish_connection(ActiveRecord::Base.configurations['test']) + ActiveRecord::Schema.verbose = false + db_namespace["schema:load"].invoke if ActiveRecord::Base.schema_format == :ruby + + begin + old_env, ENV['RAILS_ENV'] = ENV['RAILS_ENV'], 'test' + db_namespace["structure:load"].invoke if ActiveRecord::Base.schema_format == :sql + ensure + ENV['RAILS_ENV'] = old_env end + end + # desc "Recreate the test database from the current environment's database schema" + task :clone => %w(db:schema:dump db:test:load) + + # desc "Recreate the test databases from the structure.sql file" + task :clone_structure => [ "db:structure:dump", "db:test:load" ] + # desc "Empty the test database" task :purge => :environment do abcs = ActiveRecord::Base.configurations -- cgit v1.2.3 From bf6efa8d9cd0f7ce2eea7c1a0ff51f1d165cafc6 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Noe=CC=81=20Froidevaux?= Date: Sun, 13 Nov 2011 11:03:22 +0100 Subject: Fix pull request #3609 --- .../lib/active_record/connection_adapters/abstract/connection_pool.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb index 92dfb844db..0ec0576795 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/connection_pool.rb @@ -127,7 +127,7 @@ module ActiveRecord with_connection do |conn| conn.tables.each { |table| @tables[table] = true } - @tables[name] = !@tables.key?(name) && conn.table_exists?(name) + @tables[name] = conn.table_exists?(name) if !@tables.key?(name) end @tables[name] -- cgit v1.2.3 From 8f0085a483ebc6d416e902c3caff68f82934e091 Mon Sep 17 00:00:00 2001 From: Andre Arko Date: Sun, 13 Nov 2011 10:19:54 -1000 Subject: change tests to expect X-F-F over REMOTE_ADDR --- actionpack/test/dispatch/request_test.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index a611252b31..4658eeea17 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -36,7 +36,7 @@ class RequestTest < ActiveSupport::TestCase request = stub_request 'REMOTE_ADDR' => '1.2.3.4', 'HTTP_X_FORWARDED_FOR' => '3.4.5.6' - assert_equal '1.2.3.4', request.remote_ip + assert_equal '3.4.5.6', request.remote_ip request = stub_request 'REMOTE_ADDR' => '127.0.0.1', 'HTTP_X_FORWARDED_FOR' => '3.4.5.6' @@ -106,7 +106,7 @@ class RequestTest < ActiveSupport::TestCase request = stub_request 'REMOTE_ADDR' => '67.205.106.74,172.16.0.1', 'HTTP_X_FORWARDED_FOR' => '3.4.5.6' - assert_equal '67.205.106.74', request.remote_ip + assert_equal '3.4.5.6', request.remote_ip request = stub_request 'HTTP_X_FORWARDED_FOR' => 'unknown,67.205.106.73' assert_equal 'unknown', request.remote_ip -- cgit v1.2.3 From 2189bff732490aa842c88f1691993520fa1eb9ab Mon Sep 17 00:00:00 2001 From: Andre Arko Date: Sun, 13 Nov 2011 10:20:29 -1000 Subject: correctly raise IpSpoofAttackError message --- actionpack/lib/action_dispatch/middleware/remote_ip.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb index 3b813b03bb..3208256d56 100644 --- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb +++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb @@ -51,8 +51,8 @@ module ActionDispatch if check_ip && !forwarded_ips.include?(client_ip) # We don't know which came from the proxy, and which from the user raise IpSpoofAttackError, "IP spoofing attack?!" \ - "HTTP_CLIENT_IP=#{env['HTTP_CLIENT_IP'].inspect}" \ - "HTTP_X_FORWARDED_FOR=#{env['HTTP_X_FORWARDED_FOR'].inspect}" + "HTTP_CLIENT_IP=#{@env['HTTP_CLIENT_IP'].inspect}" \ + "HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}" end client_ip || forwarded_ips.last || remote_addrs.last -- cgit v1.2.3 From 2d063c6269a546c8bab4b36c027246f582bfaa13 Mon Sep 17 00:00:00 2001 From: Andre Arko Date: Sun, 13 Nov 2011 10:20:55 -1000 Subject: turns out the tests expect remote_addrs.first --- actionpack/lib/action_dispatch/middleware/remote_ip.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb index 3208256d56..5f81b639ae 100644 --- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb +++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb @@ -55,7 +55,7 @@ module ActionDispatch "HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}" end - client_ip || forwarded_ips.last || remote_addrs.last + client_ip || forwarded_ips.last || remote_addrs.first end protected -- cgit v1.2.3 From 9c4532bf74604ae1c52a44d1aa8c1022a312ff88 Mon Sep 17 00:00:00 2001 From: Andre Arko Date: Sun, 13 Nov 2011 10:22:12 -1000 Subject: remove ignored flag, fixes warnings --- actionpack/lib/action_dispatch/middleware/remote_ip.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb index 5f81b639ae..58e25aed5a 100644 --- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb +++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb @@ -19,7 +19,7 @@ module ActionDispatch @app = app @check_ip_spoofing = check_ip_spoofing if custom_proxies - custom_regexp = Regexp.new(custom_proxies, "i") + custom_regexp = Regexp.new(custom_proxies) @trusted_proxies = Regexp.union(TRUSTED_PROXIES, custom_regexp) else @trusted_proxies = TRUSTED_PROXIES -- cgit v1.2.3 From e0eb2292bb8dfce150cd7c1d9948beff14f0cd7c Mon Sep 17 00:00:00 2001 From: Guillermo Iguaran Date: Mon, 14 Nov 2011 06:25:03 -0500 Subject: Upgrade Sprockets to 2.1.0 This version brings bug fixes for performance and caching. --- actionpack/actionpack.gemspec | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/actionpack.gemspec b/actionpack/actionpack.gemspec index a446531a6b..9da9143de3 100644 --- a/actionpack/actionpack.gemspec +++ b/actionpack/actionpack.gemspec @@ -24,7 +24,7 @@ Gem::Specification.new do |s| s.add_dependency('rack', '~> 1.3.5') s.add_dependency('rack-test', '~> 0.6.1') s.add_dependency('journey', '~> 1.0.0') - s.add_dependency('sprockets', '~> 2.1.0.beta') + s.add_dependency('sprockets', '~> 2.1.0') s.add_dependency('erubis', '~> 2.7.0') s.add_development_dependency('tzinfo', '~> 0.3.29') -- cgit v1.2.3 From da02f792fe78535628f86bf983308800207b4225 Mon Sep 17 00:00:00 2001 From: Jon Leighton Date: Mon, 14 Nov 2011 11:28:12 +0000 Subject: Sync CHANGELOGs from 3-1-stable --- actionpack/CHANGELOG.md | 11 +++++++++++ activerecord/CHANGELOG.md | 21 +++++++++++++++++++++ railties/CHANGELOG.md | 10 ++++++++++ 3 files changed, 42 insertions(+) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 74711c0320..3e2dbe4e13 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -64,6 +64,15 @@ ## Rails 3.1.2 (unreleased) ## +* Ensure that the format isn't applied twice to the cache key, else it becomes impossible + to target with expire_action. + + *Christopher Meiklejohn* + +* Swallow error when can't unmarshall object from session. + + *Bruno Zanchet* + * Implement a workaround for a bug in ruby-1.9.3p0 where an error would be raised while attempting to convert a template from one encoding to another. @@ -76,6 +85,8 @@ *Jon Leighton* +* Ensure users upgrading from 3.0.x to 3.1.x will properly upgrade their flash object in session (issues #3298 and #2509) + ## Rails 3.1.1 (unreleased) ## * javascript_path and stylesheet_path now refer to /assets if asset pipelining diff --git a/activerecord/CHANGELOG.md b/activerecord/CHANGELOG.md index a79f4df570..65578c1dc9 100644 --- a/activerecord/CHANGELOG.md +++ b/activerecord/CHANGELOG.md @@ -67,6 +67,27 @@ ## Rails 3.1.2 (unreleased) ## +* Fix bug with PostgreSQLAdapter#indexes. When the search path has multiple schemas, spaces + were not being stripped from the schema names after the first. + + *Sean Kirby* + +* Preserve SELECT columns on the COUNT for finder_sql when possible. *GH 3503* + + *Justin Mazzi* + +* Reset prepared statement cache when schema changes impact statement results. *GH 3335* + + *Aaron Patterson* + +* Postgres: Do not attempt to deallocate a statement if the connection is no longer active. + + *Ian Leitch* + +* Prevent QueryCache leaking database connections. *GH 3243* + + *Mark J. Titorenko* + * Fix bug where building the conditions of a nested through association could potentially modify the conditions of the through and/or source association. If you have experienced bugs with conditions appearing in the wrong queries when using nested through associations, diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 0c59f59917..b05ac21b49 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -30,6 +30,16 @@ Plugins developers need to special case their initializers that are meant to be run in the assets group by adding :group => :assets. +## Rails 3.1.2 (unreleased) ## + +* Engines: don't blow up if db/seeds.rb is missing. + + *Jeremy Kemper* + +* `rails new foo --skip-test-unit` should not add the `:test` task to the rake default task. + *GH 2564* + + *José Valim* ## Rails 3.1.0 (August 30, 2011) ## -- cgit v1.2.3 From c7b84689a4d59b9f2c5773b6a8527adb68e04560 Mon Sep 17 00:00:00 2001 From: Jon Leighton Date: Mon, 14 Nov 2011 11:29:57 +0000 Subject: Add note about syncing CHANGELOGs --- RELEASING_RAILS.rdoc | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/RELEASING_RAILS.rdoc b/RELEASING_RAILS.rdoc index cbc9d0e1de..b7ebc4600c 100644 --- a/RELEASING_RAILS.rdoc +++ b/RELEASING_RAILS.rdoc @@ -81,6 +81,10 @@ You can review the commits for the 3.0.10 release like this: [aaron@higgins rails (3-0-10)]$ git log v3.0.9.. +If you're doing a stable branch release, you should also ensure that all of +the CHANGELOG entries in the stable branch are also synced to the master +branch. + === Update the RAILS_VERSION file to include the RC. === Release the gem. -- cgit v1.2.3 From 49cd6a0ff7a3f5b0e85707961c952bdc3b0a178f Mon Sep 17 00:00:00 2001 From: Guillermo Iguaran Date: Mon, 14 Nov 2011 07:07:38 -0500 Subject: Added therubyrhino to default Gemfile under JRuby --- railties/lib/rails/generators/app_base.rb | 1 + railties/test/generators/app_generator_test.rb | 7 +++++++ 2 files changed, 8 insertions(+) diff --git a/railties/lib/rails/generators/app_base.rb b/railties/lib/rails/generators/app_base.rb index 0a79d6e86e..3fbde0d989 100644 --- a/railties/lib/rails/generators/app_base.rb +++ b/railties/lib/rails/generators/app_base.rb @@ -199,6 +199,7 @@ module Rails group :assets do gem 'sass-rails', :git => 'git://github.com/rails/sass-rails.git' gem 'coffee-rails', :git => 'git://github.com/rails/coffee-rails.git' + #{"gem 'therubyrhino'\n" if defined?(JRUBY_VERSION)} gem 'uglifier', '>= 1.0.3' end GEMFILE diff --git a/railties/test/generators/app_generator_test.rb b/railties/test/generators/app_generator_test.rb index 955ed09361..a1bd2fbaa5 100644 --- a/railties/test/generators/app_generator_test.rb +++ b/railties/test/generators/app_generator_test.rb @@ -208,6 +208,13 @@ class AppGeneratorTest < Rails::Generators::TestCase assert_file "test/performance/browsing_test.rb" end + def test_inclusion_of_therubyrhino_under_jruby + if defined?(JRUBY_VERSION) + run_generator([destination_root]) + assert_file "Gemfile", /gem\s+["']therubyrhino["']$/ + end + end + def test_creation_of_a_test_directory run_generator assert_file 'test' -- cgit v1.2.3 From 4d8081b6e7ffa88b4fc74187fb1599ef9b27e6ed Mon Sep 17 00:00:00 2001 From: Jon Leighton Date: Mon, 14 Nov 2011 12:08:03 +0000 Subject: Add note about checking postgres tests before release --- RELEASING_RAILS.rdoc | 8 ++++++++ 1 file changed, 8 insertions(+) diff --git a/RELEASING_RAILS.rdoc b/RELEASING_RAILS.rdoc index b7ebc4600c..bd75b36b2a 100644 --- a/RELEASING_RAILS.rdoc +++ b/RELEASING_RAILS.rdoc @@ -25,6 +25,12 @@ for Rails. You can check the status of his tests here: Do not release with Red AWDwR tests. +=== Are the postgres tests green? If not, make them green + +Currently Travis CI doesn't run the Active Record postgres tests. They are +working to resolve this, but in the mean time, it is crucial to ensure that +the tests are still green before release. + === Do we have any git dependencies? If so, contact those authors. Having git dependencies indicates that we depend on unreleased code. @@ -60,6 +66,8 @@ for today: === Is Sam Ruby happy? If not, make him happy. +=== Are the postgres tests green? If not, make them green + === Contact the security team. CVE emails must be sent on this day. === Create a release branch. -- cgit v1.2.3 From ca3b4689375f0f65275407f28981c58a15d50193 Mon Sep 17 00:00:00 2001 From: Jon Leighton Date: Mon, 14 Nov 2011 12:23:43 +0000 Subject: Sync changelog entry --- actionpack/CHANGELOG.md | 2 ++ 1 file changed, 2 insertions(+) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 3e2dbe4e13..4c265c41d8 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -64,6 +64,8 @@ ## Rails 3.1.2 (unreleased) ## +* Upgrade sprockets dependency to ~> 2.1.0 + * Ensure that the format isn't applied twice to the cache key, else it becomes impossible to target with expire_action. -- cgit v1.2.3 From af64ac4e5ce8406137d5520fa88e8f652ab703e9 Mon Sep 17 00:00:00 2001 From: Oscar Del Ben Date: Mon, 14 Nov 2011 16:56:05 +0100 Subject: use any? instead of !empty? --- activemodel/lib/active_model/dirty.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activemodel/lib/active_model/dirty.rb b/activemodel/lib/active_model/dirty.rb index 166cccf161..026f077ee7 100644 --- a/activemodel/lib/active_model/dirty.rb +++ b/activemodel/lib/active_model/dirty.rb @@ -98,7 +98,7 @@ module ActiveModel # person.name = 'bob' # person.changed? # => true def changed? - !changed_attributes.empty? + changed_attributes.any? end # List of attributes with unsaved changes. -- cgit v1.2.3 From 156784fef1f66dddcdfb753839f2d37bbee41655 Mon Sep 17 00:00:00 2001 From: Jon Leighton Date: Mon, 14 Nov 2011 16:51:02 +0000 Subject: rake release should push the tag --- tasks/release.rb | 1 + 1 file changed, 1 insertion(+) diff --git a/tasks/release.rb b/tasks/release.rb index 33aaee5a4b..191c014f9f 100644 --- a/tasks/release.rb +++ b/tasks/release.rb @@ -116,6 +116,7 @@ namespace :all do task :tag do sh "git tag #{tag}" + sh "git push --tags" end task :release => %w(ensure_clean_state build commit tag push) -- cgit v1.2.3 From be777b30d581aa3488f1c7be14783b58a1567b82 Mon Sep 17 00:00:00 2001 From: Jon Leighton Date: Mon, 14 Nov 2011 17:08:24 +0000 Subject: Add a note to REALEASING_RAILS about testing the gem locally before releasing --- RELEASING_RAILS.rdoc | 9 +++++++++ 1 file changed, 9 insertions(+) diff --git a/RELEASING_RAILS.rdoc b/RELEASING_RAILS.rdoc index bd75b36b2a..1dfcfe2488 100644 --- a/RELEASING_RAILS.rdoc +++ b/RELEASING_RAILS.rdoc @@ -95,6 +95,14 @@ branch. === Update the RAILS_VERSION file to include the RC. +=== Build and test the gem. + +Run `rake install` to generate the gems and install them locally. Then try +generating a new app and ensure that nothing explodes. + +This will stop you looking silly when you push an RC to rubygems.org and then +realise it is broken. + === Release the gem. IMPORTANT: Due to YAML parse problems on the rubygems.org server, it is safest @@ -162,6 +170,7 @@ Today, do this stuff in this order: * Apply security patches to the release branch * Update CHANGELOG with security fixes. * Update RAILS_VERSION to remove the rc +* Build and test the gem * Release the gems * Email security lists * Email general announcement lists -- cgit v1.2.3 From 9fa329b7544b15cdf5751d518e380abc82468df0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Mon, 14 Nov 2011 20:12:17 +0100 Subject: Speed up attribute invocation by checking if both name and calls are compilable. --- activemodel/lib/active_model/attribute_methods.rb | 59 ++++++++++++---------- .../lib/active_record/attribute_methods/read.rb | 2 +- .../lib/active_record/attribute_methods/write.rb | 2 +- 3 files changed, 34 insertions(+), 29 deletions(-) diff --git a/activemodel/lib/active_model/attribute_methods.rb b/activemodel/lib/active_model/attribute_methods.rb index ef0b95424e..e69cb5c459 100644 --- a/activemodel/lib/active_model/attribute_methods.rb +++ b/activemodel/lib/active_model/attribute_methods.rb @@ -57,7 +57,8 @@ module ActiveModel module AttributeMethods extend ActiveSupport::Concern - COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?=]?\z/ + NAME_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?=]?\z/ + CALL_COMPILABLE_REGEXP = /\A[a-zA-Z_]\w*[!?]?\z/ included do class_attribute :attribute_method_matchers, :instance_writer => false @@ -112,7 +113,7 @@ module ActiveModel # If we can compile the method name, do it. Otherwise use define_method. # This is an important *optimization*, please don't change it. define_method # has slower dispatch and consumes more memory. - if name =~ COMPILABLE_REGEXP + if name =~ NAME_COMPILABLE_REGEXP sing.class_eval <<-RUBY, __FILE__, __LINE__ + 1 def #{name}; #{value.nil? ? 'nil' : value.to_s.inspect}; end RUBY @@ -240,18 +241,7 @@ module ActiveModel attribute_method_matchers.each do |matcher| matcher_new = matcher.method_name(new_name).to_s matcher_old = matcher.method_name(old_name).to_s - - if matcher_new =~ COMPILABLE_REGEXP && matcher_old =~ COMPILABLE_REGEXP - module_eval <<-RUBY, __FILE__, __LINE__ + 1 - def #{matcher_new}(*args) - send(:#{matcher_old}, *args) - end - RUBY - else - define_method(matcher_new) do |*args| - send(matcher_old, *args) - end - end + define_optimized_call self, matcher_new, matcher_old end end @@ -293,17 +283,7 @@ module ActiveModel if respond_to?(generate_method) send(generate_method, attr_name) else - if method_name =~ COMPILABLE_REGEXP - defn = "def #{method_name}(*args)" - else - defn = "define_method(:'#{method_name}') do |*args|" - end - - generated_attribute_methods.module_eval <<-RUBY, __FILE__, __LINE__ + 1 - #{defn} - send(:#{matcher.method_missing_target}, '#{attr_name}', *args) - end - RUBY + define_optimized_call generated_attribute_methods, method_name, matcher.method_missing_target, attr_name.to_s end end end @@ -342,11 +322,11 @@ module ActiveModel # used to alleviate the GC, which ultimately also speeds up the app # significantly (in our case our test suite finishes 10% faster with # this cache). - def attribute_method_matchers_cache + def attribute_method_matchers_cache #:nodoc: @attribute_method_matchers_cache ||= {} end - def attribute_method_matcher(method_name) + def attribute_method_matcher(method_name) #:nodoc: if attribute_method_matchers_cache.key?(method_name) attribute_method_matchers_cache[method_name] else @@ -359,6 +339,31 @@ module ActiveModel end end + # Define a method `name` in `mod` that dispatches to `send` + # using the given `extra` args. This fallbacks `define_method` + # and `send` if the given names cannot be compiled. + def define_optimized_call(mod, name, send, *extra) #:nodoc: + if name =~ NAME_COMPILABLE_REGEXP + defn = "def #{name}(*args)" + else + defn = "define_method(:'#{name}') do |*args|" + end + + extra = (extra.map(&:inspect) << "*args").join(", ") + + if send =~ CALL_COMPILABLE_REGEXP + target = "#{send}(#{extra})" + else + target = "send(:'#{send}', #{extra})" + end + + mod.module_eval <<-RUBY, __FILE__, __LINE__ + 1 + #{defn} + #{target} + end + RUBY + end + class AttributeMethodMatcher attr_reader :prefix, :suffix, :method_missing_target diff --git a/activerecord/lib/active_record/attribute_methods/read.rb b/activerecord/lib/active_record/attribute_methods/read.rb index 4174e4da09..4a5afcd585 100644 --- a/activerecord/lib/active_record/attribute_methods/read.rb +++ b/activerecord/lib/active_record/attribute_methods/read.rb @@ -77,7 +77,7 @@ module ActiveRecord # # The second, slower, branch is necessary to support instances where the database # returns columns with extra stuff in (like 'my_column(omg)'). - if method_name =~ ActiveModel::AttributeMethods::COMPILABLE_REGEXP + if method_name =~ ActiveModel::AttributeMethods::NAME_COMPILABLE_REGEXP generated_attribute_methods.module_eval <<-STR, __FILE__, __LINE__ def _#{method_name} #{access_code} diff --git a/activerecord/lib/active_record/attribute_methods/write.rb b/activerecord/lib/active_record/attribute_methods/write.rb index e9cdb130db..eb585ee906 100644 --- a/activerecord/lib/active_record/attribute_methods/write.rb +++ b/activerecord/lib/active_record/attribute_methods/write.rb @@ -10,7 +10,7 @@ module ActiveRecord module ClassMethods protected def define_method_attribute=(attr_name) - if attr_name =~ ActiveModel::AttributeMethods::COMPILABLE_REGEXP + if attr_name =~ ActiveModel::AttributeMethods::NAME_COMPILABLE_REGEXP generated_attribute_methods.module_eval("def #{attr_name}=(new_value); write_attribute('#{attr_name}', new_value); end", __FILE__, __LINE__) else generated_attribute_methods.send(:define_method, "#{attr_name}=") do |new_value| -- cgit v1.2.3 From 00a0a4ddebe0160f851d28e29d5fb7e8e7a2a5dc Mon Sep 17 00:00:00 2001 From: Andre Arko Date: Mon, 14 Nov 2011 11:20:20 -1000 Subject: cleaner names --- actionpack/lib/action_dispatch/middleware/remote_ip.rb | 12 ++++++------ 1 file changed, 6 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb index 58e25aed5a..446fcce823 100644 --- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb +++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb @@ -13,16 +13,16 @@ module ActionDispatch )\. }x - attr_reader :check_ip_spoofing, :trusted_proxies + attr_reader :check_ip, :proxies def initialize(app, check_ip_spoofing = true, custom_proxies = nil) @app = app - @check_ip_spoofing = check_ip_spoofing + @check_ip = check_ip_spoofing if custom_proxies custom_regexp = Regexp.new(custom_proxies) - @trusted_proxies = Regexp.union(TRUSTED_PROXIES, custom_regexp) + @proxies = Regexp.union(TRUSTED_PROXIES, custom_regexp) else - @trusted_proxies = TRUSTED_PROXIES + @proxies = TRUSTED_PROXIES end end @@ -47,7 +47,7 @@ module ActionDispatch forwarded_ips = ips_from('HTTP_X_FORWARDED_FOR') remote_addrs = ips_from('REMOTE_ADDR') - check_ip = client_ip && @middleware.check_ip_spoofing + check_ip = client_ip && @middleware.check_ip if check_ip && !forwarded_ips.include?(client_ip) # We don't know which came from the proxy, and which from the user raise IpSpoofAttackError, "IP spoofing attack?!" \ @@ -62,7 +62,7 @@ module ActionDispatch def ips_from(header) ips = @env[header] ? @env[header].strip.split(/[,\s]+/) : [] - ips.reject{|ip| ip =~ @middleware.trusted_proxies } + ips.reject{|ip| ip =~ @middleware.proxies } end end -- cgit v1.2.3 From cda1a5d5fe002ca92aca01586e8a60439605f960 Mon Sep 17 00:00:00 2001 From: Andre Arko Date: Mon, 14 Nov 2011 11:20:57 -1000 Subject: memoize the relatively expensive remote IP code --- actionpack/lib/action_dispatch/middleware/remote_ip.rb | 8 +++++++- 1 file changed, 7 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb index 446fcce823..ee0d19a50d 100644 --- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb +++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb @@ -42,7 +42,7 @@ module ActionDispatch # HTTP_X_FORWARDED_FOR may be a comma-delimited list in the case of # multiple chained proxies. The last address which is not a known proxy # will be the originating IP. - def to_s + def calculate_ip client_ip = @env['HTTP_CLIENT_IP'] forwarded_ips = ips_from('HTTP_X_FORWARDED_FOR') remote_addrs = ips_from('REMOTE_ADDR') @@ -58,6 +58,12 @@ module ActionDispatch client_ip || forwarded_ips.last || remote_addrs.first end + def to_s + return @ip if @calculated_ip + @calculated_ip = true + @ip = calculate_ip + end + protected def ips_from(header) -- cgit v1.2.3 From 4f2bf6491cbc482d25a9357c2eb7fc8047d4f12e Mon Sep 17 00:00:00 2001 From: Andre Arko Date: Mon, 14 Nov 2011 11:22:44 -1000 Subject: Return the calculated remote_ip or ip This was an especially nasty bug introduced in 317f4e2, by the way that an instance of GetIp is not nil, but GetIp#to_s could sometimes return nil. Gross, huh? --- actionpack/lib/action_dispatch/http/request.rb | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 69ca050d0c..b10f6b48c7 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -157,7 +157,8 @@ module ActionDispatch # Originating IP address, usually set by the RemoteIp middleware. def remote_ip - @remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s + # Coerce the remote_ip object into a string, because to_s could return nil + @remote_ip ||= @env["action_dispatch.remote_ip"].to_s || ip end # Returns the unique request id, which is based off either the X-Request-Id header that can -- cgit v1.2.3 From b8c85de62004868a34a27e58731f2a9e37aeebd0 Mon Sep 17 00:00:00 2001 From: Andre Arko Date: Mon, 14 Nov 2011 12:29:51 -1000 Subject: add test for bug fixed in 4f2bf64 --- actionpack/test/dispatch/request_test.rb | 5 +++++ 1 file changed, 5 insertions(+) diff --git a/actionpack/test/dispatch/request_test.rb b/actionpack/test/dispatch/request_test.rb index 4658eeea17..4d805464c2 100644 --- a/actionpack/test/dispatch/request_test.rb +++ b/actionpack/test/dispatch/request_test.rb @@ -89,6 +89,11 @@ class RequestTest < ActiveSupport::TestCase assert_equal '9.9.9.9', request.remote_ip end + test "remote ip when the remote ip middleware returns nil" do + request = stub_request 'REMOTE_ADDR' => '127.0.0.1' + assert_equal '127.0.0.1', request.remote_ip + end + test "remote ip with user specified trusted proxies" do @trusted_proxies = /^67\.205\.106\.73$/i -- cgit v1.2.3 From d743954792ccf5975a11ee88cdd690e8f1915728 Mon Sep 17 00:00:00 2001 From: Andre Arko Date: Mon, 14 Nov 2011 16:43:21 -1000 Subject: GetIp#to_s should never return nil. That's icky. --- actionpack/lib/action_dispatch/http/request.rb | 5 ++--- actionpack/lib/action_dispatch/middleware/remote_ip.rb | 9 ++++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index b10f6b48c7..0e8bd0bd6d 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -155,10 +155,9 @@ module ActionDispatch @ip ||= super end - # Originating IP address, usually set by the RemoteIp middleware. + # Originating IP address from the RemoteIp middleware. def remote_ip - # Coerce the remote_ip object into a string, because to_s could return nil - @remote_ip ||= @env["action_dispatch.remote_ip"].to_s || ip + @remote_ip ||= @env["action_dispatch.remote_ip"] end # Returns the unique request id, which is based off either the X-Request-Id header that can diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb index ee0d19a50d..77aa4e743e 100644 --- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb +++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb @@ -55,7 +55,10 @@ module ActionDispatch "HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}" end - client_ip || forwarded_ips.last || remote_addrs.first + not_proxy = client_ip || forwarded_ips.last || remote_addrs.first + + # Return first REMOTE_ADDR if there are no other options + not_proxy || ips_from('REMOTE_ADDR', :all).first end def to_s @@ -66,9 +69,9 @@ module ActionDispatch protected - def ips_from(header) + def ips_from(header, allow_proxies = false) ips = @env[header] ? @env[header].strip.split(/[,\s]+/) : [] - ips.reject{|ip| ip =~ @middleware.proxies } + allow_proxies ? ips : ips.reject{|ip| ip =~ @middleware.proxies } end end -- cgit v1.2.3 From e1b79c56bef43d858fc59dbe7506039414c9e4ed Mon Sep 17 00:00:00 2001 From: Gabriel Sobrinho Date: Tue, 1 Nov 2011 08:40:30 -0200 Subject: Failing test case for issue #3483 --- activerecord/test/cases/finder_test.rb | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 69754d23b9..688679f081 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -374,6 +374,10 @@ class FinderTest < ActiveRecord::TestCase assert_equal [1], Comment.find(:all, :conditions => { :id => 1..1, :post_id => 1..10 }).map(&:id).sort end + def test_find_on_hash_conditions_with_array_of_integers_and_ranges + assert_equal [1,2,3], Comment.find(:all, :conditions => { :post_id => [1..2, 3, 4, 5..10]}).map(&:id).sort + end + def test_find_on_multiple_hash_conditions assert Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => false }) assert_raise(ActiveRecord::RecordNotFound) { Topic.find(1, :conditions => { :author_name => "David", :title => "The First Topic", :replies_count => 1, :approved => true }) } -- cgit v1.2.3 From 63a22ca6169f06d10cd843ea3a75df6498fdcf7d Mon Sep 17 00:00:00 2001 From: Ryan Naughton Date: Mon, 14 Nov 2011 21:43:27 -0600 Subject: Fixes issue #3483, regarding using a mixture of ranges and discrete values in find conditions. Paired with Joey Schoblaska. --- .../lib/active_record/relation/predicate_builder.rb | 14 ++++++++------ activerecord/test/cases/finder_test.rb | 2 +- 2 files changed, 9 insertions(+), 7 deletions(-) diff --git a/activerecord/lib/active_record/relation/predicate_builder.rb b/activerecord/lib/active_record/relation/predicate_builder.rb index 7e8ddd1b5d..af167dc59b 100644 --- a/activerecord/lib/active_record/relation/predicate_builder.rb +++ b/activerecord/lib/active_record/relation/predicate_builder.rb @@ -22,21 +22,23 @@ module ActiveRecord value = value.select(value.klass.arel_table[value.klass.primary_key]) if value.select_values.empty? attribute.in(value.arel.ast) when Array, ActiveRecord::Associations::CollectionProxy - values = value.to_a.map { |x| - x.is_a?(ActiveRecord::Base) ? x.id : x - } + values = value.to_a.map {|x| x.is_a?(ActiveRecord::Base) ? x.id : x} + ranges, values = values.partition {|value| value.is_a?(Range) || value.is_a?(Arel::Relation)} + + array_predicates = ranges.map {|range| attribute.in(range)} if values.include?(nil) values = values.compact if values.empty? - attribute.eq nil + array_predicates << attribute.eq(nil) else - attribute.in(values.compact).or attribute.eq(nil) + array_predicates << attribute.in(values.compact).or(attribute.eq(nil)) end else - attribute.in(values) + array_predicates << attribute.in(values) end + array_predicates.inject {|composite, predicate| composite.or(predicate)} when Range, Arel::Relation attribute.in(value) when ActiveRecord::Base diff --git a/activerecord/test/cases/finder_test.rb b/activerecord/test/cases/finder_test.rb index 688679f081..05c4b15407 100644 --- a/activerecord/test/cases/finder_test.rb +++ b/activerecord/test/cases/finder_test.rb @@ -375,7 +375,7 @@ class FinderTest < ActiveRecord::TestCase end def test_find_on_hash_conditions_with_array_of_integers_and_ranges - assert_equal [1,2,3], Comment.find(:all, :conditions => { :post_id => [1..2, 3, 4, 5..10]}).map(&:id).sort + assert_equal [1,2,3,5,6,7,8,9], Comment.find(:all, :conditions => {:id => [1..2, 3, 5, 6..8, 9]}).map(&:id).sort end def test_find_on_multiple_hash_conditions -- cgit v1.2.3 From 76b6027cd80685617b5bf5ceb986fd5dc8e213b2 Mon Sep 17 00:00:00 2001 From: Arun Agrawal Date: Fri, 11 Nov 2011 15:34:47 +0530 Subject: Unused variable removed --- railties/test/application/console_test.rb | 1 - railties/test/application/rackup_test.rb | 2 +- 2 files changed, 1 insertion(+), 2 deletions(-) diff --git a/railties/test/application/console_test.rb b/railties/test/application/console_test.rb index b3745f194e..2073c780bf 100644 --- a/railties/test/application/console_test.rb +++ b/railties/test/application/console_test.rb @@ -71,7 +71,6 @@ class ConsoleTest < Test::Unit::TestCase assert !User.new.respond_to?(:age) silence_stream(STDOUT) { irb_context.reload! } - session = irb_context.new_session assert User.new.respond_to?(:age) end diff --git a/railties/test/application/rackup_test.rb b/railties/test/application/rackup_test.rb index ff9cdcadc7..86e1995def 100644 --- a/railties/test/application/rackup_test.rb +++ b/railties/test/application/rackup_test.rb @@ -6,7 +6,7 @@ module ApplicationTests def rackup require "rack" - app, options = Rack::Builder.parse_file("#{app_path}/config.ru") + app, _ = Rack::Builder.parse_file("#{app_path}/config.ru") app end -- cgit v1.2.3 From 83bf0b626cf2134260903e57d74f67de57384073 Mon Sep 17 00:00:00 2001 From: Vijay Dev Date: Wed, 16 Nov 2011 02:36:36 +0530 Subject: refactor test_multiple_of --- activesupport/test/core_ext/integer_ext_test.rb | 7 +++---- 1 file changed, 3 insertions(+), 4 deletions(-) diff --git a/activesupport/test/core_ext/integer_ext_test.rb b/activesupport/test/core_ext/integer_ext_test.rb index fe8c7eb224..b1f5f70a70 100644 --- a/activesupport/test/core_ext/integer_ext_test.rb +++ b/activesupport/test/core_ext/integer_ext_test.rb @@ -2,6 +2,8 @@ require 'abstract_unit' require 'active_support/core_ext/integer' class IntegerExtTest < Test::Unit::TestCase + PRIME = 22953686867719691230002707821868552601124472329079 + def test_multiple_of [ -7, 0, 7, 14 ].each { |i| assert i.multiple_of?(7) } [ -7, 7, 14 ].each { |i| assert ! i.multiple_of?(6) } @@ -11,10 +13,7 @@ class IntegerExtTest < Test::Unit::TestCase assert !5.multiple_of?(0) # test with a prime - assert !22953686867719691230002707821868552601124472329079.multiple_of?(2) - assert !22953686867719691230002707821868552601124472329079.multiple_of?(3) - assert !22953686867719691230002707821868552601124472329079.multiple_of?(5) - assert !22953686867719691230002707821868552601124472329079.multiple_of?(7) + [2, 3, 5, 7].each { |i| assert !PRIME.multiple_of?(i) } end def test_ordinalize -- cgit v1.2.3 From 8d1a2b3ecde5a8745b3eaab4763a71d80ca3441f Mon Sep 17 00:00:00 2001 From: Jon Leighton Date: Tue, 15 Nov 2011 22:47:18 +0000 Subject: Revert "Merge pull request #3640 from indirect/remote_ip" This reverts commit 6491aadc525b8703708e0fd0fbf05bd436a47801, reversing changes made to 83bf0b626cf2134260903e57d74f67de57384073. See https://github.com/rails/rails/pull/3640#issuecomment-2752761 for explanation. --- actionpack/lib/action_dispatch/http/request.rb | 5 +++-- actionpack/lib/action_dispatch/middleware/remote_ip.rb | 9 +++------ 2 files changed, 6 insertions(+), 8 deletions(-) diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 0e8bd0bd6d..b10f6b48c7 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -155,9 +155,10 @@ module ActionDispatch @ip ||= super end - # Originating IP address from the RemoteIp middleware. + # Originating IP address, usually set by the RemoteIp middleware. def remote_ip - @remote_ip ||= @env["action_dispatch.remote_ip"] + # Coerce the remote_ip object into a string, because to_s could return nil + @remote_ip ||= @env["action_dispatch.remote_ip"].to_s || ip end # Returns the unique request id, which is based off either the X-Request-Id header that can diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb index 77aa4e743e..ee0d19a50d 100644 --- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb +++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb @@ -55,10 +55,7 @@ module ActionDispatch "HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}" end - not_proxy = client_ip || forwarded_ips.last || remote_addrs.first - - # Return first REMOTE_ADDR if there are no other options - not_proxy || ips_from('REMOTE_ADDR', :all).first + client_ip || forwarded_ips.last || remote_addrs.first end def to_s @@ -69,9 +66,9 @@ module ActionDispatch protected - def ips_from(header, allow_proxies = false) + def ips_from(header) ips = @env[header] ? @env[header].strip.split(/[,\s]+/) : [] - allow_proxies ? ips : ips.reject{|ip| ip =~ @middleware.proxies } + ips.reject{|ip| ip =~ @middleware.proxies } end end -- cgit v1.2.3 From bc00514b6236d701d3e660c4ec365a767a070a91 Mon Sep 17 00:00:00 2001 From: rpq Date: Tue, 15 Nov 2011 23:20:21 -0500 Subject: added comma --- railties/guides/source/asset_pipeline.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/asset_pipeline.textile b/railties/guides/source/asset_pipeline.textile index 6ff5e87b6d..3681501293 100644 --- a/railties/guides/source/asset_pipeline.textile +++ b/railties/guides/source/asset_pipeline.textile @@ -438,7 +438,7 @@ location ~ ^/assets/ { } -When files are precompiled, Sprockets also creates a "gzipped":http://en.wikipedia.org/wiki/Gzip (.gz) version of your assets. Web servers are typically configured to use a moderate compression ratio as a compromise, but since precompilation happens once Sprockets uses the maximum compression ratio, thus reducing the size of the data transfer to the minimum. On the other hand, web servers can be configured to serve compressed content directly from disk, rather than deflating non-compressed files themselves. +When files are precompiled, Sprockets also creates a "gzipped":http://en.wikipedia.org/wiki/Gzip (.gz) version of your assets. Web servers are typically configured to use a moderate compression ratio as a compromise, but since precompilation happens once, Sprockets uses the maximum compression ratio, thus reducing the size of the data transfer to the minimum. On the other hand, web servers can be configured to serve compressed content directly from disk, rather than deflating non-compressed files themselves. Nginx is able to do this automatically enabling +gzip_static+: -- cgit v1.2.3 From 21c5a0a104bf04e3e7b63aa5f0104f1c666655c3 Mon Sep 17 00:00:00 2001 From: Daniel Dyba Date: Sat, 16 Jul 2011 16:54:03 -0700 Subject: Changed Commands module to RailsCommands. This is to avoid a conflict that occurs when you add Rake to your Gemfile. There is a Commands Object in Rake that conflicts with the Commands module in plugin.rb. See rails issue #1866. --- railties/lib/rails/commands/plugin.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/railties/lib/rails/commands/plugin.rb b/railties/lib/rails/commands/plugin.rb index 4df849447d..aa4551c806 100644 --- a/railties/lib/rails/commands/plugin.rb +++ b/railties/lib/rails/commands/plugin.rb @@ -274,7 +274,7 @@ end # load default environment and parse arguments require 'optparse' -module Commands +module RailsCommands class Plugin attr_reader :environment, :script_name def initialize @@ -327,7 +327,7 @@ module Commands command = general.shift if command =~ /^(install|remove)$/ - command = Commands.const_get(command.capitalize).new(self) + command = RailsCommands.const_get(command.capitalize).new(self) command.parse!(sub) else puts "Unknown command: #{command}" unless command.blank? @@ -539,4 +539,4 @@ class RecursiveHTTPFetcher end end -Commands::Plugin.parse! +RailsCommands::Plugin.parse! -- cgit v1.2.3 From 325abe9fbad58b6a0e119a678039b195247a57bc Mon Sep 17 00:00:00 2001 From: Daniel Dyba Date: Sun, 17 Jul 2011 23:09:51 -0700 Subject: Substituted RailsCommands for Rails::Commands --- railties/lib/rails/commands/plugin.rb | 320 +++++++++++++++++----------------- 1 file changed, 161 insertions(+), 159 deletions(-) diff --git a/railties/lib/rails/commands/plugin.rb b/railties/lib/rails/commands/plugin.rb index aa4551c806..c99a2e6685 100644 --- a/railties/lib/rails/commands/plugin.rb +++ b/railties/lib/rails/commands/plugin.rb @@ -274,198 +274,200 @@ end # load default environment and parse arguments require 'optparse' -module RailsCommands - class Plugin - attr_reader :environment, :script_name - def initialize - @environment = RailsEnvironment.default - @rails_root = RailsEnvironment.default.root - @script_name = File.basename($0) - end +module Rails + module Commands + class Plugin + attr_reader :environment, :script_name + def initialize + @environment = RailsEnvironment.default + @rails_root = RailsEnvironment.default.root + @script_name = File.basename($0) + end - def environment=(value) - @environment = value - RailsEnvironment.default = value - end + def environment=(value) + @environment = value + RailsEnvironment.default = value + end - def options - OptionParser.new do |o| - o.set_summary_indent(' ') - o.banner = "Usage: plugin [OPTIONS] command" - o.define_head "Rails plugin manager." + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: plugin [OPTIONS] command" + o.define_head "Rails plugin manager." - o.separator "" - o.separator "GENERAL OPTIONS" + o.separator "" + o.separator "GENERAL OPTIONS" - o.on("-r", "--root=DIR", String, - "Set an explicit rails app directory.", - "Default: #{@rails_root}") { |rails_root| @rails_root = rails_root; self.environment = RailsEnvironment.new(@rails_root) } + o.on("-r", "--root=DIR", String, + "Set an explicit rails app directory.", + "Default: #{@rails_root}") { |rails_root| @rails_root = rails_root; self.environment = RailsEnvironment.new(@rails_root) } - o.on("-v", "--verbose", "Turn on verbose output.") { |verbose| $verbose = verbose } - o.on("-h", "--help", "Show this help message.") { puts o; exit } + o.on("-v", "--verbose", "Turn on verbose output.") { |verbose| $verbose = verbose } + o.on("-h", "--help", "Show this help message.") { puts o; exit } - o.separator "" - o.separator "COMMANDS" + o.separator "" + o.separator "COMMANDS" - o.separator " install Install plugin(s) from known repositories or URLs." - o.separator " remove Uninstall plugins." + o.separator " install Install plugin(s) from known repositories or URLs." + o.separator " remove Uninstall plugins." - o.separator "" - o.separator "EXAMPLES" - o.separator " Install a plugin from a subversion URL:" - o.separator " #{@script_name} plugin install http://example.com/my_svn_plugin\n" - o.separator " Install a plugin from a git URL:" - o.separator " #{@script_name} plugin install git://github.com/SomeGuy/my_awesome_plugin.git\n" - o.separator " Install a plugin and add a svn:externals entry to vendor/plugins" - o.separator " #{@script_name} plugin install -x my_svn_plugin\n" + o.separator "" + o.separator "EXAMPLES" + o.separator " Install a plugin from a subversion URL:" + o.separator " #{@script_name} plugin install http://example.com/my_svn_plugin\n" + o.separator " Install a plugin from a git URL:" + o.separator " #{@script_name} plugin install git://github.com/SomeGuy/my_awesome_plugin.git\n" + o.separator " Install a plugin and add a svn:externals entry to vendor/plugins" + o.separator " #{@script_name} plugin install -x my_svn_plugin\n" + end end - end - def parse!(args=ARGV) - general, sub = split_args(args) - options.parse!(general) + def parse!(args=ARGV) + general, sub = split_args(args) + options.parse!(general) - command = general.shift - if command =~ /^(install|remove)$/ - command = RailsCommands.const_get(command.capitalize).new(self) - command.parse!(sub) - else - puts "Unknown command: #{command}" unless command.blank? - puts options - exit 1 + command = general.shift + if command =~ /^(install|remove)$/ + command = Commands.const_get(command.capitalize).new(self) + command.parse!(sub) + else + puts "Unknown command: #{command}" unless command.blank? + puts options + exit 1 + end end - end - def split_args(args) - left = [] - left << args.shift while args[0] and args[0] =~ /^-/ - left << args.shift if args[0] - [left, args] - end - - def self.parse!(args=ARGV) - Plugin.new.parse!(args) - end - end + def split_args(args) + left = [] + left << args.shift while args[0] and args[0] =~ /^-/ + left << args.shift if args[0] + [left, args] + end - class Install - def initialize(base_command) - @base_command = base_command - @method = :http - @options = { :quiet => false, :revision => nil, :force => false } + def self.parse!(args=ARGV) + Plugin.new.parse!(args) + end end - def options - OptionParser.new do |o| - o.set_summary_indent(' ') - o.banner = "Usage: #{@base_command.script_name} install PLUGIN [PLUGIN [PLUGIN] ...]" - o.define_head "Install one or more plugins." - o.separator "" - o.separator "Options:" - o.on( "-x", "--externals", - "Use svn:externals to grab the plugin.", - "Enables plugin updates and plugin versioning.") { |v| @method = :externals } - o.on( "-o", "--checkout", - "Use svn checkout to grab the plugin.", - "Enables updating but does not add a svn:externals entry.") { |v| @method = :checkout } - o.on( "-e", "--export", - "Use svn export to grab the plugin.", - "Exports the plugin, allowing you to check it into your local repository. Does not enable updates or add an svn:externals entry.") { |v| @method = :export } - o.on( "-q", "--quiet", - "Suppresses the output from installation.", - "Ignored if -v is passed (rails plugin -v install ...)") { |v| @options[:quiet] = true } - o.on( "-r REVISION", "--revision REVISION", - "Checks out the given revision from subversion or git.", - "Ignored if subversion/git is not used.") { |v| @options[:revision] = v } - o.on( "-f", "--force", - "Reinstalls a plugin if it's already installed.") { |v| @options[:force] = true } - o.separator "" - o.separator "You can specify plugin names as given in 'plugin list' output or absolute URLs to " - o.separator "a plugin repository." + class Install + def initialize(base_command) + @base_command = base_command + @method = :http + @options = { :quiet => false, :revision => nil, :force => false } end - end - def determine_install_method - best = @base_command.environment.best_install_method - @method = :http if best == :http and @method == :export - case - when (best == :http and @method != :http) - msg = "Cannot install using subversion because `svn' cannot be found in your PATH" - when (best == :export and (@method != :export and @method != :http)) - msg = "Cannot install using #{@method} because this project is not under subversion." - when (best != :externals and @method == :externals) - msg = "Cannot install using externals because vendor/plugins is not under subversion." + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{@base_command.script_name} install PLUGIN [PLUGIN [PLUGIN] ...]" + o.define_head "Install one or more plugins." + o.separator "" + o.separator "Options:" + o.on( "-x", "--externals", + "Use svn:externals to grab the plugin.", + "Enables plugin updates and plugin versioning.") { |v| @method = :externals } + o.on( "-o", "--checkout", + "Use svn checkout to grab the plugin.", + "Enables updating but does not add a svn:externals entry.") { |v| @method = :checkout } + o.on( "-e", "--export", + "Use svn export to grab the plugin.", + "Exports the plugin, allowing you to check it into your local repository. Does not enable updates or add an svn:externals entry.") { |v| @method = :export } + o.on( "-q", "--quiet", + "Suppresses the output from installation.", + "Ignored if -v is passed (rails plugin -v install ...)") { |v| @options[:quiet] = true } + o.on( "-r REVISION", "--revision REVISION", + "Checks out the given revision from subversion or git.", + "Ignored if subversion/git is not used.") { |v| @options[:revision] = v } + o.on( "-f", "--force", + "Reinstalls a plugin if it's already installed.") { |v| @options[:force] = true } + o.separator "" + o.separator "You can specify plugin names as given in 'plugin list' output or absolute URLs to " + o.separator "a plugin repository." + end end - if msg - puts msg - exit 1 + + def determine_install_method + best = @base_command.environment.best_install_method + @method = :http if best == :http and @method == :export + case + when (best == :http and @method != :http) + msg = "Cannot install using subversion because `svn' cannot be found in your PATH" + when (best == :export and (@method != :export and @method != :http)) + msg = "Cannot install using #{@method} because this project is not under subversion." + when (best != :externals and @method == :externals) + msg = "Cannot install using externals because vendor/plugins is not under subversion." + end + if msg + puts msg + exit 1 + end + @method end - @method - end - def parse!(args) - options.parse!(args) - if args.blank? - puts options + def parse!(args) + options.parse!(args) + if args.blank? + puts options + exit 1 + end + environment = @base_command.environment + install_method = determine_install_method + puts "Plugins will be installed using #{install_method}" if $verbose + args.each do |name| + ::Plugin.find(name).install(install_method, @options) + end + rescue StandardError => e + puts "Plugin not found: #{args.inspect}" + puts e.inspect if $verbose exit 1 end - environment = @base_command.environment - install_method = determine_install_method - puts "Plugins will be installed using #{install_method}" if $verbose - args.each do |name| - ::Plugin.find(name).install(install_method, @options) - end - rescue StandardError => e - puts "Plugin not found: #{args.inspect}" - puts e.inspect if $verbose - exit 1 - end - end - - class Remove - def initialize(base_command) - @base_command = base_command end - def options - OptionParser.new do |o| - o.set_summary_indent(' ') - o.banner = "Usage: #{@base_command.script_name} remove name [name]..." - o.define_head "Remove plugins." + class Remove + def initialize(base_command) + @base_command = base_command end - end - def parse!(args) - options.parse!(args) - if args.blank? - puts options - exit 1 + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{@base_command.script_name} remove name [name]..." + o.define_head "Remove plugins." + end end - root = @base_command.environment.root - args.each do |name| - ::Plugin.new(name).uninstall + + def parse!(args) + options.parse!(args) + if args.blank? + puts options + exit 1 + end + root = @base_command.environment.root + args.each do |name| + ::Plugin.new(name).uninstall + end end end - end - class Info - def initialize(base_command) - @base_command = base_command - end + class Info + def initialize(base_command) + @base_command = base_command + end - def options - OptionParser.new do |o| - o.set_summary_indent(' ') - o.banner = "Usage: #{@base_command.script_name} info name [name]..." - o.define_head "Shows plugin info at {url}/about.yml." + def options + OptionParser.new do |o| + o.set_summary_indent(' ') + o.banner = "Usage: #{@base_command.script_name} info name [name]..." + o.define_head "Shows plugin info at {url}/about.yml." + end end - end - def parse!(args) - options.parse!(args) - args.each do |name| - puts ::Plugin.find(name).info - puts + def parse!(args) + options.parse!(args) + args.each do |name| + puts ::Plugin.find(name).info + puts + end end end end @@ -539,4 +541,4 @@ class RecursiveHTTPFetcher end end -RailsCommands::Plugin.parse! +Rails::Commands::Plugin.parse! -- cgit v1.2.3 From df5ec41b891bbd0392e9bb9695c17e3821903eed Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 16 Nov 2011 10:42:56 -0800 Subject: bundler treats trunk ruby as ruby 1.9, hack around that for now --- Gemfile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/Gemfile b/Gemfile index 1b7d478f18..97605e32b5 100644 --- a/Gemfile +++ b/Gemfile @@ -41,7 +41,7 @@ platforms :mri_18 do end platforms :mri_19 do - gem "ruby-debug19", :require => "ruby-debug" unless ENV['TRAVIS'] + gem "ruby-debug19", :require => "ruby-debug" unless ENV['TRAVIS'] || RUBY_VERSION >= "2.0" end platforms :mri do -- cgit v1.2.3 From 61228e9a0cafb5f0a8e4f2c24da36ef9261840fb Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 16 Nov 2011 13:25:55 -0800 Subject: fixing tests on PG --- activerecord/test/cases/adapters/postgresql/schema_test.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/test/cases/adapters/postgresql/schema_test.rb b/activerecord/test/cases/adapters/postgresql/schema_test.rb index 19669bdeb0..467e5d7b86 100644 --- a/activerecord/test/cases/adapters/postgresql/schema_test.rb +++ b/activerecord/test/cases/adapters/postgresql/schema_test.rb @@ -197,7 +197,7 @@ class SchemaTest < ActiveRecord::TestCase end def test_dump_indexes_for_schema_multiple_schemas_in_search_path - do_dump_index_tests_for_schema("public, #{SCHEMA_NAME}", INDEX_A_COLUMN, INDEX_B_COLUMN_S1) + do_dump_index_tests_for_schema("public, #{SCHEMA_NAME}", INDEX_A_COLUMN, INDEX_B_COLUMN_S1, INDEX_D_COLUMN) end def test_with_uppercase_index_name -- cgit v1.2.3 From 3a6a10a36d9fdcc6de0f2a8e81c73001c1331072 Mon Sep 17 00:00:00 2001 From: Jon Leighton Date: Wed, 16 Nov 2011 21:37:10 +0000 Subject: Switch from marshal format to plain text for the encoding conversions dump. This is for windows compatibility. Fixes #3644. --- .../lib/action_view/data/encoding_conversions.dump | Bin 122854 -> 0 bytes .../lib/action_view/data/encoding_conversions.txt | 88 +++++++++++++++++++++ actionpack/lib/action_view/template.rb | 13 +-- 3 files changed, 95 insertions(+), 6 deletions(-) delete mode 100644 actionpack/lib/action_view/data/encoding_conversions.dump create mode 100644 actionpack/lib/action_view/data/encoding_conversions.txt diff --git a/actionpack/lib/action_view/data/encoding_conversions.dump b/actionpack/lib/action_view/data/encoding_conversions.dump deleted file mode 100644 index 18d7cd448b..0000000000 Binary files a/actionpack/lib/action_view/data/encoding_conversions.dump and /dev/null differ diff --git a/actionpack/lib/action_view/data/encoding_conversions.txt b/actionpack/lib/action_view/data/encoding_conversions.txt new file mode 100644 index 0000000000..fdfbe28803 --- /dev/null +++ b/actionpack/lib/action_view/data/encoding_conversions.txt @@ -0,0 +1,88 @@ +ASCII-8BIT:UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +UTF-8:ASCII-8BIT,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +US-ASCII:ASCII-8BIT,UTF-8,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +Big5:ASCII-8BIT,UTF-8,US-ASCII,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +Big5-HKSCS:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +Big5-UAO:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +CP949:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +EUC-JP:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +EUC-KR:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +GB18030:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +GBK:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +ISO-8859-1:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +ISO-8859-2:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +ISO-8859-3:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +ISO-8859-4:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +ISO-8859-5:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +ISO-8859-6:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +ISO-8859-7:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +ISO-8859-8:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +ISO-8859-9:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +ISO-8859-10:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +ISO-8859-11:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +ISO-8859-13:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +ISO-8859-14:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +ISO-8859-15:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +KOI8-R:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +KOI8-U:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +Shift_JIS:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +UTF-16BE:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +UTF-16LE:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +UTF-32BE:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +UTF-32LE:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +Windows-1251:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +IBM437:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +IBM737:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +IBM775:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +CP850:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +IBM852:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +CP852:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +IBM855:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +CP855:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +IBM857:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +IBM860:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +IBM861:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +IBM862:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +IBM863:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +IBM865:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +IBM866:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +IBM869:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +macCroatian:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +macCyrillic:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +macGreek:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +macIceland:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +macRoman:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +macRomania:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +macTurkish:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +macUkraine:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +CP950:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +CP951:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +stateless-ISO-2022-JP:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +eucJP-ms:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +CP51932:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +GB2312:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +GB12345:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +ISO-2022-JP:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +CP50220:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +CP50221:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +Windows-1252:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +Windows-1250:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +Windows-1256:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +Windows-1253:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +Windows-1255:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +Windows-1254:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +TIS-620:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +Windows-874:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +Windows-1257:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +Windows-31J:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +UTF8-MAC:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +UTF-16:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +UTF-32:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +UTF8-DoCoMo:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +SJIS-DoCoMo:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +UTF8-KDDI:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +SJIS-KDDI:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +ISO-2022-JP-KDDI:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +stateless-ISO-2022-JP-KDDI:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,UTF8-SoftBank,SJIS-SoftBank +UTF8-SoftBank:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,SJIS-SoftBank +SJIS-SoftBank:ASCII-8BIT,UTF-8,US-ASCII,Big5,Big5-HKSCS,Big5-UAO,CP949,EUC-JP,EUC-KR,GB18030,GBK,ISO-8859-1,ISO-8859-2,ISO-8859-3,ISO-8859-4,ISO-8859-5,ISO-8859-6,ISO-8859-7,ISO-8859-8,ISO-8859-9,ISO-8859-10,ISO-8859-11,ISO-8859-13,ISO-8859-14,ISO-8859-15,KOI8-R,KOI8-U,Shift_JIS,UTF-16BE,UTF-16LE,UTF-32BE,UTF-32LE,Windows-1251,IBM437,IBM737,IBM775,CP850,IBM852,CP852,IBM855,CP855,IBM857,IBM860,IBM861,IBM862,IBM863,IBM865,IBM866,IBM869,macCroatian,macCyrillic,macGreek,macIceland,macRoman,macRomania,macTurkish,macUkraine,CP950,CP951,stateless-ISO-2022-JP,eucJP-ms,CP51932,GB2312,GB12345,ISO-2022-JP,CP50220,CP50221,Windows-1252,Windows-1250,Windows-1256,Windows-1253,Windows-1255,Windows-1254,TIS-620,Windows-874,Windows-1257,Windows-31J,UTF8-MAC,UTF-16,UTF-32,UTF8-DoCoMo,SJIS-DoCoMo,UTF8-KDDI,SJIS-KDDI,ISO-2022-JP-KDDI,stateless-ISO-2022-JP-KDDI,UTF8-SoftBank \ No newline at end of file diff --git a/actionpack/lib/action_view/template.rb b/actionpack/lib/action_view/template.rb index fbc135c4a7..8d69880308 100644 --- a/actionpack/lib/action_view/template.rb +++ b/actionpack/lib/action_view/template.rb @@ -12,18 +12,19 @@ if defined?(RUBY_ENGINE) && RUBY_ENGINE == 'ruby' && RUBY_VERSION == '1.9.3' && # able to find it (due to a bug). # # However, we don't know what conversions we may need to do a runtime. So we load up a - # marshal-dumped structure which contains a pre-generated list of all the possible conversions, + # text file which contains a pre-generated list of all the possible conversions, # and we load all of them. # # In my testing this increased the process size by about 3.9 MB (after the conversions array # is GC'd) and took around 170ms to run, which seems acceptable for a workaround. # - # The script to dump the conversions is: https://gist.github.com/1342729 + # The script to dump the conversions is: https://gist.github.com/1371499 - filename = File.join(File.dirname(__FILE__), 'data', 'encoding_conversions.dump') - conversions = Marshal.load(File.read(filename)) - conversions.each do |from, to_array| - to_array.each do |to| + filename = File.join(File.dirname(__FILE__), 'data', 'encoding_conversions.txt') + conversions = File.read(filename) + conversions.split("\n").map do |line| + from, to_array = line.split(':') + to_array.split(',').each do |to| Encoding::Converter.new(from, to) end end -- cgit v1.2.3 From cef1e14e09e5654895f264a2cff1c0898672b1f0 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 16 Nov 2011 14:45:18 -0800 Subject: removing some useless conditionals --- activerecord/lib/active_record/railties/databases.rake | 18 ++++++++---------- 1 file changed, 8 insertions(+), 10 deletions(-) diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index aea928b443..f3cb2a971e 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -1,8 +1,8 @@ require 'active_support/core_ext/object/inclusion' +require 'active_record' db_namespace = namespace :db do task :load_config => :rails_env do - require 'active_record' ActiveRecord::Base.configurations = Rails.application.config.database_configuration ActiveRecord::Migrator.migrations_paths = Rails.application.paths['db/migrate'].to_a @@ -279,16 +279,14 @@ db_namespace = namespace :db do # desc "Raises an error if there are pending migrations" task :abort_if_pending_migrations => :environment do - if defined? ActiveRecord - pending_migrations = ActiveRecord::Migrator.new(:up, ActiveRecord::Migrator.migrations_paths).pending_migrations + pending_migrations = ActiveRecord::Migrator.new(:up, ActiveRecord::Migrator.migrations_paths).pending_migrations - if pending_migrations.any? - puts "You have #{pending_migrations.size} pending migrations:" - pending_migrations.each do |pending_migration| - puts ' %4d %s' % [pending_migration.version, pending_migration.name] - end - abort %{Run `rake db:migrate` to update your database then try again.} + if pending_migrations.any? + puts "You have #{pending_migrations.size} pending migrations:" + pending_migrations.each do |pending_migration| + puts ' %4d %s' % [pending_migration.version, pending_migration.name] end + abort %{Run `rake db:migrate` to update your database then try again.} end end @@ -498,7 +496,7 @@ db_namespace = namespace :db do # desc 'Check for pending migrations and load the test schema' task :prepare => 'db:abort_if_pending_migrations' do - if defined?(ActiveRecord) && !ActiveRecord::Base.configurations.blank? + unless ActiveRecord::Base.configurations.blank? db_namespace[{ :sql => 'test:clone_structure', :ruby => 'test:load' }[ActiveRecord::Base.schema_format]].invoke end end -- cgit v1.2.3 From f05ccf805a6d2a3ed73ef9928577e8b0ebbb3c49 Mon Sep 17 00:00:00 2001 From: Andre Arko Date: Wed, 16 Nov 2011 12:53:43 -1000 Subject: Revert "Revert "Merge pull request #3640 from indirect/remote_ip"" This reverts commit 8d1a2b3ecde5a8745b3eaab4763a71d80ca3441f, because I have fixed the issues this commit caused in the next commit. --- actionpack/lib/action_dispatch/http/request.rb | 5 ++--- actionpack/lib/action_dispatch/middleware/remote_ip.rb | 9 ++++++--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index b10f6b48c7..0e8bd0bd6d 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -155,10 +155,9 @@ module ActionDispatch @ip ||= super end - # Originating IP address, usually set by the RemoteIp middleware. + # Originating IP address from the RemoteIp middleware. def remote_ip - # Coerce the remote_ip object into a string, because to_s could return nil - @remote_ip ||= @env["action_dispatch.remote_ip"].to_s || ip + @remote_ip ||= @env["action_dispatch.remote_ip"] end # Returns the unique request id, which is based off either the X-Request-Id header that can diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb index ee0d19a50d..77aa4e743e 100644 --- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb +++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb @@ -55,7 +55,10 @@ module ActionDispatch "HTTP_X_FORWARDED_FOR=#{@env['HTTP_X_FORWARDED_FOR'].inspect}" end - client_ip || forwarded_ips.last || remote_addrs.first + not_proxy = client_ip || forwarded_ips.last || remote_addrs.first + + # Return first REMOTE_ADDR if there are no other options + not_proxy || ips_from('REMOTE_ADDR', :all).first end def to_s @@ -66,9 +69,9 @@ module ActionDispatch protected - def ips_from(header) + def ips_from(header, allow_proxies = false) ips = @env[header] ? @env[header].strip.split(/[,\s]+/) : [] - ips.reject{|ip| ip =~ @middleware.proxies } + allow_proxies ? ips : ips.reject{|ip| ip =~ @middleware.proxies } end end -- cgit v1.2.3 From 5621abd5698536f1676306930f6aef105d7ae6dc Mon Sep 17 00:00:00 2001 From: Andre Arko Date: Wed, 16 Nov 2011 12:49:15 -1000 Subject: :facepalm: Request#remote_ip has to work without the middleware --- actionpack/lib/action_dispatch/http/request.rb | 4 ++-- actionpack/lib/action_dispatch/middleware/remote_ip.rb | 2 +- 2 files changed, 3 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 0e8bd0bd6d..4e6feb708e 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -155,9 +155,9 @@ module ActionDispatch @ip ||= super end - # Originating IP address from the RemoteIp middleware. + # Originating IP address, usually set by the RemoteIp middleware. def remote_ip - @remote_ip ||= @env["action_dispatch.remote_ip"] + @remote_ip ||= @env["action_dispatch.remote_ip"] || ip end # Returns the unique request id, which is based off either the X-Request-Id header that can diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb index 77aa4e743e..3a88f2ca43 100644 --- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb +++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb @@ -58,7 +58,7 @@ module ActionDispatch not_proxy = client_ip || forwarded_ips.last || remote_addrs.first # Return first REMOTE_ADDR if there are no other options - not_proxy || ips_from('REMOTE_ADDR', :all).first + not_proxy || ips_from('REMOTE_ADDR', :allow_proxies).first end def to_s -- cgit v1.2.3 From a9044d011790063e17159209f7bb1cbea255d4dd Mon Sep 17 00:00:00 2001 From: Andre Arko Date: Wed, 16 Nov 2011 12:58:49 -1000 Subject: the object itself isn't the IP, #to_s is the IP --- actionpack/lib/action_dispatch/http/request.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/http/request.rb b/actionpack/lib/action_dispatch/http/request.rb index 4e6feb708e..69ca050d0c 100644 --- a/actionpack/lib/action_dispatch/http/request.rb +++ b/actionpack/lib/action_dispatch/http/request.rb @@ -157,7 +157,7 @@ module ActionDispatch # Originating IP address, usually set by the RemoteIp middleware. def remote_ip - @remote_ip ||= @env["action_dispatch.remote_ip"] || ip + @remote_ip ||= (@env["action_dispatch.remote_ip"] || ip).to_s end # Returns the unique request id, which is based off either the X-Request-Id header that can -- cgit v1.2.3 From 73cb0f98289923c8fa0287bf1cc8857664078d43 Mon Sep 17 00:00:00 2001 From: James Adam Date: Wed, 26 Oct 2011 12:51:38 +0100 Subject: `ActiveRecord::Base#becomes` should retain the errors of the original object. This commit contains a simple failing test that demonstrates the behaviour we expect, and a fix. When using `becomes` to transform the type of an object, it should retain any error information that was present on the original instance. --- activerecord/lib/active_record/persistence.rb | 1 + activerecord/test/cases/base_test.rb | 8 ++++++++ 2 files changed, 9 insertions(+) diff --git a/activerecord/lib/active_record/persistence.rb b/activerecord/lib/active_record/persistence.rb index 5e65e46a7d..f047a1d9fa 100644 --- a/activerecord/lib/active_record/persistence.rb +++ b/activerecord/lib/active_record/persistence.rb @@ -114,6 +114,7 @@ module ActiveRecord became.instance_variable_set("@attributes_cache", @attributes_cache) became.instance_variable_set("@new_record", new_record?) became.instance_variable_set("@destroyed", destroyed?) + became.instance_variable_set("@errors", errors) became.type = klass.name unless self.class.descends_from_active_record? became end diff --git a/activerecord/test/cases/base_test.rb b/activerecord/test/cases/base_test.rb index fdb656fe13..997c9e7e9d 100644 --- a/activerecord/test/cases/base_test.rb +++ b/activerecord/test/cases/base_test.rb @@ -1761,6 +1761,14 @@ class BasicsTest < ActiveRecord::TestCase assert_equal "The First Topic", topics(:first).becomes(Reply).title end + def test_becomes_includes_errors + company = Company.new(:name => nil) + assert !company.valid? + original_errors = company.errors + client = company.becomes(Client) + assert_equal original_errors, client.errors + end + def test_silence_sets_log_level_to_error_in_block original_logger = ActiveRecord::Base.logger log = StringIO.new -- cgit v1.2.3 From 38d26b0cb56d82093889efa95992a35ba3bb9f29 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 16 Nov 2011 15:01:02 -0800 Subject: Move conditionals to separate tasks so they can be reused. --- activerecord/lib/active_record/railties/databases.rake | 18 +++++++++++------- 1 file changed, 11 insertions(+), 7 deletions(-) diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index f3cb2a971e..ee611f6a2a 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -291,15 +291,11 @@ db_namespace = namespace :db do end desc 'Create the database, load the schema, and initialize with the seed data (use db:reset to also drop the db first)' - task :setup => :environment do - db_namespace["create"].invoke - db_namespace["schema:load"].invoke if ActiveRecord::Base.schema_format == :ruby - db_namespace["structure:load"].invoke if ActiveRecord::Base.schema_format == :sql - db_namespace["seed"].invoke - end + task :setup => :seed desc 'Load the seed data from db/seeds.rb' - task :seed => 'db:abort_if_pending_migrations' do + task :seed => ['db:schema:load_if_ruby', 'db:structure:load_if_sql'] do + db_namespace['abort_if_pending_migrations'].invoke Rails.application.load_seed end @@ -362,6 +358,10 @@ db_namespace = namespace :db do abort %{#{file} doesn't exist yet. Run `rake db:migrate` to create it then try again. If you do not intend to use a database, you should instead alter #{Rails.root}/config/application.rb to limit the frameworks that will be loaded} end end + + task :load_if_ruby => 'db:create' do + db_namespace["schema:load"].invoke if ActiveRecord::Base.schema_format == :ruby + end end namespace :structure do @@ -437,6 +437,10 @@ db_namespace = namespace :db do raise "Task not supported by '#{abcs[env]['adapter']}'" end end + + task :load_if_sql => 'db:create' do + db_namespace["structure:load"].invoke if ActiveRecord::Base.schema_format == :sql + end end namespace :test do -- cgit v1.2.3 From ca69408b49156f98a76adfc03088b6b067fabc61 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 16 Nov 2011 15:27:47 -0800 Subject: Reduce schema format tests --- .../lib/active_record/railties/databases.rake | 24 +++++++++++++--------- 1 file changed, 14 insertions(+), 10 deletions(-) diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index ee611f6a2a..d19208dd76 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -150,8 +150,16 @@ db_namespace = namespace :db do task :migrate => [:environment, :load_config] do ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) - db_namespace["schema:dump"].invoke if ActiveRecord::Base.schema_format == :ruby - db_namespace["structure:dump"].invoke if ActiveRecord::Base.schema_format == :sql + db_namespace['_dump'].invoke + end + + task :_dump do + case ActiveRecord::Base.schema_format + when :ruby then db_namespace["schema:dump"].invoke + when :sql then db_namespace["structure:dump"].invoke + else + raise "unknown schema format #{ActiveRecord::Base.schema_format}" + end end namespace :migrate do @@ -174,8 +182,7 @@ db_namespace = namespace :db do version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil raise 'VERSION is required' unless version ActiveRecord::Migrator.run(:up, ActiveRecord::Migrator.migrations_paths, version) - db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby - db_namespace['structure:dump'].invoke if ActiveRecord::Base.schema_format == :sql + db_namespace['_dump'].invoke end # desc 'Runs the "down" for a given migration VERSION.' @@ -183,8 +190,7 @@ db_namespace = namespace :db do version = ENV['VERSION'] ? ENV['VERSION'].to_i : nil raise 'VERSION is required' unless version ActiveRecord::Migrator.run(:down, ActiveRecord::Migrator.migrations_paths, version) - db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby - db_namespace['structure:dump'].invoke if ActiveRecord::Base.schema_format == :sql + db_namespace['_dump'].invoke end desc 'Display status of migrations' @@ -224,16 +230,14 @@ db_namespace = namespace :db do task :rollback => [:environment, :load_config] do step = ENV['STEP'] ? ENV['STEP'].to_i : 1 ActiveRecord::Migrator.rollback(ActiveRecord::Migrator.migrations_paths, step) - db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby - db_namespace['structure:dump'].invoke if ActiveRecord::Base.schema_format == :sql + db_namespace['_dump'].invoke end # desc 'Pushes the schema to the next version (specify steps w/ STEP=n).' task :forward => [:environment, :load_config] do step = ENV['STEP'] ? ENV['STEP'].to_i : 1 ActiveRecord::Migrator.forward(ActiveRecord::Migrator.migrations_paths, step) - db_namespace['schema:dump'].invoke if ActiveRecord::Base.schema_format == :ruby - db_namespace['structure:dump'].invoke if ActiveRecord::Base.schema_format == :sql + db_namespace['_dump'].invoke end # desc 'Drops and recreates the database from db/schema.rb for the current environment and loads the seeds.' -- cgit v1.2.3 From b96aaf8ccbd06986dc489a9f29ea4092dd5cf911 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 16 Nov 2011 15:30:00 -0800 Subject: dbfile isn't supported anymore, so remove --- activerecord/lib/active_record/railties/databases.rake | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index d19208dd76..ef8f815207 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -388,7 +388,7 @@ db_namespace = namespace :db do `pg_dump -i -s -x -O -f db/structure.sql #{search_path} #{abcs[Rails.env]['database']}` raise 'Error dumping database' if $?.exitstatus == 1 when /sqlite/ - dbfile = abcs[Rails.env]['database'] || abcs[Rails.env]['dbfile'] + dbfile = abcs[Rails.env]['database'] `sqlite3 #{dbfile} .schema > db/structure.sql` when 'sqlserver' `smoscript -s #{abcs[Rails.env]['host']} -d #{abcs[Rails.env]['database']} -u #{abcs[Rails.env]['username']} -p #{abcs[Rails.env]['password']} -f db\\structure.sql -A -U` @@ -424,7 +424,7 @@ db_namespace = namespace :db do ENV['PGUSER'] = abcs[env]['username'].to_s if abcs[env]['username'] `psql -f "#{Rails.root}/db/structure.sql" #{abcs[env]['database']} #{abcs[env]['template']}` when /sqlite/ - dbfile = abcs[env]['database'] || abcs[env]['dbfile'] + dbfile = abcs[env]['database'] `sqlite3 #{dbfile} < "#{Rails.root}/db/structure.sql"` when 'sqlserver' `sqlcmd -S #{abcs[env]['host']} -d #{abcs[env]['database']} -U #{abcs[env]['username']} -P #{abcs[env]['password']} -i db\\structure.sql` @@ -481,7 +481,7 @@ db_namespace = namespace :db do drop_database(abcs['test']) create_database(abcs['test']) when /sqlite/ - dbfile = abcs['test']['database'] || abcs['test']['dbfile'] + dbfile = abcs['test']['database'] File.delete(dbfile) if File.exist?(dbfile) when 'sqlserver' test = abcs.deep_dup['test'] -- cgit v1.2.3 From 97ca6358c5fbd5458b684d6d46489b7b68c48225 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 16 Nov 2011 15:40:20 -0800 Subject: Join method uses empty string by default, so remove it --- .../lib/active_record/connection_adapters/abstract_mysql_adapter.rb | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb index baf4c043c4..e26b10fa97 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract_mysql_adapter.rb @@ -389,11 +389,11 @@ module ActiveRecord sql = "SHOW TABLES" end - select_all(sql).map do |table| + select_all(sql).map { |table| table.delete('Table_type') sql = "SHOW CREATE TABLE #{quote_table_name(table.to_a.first.last)}" exec_without_stmt(sql).first['Create Table'] + ";\n\n" - end.join("") + }.join end # Drops the database specified on the +name+ attribute -- cgit v1.2.3 From bb95e815380c85d14afade807ebb2dd227d90b9e Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 16 Nov 2011 15:51:25 -0800 Subject: Adding a deprecation warning for use of the schema_info table. --- .../lib/active_record/connection_adapters/abstract/schema_statements.rb | 2 ++ 1 file changed, 2 insertions(+) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 11da84e245..7742f32213 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -1,4 +1,5 @@ require 'active_support/core_ext/array/wrap' +require 'active_support/deprecation/reporting' module ActiveRecord module ConnectionAdapters # :nodoc: @@ -445,6 +446,7 @@ module ActiveRecord si_table = Base.table_name_prefix + 'schema_info' + Base.table_name_suffix if table_exists?(si_table) + ActiveRecord::Deprecation.warn "Usage of the schema table `#{si_table}` is deprecated. Please switch to using `schema_migrations` table" old_version = select_value("SELECT version FROM #{quote_table_name(si_table)}").to_i assume_migrated_upto_version(old_version) -- cgit v1.2.3 From 64a3175eea3c121f30a5a24034aa1ed400c80b12 Mon Sep 17 00:00:00 2001 From: capps Date: Wed, 16 Nov 2011 15:57:54 -0800 Subject: "denoted" instead of "donated" "parentheses" instead of "use brackets" --- railties/guides/source/i18n.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile index 2d4cc13571..e9477e84cf 100644 --- a/railties/guides/source/i18n.textile +++ b/railties/guides/source/i18n.textile @@ -231,7 +231,7 @@ end Now, when you call the +books_path+ method you should get +"/en/books"+ (for the default locale). An URL like +http://localhost:3001/nl/books+ should load the Netherlands locale, then, and following calls to +books_path+ should return +"/nl/books"+ (because the locale changed). -If you don't want to force the use of a locale in your routes you can use an optional path scope (donated by the use brackets) like so: +If you don't want to force the use of a locale in your routes you can use an optional path scope (denoted by the parentheses) like so: # config/routes.rb -- cgit v1.2.3 From 4c1a1933cbc5ab96efe340a3b31ac5fee12c99c8 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 16 Nov 2011 16:18:00 -0800 Subject: No need to `readlines` then `join`, just use `read` :heart: --- activerecord/lib/active_record/railties/databases.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index ef8f815207..5964fa9cb0 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -414,7 +414,7 @@ db_namespace = namespace :db do when /mysql/ ActiveRecord::Base.establish_connection(abcs[env]) ActiveRecord::Base.connection.execute('SET foreign_key_checks = 0') - IO.readlines("#{Rails.root}/db/structure.sql").join.split("\n\n").each do |table| + IO.read("#{Rails.root}/db/structure.sql").split("\n\n").each do |table| ActiveRecord::Base.connection.execute(table) end when /postgresql/ -- cgit v1.2.3 From e3671422556ac61f39539264713ba9c04814b80f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Wed, 16 Nov 2011 16:55:11 -0800 Subject: Initialize our instance variables. --- actionpack/lib/action_dispatch/middleware/remote_ip.rb | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb index 3a88f2ca43..8dbe3af6f1 100644 --- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb +++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb @@ -33,7 +33,9 @@ module ActionDispatch class GetIp def initialize(env, middleware) - @env, @middleware = env, middleware + @env = env + @middleware = middleware + @calculate_ip = false end # Determines originating IP address. REMOTE_ADDR is the standard -- cgit v1.2.3 From c2b6f63bbe2740fd63a36eeefe17d51813a17324 Mon Sep 17 00:00:00 2001 From: Alexander Uvarov Date: Thu, 17 Nov 2011 12:04:46 +0600 Subject: Fix impractical I18n lookup in nested fields_for --- actionpack/lib/action_view/helpers/form_helper.rb | 10 ++++- actionpack/test/template/form_helper_test.rb | 46 +++++++++++++++++++++++ 2 files changed, 55 insertions(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/helpers/form_helper.rb b/actionpack/lib/action_view/helpers/form_helper.rb index f148ffbd73..d687866d0d 100644 --- a/actionpack/lib/action_view/helpers/form_helper.rb +++ b/actionpack/lib/action_view/helpers/form_helper.rb @@ -995,8 +995,16 @@ module ActionView label_tag(name_and_id["id"], options, &block) else content = if text.blank? + object_name.gsub!(/\[(.*)_attributes\]\[\d\]/, '.\1') method_and_value = tag_value.present? ? "#{method_name}.#{tag_value}" : method_name - I18n.t("helpers.label.#{object_name}.#{method_and_value}", :default => "").presence + + if object.respond_to?(:to_model) + key = object.class.model_name.i18n_key + i18n_default = ["#{key}.#{method_and_value}".to_sym, ""] + end + + i18n_default ||= "" + I18n.t("#{object_name}.#{method_and_value}", :default => i18n_default, :scope => "helpers.label").presence else text.to_s end diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 6434d9645e..34486bb151 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -27,7 +27,13 @@ class FormHelperTest < ActionView::TestCase :body => "Write entire text here", :color => { :red => "Rojo" + }, + :comments => { + :body => "Write body here" } + }, + :tag => { + :value => "Tag" } } } @@ -68,6 +74,12 @@ class FormHelperTest < ActionView::TestCase @post.secret = 1 @post.written_on = Date.new(2004, 6, 15) + @post.comments = [] + @post.comments << @comment + + @post.tags = [] + @post.tags << Tag.new + @blog_post = Blog::Post.new("And his name will be forty and four.", 44) end @@ -151,6 +163,40 @@ class FormHelperTest < ActionView::TestCase I18n.locale = old_locale end + def test_label_with_locales_and_nested_attributes + old_locale, I18n.locale = I18n.locale, :label + form_for(@post, :html => { :id => 'create-post' }) do |f| + f.fields_for(:comments) do |cf| + concat cf.label(:body) + end + end + + expected = whole_form("/posts/123", "create-post" , "edit_post", :method => "put") do + "" + end + + assert_dom_equal expected, output_buffer + ensure + I18n.locale = old_locale + end + + def test_label_with_locales_fallback_and_nested_attributes + old_locale, I18n.locale = I18n.locale, :label + form_for(@post, :html => { :id => 'create-post' }) do |f| + f.fields_for(:tags) do |cf| + concat cf.label(:value) + end + end + + expected = whole_form("/posts/123", "create-post" , "edit_post", :method => "put") do + "" + end + + assert_dom_equal expected, output_buffer + ensure + I18n.locale = old_locale + end + def test_label_with_for_attribute_as_symbol assert_dom_equal('', label(:post, :title, nil, :for => "my_for")) end -- cgit v1.2.3 From 5ccd9bcc1a3012573e6ab19ac52abf8917374bea Mon Sep 17 00:00:00 2001 From: Arun Agrawal Date: Thu, 17 Nov 2011 12:24:40 +0530 Subject: No need to `readlines` then `join`, just use `read` :heart: same as 4c1a1933cbc5ab96efe340a3b31ac5fee12c99c8 --- activerecord/lib/active_record/railties/databases.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 5964fa9cb0..4ffcf6dbc1 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -430,7 +430,7 @@ db_namespace = namespace :db do `sqlcmd -S #{abcs[env]['host']} -d #{abcs[env]['database']} -U #{abcs[env]['username']} -P #{abcs[env]['password']} -i db\\structure.sql` when 'oci', 'oracle' ActiveRecord::Base.establish_connection(abcs[env]) - IO.readlines("#{Rails.root}/db/structure.sql").join.split(";\n\n").each do |ddl| + IO.read("#{Rails.root}/db/structure.sql").split(";\n\n").each do |ddl| ActiveRecord::Base.connection.execute(ddl) end when 'firebird' -- cgit v1.2.3 From c3ae1d2aec400d6aaea78bd94eb7845b71f1ec15 Mon Sep 17 00:00:00 2001 From: Arun Agrawal Date: Thu, 17 Nov 2011 12:50:19 +0530 Subject: It should be @calculated_ip not @calculate_ip We are using @calculated_ip. @calculate_ip is no where used --- actionpack/lib/action_dispatch/middleware/remote_ip.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/middleware/remote_ip.rb b/actionpack/lib/action_dispatch/middleware/remote_ip.rb index 8dbe3af6f1..66ece60860 100644 --- a/actionpack/lib/action_dispatch/middleware/remote_ip.rb +++ b/actionpack/lib/action_dispatch/middleware/remote_ip.rb @@ -35,7 +35,7 @@ module ActionDispatch def initialize(env, middleware) @env = env @middleware = middleware - @calculate_ip = false + @calculated_ip = false end # Determines originating IP address. REMOTE_ADDR is the standard -- cgit v1.2.3 From 0af93089deaf6dc9428c0f2f7a376cdb6f81daee Mon Sep 17 00:00:00 2001 From: Alex Tambellini Date: Thu, 17 Nov 2011 09:44:17 -0500 Subject: Move schema_format :sql config setting from test.rb to application.rb I've moved the schema_format :sql config setting to application.rb because you would never enable this only for the test environment. If you use database constraints or database specific data types you would want all of your environments to use them. --- railties/guides/code/getting_started/config/application.rb | 5 +++++ railties/guides/code/getting_started/config/environments/test.rb | 5 ----- .../lib/rails/generators/rails/app/templates/config/application.rb | 5 +++++ .../generators/rails/app/templates/config/environments/test.rb.tt | 5 ----- 4 files changed, 10 insertions(+), 10 deletions(-) diff --git a/railties/guides/code/getting_started/config/application.rb b/railties/guides/code/getting_started/config/application.rb index e914b5a80e..e16da30f72 100644 --- a/railties/guides/code/getting_started/config/application.rb +++ b/railties/guides/code/getting_started/config/application.rb @@ -39,6 +39,11 @@ module Blog # Configure sensitive parameters which will be filtered from the log file. config.filter_parameters += [:password] + # Use SQL instead of Active Record's schema dumper when creating the database. + # This is necessary if your schema can't be completely dumped by the schema dumper, + # like if you have constraints or database-specific column types + # config.active_record.schema_format = :sql + # Enable the asset pipeline config.assets.enabled = true diff --git a/railties/guides/code/getting_started/config/environments/test.rb b/railties/guides/code/getting_started/config/environments/test.rb index 833241ace3..08697cbbc9 100644 --- a/railties/guides/code/getting_started/config/environments/test.rb +++ b/railties/guides/code/getting_started/config/environments/test.rb @@ -29,11 +29,6 @@ Blog::Application.configure do # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test - # Use SQL instead of Active Record's schema dumper when creating the test database. - # This is necessary if your schema can't be completely dumped by the schema dumper, - # like if you have constraints or database-specific column types - # config.active_record.schema_format = :sql - # Print deprecation notices to the stderr config.active_support.deprecation = :stderr diff --git a/railties/lib/rails/generators/rails/app/templates/config/application.rb b/railties/lib/rails/generators/rails/app/templates/config/application.rb index 13fbe9e526..40fd843b1b 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/application.rb +++ b/railties/lib/rails/generators/rails/app/templates/config/application.rb @@ -49,6 +49,11 @@ module <%= app_const_base %> # Configure sensitive parameters which will be filtered from the log file. config.filter_parameters += [:password] + # Use SQL instead of Active Record's schema dumper when creating the database. + # This is necessary if your schema can't be completely dumped by the schema dumper, + # like if you have constraints or database-specific column types + # config.active_record.schema_format = :sql + <% unless options.skip_sprockets? -%> # Enable the asset pipeline config.assets.enabled = true diff --git a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt index 80198cc21e..37a8b81dad 100644 --- a/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt +++ b/railties/lib/rails/generators/rails/app/templates/config/environments/test.rb.tt @@ -29,11 +29,6 @@ # ActionMailer::Base.deliveries array. config.action_mailer.delivery_method = :test - # Use SQL instead of Active Record's schema dumper when creating the test database. - # This is necessary if your schema can't be completely dumped by the schema dumper, - # like if you have constraints or database-specific column types - # config.active_record.schema_format = :sql - <%- unless options.skip_active_record? -%> # Raise exception on mass assignment protection for ActiveRecord models config.active_record.mass_assignment_sanitizer = :strict -- cgit v1.2.3 From 8d17af23ef5202e7c28bbf701e247bce6011df07 Mon Sep 17 00:00:00 2001 From: Sunny Ripert Date: Thu, 17 Nov 2011 17:05:53 +0100 Subject: Guides: better example to find the last sent email --- railties/guides/source/testing.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/testing.textile b/railties/guides/source/testing.textile index 2341a3522c..5dbbe2c0f0 100644 --- a/railties/guides/source/testing.textile +++ b/railties/guides/source/testing.textile @@ -927,7 +927,7 @@ class UserControllerTest < ActionController::TestCase assert_difference 'ActionMailer::Base.deliveries.size', +1 do post :invite_friend, :email => 'friend@example.com' end - invite_email = ActionMailer::Base.deliveries.first + invite_email = ActionMailer::Base.deliveries.last assert_equal "You have been invited by me@example.com", invite_email.subject assert_equal 'friend@example.com', invite_email.to[0] -- cgit v1.2.3 From a89fabbb0266e9a5a391a88fd39b97d08879412f Mon Sep 17 00:00:00 2001 From: Oscar Del Ben Date: Thu, 17 Nov 2011 18:49:47 +0100 Subject: Cleanup of databases.rake psql env variables --- activerecord/lib/active_record/railties/databases.rake | 17 +++++++++-------- 1 file changed, 9 insertions(+), 8 deletions(-) diff --git a/activerecord/lib/active_record/railties/databases.rake b/activerecord/lib/active_record/railties/databases.rake index 4ffcf6dbc1..589ed3bd11 100644 --- a/activerecord/lib/active_record/railties/databases.rake +++ b/activerecord/lib/active_record/railties/databases.rake @@ -377,10 +377,7 @@ db_namespace = namespace :db do ActiveRecord::Base.establish_connection(abcs[Rails.env]) File.open("#{Rails.root}/db/structure.sql", "w:utf-8") { |f| f << ActiveRecord::Base.connection.structure_dump } when /postgresql/ - ENV['PGHOST'] = abcs[Rails.env]['host'] if abcs[Rails.env]['host'] - ENV['PGPORT'] = abcs[Rails.env]['port'].to_s if abcs[Rails.env]['port'] - ENV['PGPASSWORD'] = abcs[Rails.env]['password'].to_s if abcs[Rails.env]['password'] - ENV['PGUSER'] = abcs[Rails.env]['username'].to_s if abcs[Rails.env]['username'] + set_psql_env(abcs[Rails.env]) search_path = abcs[Rails.env]['schema_search_path'] unless search_path.blank? search_path = search_path.split(",").map{|search_path_part| "--schema=#{search_path_part.strip}" }.join(" ") @@ -418,10 +415,7 @@ db_namespace = namespace :db do ActiveRecord::Base.connection.execute(table) end when /postgresql/ - ENV['PGHOST'] = abcs[env]['host'] if abcs[env]['host'] - ENV['PGPORT'] = abcs[env]['port'].to_s if abcs[env]['port'] - ENV['PGPASSWORD'] = abcs[env]['password'].to_s if abcs[env]['password'] - ENV['PGUSER'] = abcs[env]['username'].to_s if abcs[env]['username'] + set_psql_env(abcs[env]) `psql -f "#{Rails.root}/db/structure.sql" #{abcs[env]['database']} #{abcs[env]['template']}` when /sqlite/ dbfile = abcs[env]['database'] @@ -599,3 +593,10 @@ end def firebird_db_string(config) FireRuby::Database.db_string_for(config.symbolize_keys) end + +def set_psql_env(config) + ENV['PGHOST'] = config['host'] if config['host'] + ENV['PGPORT'] = config['port'].to_s if config['port'] + ENV['PGPASSWORD'] = config['password'].to_s if config['password'] + ENV['PGUSER'] = config['username'].to_s if config['username'] +end -- cgit v1.2.3 From 649f2513a453cae319be8b63a9de25ae11fb9e8f Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 17 Nov 2011 10:00:33 -0800 Subject: Revert "Merge pull request #3603 from vijaydev/change_table_without_block_arg" This reverts commit 81fad6a270ec3cbbb88553c9f2e8200c34fd4d13, reversing changes made to 23101de283de13517e30c4c3d1ecc65525264886. Conflicts: activerecord/test/cases/migration_test.rb --- .../abstract/schema_statements.rb | 19 +++----- activerecord/test/cases/migration_test.rb | 54 ---------------------- 2 files changed, 7 insertions(+), 66 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 7742f32213..1d837f29be 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -242,19 +242,14 @@ module ActiveRecord # # See also Table for details on # all of the various column transformation - def change_table(table_name, options = {}, &blk) - bulk_change = supports_bulk_alter? && options[:bulk] - recorder = bulk_change ? ActiveRecord::Migration::CommandRecorder.new(self) : self - table = Table.new(table_name, recorder) - - if block_given? - if blk.arity == 1 - yield table - else - table.instance_eval(&blk) - end + def change_table(table_name, options = {}) + if supports_bulk_alter? && options[:bulk] + recorder = ActiveRecord::Migration::CommandRecorder.new(self) + yield Table.new(table_name, recorder) + bulk_change_table(table_name, recorder.commands) + else + yield Table.new(table_name, self) end - bulk_change_table(table_name, recorder.commands) if bulk_change end # Renames a table. diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index 75eb9c2bce..ae5b2a62b5 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -1757,60 +1757,6 @@ if ActiveRecord::Base.connection.supports_migrations? end end end - - def test_change_table_without_block_parameter_no_bulk - Person.connection.create_table :testings, :force => true do - string :foo - end - assert Person.connection.column_exists?(:testings, :foo, :string) - - Person.connection.change_table :testings do - remove :foo - integer :bar - end - - assert_equal %w(bar id), Person.connection.columns(:testings).map { |c| c.name }.sort - ensure - Person.connection.drop_table :testings rescue nil - end - - if ActiveRecord::Base.connection.supports_bulk_alter? - def test_change_table_without_block_parameter_with_bulk - Person.connection.create_table :testings, :force => true do - string :foo - end - assert Person.connection.column_exists?(:testings, :foo, :string) - - assert_queries(1) do - Person.connection.change_table(:testings, :bulk => true) do - integer :bar - string :foo_bar - end - end - - assert_equal %w(bar foo foo_bar id), Person.connection.columns(:testings).map { |c| c.name }.sort - ensure - Person.connection.drop_table :testings rescue nil - end - end - - def test_change_table_should_not_have_mixed_syntax - Person.connection.create_table :testings, :force => true do - string :foo - end - assert_raise(NoMethodError) do - Person.connection.change_table :testings do |t| - t.remove :foo - integer :bar - end - end - assert_raise(NameError) do - Person.connection.change_table :testings do - t.remove :foo - integer :bar - end - end - end end # SexierMigrationsTest class MigrationLoggerTest < ActiveRecord::TestCase -- cgit v1.2.3 From a2f14e23441ec016f9643b9054f409006b0e16b3 Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 17 Nov 2011 10:28:23 -0800 Subject: Revert "Merge pull request #1163 from amatsuda/sexier_migration_31" This reverts commit 0e407a90413d8a19002b85508d811ccdf2190783, reversing changes made to 533a9f84b035756eedf9fdccf0c494dc9701ba72. Conflicts: activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb activerecord/test/cases/migration_test.rb --- .../abstract/schema_statements.rb | 10 +- .../connection_adapters/sqlite_adapter.rb | 18 ++-- activerecord/lib/active_record/session_store.rb | 7 +- activerecord/test/cases/migration_test.rb | 120 --------------------- 4 files changed, 13 insertions(+), 142 deletions(-) diff --git a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb index 1d837f29be..faa42e2d19 100644 --- a/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb +++ b/activerecord/lib/active_record/connection_adapters/abstract/schema_statements.rb @@ -155,17 +155,11 @@ module ActiveRecord # ) # # See also TableDefinition#column for details on how to create columns. - def create_table(table_name, options = {}, &blk) + def create_table(table_name, options = {}) td = table_definition td.primary_key(options[:primary_key] || Base.get_primary_key(table_name.to_s.singularize)) unless options[:id] == false - if block_given? - if blk.arity == 1 - yield td - else - td.instance_eval(&blk) - end - end + yield td if block_given? if options[:force] && table_exists?(table_name) drop_table(table_name) diff --git a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb index 35df0a1542..bc3804b3d9 100644 --- a/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb +++ b/activerecord/lib/active_record/connection_adapters/sqlite_adapter.rb @@ -480,30 +480,28 @@ module ActiveRecord drop_table(from) end - def copy_table(from, to, options = {}, &block) #:nodoc: - from_columns, from_primary_key = columns(from), primary_key(from) - options = options.merge(:id => (!from_columns.detect {|c| c.name == 'id'}.nil? && 'id' == primary_key(from).to_s)) - table_definition = nil + def copy_table(from, to, options = {}) #:nodoc: + options = options.merge(:id => (!columns(from).detect{|c| c.name == 'id'}.nil? && 'id' == primary_key(from).to_s)) create_table(to, options) do |definition| - table_definition = definition - from_columns.each do |column| + @definition = definition + columns(from).each do |column| column_name = options[:rename] ? (options[:rename][column.name] || options[:rename][column.name.to_sym] || column.name) : column.name - table_definition.column(column_name, column.type, + @definition.column(column_name, column.type, :limit => column.limit, :default => column.default, :precision => column.precision, :scale => column.scale, :null => column.null) end - table_definition.primary_key from_primary_key if from_primary_key - table_definition.instance_eval(&block) if block + @definition.primary_key(primary_key(from)) if primary_key(from) + yield @definition if block_given? end copy_table_indexes(from, to, options[:rename] || {}) copy_table_contents(from, to, - table_definition.columns.map {|column| column.name}, + @definition.columns.map {|column| column.name}, options[:rename] || {}) end diff --git a/activerecord/lib/active_record/session_store.rb b/activerecord/lib/active_record/session_store.rb index 92550c7efc..76c37cc367 100644 --- a/activerecord/lib/active_record/session_store.rb +++ b/activerecord/lib/active_record/session_store.rb @@ -64,13 +64,12 @@ module ActiveRecord end def create_table! - id_col_name, data_col_name = session_id_column, data_column_name connection_pool.clear_table_cache!(table_name) connection.create_table(table_name) do |t| - t.string id_col_name, :limit => 255 - t.text data_col_name + t.string session_id_column, :limit => 255 + t.text data_column_name end - connection.add_index table_name, id_col_name, :unique => true + connection.add_index table_name, session_id_column, :unique => true end end diff --git a/activerecord/test/cases/migration_test.rb b/activerecord/test/cases/migration_test.rb index ae5b2a62b5..3e219f2a49 100644 --- a/activerecord/test/cases/migration_test.rb +++ b/activerecord/test/cases/migration_test.rb @@ -1639,126 +1639,6 @@ if ActiveRecord::Base.connection.supports_migrations? end - class SexyMigrationsTest < ActiveRecord::TestCase - def test_references_column_type_adds_id - with_new_table do |t| - t.expects(:column).with('customer_id', :integer, {}) - t.references :customer - end - end - - def test_references_column_type_with_polymorphic_adds_type - with_new_table do |t| - t.expects(:column).with('taggable_type', :string, {}) - t.expects(:column).with('taggable_id', :integer, {}) - t.references :taggable, :polymorphic => true - end - end - - def test_references_column_type_with_polymorphic_and_options_null_is_false_adds_table_flag - with_new_table do |t| - t.expects(:column).with('taggable_type', :string, {:null => false}) - t.expects(:column).with('taggable_id', :integer, {:null => false}) - t.references :taggable, :polymorphic => true, :null => false - end - end - - def test_belongs_to_works_like_references - with_new_table do |t| - t.expects(:column).with('customer_id', :integer, {}) - t.belongs_to :customer - end - end - - def test_timestamps_creates_updated_at_and_created_at - with_new_table do |t| - t.expects(:column).with(:created_at, :datetime, kind_of(Hash)) - t.expects(:column).with(:updated_at, :datetime, kind_of(Hash)) - t.timestamps - end - end - - def test_integer_creates_integer_column - with_new_table do |t| - t.expects(:column).with(:foo, 'integer', {}) - t.expects(:column).with(:bar, 'integer', {}) - t.integer :foo, :bar - end - end - - def test_string_creates_string_column - with_new_table do |t| - t.expects(:column).with(:foo, 'string', {}) - t.expects(:column).with(:bar, 'string', {}) - t.string :foo, :bar - end - end - - if current_adapter?(:PostgreSQLAdapter) || current_adapter?(:SQLite3Adapter) || current_adapter?(:MysqlAdapter) || current_adapter?(:Mysql2Adapter) - def test_xml_creates_xml_column - type = current_adapter?(:PostgreSQLAdapter) ? 'xml' : :text - - with_new_table do |t| - t.expects(:column).with(:data, type, {}) - t.xml :data - end - end - else - def test_xml_creates_xml_column - with_new_table do |t| - assert_raises(NotImplementedError) do - t.xml :data - end - end - end - end - - protected - def with_new_table - Person.connection.create_table :delete_me, :force => true do |t| - yield t - end - ensure - Person.connection.drop_table :delete_me rescue nil - end - - end # SexyMigrationsTest - - class SexierMigrationsTest < ActiveRecord::TestCase - def test_create_table_with_column_without_block_parameter - Person.connection.create_table :testings, :force => true do - column :foo, :string - end - assert Person.connection.column_exists?(:testings, :foo, :string) - ensure - Person.connection.drop_table :testings rescue nil - end - - def test_create_table_with_sexy_column_without_block_parameter - Person.connection.create_table :testings, :force => true do - integer :bar - end - assert Person.connection.column_exists?(:testings, :bar, :integer) - ensure - Person.connection.drop_table :testings rescue nil - end - - def test_create_table_should_not_have_mixed_syntax - assert_raise(NoMethodError) do - Person.connection.create_table :testings, :force => true do |t| - t.string :foo - integer :bar - end - end - assert_raise(NameError) do - Person.connection.create_table :testings, :force => true do - t.string :foo - integer :bar - end - end - end - end # SexierMigrationsTest - class MigrationLoggerTest < ActiveRecord::TestCase def test_migration_should_be_run_without_logger previous_logger = ActiveRecord::Base.logger -- cgit v1.2.3 From 9b534060bfafbf1db36e73bb3e538b8c412dcc54 Mon Sep 17 00:00:00 2001 From: Vijay Dev Date: Fri, 18 Nov 2011 01:17:21 +0530 Subject: update guide: db:structure:dump produces structure.sql now --- railties/guides/source/migrations.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/migrations.textile b/railties/guides/source/migrations.textile index 23e36b39f9..5b52a93853 100644 --- a/railties/guides/source/migrations.textile +++ b/railties/guides/source/migrations.textile @@ -658,7 +658,7 @@ In many ways this is exactly what it is. This file is created by inspecting the There is however a trade-off: +db/schema.rb+ cannot express database specific items such as foreign key constraints, triggers, or stored procedures. While in a migration you can execute custom SQL statements, the schema dumper cannot reconstitute those statements from the database. If you are using features like this, then you should set the schema format to +:sql+. -Instead of using Active Record's schema dumper, the database's structure will be dumped using a tool specific to the database (via the +db:structure:dump+ Rake task) into +db/#{Rails.env}_structure.sql+. For example, for the PostgreSQL RDBMS, the +pg_dump+ utility is used. For MySQL, this file will contain the output of +SHOW CREATE TABLE+ for the various tables. Loading these schemas is simply a question of executing the SQL statements they contain. By definition, this will create a perfect copy of the database's structure. Using the +:sql+ schema format will, however, prevent loading the schema into a RDBMS other than the one used to create it. +Instead of using Active Record's schema dumper, the database's structure will be dumped using a tool specific to the database (via the +db:structure:dump+ Rake task) into +db/structure.sql+. For example, for the PostgreSQL RDBMS, the +pg_dump+ utility is used. For MySQL, this file will contain the output of +SHOW CREATE TABLE+ for the various tables. Loading these schemas is simply a question of executing the SQL statements they contain. By definition, this will create a perfect copy of the database's structure. Using the +:sql+ schema format will, however, prevent loading the schema into a RDBMS other than the one used to create it. h4. Schema Dumps and Source Control -- cgit v1.2.3 From 8d83e339fc91bfba51bb30384efc1bca6a099a53 Mon Sep 17 00:00:00 2001 From: gregolsen Date: Thu, 17 Nov 2011 22:17:33 +0200 Subject: updating API docstring so that user can infer default value --- activesupport/lib/active_support/core_ext/date/calculations.rb | 4 ++-- activesupport/lib/active_support/core_ext/time/calculations.rb | 4 ++-- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index 26a99658cc..c6d5f29690 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -192,13 +192,13 @@ class Date alias :sunday :end_of_week alias :at_end_of_week :end_of_week - # Returns a new Date/DateTime representing the start of the given day in the previous week (default is Monday). + # Returns a new Date/DateTime representing the start of the given day in the previous week (default is :monday). def prev_week(day = :monday) result = (self - 7).beginning_of_week + DAYS_INTO_WEEK[day] self.acts_like?(:time) ? result.change(:hour => 0) : result end - # Returns a new Date/DateTime representing the start of the given day in next week (default is Monday). + # Returns a new Date/DateTime representing the start of the given day in next week (default is :monday). def next_week(day = :monday) result = (self + 7).beginning_of_week + DAYS_INTO_WEEK[day] self.acts_like?(:time) ? result.change(:hour => 0) : result diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index 372dd69212..43cd103481 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -176,12 +176,12 @@ class Time end alias :at_end_of_week :end_of_week - # Returns a new Time representing the start of the given day in the previous week (default is Monday). + # Returns a new Time representing the start of the given day in the previous week (default is :monday). def prev_week(day = :monday) ago(1.week).beginning_of_week.since(DAYS_INTO_WEEK[day].day).change(:hour => 0) end - # Returns a new Time representing the start of the given day in next week (default is Monday). + # Returns a new Time representing the start of the given day in next week (default is :monday). def next_week(day = :monday) since(1.week).beginning_of_week.since(DAYS_INTO_WEEK[day].day).change(:hour => 0) end -- cgit v1.2.3 From e37597072587e24a1daf62191fa22e7dbbeaffea Mon Sep 17 00:00:00 2001 From: Philip Arndt Date: Fri, 18 Nov 2011 10:17:16 +1300 Subject: Fixed typo: expect -> expected --- activesupport/lib/active_support/dependencies.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index b3ac271778..db90de4682 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -428,7 +428,7 @@ module ActiveSupport #:nodoc: end # Attempt to autoload the provided module name by searching for a directory - # matching the expect path suffix. If found, the module is created and assigned + # matching the expected path suffix. If found, the module is created and assigned # to +into+'s constants with the name +const_name+. Provided that the directory # was loaded from a reloadable base path, it is added to the set of constants # that are to be unloaded. -- cgit v1.2.3 From d57d8098fc269a26ea0051a9027a33af1a9a4b2b Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Thu, 17 Nov 2011 23:07:06 +0100 Subject: warn the user values are directly interpolated into _html translation strings --- actionpack/lib/action_view/helpers/translation_helper.rb | 2 ++ railties/guides/source/i18n.textile | 12 ++++++++++++ 2 files changed, 14 insertions(+) diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index be64dc823e..0e6c3c5724 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -43,6 +43,8 @@ module ActionView # a safe HTML string that won't be escaped by other HTML helper methods. This # naming convention helps to identify translations that include HTML tags so that # you know what kind of output to expect when you call translate in a template. + # Note however that rule extends to interpolated values, so you are responsible + # for passing them already escaped in the call, if they need to be. def translate(key, options = {}) options.merge!(:rescue_format => :html) unless options.key?(:rescue_format) translation = I18n.translate(scope_key_by_partial(key), options) diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile index 2d4cc13571..43afa6c9e2 100644 --- a/railties/guides/source/i18n.textile +++ b/railties/guides/source/i18n.textile @@ -634,6 +634,18 @@ en: !images/i18n/demo_html_safe.png(i18n demo html safe)! +Please note that values are interpolated directly into the translation. +If they need to be escaped you need to pass them already escaped in the +t+ call. + + +# config/locales/en.yml +en: + welcome_html: Welcome %{name}! + +<%# Note the call to h() to avoid injection %> +<%= t('welcome_html', :name => h(user.name)) %> + + h3. How to Store your Custom Translations The Simple backend shipped with Active Support allows you to store translations in both plain Ruby and YAML format. [2] -- cgit v1.2.3 From a437986f433a775c7c370ad3f0273938015699df Mon Sep 17 00:00:00 2001 From: Aaron Patterson Date: Thu, 17 Nov 2011 14:41:53 -0800 Subject: allow people to set a local .Gemfile so that things like ruby-debug are not required for regular development --- .gitignore | 1 + Gemfile | 7 ++----- 2 files changed, 3 insertions(+), 5 deletions(-) diff --git a/.gitignore b/.gitignore index aa14cee911..52a431867c 100644 --- a/.gitignore +++ b/.gitignore @@ -2,6 +2,7 @@ # Check out http://help.github.com/ignore-files/ for how to set that up. debug.log +.Gemfile /.bundle /.rbenv-version /.rvmrc diff --git a/Gemfile b/Gemfile index 97605e32b5..b0fbb3b310 100644 --- a/Gemfile +++ b/Gemfile @@ -36,13 +36,11 @@ gem "memcache-client", ">= 1.8.5" platforms :mri_18 do gem "system_timer" - gem "ruby-debug", ">= 0.10.3" unless ENV['TRAVIS'] gem "json" end -platforms :mri_19 do - gem "ruby-debug19", :require => "ruby-debug" unless ENV['TRAVIS'] || RUBY_VERSION >= "2.0" -end +# Add your own local bundler stuff +instance_eval File.read ".Gemfile" if File.exists? ".Gemfile" platforms :mri do group :test do @@ -69,7 +67,6 @@ platforms :ruby do end platforms :jruby do - gem "ruby-debug", ">= 0.10.3" gem "json" gem "activerecord-jdbcsqlite3-adapter", ">= 1.2.0" -- cgit v1.2.3 From 1079724fe643fe63e6d58a37274c2cf0ff172a8b Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Thu, 17 Nov 2011 23:59:19 +0100 Subject: Revert "warn the user values are directly interpolated into _html translation strings" Reason: After another round of discussion, it has been decided to let interpolation deal with unsafe strings as it should do. This reverts commit d57d8098fc269a26ea0051a9027a33af1a9a4b2b. --- actionpack/lib/action_view/helpers/translation_helper.rb | 2 -- railties/guides/source/i18n.textile | 12 ------------ 2 files changed, 14 deletions(-) diff --git a/actionpack/lib/action_view/helpers/translation_helper.rb b/actionpack/lib/action_view/helpers/translation_helper.rb index 0e6c3c5724..be64dc823e 100644 --- a/actionpack/lib/action_view/helpers/translation_helper.rb +++ b/actionpack/lib/action_view/helpers/translation_helper.rb @@ -43,8 +43,6 @@ module ActionView # a safe HTML string that won't be escaped by other HTML helper methods. This # naming convention helps to identify translations that include HTML tags so that # you know what kind of output to expect when you call translate in a template. - # Note however that rule extends to interpolated values, so you are responsible - # for passing them already escaped in the call, if they need to be. def translate(key, options = {}) options.merge!(:rescue_format => :html) unless options.key?(:rescue_format) translation = I18n.translate(scope_key_by_partial(key), options) diff --git a/railties/guides/source/i18n.textile b/railties/guides/source/i18n.textile index 43afa6c9e2..2d4cc13571 100644 --- a/railties/guides/source/i18n.textile +++ b/railties/guides/source/i18n.textile @@ -634,18 +634,6 @@ en: !images/i18n/demo_html_safe.png(i18n demo html safe)! -Please note that values are interpolated directly into the translation. -If they need to be escaped you need to pass them already escaped in the +t+ call. - - -# config/locales/en.yml -en: - welcome_html: Welcome %{name}! - -<%# Note the call to h() to avoid injection %> -<%= t('welcome_html', :name => h(user.name)) %> - - h3. How to Store your Custom Translations The Simple backend shipped with Active Support allows you to store translations in both plain Ruby and YAML format. [2] -- cgit v1.2.3 From e8d57f361a9982382f75449ec0d65d6c798b9ce2 Mon Sep 17 00:00:00 2001 From: lest Date: Thu, 17 Nov 2011 18:29:55 +0300 Subject: _html translation should escape interpolated arguments --- actionpack/CHANGELOG.md | 14 ++++++++++++++ actionpack/lib/action_view/helpers/translation_helper.rb | 13 +++++++++---- actionpack/test/template/translation_helper_test.rb | 6 ++++++ 3 files changed, 29 insertions(+), 4 deletions(-) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index 4c265c41d8..9d847c763b 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -64,6 +64,20 @@ ## Rails 3.1.2 (unreleased) ## +* Fix XSS security vulnerability in the `translate` helper method. When using interpolation + in combination with HTML-safe translations, the interpolated input would not get HTML + escaped. *GH 3664* + + Before: + + translate('foo_html', :something => '\n}, javascript_include_tag(:application, :debug => true) + assert_match %r{}, + javascript_include_tag('jquery.plugin', :digest => false) + @config.assets.compile = true @config.assets.debug = true assert_match %r{}, -- cgit v1.2.3 From 3a1d51959bf569a7419fe8ab9416b338334b4800 Mon Sep 17 00:00:00 2001 From: lest Date: Tue, 22 Nov 2011 17:36:58 +0300 Subject: deprecation warning, changelog entry --- actionpack/CHANGELOG.md | 4 ++++ actionpack/lib/action_dispatch/middleware/show_exceptions.rb | 4 +++- 2 files changed, 7 insertions(+), 1 deletion(-) diff --git a/actionpack/CHANGELOG.md b/actionpack/CHANGELOG.md index dc52262503..fe422f71d5 100644 --- a/actionpack/CHANGELOG.md +++ b/actionpack/CHANGELOG.md @@ -1,5 +1,9 @@ ## Rails 3.2.0 (unreleased) ## +* Refactor ActionDispatch::ShowExceptions. Controller is responsible for choice to show exceptions. *Sergey Nartimov* + + It's possible to override +show_detailed_exceptions?+ in controllers to specify which requests should provide debugging information on errors. + * Responders now return 204 No Content for API requests without a response body (as in the new scaffold) *José Valim* * Added ActionDispatch::RequestId middleware that'll make a unique X-Request-Id header available to the response and enables the ActionDispatch::Request#uuid method. This makes it easy to trace requests from end-to-end in the stack and to identify individual requests in mixed logs like Syslog *DHH* diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index 569063f4db..52dce4cc81 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -2,6 +2,7 @@ require 'active_support/core_ext/exception' require 'action_controller/metal/exceptions' require 'active_support/notifications' require 'action_dispatch/http/request' +require 'active_support/deprecation' module ActionDispatch # This middleware rescues any exception returned by the application and renders @@ -38,7 +39,8 @@ module ActionDispatch "application's log file and/or the web server's log file to find out what " << "went wrong."]] - def initialize(app) + def initialize(app, consider_all_requests_local = nil) + ActiveSupport::Deprecation.warn "Passing consider_all_requests_local option to ActionDispatch::ShowExceptions middleware no longer works" unless consider_all_requests_local.nil? @app = app end -- cgit v1.2.3 From 5c61b7230eaa316ce64f4031234b425c35668988 Mon Sep 17 00:00:00 2001 From: Vijay Dev Date: Tue, 22 Nov 2011 23:14:13 +0530 Subject: Revert "Added links to the 'Rails Initialization Guide', marked as a work in progress" This reverts commit 6b4939c3fd22924ee3d906f08fe739d848f80a3d. Reason: This guide is still incomplete and is not yet reviewed or ready to go to the index. --- railties/guides/source/index.html.erb | 4 ---- railties/guides/source/layout.html.erb | 1 - 2 files changed, 5 deletions(-) diff --git a/railties/guides/source/index.html.erb b/railties/guides/source/index.html.erb index a8e12174ed..c9a8c4fa5c 100644 --- a/railties/guides/source/index.html.erb +++ b/railties/guides/source/index.html.erb @@ -134,10 +134,6 @@ Ruby on Rails Guides <%= guide('Asset Pipeline', 'asset_pipeline.html') do %>

This guide documents the asset pipeline.

<% end %> - -<%= guide('The Rails Initialization Process', 'initialization.html', :work_in_progress => true) do %> -

This guide explains the internals of the Rails initialization process as of Rails 3.1

-<% end %>

Extending Rails

diff --git a/railties/guides/source/layout.html.erb b/railties/guides/source/layout.html.erb index 83a17988ab..4c979888b7 100644 --- a/railties/guides/source/layout.html.erb +++ b/railties/guides/source/layout.html.erb @@ -72,7 +72,6 @@
Rails Command Line Tools and Rake Tasks
Caching with Rails
Asset Pipeline
-
Rails Initialization Process
Extending Rails
The Basics of Creating Rails Plugins
-- cgit v1.2.3 From 05e02deb686fc21f99c2d1dcf3abc987796e0e19 Mon Sep 17 00:00:00 2001 From: Marc-Andre Lafortune Date: Tue, 22 Nov 2011 15:16:23 -0500 Subject: Make explicit the default media when calling stylesheet_tag and change the default generators. --- .../action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb | 3 +++ .../guides/code/getting_started/app/views/layouts/application.html.erb | 2 +- .../rails/app/templates/app/views/layouts/application.html.erb.tt | 2 +- .../templates/app/views/layouts/%name%/application.html.erb.tt | 2 +- 4 files changed, 6 insertions(+), 3 deletions(-) diff --git a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb index 343153c8c5..41958c6559 100644 --- a/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb +++ b/actionpack/lib/action_view/helpers/asset_tag_helpers/stylesheet_tag_helpers.rb @@ -68,6 +68,9 @@ module ActionView # Returns a stylesheet link tag for the sources specified as arguments. If # you don't specify an extension, .css will be appended automatically. # You can modify the link attributes by passing a hash as the last argument. + # For historical reasons, the 'media' attribute will always be present and defaults + # to "screen", so you must explicitely set it to "all" for the stylesheet(s) to + # apply to all media types. # # ==== Examples # stylesheet_link_tag "style" # => diff --git a/railties/guides/code/getting_started/app/views/layouts/application.html.erb b/railties/guides/code/getting_started/app/views/layouts/application.html.erb index 1e1e4b9a99..7fd6b4f516 100644 --- a/railties/guides/code/getting_started/app/views/layouts/application.html.erb +++ b/railties/guides/code/getting_started/app/views/layouts/application.html.erb @@ -2,7 +2,7 @@ Blog - <%= stylesheet_link_tag "application" %> + <%= stylesheet_link_tag "application", :media => "all" %> <%= javascript_include_tag "application" %> <%= csrf_meta_tags %> diff --git a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt index c63d1b6ac5..bba96a7431 100644 --- a/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt +++ b/railties/lib/rails/generators/rails/app/templates/app/views/layouts/application.html.erb.tt @@ -2,7 +2,7 @@ <%= camelized %> - <%%= stylesheet_link_tag "application" %> + <%%= stylesheet_link_tag "application", :media => "all" %> <%%= javascript_include_tag "application" %> <%%= csrf_meta_tags %> diff --git a/railties/lib/rails/generators/rails/plugin_new/templates/app/views/layouts/%name%/application.html.erb.tt b/railties/lib/rails/generators/rails/plugin_new/templates/app/views/layouts/%name%/application.html.erb.tt index 01550dec2f..bd983fb90f 100644 --- a/railties/lib/rails/generators/rails/plugin_new/templates/app/views/layouts/%name%/application.html.erb.tt +++ b/railties/lib/rails/generators/rails/plugin_new/templates/app/views/layouts/%name%/application.html.erb.tt @@ -2,7 +2,7 @@ <%= camelized %> - <%%= stylesheet_link_tag "<%= name %>/application" %> + <%%= stylesheet_link_tag "<%= name %>/application", :media => "all" %> <%%= javascript_include_tag "<%= name %>/application" %> <%%= csrf_meta_tags %> -- cgit v1.2.3 From a4912078c7d345a347f023ef28c0986458bdebf5 Mon Sep 17 00:00:00 2001 From: Marc-Andre Lafortune Date: Tue, 22 Nov 2011 15:37:16 -0500 Subject: Fix inconsistencies with Time{WithZone}#{hash,eql?} --- .../lib/active_support/core_ext/time/calculations.rb | 10 ++++++++++ activesupport/lib/active_support/time_with_zone.rb | 7 +++++-- activesupport/test/core_ext/time_ext_test.rb | 6 ++++++ activesupport/test/core_ext/time_with_zone_test.rb | 11 +++++++++-- 4 files changed, 30 insertions(+), 4 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index 372dd69212..7a55086452 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -312,4 +312,14 @@ class Time end alias_method :compare_without_coercion, :<=> alias_method :<=>, :compare_with_coercion + + # Layers additional behavior on Time#eql? so that ActiveSupport::TimeWithZone instances + # can be eql? to an equivalent Time + def eql_with_coercion(other) + # if other is an ActiveSupport::TimeWithZone, coerce a Time instance from it so we can do eql? comparison + other = other.comparable_time if other.respond_to?(:comparable_time) + eql_without_coercion(other) + end + alias_method :eql_without_coercion, :eql? + alias_method :eql?, :eql_with_coercion end diff --git a/activesupport/lib/active_support/time_with_zone.rb b/activesupport/lib/active_support/time_with_zone.rb index 63279d0e6d..d3adf671a0 100644 --- a/activesupport/lib/active_support/time_with_zone.rb +++ b/activesupport/lib/active_support/time_with_zone.rb @@ -203,7 +203,11 @@ module ActiveSupport end def eql?(other) - utc == other + utc.eql?(other) + end + + def hash + utc.hash end def +(other) @@ -277,7 +281,6 @@ module ActiveSupport def to_i utc.to_i end - alias_method :hash, :to_i alias_method :tv_sec, :to_i # A TimeWithZone acts like a Time, so just return +self+. diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index ab9be4b18b..e7090eed08 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -744,6 +744,12 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase assert_equal(-1, Time.utc(2000) <=> ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1, 0, 0, 1), ActiveSupport::TimeZone['UTC'] )) end + def test_eql? + assert_equal true, Time.utc(2000).eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['UTC']) ) + assert_equal true, Time.utc(2000).eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]) ) + assert_equal false,Time.utc(2000, 1, 1, 0, 0, 1).eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone['UTC']) ) + end + def test_minus_with_time_with_zone assert_equal 86_400.0, Time.utc(2000, 1, 2) - ActiveSupport::TimeWithZone.new( Time.utc(2000, 1, 1), ActiveSupport::TimeZone['UTC'] ) end diff --git a/activesupport/test/core_ext/time_with_zone_test.rb b/activesupport/test/core_ext/time_with_zone_test.rb index b2309ae806..9d9e411c28 100644 --- a/activesupport/test/core_ext/time_with_zone_test.rb +++ b/activesupport/test/core_ext/time_with_zone_test.rb @@ -200,8 +200,15 @@ class TimeWithZoneTest < Test::Unit::TestCase end def test_eql? - assert @twz.eql?(Time.utc(2000)) - assert @twz.eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]) ) + assert_equal true, @twz.eql?(Time.utc(2000)) + assert_equal true, @twz.eql?( ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]) ) + assert_equal false, @twz.eql?( Time.utc(2000, 1, 1, 0, 0, 1) ) + assert_equal false, @twz.eql?( DateTime.civil(1999, 12, 31, 23, 59, 59) ) + end + + def test_hash + assert_equal Time.utc(2000).hash, @twz.hash + assert_equal Time.utc(2000).hash, ActiveSupport::TimeWithZone.new(Time.utc(2000), ActiveSupport::TimeZone["Hawaii"]).hash end def test_plus_with_integer -- cgit v1.2.3 From 745b7a14377607c5fddb8cd1151d69267095242c Mon Sep 17 00:00:00 2001 From: Marc Chung Date: Tue, 22 Nov 2011 17:04:19 -0700 Subject: - Avoid using .first since it will create an additional query. - Handle scenario where @posts is empty. --- actionpack/lib/action_view/helpers/atom_feed_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb index 39c37b25dc..73824dc1f8 100644 --- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb +++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb @@ -32,7 +32,7 @@ module ActionView # app/views/posts/index.atom.builder: # atom_feed do |feed| # feed.title("My great blog!") - # feed.updated(@posts.first.created_at) + # feed.updated(@posts[0].created_at) if @posts.length > 0 # # @posts.each do |post| # feed.entry(post) do |entry| -- cgit v1.2.3 From d78a7026fcd5eb0cd91e3da2a125ddfcf53e7a7a Mon Sep 17 00:00:00 2001 From: Sergey Parizhskiy Date: Wed, 23 Nov 2011 14:10:30 +0200 Subject: improved code stats calculation, check on multiline comments and rewrite regexps according to a class naming convention --- railties/lib/rails/code_statistics.rb | 17 ++++++++++++++--- 1 file changed, 14 insertions(+), 3 deletions(-) diff --git a/railties/lib/rails/code_statistics.rb b/railties/lib/rails/code_statistics.rb index e6822b75b7..435ea83ad8 100644 --- a/railties/lib/rails/code_statistics.rb +++ b/railties/lib/rails/code_statistics.rb @@ -38,11 +38,22 @@ class CodeStatistics #:nodoc: next unless file_name =~ pattern f = File.open(directory + "/" + file_name) - + comment_started = false while line = f.gets stats["lines"] += 1 - stats["classes"] += 1 if line =~ /class [A-Z]/ - stats["methods"] += 1 if line =~ /def [a-z]/ + if(comment_started) + if line =~ /^=end/ + comment_started = false + end + next + else + if line =~ /^=begin/ + comment_started = true + next + end + end + stats["classes"] += 1 if line =~ /^\s*class\s+[_A-Z]/ + stats["methods"] += 1 if line =~ /^\s*def\s+[_a-z]/ stats["codelines"] += 1 unless line =~ /^\s*$/ || line =~ /^\s*#/ end end -- cgit v1.2.3 From 453f5534b4a8517c6fd39702fc98f0c6f1d5fd9e Mon Sep 17 00:00:00 2001 From: kennyj Date: Thu, 24 Nov 2011 00:10:34 +0900 Subject: Warnings removed. (ambiguous first argument) --- actionpack/test/controller/show_exceptions_test.rb | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/actionpack/test/controller/show_exceptions_test.rb b/actionpack/test/controller/show_exceptions_test.rb index 39245e9574..74067cb895 100644 --- a/actionpack/test/controller/show_exceptions_test.rb +++ b/actionpack/test/controller/show_exceptions_test.rb @@ -22,7 +22,7 @@ module ShowExceptions ['127.0.0.1', '127.0.0.127', '::1', '0:0:0:0:0:0:0:1', '0:0:0:0:0:0:0:1%0'].each do |ip_address| self.remote_addr = ip_address get '/' - assert_match /boom/, body + assert_match(/boom/, body) end end @@ -31,7 +31,7 @@ module ShowExceptions @app = ShowExceptionsController.action(:boom) self.remote_addr = '208.77.188.166' get '/' - assert_match /boom/, body + assert_match(/boom/, body) end end @@ -53,7 +53,7 @@ module ShowExceptions test 'show diagnostics message' do @app = ShowExceptionsOverridenController.action(:boom) get '/', {'detailed' => '1'} - assert_match /boom/, body + assert_match(/boom/, body) end end end -- cgit v1.2.3 From ea70e027b63a1b8bfe4087a4de978ad4eef5575b Mon Sep 17 00:00:00 2001 From: kennyj Date: Wed, 23 Nov 2011 23:49:43 +0900 Subject: Remove unreachable code, and add additional testcases. --- actionpack/lib/action_dispatch/middleware/params_parser.rb | 7 +------ actionpack/test/dispatch/request/json_params_parsing_test.rb | 12 ++++++++++++ actionpack/test/dispatch/request/xml_params_parsing_test.rb | 12 ++++++++++++ 3 files changed, 25 insertions(+), 6 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/params_parser.rb b/actionpack/lib/action_dispatch/middleware/params_parser.rb index d4208ca96e..84e3dd16dd 100644 --- a/actionpack/lib/action_dispatch/middleware/params_parser.rb +++ b/actionpack/lib/action_dispatch/middleware/params_parser.rb @@ -54,12 +54,7 @@ module ActionDispatch rescue Exception => e # YAML, XML or Ruby code block errors logger.debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}" - raise - { "body" => request.raw_post, - "content_type" => request.content_mime_type, - "content_length" => request.content_length, - "exception" => "#{e.message} (#{e.class})", - "backtrace" => e.backtrace } + raise e end def content_type_from_legacy_post_data_format_header(env) diff --git a/actionpack/test/dispatch/request/json_params_parsing_test.rb b/actionpack/test/dispatch/request/json_params_parsing_test.rb index d854d55173..d481a2df13 100644 --- a/actionpack/test/dispatch/request/json_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/json_params_parsing_test.rb @@ -45,6 +45,18 @@ class JsonParamsParsingTest < ActionDispatch::IntegrationTest end end + test "occurring a parse error if parsing unsuccessful" do + with_test_routing do + begin + $stderr = StringIO.new # suppress the log + json = "[\"person]\": {\"name\": \"David\"}}" + assert_raise(MultiJson::DecodeError) { post "/parse", json, {'CONTENT_TYPE' => 'application/json', 'action_dispatch.show_exceptions' => false} } + ensure + $stderr = STDERR + end + end + end + private def assert_parses(expected, actual, headers = {}) with_test_routing do diff --git a/actionpack/test/dispatch/request/xml_params_parsing_test.rb b/actionpack/test/dispatch/request/xml_params_parsing_test.rb index 38453dfe48..65a25557b7 100644 --- a/actionpack/test/dispatch/request/xml_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/xml_params_parsing_test.rb @@ -67,6 +67,18 @@ class XmlParamsParsingTest < ActionDispatch::IntegrationTest end end + test "occurring a parse error if parsing unsuccessful" do + with_test_routing do + begin + $stderr = StringIO.new # suppress the log + xml = "David" + assert_raise(REXML::ParseException) { post "/parse", xml, default_headers.merge('action_dispatch.show_exceptions' => false) } + ensure + $stderr = STDERR + end + end + end + test "parses multiple files" do xml = <<-end_body -- cgit v1.2.3 From d8e6dc9cf12096908a7a53dec07a397ba23b1088 Mon Sep 17 00:00:00 2001 From: Olek Janiszewski Date: Wed, 23 Nov 2011 18:06:15 +0100 Subject: Fix #3737 AS::expand_cache_key generates wrong key in certain situations `cache_key` method is never called when the argument is a 1-element array with something that responds to `cache_key` --- activesupport/lib/active_support/cache.rb | 14 ++++---------- activesupport/test/caching_test.rb | 12 ++++++++++-- 2 files changed, 14 insertions(+), 12 deletions(-) diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 2bf24558a6..cfd1a6dbe3 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -81,16 +81,10 @@ module ActiveSupport end expanded_cache_key << - if key.respond_to?(:cache_key) - key.cache_key - elsif key.is_a?(Array) - if key.size > 1 - key.collect { |element| expand_cache_key(element) }.to_param - else - key.first.to_param - end - elsif key - key.to_param + case + when key.respond_to?(:cache_key) then key.cache_key + when key.is_a?(Array) then key.map { |element| expand_cache_key(element) }.to_param + when key then key.to_param end.to_s expanded_cache_key diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index b1b6de0613..5c844b8122 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -12,10 +12,10 @@ class CacheKeyTest < ActiveSupport::TestCase begin ENV['RAILS_CACHE_ID'] = 'c99' assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key(:foo) - assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key([:foo]) + assert_equal 'c99/c99/foo', ActiveSupport::Cache.expand_cache_key([:foo]) assert_equal 'c99/c99/foo/c99/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar]) assert_equal 'nm/c99/foo', ActiveSupport::Cache.expand_cache_key(:foo, :nm) - assert_equal 'nm/c99/foo', ActiveSupport::Cache.expand_cache_key([:foo], :nm) + assert_equal 'nm/c99/c99/foo', ActiveSupport::Cache.expand_cache_key([:foo], :nm) assert_equal 'nm/c99/c99/foo/c99/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar], :nm) ensure ENV['RAILS_CACHE_ID'] = nil @@ -50,6 +50,14 @@ class CacheKeyTest < ActiveSupport::TestCase assert_equal 'foo_key', ActiveSupport::Cache.expand_cache_key(key) end + def test_array_with_something_that_responds_to_cache_key + key = 'foo' + def key.cache_key + :foo_key + end + assert_equal 'foo_key', ActiveSupport::Cache.expand_cache_key([key]) + end + end class CacheStoreSettingTest < ActiveSupport::TestCase -- cgit v1.2.3 From a650dd05f82028e8d1310f1b68b7bc430ea47dcf Mon Sep 17 00:00:00 2001 From: Olek Janiszewski Date: Wed, 23 Nov 2011 18:11:36 +0100 Subject: Fix #3737 AS::expand_cache_key generates wrong key in certain situations (part 2) `nil` and `false` both expand to `""` (empty string), while `true` expands to `"true"`; `false` should expand to `"false"` --- activesupport/lib/active_support/cache.rb | 2 +- activesupport/test/caching_test.rb | 15 +++++++++++++-- 2 files changed, 14 insertions(+), 3 deletions(-) diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index cfd1a6dbe3..12eeff4f4b 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -84,7 +84,7 @@ module ActiveSupport case when key.respond_to?(:cache_key) then key.cache_key when key.is_a?(Array) then key.map { |element| expand_cache_key(element) }.to_param - when key then key.to_param + else key.to_param end.to_s expanded_cache_key diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index 5c844b8122..8871b1b0e9 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -42,7 +42,7 @@ class CacheKeyTest < ActiveSupport::TestCase end end - def test_respond_to_cache_key + def test_expand_cache_key_respond_to_cache_key key = 'foo' def key.cache_key :foo_key @@ -50,7 +50,7 @@ class CacheKeyTest < ActiveSupport::TestCase assert_equal 'foo_key', ActiveSupport::Cache.expand_cache_key(key) end - def test_array_with_something_that_responds_to_cache_key + def test_expand_cache_key_array_with_something_that_responds_to_cache_key key = 'foo' def key.cache_key :foo_key @@ -58,6 +58,17 @@ class CacheKeyTest < ActiveSupport::TestCase assert_equal 'foo_key', ActiveSupport::Cache.expand_cache_key([key]) end + def test_expand_cache_key_of_nil + assert_equal '', ActiveSupport::Cache.expand_cache_key(nil) + end + + def test_expand_cache_key_of_false + assert_equal 'false', ActiveSupport::Cache.expand_cache_key(false) + end + + def test_expand_cache_key_of_true + assert_equal 'true', ActiveSupport::Cache.expand_cache_key(true) + end end class CacheStoreSettingTest < ActiveSupport::TestCase -- cgit v1.2.3 From 3ee0116c949dfc5760d60fe9c77168fb3868a4ec Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 23 Nov 2011 18:22:09 +0000 Subject: Optimize cache expansion by skipping rails cache id in nested keys. --- activesupport/lib/active_support/cache.rb | 114 ++++++++++++++++-------------- activesupport/test/caching_test.rb | 8 +-- 2 files changed, 64 insertions(+), 58 deletions(-) diff --git a/activesupport/lib/active_support/cache.rb b/activesupport/lib/active_support/cache.rb index 12eeff4f4b..07f5fcdeb3 100644 --- a/activesupport/lib/active_support/cache.rb +++ b/activesupport/lib/active_support/cache.rb @@ -25,69 +25,75 @@ module ActiveSupport autoload :LocalCache, 'active_support/cache/strategy/local_cache' end - # Creates a new CacheStore object according to the given options. - # - # If no arguments are passed to this method, then a new - # ActiveSupport::Cache::MemoryStore object will be returned. - # - # If you pass a Symbol as the first argument, then a corresponding cache - # store class under the ActiveSupport::Cache namespace will be created. - # For example: - # - # ActiveSupport::Cache.lookup_store(:memory_store) - # # => returns a new ActiveSupport::Cache::MemoryStore object - # - # ActiveSupport::Cache.lookup_store(:mem_cache_store) - # # => returns a new ActiveSupport::Cache::MemCacheStore object - # - # Any additional arguments will be passed to the corresponding cache store - # class's constructor: - # - # ActiveSupport::Cache.lookup_store(:file_store, "/tmp/cache") - # # => same as: ActiveSupport::Cache::FileStore.new("/tmp/cache") - # - # If the first argument is not a Symbol, then it will simply be returned: - # - # ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new) - # # => returns MyOwnCacheStore.new - def self.lookup_store(*store_option) - store, *parameters = *Array.wrap(store_option).flatten - - case store - when Symbol - store_class_name = store.to_s.camelize - store_class = - begin - require "active_support/cache/#{store}" - rescue LoadError => e - raise "Could not find cache store adapter for #{store} (#{e})" - else - ActiveSupport::Cache.const_get(store_class_name) - end - store_class.new(*parameters) - when nil - ActiveSupport::Cache::MemoryStore.new - else - store + class << self + # Creates a new CacheStore object according to the given options. + # + # If no arguments are passed to this method, then a new + # ActiveSupport::Cache::MemoryStore object will be returned. + # + # If you pass a Symbol as the first argument, then a corresponding cache + # store class under the ActiveSupport::Cache namespace will be created. + # For example: + # + # ActiveSupport::Cache.lookup_store(:memory_store) + # # => returns a new ActiveSupport::Cache::MemoryStore object + # + # ActiveSupport::Cache.lookup_store(:mem_cache_store) + # # => returns a new ActiveSupport::Cache::MemCacheStore object + # + # Any additional arguments will be passed to the corresponding cache store + # class's constructor: + # + # ActiveSupport::Cache.lookup_store(:file_store, "/tmp/cache") + # # => same as: ActiveSupport::Cache::FileStore.new("/tmp/cache") + # + # If the first argument is not a Symbol, then it will simply be returned: + # + # ActiveSupport::Cache.lookup_store(MyOwnCacheStore.new) + # # => returns MyOwnCacheStore.new + def lookup_store(*store_option) + store, *parameters = *Array.wrap(store_option).flatten + + case store + when Symbol + store_class_name = store.to_s.camelize + store_class = + begin + require "active_support/cache/#{store}" + rescue LoadError => e + raise "Could not find cache store adapter for #{store} (#{e})" + else + ActiveSupport::Cache.const_get(store_class_name) + end + store_class.new(*parameters) + when nil + ActiveSupport::Cache::MemoryStore.new + else + store + end end - end - def self.expand_cache_key(key, namespace = nil) - expanded_cache_key = namespace ? "#{namespace}/" : "" + def expand_cache_key(key, namespace = nil) + expanded_cache_key = namespace ? "#{namespace}/" : "" + + prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"] + if prefix + expanded_cache_key << "#{prefix}/" + end - prefix = ENV["RAILS_CACHE_ID"] || ENV["RAILS_APP_VERSION"] - if prefix - expanded_cache_key << "#{prefix}/" + expanded_cache_key << retrieve_cache_key(key) + expanded_cache_key end - expanded_cache_key << + private + + def retrieve_cache_key(key) case when key.respond_to?(:cache_key) then key.cache_key - when key.is_a?(Array) then key.map { |element| expand_cache_key(element) }.to_param + when key.is_a?(Array) then key.map { |element| retrieve_cache_key(element) }.to_param else key.to_param end.to_s - - expanded_cache_key + end end # An abstract cache store class. There are multiple cache store diff --git a/activesupport/test/caching_test.rb b/activesupport/test/caching_test.rb index 8871b1b0e9..5d7464c623 100644 --- a/activesupport/test/caching_test.rb +++ b/activesupport/test/caching_test.rb @@ -12,11 +12,11 @@ class CacheKeyTest < ActiveSupport::TestCase begin ENV['RAILS_CACHE_ID'] = 'c99' assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key(:foo) - assert_equal 'c99/c99/foo', ActiveSupport::Cache.expand_cache_key([:foo]) - assert_equal 'c99/c99/foo/c99/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar]) + assert_equal 'c99/foo', ActiveSupport::Cache.expand_cache_key([:foo]) + assert_equal 'c99/foo/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar]) assert_equal 'nm/c99/foo', ActiveSupport::Cache.expand_cache_key(:foo, :nm) - assert_equal 'nm/c99/c99/foo', ActiveSupport::Cache.expand_cache_key([:foo], :nm) - assert_equal 'nm/c99/c99/foo/c99/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar], :nm) + assert_equal 'nm/c99/foo', ActiveSupport::Cache.expand_cache_key([:foo], :nm) + assert_equal 'nm/c99/foo/bar', ActiveSupport::Cache.expand_cache_key([:foo, :bar], :nm) ensure ENV['RAILS_CACHE_ID'] = nil end -- cgit v1.2.3 From fd86a1b6b068df87164d5763bdcd4a323a1e76f4 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 23 Nov 2011 19:06:45 +0000 Subject: Rely on a public contract between railties instead of accessing railtie methods directly. --- .../abstract_controller/railties/routes_helpers.rb | 4 +- actionpack/lib/action_controller/railties/paths.rb | 7 ++-- .../test/activerecord/polymorphic_routes_test.rb | 6 +-- actionpack/test/lib/controller/fake_models.rb | 4 +- actionpack/test/template/form_helper_test.rb | 2 +- activemodel/lib/active_model/naming.rb | 24 ++++++------ activemodel/test/cases/naming_test.rb | 38 +++++++++++++++++-- activemodel/test/models/blog_post.rb | 8 +--- railties/lib/rails/application.rb | 4 ++ railties/lib/rails/engine.rb | 43 +++++++++++++--------- railties/lib/rails/railtie.rb | 2 +- railties/test/railties/engine_test.rb | 6 +-- 12 files changed, 94 insertions(+), 54 deletions(-) diff --git a/actionpack/lib/abstract_controller/railties/routes_helpers.rb b/actionpack/lib/abstract_controller/railties/routes_helpers.rb index dec1e9d6d9..6684f46f64 100644 --- a/actionpack/lib/abstract_controller/railties/routes_helpers.rb +++ b/actionpack/lib/abstract_controller/railties/routes_helpers.rb @@ -5,8 +5,8 @@ module AbstractController Module.new do define_method(:inherited) do |klass| super(klass) - if namespace = klass.parents.detect {|m| m.respond_to?(:_railtie) } - klass.send(:include, namespace._railtie.routes.url_helpers) + if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_routes_url_helpers) } + klass.send(:include, namespace.railtie_routes_url_helpers) else klass.send(:include, routes.url_helpers) end diff --git a/actionpack/lib/action_controller/railties/paths.rb b/actionpack/lib/action_controller/railties/paths.rb index 699c44c62c..bbe63149ad 100644 --- a/actionpack/lib/action_controller/railties/paths.rb +++ b/actionpack/lib/action_controller/railties/paths.rb @@ -6,13 +6,14 @@ module ActionController define_method(:inherited) do |klass| super(klass) - if namespace = klass.parents.detect {|m| m.respond_to?(:_railtie) } - paths = namespace._railtie.paths["app/helpers"].existent + if namespace = klass.parents.detect { |m| m.respond_to?(:railtie_helpers_paths) } + paths = namespace.railtie_helpers_paths else - paths = app.config.helpers_paths + paths = app.helpers_paths end klass.helpers_path = paths + if klass.superclass == ActionController::Base && ActionController::Base.include_all_helpers klass.helper :all end diff --git a/actionpack/test/activerecord/polymorphic_routes_test.rb b/actionpack/test/activerecord/polymorphic_routes_test.rb index 20d11377f6..fc829aa6b4 100644 --- a/actionpack/test/activerecord/polymorphic_routes_test.rb +++ b/actionpack/test/activerecord/polymorphic_routes_test.rb @@ -34,10 +34,8 @@ module Blog set_table_name 'projects' end - def self._railtie - o = Object.new - def o.railtie_name; "blog" end - o + def self.use_relative_model_naming? + true end end diff --git a/actionpack/test/lib/controller/fake_models.rb b/actionpack/test/lib/controller/fake_models.rb index 363403092b..f2362714d7 100644 --- a/actionpack/test/lib/controller/fake_models.rb +++ b/actionpack/test/lib/controller/fake_models.rb @@ -175,8 +175,8 @@ class HashBackedAuthor < Hash end module Blog - def self._railtie - self + def self.use_relative_model_naming? + true end class Post < Struct.new(:title, :id) diff --git a/actionpack/test/template/form_helper_test.rb b/actionpack/test/template/form_helper_test.rb index 34486bb151..ccedcd7dac 100644 --- a/actionpack/test/template/form_helper_test.rb +++ b/actionpack/test/template/form_helper_test.rb @@ -740,7 +740,7 @@ class FormHelperTest < ActionView::TestCase assert_dom_equal expected, output_buffer end - def test_form_for_with_isolated_namespaced_model + def test_form_for_with_model_using_relative_model_naming form_for(@blog_post) do |f| concat f.text_field :title concat f.submit('Edit post') diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb index f16459ede2..2566920d63 100644 --- a/activemodel/lib/active_model/naming.rb +++ b/activemodel/lib/active_model/naming.rb @@ -13,18 +13,18 @@ module ActiveModel def initialize(klass, namespace = nil, name = nil) name ||= klass.name super(name) - @unnamespaced = self.sub(/^#{namespace.name}::/, '') if namespace - @klass = klass - @singular = _singularize(self).freeze - @plural = ActiveSupport::Inflector.pluralize(@singular).freeze - @element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self)).freeze - @human = ActiveSupport::Inflector.humanize(@element).freeze - @collection = ActiveSupport::Inflector.tableize(self).freeze + @unnamespaced = self.sub(/^#{namespace.name}::/, '') if namespace + @klass = klass + @singular = _singularize(self).freeze + @plural = ActiveSupport::Inflector.pluralize(@singular).freeze + @element = ActiveSupport::Inflector.underscore(ActiveSupport::Inflector.demodulize(self)).freeze + @human = ActiveSupport::Inflector.humanize(@element).freeze + @collection = ActiveSupport::Inflector.tableize(self).freeze @partial_path = "#{@collection}/#{@element}".freeze - @param_key = (namespace ? _singularize(@unnamespaced) : @singular).freeze - @route_key = (namespace ? ActiveSupport::Inflector.pluralize(@param_key) : @plural).freeze - @i18n_key = self.underscore.to_sym + @param_key = (namespace ? _singularize(@unnamespaced) : @singular).freeze + @route_key = (namespace ? ActiveSupport::Inflector.pluralize(@param_key) : @plural).freeze + @i18n_key = self.underscore.to_sym end # Transform the model name into a more humane format, using I18n. By default, @@ -79,7 +79,9 @@ module ActiveModel # used to retrieve all kinds of naming-related information. def model_name @_model_name ||= begin - namespace = self.parents.detect { |n| n.respond_to?(:_railtie) } + namespace = self.parents.detect do |n| + n.respond_to?(:use_relative_model_naming?) && n.use_relative_model_naming? + end ActiveModel::Name.new(self, namespace) end end diff --git a/activemodel/test/cases/naming_test.rb b/activemodel/test/cases/naming_test.rb index 5f943729dd..a5ee2d6090 100644 --- a/activemodel/test/cases/naming_test.rb +++ b/activemodel/test/cases/naming_test.rb @@ -74,10 +74,6 @@ class NamingWithNamespacedModelInIsolatedNamespaceTest < ActiveModel::TestCase def test_param_key assert_equal 'post', @model_name.param_key end - - def test_recognizing_namespace - assert_equal 'Post', Blog::Post.model_name.instance_variable_get("@unnamespaced") - end end class NamingWithNamespacedModelInSharedNamespaceTest < ActiveModel::TestCase @@ -160,6 +156,40 @@ class NamingWithSuppliedModelNameTest < ActiveModel::TestCase end end +class NamingUsingRelativeModelNameTest < ActiveModel::TestCase + def setup + @model_name = Blog::Post.model_name + end + + def test_singular + assert_equal 'blog_post', @model_name.singular + end + + def test_plural + assert_equal 'blog_posts', @model_name.plural + end + + def test_element + assert_equal 'post', @model_name.element + end + + def test_collection + assert_equal 'blog/posts', @model_name.collection + end + + def test_human + assert_equal 'Post', @model_name.human + end + + def test_route_key + assert_equal 'posts', @model_name.route_key + end + + def test_param_key + assert_equal 'post', @model_name.param_key + end +end + class NamingHelpersTest < Test::Unit::TestCase def setup @klass = Contact diff --git a/activemodel/test/models/blog_post.rb b/activemodel/test/models/blog_post.rb index d289177259..46eba857df 100644 --- a/activemodel/test/models/blog_post.rb +++ b/activemodel/test/models/blog_post.rb @@ -1,10 +1,6 @@ module Blog - def self._railtie - Object.new - end - - def self.table_name_prefix - "blog_" + def self.use_relative_model_naming? + true end class Post diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 817fa39cab..0b72a7ed84 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -141,6 +141,10 @@ module Rails self end + def helpers_paths + config.helpers_paths + end + protected alias :build_middleware_stack :app diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index d652c6b7fe..335a0fb1b5 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -371,20 +371,28 @@ module Rails self.routes.default_scope = { :module => ActiveSupport::Inflector.underscore(mod.name) } self.isolated = true - unless mod.respond_to?(:_railtie) - name = engine_name - _railtie = self + unless mod.respond_to?(:railtie_namespace) + name, railtie = engine_name, self + mod.singleton_class.instance_eval do - define_method(:_railtie) do - _railtie - end + define_method(:railtie_namespace) { railtie } unless mod.respond_to?(:table_name_prefix) - define_method(:table_name_prefix) do - "#{name}_" - end + define_method(:table_name_prefix) { "#{name}_" } + end + + unless mod.respond_to?(:use_relative_model_naming?) + class_eval "def use_relative_model_naming?; true; end", __FILE__, __LINE__ + end + + unless mod.respond_to?(:railtie_helpers_paths) + define_method(:railtie_helpers_paths) { railtie.helpers_paths } + end + + unless mod.respond_to?(:railtie_routes_url_helpers) + define_method(:railtie_routes_url_helpers) { railtie.routes_url_helpers } end - end + end end end @@ -429,13 +437,6 @@ module Rails def helpers @helpers ||= begin helpers = Module.new - - helpers_paths = if config.respond_to?(:helpers_paths) - config.helpers_paths - else - paths["app/helpers"].existent - end - all = ActionController::Base.all_helpers_from_path(helpers_paths) ActionController::Base.modules_for_helpers(all).each do |mod| helpers.send(:include, mod) @@ -444,6 +445,14 @@ module Rails end end + def helpers_paths + paths["app/helpers"].existent + end + + def routes_url_helpers + routes.url_helpers + end + def app @app ||= begin config.middleware = config.middleware.merge_into(default_middleware_stack) diff --git a/railties/lib/rails/railtie.rb b/railties/lib/rails/railtie.rb index e8fb1f3d98..f0991b99af 100644 --- a/railties/lib/rails/railtie.rb +++ b/railties/lib/rails/railtie.rb @@ -196,7 +196,7 @@ module Rails end def railtie_namespace - @railtie_namespace ||= self.class.parents.detect { |n| n.respond_to?(:_railtie) } + @railtie_namespace ||= self.class.parents.detect { |n| n.respond_to?(:railtie_namespace) } end end end diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index 22dbcf9644..e8081c977f 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -323,7 +323,7 @@ module RailtiesTest assert_equal "bukkits_", Bukkits.table_name_prefix assert_equal "bukkits", Bukkits::Engine.engine_name - assert_equal Bukkits._railtie, Bukkits::Engine + assert_equal Bukkits.railtie_namespace, Bukkits::Engine assert ::Bukkits::MyMailer.method_defined?(:foo_path) assert !::Bukkits::MyMailer.method_defined?(:bar_path) @@ -467,7 +467,7 @@ module RailtiesTest assert_nil Rails.application.load_seed end - test "using namespace more than once on one module should not overwrite _railtie method" do + test "using namespace more than once on one module should not overwrite railtie_namespace method" do @plugin.write "lib/bukkits.rb", <<-RUBY module AppTemplate class Engine < ::Rails::Engine @@ -484,7 +484,7 @@ module RailtiesTest boot_rails - assert_equal AppTemplate._railtie, AppTemplate::Engine + assert_equal AppTemplate.railtie_namespace, AppTemplate::Engine end test "properly reload routes" do -- cgit v1.2.3 From 40b19e063592fc30705f17aafe6a458e7b622ff2 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Tue, 22 Nov 2011 23:26:27 +0100 Subject: Allow to change engine's loading priority with config.railties_order= --- railties/lib/rails/application.rb | 22 +++++ railties/lib/rails/application/configuration.rb | 5 +- railties/lib/rails/engine.rb | 24 ++++- railties/test/railties/engine_test.rb | 126 ++++++++++++++++++++++++ 4 files changed, 173 insertions(+), 4 deletions(-) diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 0b72a7ed84..847445d29f 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -127,6 +127,28 @@ module Rails }) end + def ordered_railties + @ordered_railties ||= begin + order = config.railties_order.map do |railtie| + if railtie == :main_app + self + elsif railtie.respond_to?(:instance) + railtie.instance + else + railtie + end + end + + all = (railties.all - order) + all.push(self) unless all.include?(self) + order.push(:all) unless order.include?(:all) + + index = order.index(:all) + order[index] = all + order.reverse.flatten + end + end + def initializers Bootstrap.initializers_for(self) + super + diff --git a/railties/lib/rails/application/configuration.rb b/railties/lib/rails/application/configuration.rb index 8f5b28faf8..e95b0f5495 100644 --- a/railties/lib/rails/application/configuration.rb +++ b/railties/lib/rails/application/configuration.rb @@ -10,8 +10,8 @@ module Rails :dependency_loading, :filter_parameters, :force_ssl, :helpers_paths, :logger, :log_tags, :preload_frameworks, :reload_plugins, :secret_token, :serve_static_assets, - :ssl_options, :static_cache_control, :session_options, - :time_zone, :whiny_nils + :ssl_options, :static_cache_control, :session_options, + :time_zone, :whiny_nils, :railties_order attr_writer :log_level attr_reader :encoding @@ -35,6 +35,7 @@ module Rails @middleware = app_middleware @generators = app_generators @cache_store = [ :file_store, "#{root}/tmp/cache/" ] + @railties_order = [:all] @assets = ActiveSupport::OrderedOptions.new @assets.enabled = false diff --git a/railties/lib/rails/engine.rb b/railties/lib/rails/engine.rb index 335a0fb1b5..5c1af99fe2 100644 --- a/railties/lib/rails/engine.rb +++ b/railties/lib/rails/engine.rb @@ -330,6 +330,17 @@ module Rails # # MyEngine::Engine.load_seed # + # == Loading priority + # + # In order to change engine's priority you can use config.railties_order in main application. + # It will affect the priority of loading views, helpers, assets and all the other files + # related to engine or application. + # + # Example: + # + # # load Blog::Engine with highest priority, followed by application and other railties + # config.railties_order = [Blog::Engine, :main_app, :all] + # class Engine < Railtie autoload :Configuration, "rails/engine/configuration" autoload :Railties, "rails/engine/railties" @@ -480,10 +491,19 @@ module Rails @routes end + def ordered_railties + railties.all + [self] + end + def initializers initializers = [] - railties.all { |r| initializers += r.initializers } - initializers += super + ordered_railties.each do |r| + if r == self + initializers += super + else + initializers += r.initializers + end + end initializers end diff --git a/railties/test/railties/engine_test.rb b/railties/test/railties/engine_test.rb index e8081c977f..400cae98b2 100644 --- a/railties/test/railties/engine_test.rb +++ b/railties/test/railties/engine_test.rb @@ -667,6 +667,132 @@ module RailtiesTest assert_equal expected, methods end + test "setting priority for engines with config.railties_order" do + @blog = engine "blog" do |plugin| + plugin.write "lib/blog.rb", <<-RUBY + module Blog + class Engine < ::Rails::Engine + end + end + RUBY + end + + @plugin.write "lib/bukkits.rb", <<-RUBY + module Bukkits + class Engine < ::Rails::Engine + isolate_namespace Bukkits + end + end + RUBY + + controller "main", <<-RUBY + class MainController < ActionController::Base + def foo + render :inline => '<%= render :partial => "shared/foo" %>' + end + + def bar + render :inline => '<%= render :partial => "shared/bar" %>' + end + end + RUBY + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + match "/foo" => "main#foo" + match "/bar" => "main#bar" + end + RUBY + + @plugin.write "app/views/shared/_foo.html.erb", <<-RUBY + Bukkit's foo partial + RUBY + + app_file "app/views/shared/_foo.html.erb", <<-RUBY + App's foo partial + RUBY + + @blog.write "app/views/shared/_bar.html.erb", <<-RUBY + Blog's bar partial + RUBY + + app_file "app/views/shared/_bar.html.erb", <<-RUBY + App's bar partial + RUBY + + @plugin.write "app/assets/javascripts/foo.js", <<-RUBY + // Bukkit's foo js + RUBY + + app_file "app/assets/javascripts/foo.js", <<-RUBY + // App's foo js + RUBY + + @blog.write "app/assets/javascripts/bar.js", <<-RUBY + // Blog's bar js + RUBY + + app_file "app/assets/javascripts/bar.js", <<-RUBY + // App's bar js + RUBY + + add_to_config("config.railties_order = [:all, :main_app, Blog::Engine]") + + boot_rails + require "#{rails_root}/config/environment" + + get("/foo") + assert_equal "Bukkit's foo partial", last_response.body.strip + + get("/bar") + assert_equal "App's bar partial", last_response.body.strip + + get("/assets/foo.js") + assert_equal "// Bukkit's foo js\n;", last_response.body.strip + + get("/assets/bar.js") + assert_equal "// App's bar js\n;", last_response.body.strip + end + + test "railties_order adds :all with lowest priority if not given" do + @plugin.write "lib/bukkits.rb", <<-RUBY + module Bukkits + class Engine < ::Rails::Engine + end + end + RUBY + + controller "main", <<-RUBY + class MainController < ActionController::Base + def foo + render :inline => '<%= render :partial => "shared/foo" %>' + end + end + RUBY + + app_file "config/routes.rb", <<-RUBY + Rails.application.routes.draw do + match "/foo" => "main#foo" + end + RUBY + + @plugin.write "app/views/shared/_foo.html.erb", <<-RUBY + Bukkit's foo partial + RUBY + + app_file "app/views/shared/_foo.html.erb", <<-RUBY + App's foo partial + RUBY + + add_to_config("config.railties_order = [Bukkits::Engine]") + + boot_rails + require "#{rails_root}/config/environment" + + get("/foo") + assert_equal "Bukkit's foo partial", last_response.body.strip + end + private def app Rails.application -- cgit v1.2.3 From 98a1717e7c094d011c89ea1ed88673a595af2de8 Mon Sep 17 00:00:00 2001 From: lest Date: Wed, 23 Nov 2011 23:36:56 +0300 Subject: configuration option to always write cookie --- .../lib/action_dispatch/middleware/cookies.rb | 5 ++- actionpack/lib/action_dispatch/railtie.rb | 4 +- actionpack/test/dispatch/cookies_test.rb | 4 +- .../test/application/middleware/cookies_test.rb | 47 ++++++++++++++++++++++ 4 files changed, 56 insertions(+), 4 deletions(-) create mode 100644 railties/test/application/middleware/cookies_test.rb diff --git a/actionpack/lib/action_dispatch/middleware/cookies.rb b/actionpack/lib/action_dispatch/middleware/cookies.rb index a4ffd40a66..51cec41a34 100644 --- a/actionpack/lib/action_dispatch/middleware/cookies.rb +++ b/actionpack/lib/action_dispatch/middleware/cookies.rb @@ -243,10 +243,13 @@ module ActionDispatch @delete_cookies.clear end + mattr_accessor :always_write_cookie + self.always_write_cookie = false + private def write_cookie?(cookie) - @secure || !cookie[:secure] || defined?(Rails.env) && Rails.env.development? + @secure || !cookie[:secure] || always_write_cookie end end diff --git a/actionpack/lib/action_dispatch/railtie.rb b/actionpack/lib/action_dispatch/railtie.rb index 1af89858d1..f18ebabf29 100644 --- a/actionpack/lib/action_dispatch/railtie.rb +++ b/actionpack/lib/action_dispatch/railtie.rb @@ -10,10 +10,12 @@ module ActionDispatch config.action_dispatch.tld_length = 1 config.action_dispatch.ignore_accept_header = false config.action_dispatch.rack_cache = {:metastore => "rails:/", :entitystore => "rails:/", :verbose => true} - initializer "action_dispatch.configure" do |app| ActionDispatch::Http::URL.tld_length = app.config.action_dispatch.tld_length ActionDispatch::Request.ignore_accept_header = app.config.action_dispatch.ignore_accept_header + + config.action_dispatch.always_write_cookie = Rails.env.development? if config.action_dispatch.always_write_cookie.nil? + ActionDispatch::Cookies::CookieJar.always_write_cookie = config.action_dispatch.always_write_cookie end end end diff --git a/actionpack/test/dispatch/cookies_test.rb b/actionpack/test/dispatch/cookies_test.rb index 49da448001..3765b7eb44 100644 --- a/actionpack/test/dispatch/cookies_test.rb +++ b/actionpack/test/dispatch/cookies_test.rb @@ -210,8 +210,8 @@ class CookiesTest < ActionController::TestCase assert_equal({"user_name" => "david"}, @response.cookies) end - def test_setting_cookie_with_secure_in_development - Rails.env.stubs(:development?).returns(true) + def test_setting_cookie_with_secure_when_always_write_cookie_is_true + ActionDispatch::Cookies::CookieJar.any_instance.stubs(:always_write_cookie).returns(true) get :authenticate_with_secure assert_cookie_header "user_name=david; path=/; secure" assert_equal({"user_name" => "david"}, @response.cookies) diff --git a/railties/test/application/middleware/cookies_test.rb b/railties/test/application/middleware/cookies_test.rb new file mode 100644 index 0000000000..13556cbed2 --- /dev/null +++ b/railties/test/application/middleware/cookies_test.rb @@ -0,0 +1,47 @@ +require 'isolation/abstract_unit' + +module ApplicationTests + class CookiesTest < Test::Unit::TestCase + include ActiveSupport::Testing::Isolation + + def new_app + File.expand_path("#{app_path}/../new_app") + end + + def setup + build_app + boot_rails + FileUtils.rm_rf("#{app_path}/config/environments") + end + + def teardown + teardown_app + FileUtils.rm_rf(new_app) if File.directory?(new_app) + end + + test 'always_write_cookie is true by default in development' do + require 'rails' + Rails.env = 'development' + require "#{app_path}/config/environment" + assert_equal true, ActionDispatch::Cookies::CookieJar.always_write_cookie + end + + test 'always_write_cookie is false by default in production' do + require 'rails' + Rails.env = 'production' + require "#{app_path}/config/environment" + assert_equal false, ActionDispatch::Cookies::CookieJar.always_write_cookie + end + + test 'always_write_cookie can be overrided' do + add_to_config <<-RUBY + config.action_dispatch.always_write_cookie = false + RUBY + + require 'rails' + Rails.env = 'development' + require "#{app_path}/config/environment" + assert_equal false, ActionDispatch::Cookies::CookieJar.always_write_cookie + end + end +end -- cgit v1.2.3 From 0536ea8c7855222111fad6df71d0d09b77ea4317 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 23 Nov 2011 21:43:03 +0000 Subject: Add safe_constantize to ActiveSupport::Dependencies. --- activesupport/lib/active_support/dependencies.rb | 33 ++++++++++++-------- activesupport/test/class_cache_test.rb | 38 ++++++++++++++---------- 2 files changed, 43 insertions(+), 28 deletions(-) diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index db90de4682..2f1066016b 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -527,7 +527,7 @@ module ActiveSupport #:nodoc: class ClassCache def initialize - @store = Hash.new { |h, k| h[k] = Inflector.constantize(k) } + @store = Hash.new end def empty? @@ -538,23 +538,23 @@ module ActiveSupport #:nodoc: @store.key?(key) end - def []=(key, value) - return unless key.respond_to?(:name) - - raise(ArgumentError, 'anonymous classes cannot be cached') if key.name.blank? - - @store[key.name] = value + def get(key) + key = key.name if key.respond_to?(:name) + @store[key] ||= Inflector.constantize(key) end - def [](key) + def safe_get(key) key = key.name if key.respond_to?(:name) - - @store[key] + @store[key] || begin + klass = Inflector.safe_constantize(key) + @store[key] = klass + end end - alias :get :[] - def store(name) - self[name] = name + def store(klass) + return self unless klass.respond_to?(:name) + raise(ArgumentError, 'anonymous classes cannot be cached') if klass.name.empty? + @store[klass.name] = klass self end @@ -571,10 +571,17 @@ module ActiveSupport #:nodoc: end # Get the reference for class named +name+. + # Raises an exception if referenced class does not exist. def constantize(name) Reference.get(name) end + # Get the reference for class named +name+ if one exists. + # Otherwise returns nil. + def safe_constantize(name) + Reference.safe_get(name) + end + # Determine if the given constant has been automatically loaded. def autoloaded?(desc) # No name => anonymous module. diff --git a/activesupport/test/class_cache_test.rb b/activesupport/test/class_cache_test.rb index 752c0ee478..87f61dcfc8 100644 --- a/activesupport/test/class_cache_test.rb +++ b/activesupport/test/class_cache_test.rb @@ -10,45 +10,53 @@ module ActiveSupport def test_empty? assert @cache.empty? - @cache[ClassCacheTest] = ClassCacheTest + @cache.store(ClassCacheTest) assert !@cache.empty? end def test_clear! assert @cache.empty? - @cache[ClassCacheTest] = ClassCacheTest + @cache.store(ClassCacheTest) assert !@cache.empty? @cache.clear! assert @cache.empty? end def test_set_key - @cache[ClassCacheTest] = ClassCacheTest + @cache.store(ClassCacheTest) assert @cache.key?(ClassCacheTest.name) end - def test_set_rejects_strings - @cache[ClassCacheTest.name] = ClassCacheTest - assert @cache.empty? - end - def test_get_with_class - @cache[ClassCacheTest] = ClassCacheTest - assert_equal ClassCacheTest, @cache[ClassCacheTest] + @cache.store(ClassCacheTest) + assert_equal ClassCacheTest, @cache.get(ClassCacheTest) end def test_get_with_name - @cache[ClassCacheTest] = ClassCacheTest - assert_equal ClassCacheTest, @cache[ClassCacheTest.name] + @cache.store(ClassCacheTest) + assert_equal ClassCacheTest, @cache.get(ClassCacheTest.name) end def test_get_constantizes assert @cache.empty? - assert_equal ClassCacheTest, @cache[ClassCacheTest.name] + assert_equal ClassCacheTest, @cache.get(ClassCacheTest.name) + end + + def test_get_constantizes_fails_on_invalid_names + assert @cache.empty? + assert_raise NameError do + @cache.get("OmgTotallyInvalidConstantName") + end end - def test_get_is_an_alias - assert_equal @cache[ClassCacheTest], @cache.get(ClassCacheTest.name) + def test_safe_get_constantizes + assert @cache.empty? + assert_equal ClassCacheTest, @cache.safe_get(ClassCacheTest.name) + end + + def test_safe_get_constantizes_doesnt_fail_on_invalid_names + assert @cache.empty? + assert_equal nil, @cache.safe_get("OmgTotallyInvalidConstantName") end def test_new_rejects_strings -- cgit v1.2.3 From 8896b4fdc8a543157cdf4dfc378607ebf6c10ab0 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 23 Nov 2011 23:18:13 +0000 Subject: Implement ArraySerializer and move old serialization API to a new namespace. The following constants were renamed: ActiveModel::Serialization => ActiveModel::Serializable ActiveModel::Serializers::JSON => ActiveModel::Serializable::JSON ActiveModel::Serializers::Xml => ActiveModel::Serializable::XML The main motivation for such a change is that `ActiveModel::Serializers::JSON` was not actually a serializer, but a module that when included allows the target to be serializable to JSON. With such changes, we were able to clean up the namespace to add true serializers as the ArraySerializer. --- activemodel/lib/active_model.rb | 4 +- activemodel/lib/active_model/serializable.rb | 156 +++++++++++++++ activemodel/lib/active_model/serializable/json.rb | 108 ++++++++++ activemodel/lib/active_model/serializable/xml.rb | 195 ++++++++++++++++++ activemodel/lib/active_model/serialization.rb | 139 +------------ activemodel/lib/active_model/serializer.rb | 60 ++++++ activemodel/lib/active_model/serializers/json.rb | 102 +--------- activemodel/lib/active_model/serializers/xml.rb | 191 +----------------- activemodel/test/cases/serializable/json_test.rb | 219 +++++++++++++++++++++ activemodel/test/cases/serializable/xml_test.rb | 206 +++++++++++++++++++ activemodel/test/cases/serializable_test.rb | 151 ++++++++++++++ activemodel/test/cases/serialization_test.rb | 151 -------------- activemodel/test/cases/serializer_test.rb | 65 +++++- .../cases/serializers/json_serialization_test.rb | 219 --------------------- .../cases/serializers/xml_serialization_test.rb | 206 ------------------- activerecord/lib/active_record/serialization.rb | 2 +- .../active_record/serializers/xml_serializer.rb | 6 +- activesupport/lib/active_support/json/encoding.rb | 8 +- 18 files changed, 1181 insertions(+), 1007 deletions(-) create mode 100644 activemodel/lib/active_model/serializable.rb create mode 100644 activemodel/lib/active_model/serializable/json.rb create mode 100644 activemodel/lib/active_model/serializable/xml.rb create mode 100644 activemodel/test/cases/serializable/json_test.rb create mode 100644 activemodel/test/cases/serializable/xml_test.rb create mode 100644 activemodel/test/cases/serializable_test.rb delete mode 100644 activemodel/test/cases/serialization_test.rb delete mode 100644 activemodel/test/cases/serializers/json_serialization_test.rb delete mode 100644 activemodel/test/cases/serializers/xml_serialization_test.rb diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index 28765b00bb..6c4fb44b0f 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -29,6 +29,7 @@ require 'active_model/version' module ActiveModel extend ActiveSupport::Autoload + autoload :ArraySerializer, 'active_model/serializer' autoload :AttributeMethods autoload :BlockValidator, 'active_model/validator' autoload :Callbacks @@ -43,8 +44,9 @@ module ActiveModel autoload :Observer, 'active_model/observing' autoload :Observing autoload :SecurePassword - autoload :Serializer + autoload :Serializable autoload :Serialization + autoload :Serializer autoload :TestCase autoload :Translation autoload :Validations diff --git a/activemodel/lib/active_model/serializable.rb b/activemodel/lib/active_model/serializable.rb new file mode 100644 index 0000000000..769e934dbe --- /dev/null +++ b/activemodel/lib/active_model/serializable.rb @@ -0,0 +1,156 @@ +require 'active_support/core_ext/hash/except' +require 'active_support/core_ext/hash/slice' +require 'active_support/core_ext/array/wrap' + +module ActiveModel + # == Active Model Serializable + # + # Provides a basic serialization to a serializable_hash for your object. + # + # A minimal implementation could be: + # + # class Person + # + # include ActiveModel::Serializable + # + # attr_accessor :name + # + # def attributes + # {'name' => name} + # end + # + # end + # + # Which would provide you with: + # + # person = Person.new + # person.serializable_hash # => {"name"=>nil} + # person.name = "Bob" + # person.serializable_hash # => {"name"=>"Bob"} + # + # You need to declare some sort of attributes hash which contains the attributes + # you want to serialize and their current value. + # + # Most of the time though, you will want to include the JSON or XML + # serializations. Both of these modules automatically include the + # ActiveModel::Serialization module, so there is no need to explicitly + # include it. + # + # So a minimal implementation including XML and JSON would be: + # + # class Person + # + # include ActiveModel::Serializable::JSON + # include ActiveModel::Serializable::XML + # + # attr_accessor :name + # + # def attributes + # {'name' => name} + # end + # + # end + # + # Which would provide you with: + # + # person = Person.new + # person.serializable_hash # => {"name"=>nil} + # person.as_json # => {"name"=>nil} + # person.to_json # => "{\"name\":null}" + # person.to_xml # => "\n {"name"=>"Bob"} + # person.as_json # => {"name"=>"Bob"} + # person.to_json # => "{\"name\":\"Bob\"}" + # person.to_xml # => "\n:only, :except and :methods . + module Serializable + extend ActiveSupport::Concern + + autoload :JSON, "active_model/serializable/json" + autoload :XML, "active_model/serializable/xml" + + include ActiveModel::Serializer::Scope + + module ClassMethods #:nodoc: + def _model_serializer + @_model_serializer ||= ActiveModel::Serializer::Finder.find(self, self) + end + end + + def serializable_hash(options = nil) + options ||= {} + + attribute_names = attributes.keys.sort + if only = options[:only] + attribute_names &= Array.wrap(only).map(&:to_s) + elsif except = options[:except] + attribute_names -= Array.wrap(except).map(&:to_s) + end + + hash = {} + attribute_names.each { |n| hash[n] = read_attribute_for_serialization(n) } + + method_names = Array.wrap(options[:methods]).select { |n| respond_to?(n) } + method_names.each { |n| hash[n] = send(n) } + + serializable_add_includes(options) do |association, records, opts| + hash[association] = if records.is_a?(Enumerable) + records.map { |a| a.serializable_hash(opts) } + else + records.serializable_hash(opts) + end + end + + hash + end + + # Returns a model serializer for this object considering its namespace. + def model_serializer + self.class._model_serializer + end + + private + + # Hook method defining how an attribute value should be retrieved for + # serialization. By default this is assumed to be an instance named after + # the attribute. Override this method in subclasses should you need to + # retrieve the value for a given attribute differently: + # + # class MyClass + # include ActiveModel::Validations + # + # def initialize(data = {}) + # @data = data + # end + # + # def read_attribute_for_serialization(key) + # @data[key] + # end + # end + # + alias :read_attribute_for_serialization :send + + # Add associations specified via the :include option. + # + # Expects a block that takes as arguments: + # +association+ - name of the association + # +records+ - the association record(s) to be serialized + # +opts+ - options for the association records + def serializable_add_includes(options = {}) #:nodoc: + return unless include = options[:include] + + unless include.is_a?(Hash) + include = Hash[Array.wrap(include).map { |n| n.is_a?(Hash) ? n.to_a.first : [n, {}] }] + end + + include.each do |association, opts| + if records = send(association) + yield association, records, opts + end + end + end + end +end diff --git a/activemodel/lib/active_model/serializable/json.rb b/activemodel/lib/active_model/serializable/json.rb new file mode 100644 index 0000000000..79173929e4 --- /dev/null +++ b/activemodel/lib/active_model/serializable/json.rb @@ -0,0 +1,108 @@ +require 'active_support/json' +require 'active_support/core_ext/class/attribute' + +module ActiveModel + # == Active Model Serializable as JSON + module Serializable + module JSON + extend ActiveSupport::Concern + include ActiveModel::Serializable + + included do + extend ActiveModel::Naming + + class_attribute :include_root_in_json + self.include_root_in_json = true + end + + # Returns a hash representing the model. Some configuration can be + # passed through +options+. + # + # The option include_root_in_json controls the top-level behavior + # of +as_json+. If true (the default) +as_json+ will emit a single root + # node named after the object's type. For example: + # + # user = User.find(1) + # user.as_json + # # => { "user": {"id": 1, "name": "Konata Izumi", "age": 16, + # "created_at": "2006/08/01", "awesome": true} } + # + # ActiveRecord::Base.include_root_in_json = false + # user.as_json + # # => {"id": 1, "name": "Konata Izumi", "age": 16, + # "created_at": "2006/08/01", "awesome": true} + # + # This behavior can also be achieved by setting the :root option to +false+ as in: + # + # user = User.find(1) + # user.as_json(root: false) + # # => {"id": 1, "name": "Konata Izumi", "age": 16, + # "created_at": "2006/08/01", "awesome": true} + # + # The remainder of the examples in this section assume include_root_in_json is set to + # false. + # + # Without any +options+, the returned Hash will include all the model's + # attributes. For example: + # + # user = User.find(1) + # user.as_json + # # => {"id": 1, "name": "Konata Izumi", "age": 16, + # "created_at": "2006/08/01", "awesome": true} + # + # The :only and :except options can be used to limit the attributes + # included, and work similar to the +attributes+ method. For example: + # + # user.as_json(:only => [ :id, :name ]) + # # => {"id": 1, "name": "Konata Izumi"} + # + # user.as_json(:except => [ :id, :created_at, :age ]) + # # => {"name": "Konata Izumi", "awesome": true} + # + # To include the result of some method calls on the model use :methods: + # + # user.as_json(:methods => :permalink) + # # => {"id": 1, "name": "Konata Izumi", "age": 16, + # "created_at": "2006/08/01", "awesome": true, + # "permalink": "1-konata-izumi"} + # + # To include associations use :include: + # + # user.as_json(:include => :posts) + # # => {"id": 1, "name": "Konata Izumi", "age": 16, + # "created_at": "2006/08/01", "awesome": true, + # "posts": [{"id": 1, "author_id": 1, "title": "Welcome to the weblog"}, + # {"id": 2, author_id: 1, "title": "So I was thinking"}]} + # + # Second level and higher order associations work as well: + # + # user.as_json(:include => { :posts => { + # :include => { :comments => { + # :only => :body } }, + # :only => :title } }) + # # => {"id": 1, "name": "Konata Izumi", "age": 16, + # "created_at": "2006/08/01", "awesome": true, + # "posts": [{"comments": [{"body": "1st post!"}, {"body": "Second!"}], + # "title": "Welcome to the weblog"}, + # {"comments": [{"body": "Don't think too hard"}], + # "title": "So I was thinking"}]} + def as_json(options = nil) + root = include_root_in_json + root = options[:root] if options.try(:key?, :root) + if root + root = self.class.model_name.element if root == true + { root => serializable_hash(options) } + else + serializable_hash(options) + end + end + + def from_json(json, include_root=include_root_in_json) + hash = ActiveSupport::JSON.decode(json) + hash = hash.values.first if include_root + self.attributes = hash + self + end + end + end +end diff --git a/activemodel/lib/active_model/serializable/xml.rb b/activemodel/lib/active_model/serializable/xml.rb new file mode 100644 index 0000000000..d11cee9b42 --- /dev/null +++ b/activemodel/lib/active_model/serializable/xml.rb @@ -0,0 +1,195 @@ +require 'active_support/core_ext/array/wrap' +require 'active_support/core_ext/class/attribute_accessors' +require 'active_support/core_ext/array/conversions' +require 'active_support/core_ext/hash/conversions' +require 'active_support/core_ext/hash/slice' + +module ActiveModel + # == Active Model Serializable as XML + module Serializable + module XML + extend ActiveSupport::Concern + include ActiveModel::Serializable + + class Serializer #:nodoc: + class Attribute #:nodoc: + attr_reader :name, :value, :type + + def initialize(name, serializable, value) + @name, @serializable = name, serializable + value = value.in_time_zone if value.respond_to?(:in_time_zone) + @value = value + @type = compute_type + end + + def decorations + decorations = {} + decorations[:encoding] = 'base64' if type == :binary + decorations[:type] = (type == :string) ? nil : type + decorations[:nil] = true if value.nil? + decorations + end + + protected + + def compute_type + return if value.nil? + type = ActiveSupport::XmlMini::TYPE_NAMES[value.class.name] + type ||= :string if value.respond_to?(:to_str) + type ||= :yaml + type + end + end + + class MethodAttribute < Attribute #:nodoc: + end + + attr_reader :options + + def initialize(serializable, options = nil) + @serializable = serializable + @options = options ? options.dup : {} + end + + def serializable_hash + @serializable.serializable_hash(@options.except(:include)) + end + + def serializable_collection + methods = Array.wrap(options[:methods]).map(&:to_s) + serializable_hash.map do |name, value| + name = name.to_s + if methods.include?(name) + self.class::MethodAttribute.new(name, @serializable, value) + else + self.class::Attribute.new(name, @serializable, value) + end + end + end + + def serialize + require 'builder' unless defined? ::Builder + + options[:indent] ||= 2 + options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent]) + + @builder = options[:builder] + @builder.instruct! unless options[:skip_instruct] + + root = (options[:root] || @serializable.class.model_name.element).to_s + root = ActiveSupport::XmlMini.rename_key(root, options) + + args = [root] + args << {:xmlns => options[:namespace]} if options[:namespace] + args << {:type => options[:type]} if options[:type] && !options[:skip_types] + + @builder.tag!(*args) do + add_attributes_and_methods + add_includes + add_extra_behavior + add_procs + yield @builder if block_given? + end + end + + private + + def add_extra_behavior + end + + def add_attributes_and_methods + serializable_collection.each do |attribute| + key = ActiveSupport::XmlMini.rename_key(attribute.name, options) + ActiveSupport::XmlMini.to_tag(key, attribute.value, + options.merge(attribute.decorations)) + end + end + + def add_includes + @serializable.send(:serializable_add_includes, options) do |association, records, opts| + add_associations(association, records, opts) + end + end + + # TODO This can likely be cleaned up to simple use ActiveSupport::XmlMini.to_tag as well. + def add_associations(association, records, opts) + merged_options = opts.merge(options.slice(:builder, :indent)) + merged_options[:skip_instruct] = true + + if records.is_a?(Enumerable) + tag = ActiveSupport::XmlMini.rename_key(association.to_s, options) + type = options[:skip_types] ? { } : {:type => "array"} + association_name = association.to_s.singularize + merged_options[:root] = association_name + + if records.empty? + @builder.tag!(tag, type) + else + @builder.tag!(tag, type) do + records.each do |record| + if options[:skip_types] + record_type = {} + else + record_class = (record.class.to_s.underscore == association_name) ? nil : record.class.name + record_type = {:type => record_class} + end + + record.to_xml merged_options.merge(record_type) + end + end + end + else + merged_options[:root] = association.to_s + records.to_xml(merged_options) + end + end + + def add_procs + if procs = options.delete(:procs) + Array.wrap(procs).each do |proc| + if proc.arity == 1 + proc.call(options) + else + proc.call(options, @serializable) + end + end + end + end + end + + # Returns XML representing the model. Configuration can be + # passed through +options+. + # + # Without any +options+, the returned XML string will include all the model's + # attributes. For example: + # + # user = User.find(1) + # user.to_xml + # + # + # + # 1 + # David + # 16 + # 2011-01-30T22:29:23Z + # + # + # The :only and :except options can be used to limit the attributes + # included, and work similar to the +attributes+ method. + # + # To include the result of some method calls on the model use :methods. + # + # To include associations use :include. + # + # For further documentation see activerecord/lib/active_record/serializers/xml_serializer.xml. + def to_xml(options = {}, &block) + Serializer.new(self, options).serialize(&block) + end + + def from_xml(xml) + self.attributes = Hash.from_xml(xml).values.first + self + end + end + end +end diff --git a/activemodel/lib/active_model/serialization.rb b/activemodel/lib/active_model/serialization.rb index a4b58ab456..439302c632 100644 --- a/activemodel/lib/active_model/serialization.rb +++ b/activemodel/lib/active_model/serialization.rb @@ -1,139 +1,10 @@ -require 'active_support/core_ext/hash/except' -require 'active_support/core_ext/hash/slice' -require 'active_support/core_ext/array/wrap' - - module ActiveModel - # == Active Model Serialization - # - # Provides a basic serialization to a serializable_hash for your object. - # - # A minimal implementation could be: - # - # class Person - # - # include ActiveModel::Serialization - # - # attr_accessor :name - # - # def attributes - # {'name' => name} - # end - # - # end - # - # Which would provide you with: - # - # person = Person.new - # person.serializable_hash # => {"name"=>nil} - # person.name = "Bob" - # person.serializable_hash # => {"name"=>"Bob"} - # - # You need to declare some sort of attributes hash which contains the attributes - # you want to serialize and their current value. - # - # Most of the time though, you will want to include the JSON or XML - # serializations. Both of these modules automatically include the - # ActiveModel::Serialization module, so there is no need to explicitly - # include it. - # - # So a minimal implementation including XML and JSON would be: - # - # class Person - # - # include ActiveModel::Serializers::JSON - # include ActiveModel::Serializers::Xml - # - # attr_accessor :name - # - # def attributes - # {'name' => name} - # end - # - # end - # - # Which would provide you with: - # - # person = Person.new - # person.serializable_hash # => {"name"=>nil} - # person.as_json # => {"name"=>nil} - # person.to_json # => "{\"name\":null}" - # person.to_xml # => "\n {"name"=>"Bob"} - # person.as_json # => {"name"=>"Bob"} - # person.to_json # => "{\"name\":\"Bob\"}" - # person.to_xml # => "\n:only, :except and :methods . module Serialization - def serializable_hash(options = nil) - options ||= {} - - attribute_names = attributes.keys.sort - if only = options[:only] - attribute_names &= Array.wrap(only).map(&:to_s) - elsif except = options[:except] - attribute_names -= Array.wrap(except).map(&:to_s) - end - - hash = {} - attribute_names.each { |n| hash[n] = read_attribute_for_serialization(n) } - - method_names = Array.wrap(options[:methods]).select { |n| respond_to?(n) } - method_names.each { |n| hash[n] = send(n) } - - serializable_add_includes(options) do |association, records, opts| - hash[association] = if records.is_a?(Enumerable) - records.map { |a| a.serializable_hash(opts) } - else - records.serializable_hash(opts) - end - end + extend ActiveSupport::Concern + include ActiveModel::Serializable - hash + included do + ActiveSupport::Deprecation.warn "ActiveModel::Serialization is deprecated in favor of ActiveModel::Serializable" end - - private - - # Hook method defining how an attribute value should be retrieved for - # serialization. By default this is assumed to be an instance named after - # the attribute. Override this method in subclasses should you need to - # retrieve the value for a given attribute differently: - # - # class MyClass - # include ActiveModel::Validations - # - # def initialize(data = {}) - # @data = data - # end - # - # def read_attribute_for_serialization(key) - # @data[key] - # end - # end - # - alias :read_attribute_for_serialization :send - - # Add associations specified via the :include option. - # - # Expects a block that takes as arguments: - # +association+ - name of the association - # +records+ - the association record(s) to be serialized - # +opts+ - options for the association records - def serializable_add_includes(options = {}) #:nodoc: - return unless include = options[:include] - - unless include.is_a?(Hash) - include = Hash[Array.wrap(include).map { |n| n.is_a?(Hash) ? n.to_a.first : [n, {}] }] - end - - include.each do |association, opts| - if records = send(association) - yield association, records, opts - end - end - end end -end +end \ No newline at end of file diff --git a/activemodel/lib/active_model/serializer.rb b/activemodel/lib/active_model/serializer.rb index 6d0746a3e8..a541a1053d 100644 --- a/activemodel/lib/active_model/serializer.rb +++ b/activemodel/lib/active_model/serializer.rb @@ -1,10 +1,70 @@ require "active_support/core_ext/class/attribute" require "active_support/core_ext/string/inflections" require "active_support/core_ext/module/anonymous" +require "active_support/core_ext/module/introspection" require "set" module ActiveModel + # Active Model Array Serializer + class ArraySerializer + attr_reader :object, :scope + + def initialize(object, scope) + @object, @scope = object, scope + end + + def serializable_array + @object.map do |item| + if serializer = Serializer::Finder.find(item, scope) + serializer.new(item, scope) + else + item + end + end + end + + def as_json(*args) + serializable_array.as_json(*args) + end + end + + # Active Model Serializer class Serializer + module Finder + mattr_accessor :constantizer + @@constantizer = ActiveSupport::Inflector + + # Finds a serializer for the given object in the given scope. + # If the object implements a +model_serializer+ method, it does + # not do a scope lookup but uses the model_serializer method instead. + def self.find(object, scope) + if object.respond_to?(:model_serializer) + object.model_serializer + else + scope = scope.class unless scope.respond_to?(:const_defined?) + object = object.class unless object.respond_to?(:name) + serializer = "#{object.name.demodulize}Serializer" + + begin + scope.const_get serializer + rescue NameError => e + raise unless e.message =~ /uninitialized constant ([\w_]+::)*#{serializer}$/ + scope.parents.each do |parent| + return parent.const_get(serializer) if parent.const_defined?(serializer) + end + nil + end + end + end + end + + # Defines the serialization scope. Core extension serializers + # are defined in this module so a scoped lookup is able to find + # core extension serializers. + module Scope + ArraySerializer = ::ActiveModel::ArraySerializer + end + module Associations class Config < Struct.new(:name, :options) def serializer diff --git a/activemodel/lib/active_model/serializers/json.rb b/activemodel/lib/active_model/serializers/json.rb index c845440120..9efd7c5f69 100644 --- a/activemodel/lib/active_model/serializers/json.rb +++ b/activemodel/lib/active_model/serializers/json.rb @@ -1,108 +1,12 @@ -require 'active_support/json' -require 'active_support/core_ext/class/attribute' - module ActiveModel - # == Active Model JSON Serializer module Serializers module JSON extend ActiveSupport::Concern - include ActiveModel::Serialization + include ActiveModel::Serializable::JSON included do - extend ActiveModel::Naming - - class_attribute :include_root_in_json - self.include_root_in_json = true - end - - # Returns a hash representing the model. Some configuration can be - # passed through +options+. - # - # The option include_root_in_json controls the top-level behavior - # of +as_json+. If true (the default) +as_json+ will emit a single root - # node named after the object's type. For example: - # - # user = User.find(1) - # user.as_json - # # => { "user": {"id": 1, "name": "Konata Izumi", "age": 16, - # "created_at": "2006/08/01", "awesome": true} } - # - # ActiveRecord::Base.include_root_in_json = false - # user.as_json - # # => {"id": 1, "name": "Konata Izumi", "age": 16, - # "created_at": "2006/08/01", "awesome": true} - # - # This behavior can also be achieved by setting the :root option to +false+ as in: - # - # user = User.find(1) - # user.as_json(root: false) - # # => {"id": 1, "name": "Konata Izumi", "age": 16, - # "created_at": "2006/08/01", "awesome": true} - # - # The remainder of the examples in this section assume include_root_in_json is set to - # false. - # - # Without any +options+, the returned Hash will include all the model's - # attributes. For example: - # - # user = User.find(1) - # user.as_json - # # => {"id": 1, "name": "Konata Izumi", "age": 16, - # "created_at": "2006/08/01", "awesome": true} - # - # The :only and :except options can be used to limit the attributes - # included, and work similar to the +attributes+ method. For example: - # - # user.as_json(:only => [ :id, :name ]) - # # => {"id": 1, "name": "Konata Izumi"} - # - # user.as_json(:except => [ :id, :created_at, :age ]) - # # => {"name": "Konata Izumi", "awesome": true} - # - # To include the result of some method calls on the model use :methods: - # - # user.as_json(:methods => :permalink) - # # => {"id": 1, "name": "Konata Izumi", "age": 16, - # "created_at": "2006/08/01", "awesome": true, - # "permalink": "1-konata-izumi"} - # - # To include associations use :include: - # - # user.as_json(:include => :posts) - # # => {"id": 1, "name": "Konata Izumi", "age": 16, - # "created_at": "2006/08/01", "awesome": true, - # "posts": [{"id": 1, "author_id": 1, "title": "Welcome to the weblog"}, - # {"id": 2, author_id: 1, "title": "So I was thinking"}]} - # - # Second level and higher order associations work as well: - # - # user.as_json(:include => { :posts => { - # :include => { :comments => { - # :only => :body } }, - # :only => :title } }) - # # => {"id": 1, "name": "Konata Izumi", "age": 16, - # "created_at": "2006/08/01", "awesome": true, - # "posts": [{"comments": [{"body": "1st post!"}, {"body": "Second!"}], - # "title": "Welcome to the weblog"}, - # {"comments": [{"body": "Don't think too hard"}], - # "title": "So I was thinking"}]} - def as_json(options = nil) - root = include_root_in_json - root = options[:root] if options.try(:key?, :root) - if root - root = self.class.model_name.element if root == true - { root => serializable_hash(options) } - else - serializable_hash(options) - end - end - - def from_json(json, include_root=include_root_in_json) - hash = ActiveSupport::JSON.decode(json) - hash = hash.values.first if include_root - self.attributes = hash - self + ActiveSupport::Deprecation.warn "ActiveModel::Serializers::JSON is deprecated in favor of ActiveModel::Serializable::JSON" end end end -end +end \ No newline at end of file diff --git a/activemodel/lib/active_model/serializers/xml.rb b/activemodel/lib/active_model/serializers/xml.rb index d61d9d7119..620390da6b 100644 --- a/activemodel/lib/active_model/serializers/xml.rb +++ b/activemodel/lib/active_model/serializers/xml.rb @@ -1,195 +1,14 @@ -require 'active_support/core_ext/array/wrap' -require 'active_support/core_ext/class/attribute_accessors' -require 'active_support/core_ext/array/conversions' -require 'active_support/core_ext/hash/conversions' -require 'active_support/core_ext/hash/slice' - module ActiveModel - # == Active Model XML Serializer module Serializers module Xml extend ActiveSupport::Concern - include ActiveModel::Serialization - - class Serializer #:nodoc: - class Attribute #:nodoc: - attr_reader :name, :value, :type - - def initialize(name, serializable, value) - @name, @serializable = name, serializable - value = value.in_time_zone if value.respond_to?(:in_time_zone) - @value = value - @type = compute_type - end - - def decorations - decorations = {} - decorations[:encoding] = 'base64' if type == :binary - decorations[:type] = (type == :string) ? nil : type - decorations[:nil] = true if value.nil? - decorations - end - - protected - - def compute_type - return if value.nil? - type = ActiveSupport::XmlMini::TYPE_NAMES[value.class.name] - type ||= :string if value.respond_to?(:to_str) - type ||= :yaml - type - end - end - - class MethodAttribute < Attribute #:nodoc: - end - - attr_reader :options - - def initialize(serializable, options = nil) - @serializable = serializable - @options = options ? options.dup : {} - end - - def serializable_hash - @serializable.serializable_hash(@options.except(:include)) - end - - def serializable_collection - methods = Array.wrap(options[:methods]).map(&:to_s) - serializable_hash.map do |name, value| - name = name.to_s - if methods.include?(name) - self.class::MethodAttribute.new(name, @serializable, value) - else - self.class::Attribute.new(name, @serializable, value) - end - end - end - - def serialize - require 'builder' unless defined? ::Builder - - options[:indent] ||= 2 - options[:builder] ||= ::Builder::XmlMarkup.new(:indent => options[:indent]) - - @builder = options[:builder] - @builder.instruct! unless options[:skip_instruct] + include ActiveModel::Serializable::XML - root = (options[:root] || @serializable.class.model_name.element).to_s - root = ActiveSupport::XmlMini.rename_key(root, options) - - args = [root] - args << {:xmlns => options[:namespace]} if options[:namespace] - args << {:type => options[:type]} if options[:type] && !options[:skip_types] - - @builder.tag!(*args) do - add_attributes_and_methods - add_includes - add_extra_behavior - add_procs - yield @builder if block_given? - end - end - - private - - def add_extra_behavior - end - - def add_attributes_and_methods - serializable_collection.each do |attribute| - key = ActiveSupport::XmlMini.rename_key(attribute.name, options) - ActiveSupport::XmlMini.to_tag(key, attribute.value, - options.merge(attribute.decorations)) - end - end - - def add_includes - @serializable.send(:serializable_add_includes, options) do |association, records, opts| - add_associations(association, records, opts) - end - end - - # TODO This can likely be cleaned up to simple use ActiveSupport::XmlMini.to_tag as well. - def add_associations(association, records, opts) - merged_options = opts.merge(options.slice(:builder, :indent)) - merged_options[:skip_instruct] = true - - if records.is_a?(Enumerable) - tag = ActiveSupport::XmlMini.rename_key(association.to_s, options) - type = options[:skip_types] ? { } : {:type => "array"} - association_name = association.to_s.singularize - merged_options[:root] = association_name - - if records.empty? - @builder.tag!(tag, type) - else - @builder.tag!(tag, type) do - records.each do |record| - if options[:skip_types] - record_type = {} - else - record_class = (record.class.to_s.underscore == association_name) ? nil : record.class.name - record_type = {:type => record_class} - end - - record.to_xml merged_options.merge(record_type) - end - end - end - else - merged_options[:root] = association.to_s - records.to_xml(merged_options) - end - end - - def add_procs - if procs = options.delete(:procs) - Array.wrap(procs).each do |proc| - if proc.arity == 1 - proc.call(options) - else - proc.call(options, @serializable) - end - end - end - end - end - - # Returns XML representing the model. Configuration can be - # passed through +options+. - # - # Without any +options+, the returned XML string will include all the model's - # attributes. For example: - # - # user = User.find(1) - # user.to_xml - # - # - # - # 1 - # David - # 16 - # 2011-01-30T22:29:23Z - # - # - # The :only and :except options can be used to limit the attributes - # included, and work similar to the +attributes+ method. - # - # To include the result of some method calls on the model use :methods. - # - # To include associations use :include. - # - # For further documentation see activerecord/lib/active_record/serializers/xml_serializer.xml. - def to_xml(options = {}, &block) - Serializer.new(self, options).serialize(&block) - end + Serializer = ActiveModel::Serializable::XML::Serializer - def from_xml(xml) - self.attributes = Hash.from_xml(xml).values.first - self + included do + ActiveSupport::Deprecation.warn "ActiveModel::Serializers::Xml is deprecated in favor of ActiveModel::Serializable::XML" end end end -end +end \ No newline at end of file diff --git a/activemodel/test/cases/serializable/json_test.rb b/activemodel/test/cases/serializable/json_test.rb new file mode 100644 index 0000000000..ad5b04091e --- /dev/null +++ b/activemodel/test/cases/serializable/json_test.rb @@ -0,0 +1,219 @@ +require 'cases/helper' +require 'models/contact' +require 'models/automobile' +require 'active_support/core_ext/object/instance_variables' + +class Contact + extend ActiveModel::Naming + include ActiveModel::Serializable::JSON + include ActiveModel::Validations + + def attributes=(hash) + hash.each do |k, v| + instance_variable_set("@#{k}", v) + end + end + + def attributes + instance_values + end unless method_defined?(:attributes) +end + +class JsonSerializationTest < ActiveModel::TestCase + def setup + @contact = Contact.new + @contact.name = 'Konata Izumi' + @contact.age = 16 + @contact.created_at = Time.utc(2006, 8, 1) + @contact.awesome = true + @contact.preferences = { 'shows' => 'anime' } + end + + test "should include root in json" do + json = @contact.to_json + + assert_match %r{^\{"contact":\{}, json + assert_match %r{"name":"Konata Izumi"}, json + assert_match %r{"age":16}, json + assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) + assert_match %r{"awesome":true}, json + assert_match %r{"preferences":\{"shows":"anime"\}}, json + end + + test "should not include root in json (class method)" do + begin + Contact.include_root_in_json = false + json = @contact.to_json + + assert_no_match %r{^\{"contact":\{}, json + assert_match %r{"name":"Konata Izumi"}, json + assert_match %r{"age":16}, json + assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) + assert_match %r{"awesome":true}, json + assert_match %r{"preferences":\{"shows":"anime"\}}, json + ensure + Contact.include_root_in_json = true + end + end + + test "should include root in json (option) even if the default is set to false" do + begin + Contact.include_root_in_json = false + json = @contact.to_json(:root => true) + assert_match %r{^\{"contact":\{}, json + ensure + Contact.include_root_in_json = true + end + end + + test "should not include root in json (option)" do + + json = @contact.to_json(:root => false) + + assert_no_match %r{^\{"contact":\{}, json + end + + test "should include custom root in json" do + json = @contact.to_json(:root => 'json_contact') + + assert_match %r{^\{"json_contact":\{}, json + assert_match %r{"name":"Konata Izumi"}, json + assert_match %r{"age":16}, json + assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) + assert_match %r{"awesome":true}, json + assert_match %r{"preferences":\{"shows":"anime"\}}, json + end + + test "should encode all encodable attributes" do + json = @contact.to_json + + assert_match %r{"name":"Konata Izumi"}, json + assert_match %r{"age":16}, json + assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) + assert_match %r{"awesome":true}, json + assert_match %r{"preferences":\{"shows":"anime"\}}, json + end + + test "should allow attribute filtering with only" do + json = @contact.to_json(:only => [:name, :age]) + + assert_match %r{"name":"Konata Izumi"}, json + assert_match %r{"age":16}, json + assert_no_match %r{"awesome":true}, json + assert !json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) + assert_no_match %r{"preferences":\{"shows":"anime"\}}, json + end + + test "should allow attribute filtering with except" do + json = @contact.to_json(:except => [:name, :age]) + + assert_no_match %r{"name":"Konata Izumi"}, json + assert_no_match %r{"age":16}, json + assert_match %r{"awesome":true}, json + assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) + assert_match %r{"preferences":\{"shows":"anime"\}}, json + end + + test "methods are called on object" do + # Define methods on fixture. + def @contact.label; "Has cheezburger"; end + def @contact.favorite_quote; "Constraints are liberating"; end + + # Single method. + assert_match %r{"label":"Has cheezburger"}, @contact.to_json(:only => :name, :methods => :label) + + # Both methods. + methods_json = @contact.to_json(:only => :name, :methods => [:label, :favorite_quote]) + assert_match %r{"label":"Has cheezburger"}, methods_json + assert_match %r{"favorite_quote":"Constraints are liberating"}, methods_json + end + + test "should return OrderedHash for errors" do + contact = Contact.new + contact.errors.add :name, "can't be blank" + contact.errors.add :name, "is too short (minimum is 2 characters)" + contact.errors.add :age, "must be 16 or over" + + hash = ActiveSupport::OrderedHash.new + hash[:name] = ["can't be blank", "is too short (minimum is 2 characters)"] + hash[:age] = ["must be 16 or over"] + assert_equal hash.to_json, contact.errors.to_json + end + + test "serializable_hash should not modify options passed in argument" do + options = { :except => :name } + @contact.serializable_hash(options) + + assert_nil options[:only] + assert_equal :name, options[:except] + end + + test "as_json should return a hash" do + json = @contact.as_json + + assert_kind_of Hash, json + assert_kind_of Hash, json['contact'] + %w(name age created_at awesome preferences).each do |field| + assert_equal @contact.send(field), json['contact'][field] + end + end + + test "from_json should set the object's attributes" do + json = @contact.to_json + result = Contact.new.from_json(json) + + assert_equal result.name, @contact.name + assert_equal result.age, @contact.age + assert_equal Time.parse(result.created_at), @contact.created_at + assert_equal result.awesome, @contact.awesome + assert_equal result.preferences, @contact.preferences + end + + test "from_json should work without a root (method parameter)" do + json = @contact.to_json(:root => false) + result = Contact.new.from_json(json, false) + + assert_equal result.name, @contact.name + assert_equal result.age, @contact.age + assert_equal Time.parse(result.created_at), @contact.created_at + assert_equal result.awesome, @contact.awesome + assert_equal result.preferences, @contact.preferences + end + + test "from_json should work without a root (class attribute)" do + begin + Contact.include_root_in_json = false + json = @contact.to_json + result = Contact.new.from_json(json) + + assert_equal result.name, @contact.name + assert_equal result.age, @contact.age + assert_equal Time.parse(result.created_at), @contact.created_at + assert_equal result.awesome, @contact.awesome + assert_equal result.preferences, @contact.preferences + ensure + Contact.include_root_in_json = true + end + end + + test "custom as_json should be honored when generating json" do + def @contact.as_json(options); { :name => name, :created_at => created_at }; end + json = @contact.to_json + + assert_match %r{"name":"Konata Izumi"}, json + assert_match %r{"created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}}, json + assert_no_match %r{"awesome":}, json + assert_no_match %r{"preferences":}, json + end + + test "custom as_json options should be extendible" do + def @contact.as_json(options = {}); super(options.merge(:only => [:name])); end + json = @contact.to_json + + assert_match %r{"name":"Konata Izumi"}, json + assert_no_match %r{"created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}}, json + assert_no_match %r{"awesome":}, json + assert_no_match %r{"preferences":}, json + end + +end diff --git a/activemodel/test/cases/serializable/xml_test.rb b/activemodel/test/cases/serializable/xml_test.rb new file mode 100644 index 0000000000..817ca1e736 --- /dev/null +++ b/activemodel/test/cases/serializable/xml_test.rb @@ -0,0 +1,206 @@ +require 'cases/helper' +require 'models/contact' +require 'active_support/core_ext/object/instance_variables' +require 'ostruct' + +class Contact + extend ActiveModel::Naming + include ActiveModel::Serializable::XML + + attr_accessor :address, :friends + + def attributes + instance_values.except("address", "friends") + end +end + +module Admin + class Contact < ::Contact + end +end + +class Customer < Struct.new(:name) +end + +class Address + extend ActiveModel::Naming + include ActiveModel::Serializable::XML + + attr_accessor :street, :city, :state, :zip + + def attributes + instance_values + end +end + +class SerializableContact < Contact + def serializable_hash(options={}) + super(options.merge(:only => [:name, :age])) + end +end + +class XmlSerializationTest < ActiveModel::TestCase + def setup + @contact = Contact.new + @contact.name = 'aaron stack' + @contact.age = 25 + @contact.created_at = Time.utc(2006, 8, 1) + @contact.awesome = false + customer = Customer.new + customer.name = "John" + @contact.preferences = customer + @contact.address = Address.new + @contact.address.street = "123 Lane" + @contact.address.city = "Springfield" + @contact.address.state = "CA" + @contact.address.zip = 11111 + @contact.friends = [Contact.new, Contact.new] + end + + test "should serialize default root" do + @xml = @contact.to_xml + assert_match %r{^}, @xml + assert_match %r{$}, @xml + end + + test "should serialize namespaced root" do + @xml = Admin::Contact.new(@contact.attributes).to_xml + assert_match %r{^}, @xml + assert_match %r{$}, @xml + end + + test "should serialize default root with namespace" do + @xml = @contact.to_xml :namespace => "http://xml.rubyonrails.org/contact" + assert_match %r{^}, @xml + assert_match %r{$}, @xml + end + + test "should serialize custom root" do + @xml = @contact.to_xml :root => 'xml_contact' + assert_match %r{^}, @xml + assert_match %r{$}, @xml + end + + test "should allow undasherized tags" do + @xml = @contact.to_xml :root => 'xml_contact', :dasherize => false + assert_match %r{^}, @xml + assert_match %r{$}, @xml + assert_match %r{ 'xml_contact', :camelize => true + assert_match %r{^}, @xml + assert_match %r{$}, @xml + assert_match %r{ 'xml_contact', :camelize => :lower + assert_match %r{^}, @xml + assert_match %r{$}, @xml + assert_match %r{aaron stack}, @xml + assert_match %r{25}, @xml + assert_no_match %r{}, @xml + end + + test "should allow skipped types" do + @xml = @contact.to_xml :skip_types => true + assert_match %r{25}, @xml + end + + test "should include yielded additions" do + @xml = @contact.to_xml do |xml| + xml.creator "David" + end + assert_match %r{David}, @xml + end + + test "should serialize string" do + assert_match %r{aaron stack}, @contact.to_xml + end + + test "should serialize nil" do + assert_match %r{}, @contact.to_xml(:methods => :pseudonyms) + end + + test "should serialize integer" do + assert_match %r{25}, @contact.to_xml + end + + test "should serialize datetime" do + assert_match %r{2006-08-01T00:00:00Z}, @contact.to_xml + end + + test "should serialize boolean" do + assert_match %r{false}, @contact.to_xml + end + + test "should serialize array" do + assert_match %r{\s*twitter\s*github\s*}, @contact.to_xml(:methods => :social) + end + + test "should serialize hash" do + assert_match %r{\s*github\s*}, @contact.to_xml(:methods => :network) + end + + test "should serialize yaml" do + assert_match %r{--- !ruby/struct:Customer(\s*)\nname: John\n}, @contact.to_xml + end + + test "should call proc on object" do + proc = Proc.new { |options| options[:builder].tag!('nationality', 'unknown') } + xml = @contact.to_xml(:procs => [ proc ]) + assert_match %r{unknown}, xml + end + + test 'should supply serializable to second proc argument' do + proc = Proc.new { |options, record| options[:builder].tag!('name-reverse', record.name.reverse) } + xml = @contact.to_xml(:procs => [ proc ]) + assert_match %r{kcats noraa}, xml + end + + test "should serialize string correctly when type passed" do + xml = @contact.to_xml :type => 'Contact' + assert_match %r{}, xml + assert_match %r{aaron stack}, xml + end + + test "include option with singular association" do + xml = @contact.to_xml :include => :address, :indent => 0 + assert xml.include?(@contact.address.to_xml(:indent => 0, :skip_instruct => true)) + end + + test "include option with plural association" do + xml = @contact.to_xml :include => :friends, :indent => 0 + assert_match %r{}, xml + assert_match %r{}, xml + end + + test "multiple includes" do + xml = @contact.to_xml :indent => 0, :skip_instruct => true, :include => [ :address, :friends ] + assert xml.include?(@contact.address.to_xml(:indent => 0, :skip_instruct => true)) + assert_match %r{}, xml + assert_match %r{}, xml + end + + test "include with options" do + xml = @contact.to_xml :indent => 0, :skip_instruct => true, :include => { :address => { :only => :city } } + assert xml.include?(%(>
Springfield
)) + end + + test "propagates skip_types option to included associations" do + xml = @contact.to_xml :include => :friends, :indent => 0, :skip_types => true + assert_match %r{}, xml + assert_match %r{}, xml + end +end diff --git a/activemodel/test/cases/serializable_test.rb b/activemodel/test/cases/serializable_test.rb new file mode 100644 index 0000000000..46ee372c6f --- /dev/null +++ b/activemodel/test/cases/serializable_test.rb @@ -0,0 +1,151 @@ +require "cases/helper" +require 'active_support/core_ext/object/instance_variables' + +class SerializationTest < ActiveModel::TestCase + class User + include ActiveModel::Serializable + + attr_accessor :name, :email, :gender, :address, :friends + + def initialize(name, email, gender) + @name, @email, @gender = name, email, gender + @friends = [] + end + + def attributes + instance_values.except("address", "friends") + end + + def foo + 'i_am_foo' + end + end + + class Address + include ActiveModel::Serializable + + attr_accessor :street, :city, :state, :zip + + def attributes + instance_values + end + end + + setup do + @user = User.new('David', 'david@example.com', 'male') + @user.address = Address.new + @user.address.street = "123 Lane" + @user.address.city = "Springfield" + @user.address.state = "CA" + @user.address.zip = 11111 + @user.friends = [User.new('Joe', 'joe@example.com', 'male'), + User.new('Sue', 'sue@example.com', 'female')] + end + + def test_method_serializable_hash_should_work + expected = {"name"=>"David", "gender"=>"male", "email"=>"david@example.com"} + assert_equal expected , @user.serializable_hash + end + + def test_method_serializable_hash_should_work_with_only_option + expected = {"name"=>"David"} + assert_equal expected , @user.serializable_hash(:only => [:name]) + end + + def test_method_serializable_hash_should_work_with_except_option + expected = {"gender"=>"male", "email"=>"david@example.com"} + assert_equal expected , @user.serializable_hash(:except => [:name]) + end + + def test_method_serializable_hash_should_work_with_methods_option + expected = {"name"=>"David", "gender"=>"male", :foo=>"i_am_foo", "email"=>"david@example.com"} + assert_equal expected , @user.serializable_hash(:methods => [:foo]) + end + + def test_method_serializable_hash_should_work_with_only_and_methods + expected = {:foo=>"i_am_foo"} + assert_equal expected , @user.serializable_hash(:only => [], :methods => [:foo]) + end + + def test_method_serializable_hash_should_work_with_except_and_methods + expected = {"gender"=>"male", :foo=>"i_am_foo"} + assert_equal expected , @user.serializable_hash(:except => [:name, :email], :methods => [:foo]) + end + + def test_should_not_call_methods_that_dont_respond + expected = {"name"=>"David", "gender"=>"male", "email"=>"david@example.com"} + assert_equal expected , @user.serializable_hash(:methods => [:bar]) + end + + def test_should_use_read_attribute_for_serialization + def @user.read_attribute_for_serialization(n) + "Jon" + end + + expected = { "name" => "Jon" } + assert_equal expected, @user.serializable_hash(:only => :name) + end + + def test_include_option_with_singular_association + expected = {"name"=>"David", "gender"=>"male", "email"=>"david@example.com", + :address=>{"street"=>"123 Lane", "city"=>"Springfield", "state"=>"CA", "zip"=>11111}} + assert_equal expected , @user.serializable_hash(:include => :address) + end + + def test_include_option_with_plural_association + expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", + :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'}, + {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]} + assert_equal expected , @user.serializable_hash(:include => :friends) + end + + def test_include_option_with_empty_association + @user.friends = [] + expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", :friends=>[]} + assert_equal expected , @user.serializable_hash(:include => :friends) + end + + def test_multiple_includes + expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", + :address=>{"street"=>"123 Lane", "city"=>"Springfield", "state"=>"CA", "zip"=>11111}, + :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'}, + {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]} + assert_equal expected , @user.serializable_hash(:include => [:address, :friends]) + end + + def test_include_with_options + expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", + :address=>{"street"=>"123 Lane"}} + assert_equal expected , @user.serializable_hash(:include => {:address => {:only => "street"}}) + end + + def test_nested_include + @user.friends.first.friends = [@user] + expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", + :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male', + :friends => [{"email"=>"david@example.com", "gender"=>"male", "name"=>"David"}]}, + {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female', :friends => []}]} + assert_equal expected , @user.serializable_hash(:include => {:friends => {:include => :friends}}) + end + + def test_only_include + expected = {"name"=>"David", :friends => [{"name" => "Joe"}, {"name" => "Sue"}]} + assert_equal expected , @user.serializable_hash(:only => :name, :include => {:friends => {:only => :name}}) + end + + def test_except_include + expected = {"name"=>"David", "email"=>"david@example.com", + :friends => [{"name" => 'Joe', "email" => 'joe@example.com'}, + {"name" => "Sue", "email" => 'sue@example.com'}]} + assert_equal expected , @user.serializable_hash(:except => :gender, :include => {:friends => {:except => :gender}}) + end + + def test_multiple_includes_with_options + expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", + :address=>{"street"=>"123 Lane"}, + :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'}, + {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]} + assert_equal expected , @user.serializable_hash(:include => [{:address => {:only => "street"}}, :friends]) + end + +end diff --git a/activemodel/test/cases/serialization_test.rb b/activemodel/test/cases/serialization_test.rb deleted file mode 100644 index b8dad9d51f..0000000000 --- a/activemodel/test/cases/serialization_test.rb +++ /dev/null @@ -1,151 +0,0 @@ -require "cases/helper" -require 'active_support/core_ext/object/instance_variables' - -class SerializationTest < ActiveModel::TestCase - class User - include ActiveModel::Serialization - - attr_accessor :name, :email, :gender, :address, :friends - - def initialize(name, email, gender) - @name, @email, @gender = name, email, gender - @friends = [] - end - - def attributes - instance_values.except("address", "friends") - end - - def foo - 'i_am_foo' - end - end - - class Address - include ActiveModel::Serialization - - attr_accessor :street, :city, :state, :zip - - def attributes - instance_values - end - end - - setup do - @user = User.new('David', 'david@example.com', 'male') - @user.address = Address.new - @user.address.street = "123 Lane" - @user.address.city = "Springfield" - @user.address.state = "CA" - @user.address.zip = 11111 - @user.friends = [User.new('Joe', 'joe@example.com', 'male'), - User.new('Sue', 'sue@example.com', 'female')] - end - - def test_method_serializable_hash_should_work - expected = {"name"=>"David", "gender"=>"male", "email"=>"david@example.com"} - assert_equal expected , @user.serializable_hash - end - - def test_method_serializable_hash_should_work_with_only_option - expected = {"name"=>"David"} - assert_equal expected , @user.serializable_hash(:only => [:name]) - end - - def test_method_serializable_hash_should_work_with_except_option - expected = {"gender"=>"male", "email"=>"david@example.com"} - assert_equal expected , @user.serializable_hash(:except => [:name]) - end - - def test_method_serializable_hash_should_work_with_methods_option - expected = {"name"=>"David", "gender"=>"male", :foo=>"i_am_foo", "email"=>"david@example.com"} - assert_equal expected , @user.serializable_hash(:methods => [:foo]) - end - - def test_method_serializable_hash_should_work_with_only_and_methods - expected = {:foo=>"i_am_foo"} - assert_equal expected , @user.serializable_hash(:only => [], :methods => [:foo]) - end - - def test_method_serializable_hash_should_work_with_except_and_methods - expected = {"gender"=>"male", :foo=>"i_am_foo"} - assert_equal expected , @user.serializable_hash(:except => [:name, :email], :methods => [:foo]) - end - - def test_should_not_call_methods_that_dont_respond - expected = {"name"=>"David", "gender"=>"male", "email"=>"david@example.com"} - assert_equal expected , @user.serializable_hash(:methods => [:bar]) - end - - def test_should_use_read_attribute_for_serialization - def @user.read_attribute_for_serialization(n) - "Jon" - end - - expected = { "name" => "Jon" } - assert_equal expected, @user.serializable_hash(:only => :name) - end - - def test_include_option_with_singular_association - expected = {"name"=>"David", "gender"=>"male", "email"=>"david@example.com", - :address=>{"street"=>"123 Lane", "city"=>"Springfield", "state"=>"CA", "zip"=>11111}} - assert_equal expected , @user.serializable_hash(:include => :address) - end - - def test_include_option_with_plural_association - expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", - :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'}, - {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]} - assert_equal expected , @user.serializable_hash(:include => :friends) - end - - def test_include_option_with_empty_association - @user.friends = [] - expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", :friends=>[]} - assert_equal expected , @user.serializable_hash(:include => :friends) - end - - def test_multiple_includes - expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", - :address=>{"street"=>"123 Lane", "city"=>"Springfield", "state"=>"CA", "zip"=>11111}, - :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'}, - {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]} - assert_equal expected , @user.serializable_hash(:include => [:address, :friends]) - end - - def test_include_with_options - expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", - :address=>{"street"=>"123 Lane"}} - assert_equal expected , @user.serializable_hash(:include => {:address => {:only => "street"}}) - end - - def test_nested_include - @user.friends.first.friends = [@user] - expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", - :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male', - :friends => [{"email"=>"david@example.com", "gender"=>"male", "name"=>"David"}]}, - {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female', :friends => []}]} - assert_equal expected , @user.serializable_hash(:include => {:friends => {:include => :friends}}) - end - - def test_only_include - expected = {"name"=>"David", :friends => [{"name" => "Joe"}, {"name" => "Sue"}]} - assert_equal expected , @user.serializable_hash(:only => :name, :include => {:friends => {:only => :name}}) - end - - def test_except_include - expected = {"name"=>"David", "email"=>"david@example.com", - :friends => [{"name" => 'Joe', "email" => 'joe@example.com'}, - {"name" => "Sue", "email" => 'sue@example.com'}]} - assert_equal expected , @user.serializable_hash(:except => :gender, :include => {:friends => {:except => :gender}}) - end - - def test_multiple_includes_with_options - expected = {"email"=>"david@example.com", "gender"=>"male", "name"=>"David", - :address=>{"street"=>"123 Lane"}, - :friends=>[{"name"=>'Joe', "email"=>'joe@example.com', "gender"=>'male'}, - {"name"=>'Sue', "email"=>'sue@example.com', "gender"=>'female'}]} - assert_equal expected , @user.serializable_hash(:include => [{:address => {:only => "street"}}, :friends]) - end - -end diff --git a/activemodel/test/cases/serializer_test.rb b/activemodel/test/cases/serializer_test.rb index 00d519dc1a..e99b3692ec 100644 --- a/activemodel/test/cases/serializer_test.rb +++ b/activemodel/test/cases/serializer_test.rb @@ -9,18 +9,34 @@ class SerializerTest < ActiveModel::TestCase def read_attribute_for_serialization(name) @attributes[name] end + + def as_json(*) + { :model => "Model" } + end end - class User < Model + class User + include ActiveModel::Serializable + attr_accessor :superuser + attr_writer :model_serializer def initialize(hash={}) - super hash.merge(:first_name => "Jose", :last_name => "Valim", :password => "oh noes yugive my password") + @model_serializer = nil + @attributes = hash.merge(:first_name => "Jose", :last_name => "Valim", :password => "oh noes yugive my password") + end + + def read_attribute_for_serialization(name) + @attributes[name] end def super_user? @superuser end + + def model_serializer + @model_serializer || super + end end class Post < Model @@ -403,4 +419,47 @@ class SerializerTest < ActiveModel::TestCase } }, serializer.as_json) end -end + + def test_array_serializer + model = Model.new + user = User.new + post = Post.new(:title => "New Post", :body => "Body of new post", :email => "tenderlove@tenderlove.com") + comments = Comment.new(:title => "Comment1", :id => 1) + post.comments = [] + + array = [model, post, comments] + serializer = ActiveModel::Serializer::Finder.find(array, user).new(array, user) + assert_equal([ + { :model => "Model" }, + { :post => { :body => "Body of new post", :comments => [], :title => "New Post" } }, + { :comment => { :title => "Comment1" } } + ], serializer.as_json) + end + + def test_array_serializer_respects_model_serializer + user = User.new(:first_name => "Jose", :last_name => "Valim") + user.model_serializer = User2Serializer + + array = [user] + serializer = ActiveModel::Serializer::Finder.find(array, user).new(array, {}) + assert_equal([ + { :user2 => { :last_name => "Valim", :first_name => "Jose", :ok => true } }, + ], serializer.as_json) + end + + def test_finder_respects_model_serializer + user = User.new(:first_name => "Jose", :last_name => "Valim") + assert_equal UserSerializer, user.model_serializer + + serializer = ActiveModel::Serializer::Finder.find(user, self).new(user, {}) + assert_equal({ + :user => { :last_name => "Valim", :first_name => "Jose"}, + }, serializer.as_json) + + user.model_serializer = User2Serializer + serializer = ActiveModel::Serializer::Finder.find(user, self).new(user, {}) + assert_equal({ + :user2 => { :last_name => "Valim", :first_name => "Jose", :ok => true }, + }, serializer.as_json) + end +end \ No newline at end of file diff --git a/activemodel/test/cases/serializers/json_serialization_test.rb b/activemodel/test/cases/serializers/json_serialization_test.rb deleted file mode 100644 index a754d610b9..0000000000 --- a/activemodel/test/cases/serializers/json_serialization_test.rb +++ /dev/null @@ -1,219 +0,0 @@ -require 'cases/helper' -require 'models/contact' -require 'models/automobile' -require 'active_support/core_ext/object/instance_variables' - -class Contact - extend ActiveModel::Naming - include ActiveModel::Serializers::JSON - include ActiveModel::Validations - - def attributes=(hash) - hash.each do |k, v| - instance_variable_set("@#{k}", v) - end - end - - def attributes - instance_values - end unless method_defined?(:attributes) -end - -class JsonSerializationTest < ActiveModel::TestCase - def setup - @contact = Contact.new - @contact.name = 'Konata Izumi' - @contact.age = 16 - @contact.created_at = Time.utc(2006, 8, 1) - @contact.awesome = true - @contact.preferences = { 'shows' => 'anime' } - end - - test "should include root in json" do - json = @contact.to_json - - assert_match %r{^\{"contact":\{}, json - assert_match %r{"name":"Konata Izumi"}, json - assert_match %r{"age":16}, json - assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) - assert_match %r{"awesome":true}, json - assert_match %r{"preferences":\{"shows":"anime"\}}, json - end - - test "should not include root in json (class method)" do - begin - Contact.include_root_in_json = false - json = @contact.to_json - - assert_no_match %r{^\{"contact":\{}, json - assert_match %r{"name":"Konata Izumi"}, json - assert_match %r{"age":16}, json - assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) - assert_match %r{"awesome":true}, json - assert_match %r{"preferences":\{"shows":"anime"\}}, json - ensure - Contact.include_root_in_json = true - end - end - - test "should include root in json (option) even if the default is set to false" do - begin - Contact.include_root_in_json = false - json = @contact.to_json(:root => true) - assert_match %r{^\{"contact":\{}, json - ensure - Contact.include_root_in_json = true - end - end - - test "should not include root in json (option)" do - - json = @contact.to_json(:root => false) - - assert_no_match %r{^\{"contact":\{}, json - end - - test "should include custom root in json" do - json = @contact.to_json(:root => 'json_contact') - - assert_match %r{^\{"json_contact":\{}, json - assert_match %r{"name":"Konata Izumi"}, json - assert_match %r{"age":16}, json - assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) - assert_match %r{"awesome":true}, json - assert_match %r{"preferences":\{"shows":"anime"\}}, json - end - - test "should encode all encodable attributes" do - json = @contact.to_json - - assert_match %r{"name":"Konata Izumi"}, json - assert_match %r{"age":16}, json - assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) - assert_match %r{"awesome":true}, json - assert_match %r{"preferences":\{"shows":"anime"\}}, json - end - - test "should allow attribute filtering with only" do - json = @contact.to_json(:only => [:name, :age]) - - assert_match %r{"name":"Konata Izumi"}, json - assert_match %r{"age":16}, json - assert_no_match %r{"awesome":true}, json - assert !json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) - assert_no_match %r{"preferences":\{"shows":"anime"\}}, json - end - - test "should allow attribute filtering with except" do - json = @contact.to_json(:except => [:name, :age]) - - assert_no_match %r{"name":"Konata Izumi"}, json - assert_no_match %r{"age":16}, json - assert_match %r{"awesome":true}, json - assert json.include?(%("created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))})) - assert_match %r{"preferences":\{"shows":"anime"\}}, json - end - - test "methods are called on object" do - # Define methods on fixture. - def @contact.label; "Has cheezburger"; end - def @contact.favorite_quote; "Constraints are liberating"; end - - # Single method. - assert_match %r{"label":"Has cheezburger"}, @contact.to_json(:only => :name, :methods => :label) - - # Both methods. - methods_json = @contact.to_json(:only => :name, :methods => [:label, :favorite_quote]) - assert_match %r{"label":"Has cheezburger"}, methods_json - assert_match %r{"favorite_quote":"Constraints are liberating"}, methods_json - end - - test "should return OrderedHash for errors" do - contact = Contact.new - contact.errors.add :name, "can't be blank" - contact.errors.add :name, "is too short (minimum is 2 characters)" - contact.errors.add :age, "must be 16 or over" - - hash = ActiveSupport::OrderedHash.new - hash[:name] = ["can't be blank", "is too short (minimum is 2 characters)"] - hash[:age] = ["must be 16 or over"] - assert_equal hash.to_json, contact.errors.to_json - end - - test "serializable_hash should not modify options passed in argument" do - options = { :except => :name } - @contact.serializable_hash(options) - - assert_nil options[:only] - assert_equal :name, options[:except] - end - - test "as_json should return a hash" do - json = @contact.as_json - - assert_kind_of Hash, json - assert_kind_of Hash, json['contact'] - %w(name age created_at awesome preferences).each do |field| - assert_equal @contact.send(field), json['contact'][field] - end - end - - test "from_json should set the object's attributes" do - json = @contact.to_json - result = Contact.new.from_json(json) - - assert_equal result.name, @contact.name - assert_equal result.age, @contact.age - assert_equal Time.parse(result.created_at), @contact.created_at - assert_equal result.awesome, @contact.awesome - assert_equal result.preferences, @contact.preferences - end - - test "from_json should work without a root (method parameter)" do - json = @contact.to_json(:root => false) - result = Contact.new.from_json(json, false) - - assert_equal result.name, @contact.name - assert_equal result.age, @contact.age - assert_equal Time.parse(result.created_at), @contact.created_at - assert_equal result.awesome, @contact.awesome - assert_equal result.preferences, @contact.preferences - end - - test "from_json should work without a root (class attribute)" do - begin - Contact.include_root_in_json = false - json = @contact.to_json - result = Contact.new.from_json(json) - - assert_equal result.name, @contact.name - assert_equal result.age, @contact.age - assert_equal Time.parse(result.created_at), @contact.created_at - assert_equal result.awesome, @contact.awesome - assert_equal result.preferences, @contact.preferences - ensure - Contact.include_root_in_json = true - end - end - - test "custom as_json should be honored when generating json" do - def @contact.as_json(options); { :name => name, :created_at => created_at }; end - json = @contact.to_json - - assert_match %r{"name":"Konata Izumi"}, json - assert_match %r{"created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}}, json - assert_no_match %r{"awesome":}, json - assert_no_match %r{"preferences":}, json - end - - test "custom as_json options should be extendible" do - def @contact.as_json(options = {}); super(options.merge(:only => [:name])); end - json = @contact.to_json - - assert_match %r{"name":"Konata Izumi"}, json - assert_no_match %r{"created_at":#{ActiveSupport::JSON.encode(Time.utc(2006, 8, 1))}}, json - assert_no_match %r{"awesome":}, json - assert_no_match %r{"preferences":}, json - end - -end diff --git a/activemodel/test/cases/serializers/xml_serialization_test.rb b/activemodel/test/cases/serializers/xml_serialization_test.rb deleted file mode 100644 index fc73d9dcd8..0000000000 --- a/activemodel/test/cases/serializers/xml_serialization_test.rb +++ /dev/null @@ -1,206 +0,0 @@ -require 'cases/helper' -require 'models/contact' -require 'active_support/core_ext/object/instance_variables' -require 'ostruct' - -class Contact - extend ActiveModel::Naming - include ActiveModel::Serializers::Xml - - attr_accessor :address, :friends - - def attributes - instance_values.except("address", "friends") - end -end - -module Admin - class Contact < ::Contact - end -end - -class Customer < Struct.new(:name) -end - -class Address - extend ActiveModel::Naming - include ActiveModel::Serializers::Xml - - attr_accessor :street, :city, :state, :zip - - def attributes - instance_values - end -end - -class SerializableContact < Contact - def serializable_hash(options={}) - super(options.merge(:only => [:name, :age])) - end -end - -class XmlSerializationTest < ActiveModel::TestCase - def setup - @contact = Contact.new - @contact.name = 'aaron stack' - @contact.age = 25 - @contact.created_at = Time.utc(2006, 8, 1) - @contact.awesome = false - customer = Customer.new - customer.name = "John" - @contact.preferences = customer - @contact.address = Address.new - @contact.address.street = "123 Lane" - @contact.address.city = "Springfield" - @contact.address.state = "CA" - @contact.address.zip = 11111 - @contact.friends = [Contact.new, Contact.new] - end - - test "should serialize default root" do - @xml = @contact.to_xml - assert_match %r{^}, @xml - assert_match %r{$}, @xml - end - - test "should serialize namespaced root" do - @xml = Admin::Contact.new(@contact.attributes).to_xml - assert_match %r{^}, @xml - assert_match %r{$}, @xml - end - - test "should serialize default root with namespace" do - @xml = @contact.to_xml :namespace => "http://xml.rubyonrails.org/contact" - assert_match %r{^}, @xml - assert_match %r{$}, @xml - end - - test "should serialize custom root" do - @xml = @contact.to_xml :root => 'xml_contact' - assert_match %r{^}, @xml - assert_match %r{$}, @xml - end - - test "should allow undasherized tags" do - @xml = @contact.to_xml :root => 'xml_contact', :dasherize => false - assert_match %r{^}, @xml - assert_match %r{$}, @xml - assert_match %r{ 'xml_contact', :camelize => true - assert_match %r{^}, @xml - assert_match %r{$}, @xml - assert_match %r{ 'xml_contact', :camelize => :lower - assert_match %r{^}, @xml - assert_match %r{$}, @xml - assert_match %r{aaron stack}, @xml - assert_match %r{25}, @xml - assert_no_match %r{}, @xml - end - - test "should allow skipped types" do - @xml = @contact.to_xml :skip_types => true - assert_match %r{25}, @xml - end - - test "should include yielded additions" do - @xml = @contact.to_xml do |xml| - xml.creator "David" - end - assert_match %r{David}, @xml - end - - test "should serialize string" do - assert_match %r{aaron stack}, @contact.to_xml - end - - test "should serialize nil" do - assert_match %r{}, @contact.to_xml(:methods => :pseudonyms) - end - - test "should serialize integer" do - assert_match %r{25}, @contact.to_xml - end - - test "should serialize datetime" do - assert_match %r{2006-08-01T00:00:00Z}, @contact.to_xml - end - - test "should serialize boolean" do - assert_match %r{false}, @contact.to_xml - end - - test "should serialize array" do - assert_match %r{\s*twitter\s*github\s*}, @contact.to_xml(:methods => :social) - end - - test "should serialize hash" do - assert_match %r{\s*github\s*}, @contact.to_xml(:methods => :network) - end - - test "should serialize yaml" do - assert_match %r{--- !ruby/struct:Customer(\s*)\nname: John\n}, @contact.to_xml - end - - test "should call proc on object" do - proc = Proc.new { |options| options[:builder].tag!('nationality', 'unknown') } - xml = @contact.to_xml(:procs => [ proc ]) - assert_match %r{unknown}, xml - end - - test 'should supply serializable to second proc argument' do - proc = Proc.new { |options, record| options[:builder].tag!('name-reverse', record.name.reverse) } - xml = @contact.to_xml(:procs => [ proc ]) - assert_match %r{kcats noraa}, xml - end - - test "should serialize string correctly when type passed" do - xml = @contact.to_xml :type => 'Contact' - assert_match %r{}, xml - assert_match %r{aaron stack}, xml - end - - test "include option with singular association" do - xml = @contact.to_xml :include => :address, :indent => 0 - assert xml.include?(@contact.address.to_xml(:indent => 0, :skip_instruct => true)) - end - - test "include option with plural association" do - xml = @contact.to_xml :include => :friends, :indent => 0 - assert_match %r{}, xml - assert_match %r{}, xml - end - - test "multiple includes" do - xml = @contact.to_xml :indent => 0, :skip_instruct => true, :include => [ :address, :friends ] - assert xml.include?(@contact.address.to_xml(:indent => 0, :skip_instruct => true)) - assert_match %r{}, xml - assert_match %r{}, xml - end - - test "include with options" do - xml = @contact.to_xml :indent => 0, :skip_instruct => true, :include => { :address => { :only => :city } } - assert xml.include?(%(>
Springfield
)) - end - - test "propagates skip_types option to included associations" do - xml = @contact.to_xml :include => :friends, :indent => 0, :skip_types => true - assert_match %r{}, xml - assert_match %r{}, xml - end -end diff --git a/activerecord/lib/active_record/serialization.rb b/activerecord/lib/active_record/serialization.rb index 5ad40d8cd9..c23514c465 100644 --- a/activerecord/lib/active_record/serialization.rb +++ b/activerecord/lib/active_record/serialization.rb @@ -2,7 +2,7 @@ module ActiveRecord #:nodoc: # = Active Record Serialization module Serialization extend ActiveSupport::Concern - include ActiveModel::Serializers::JSON + include ActiveModel::Serializable::JSON def serializable_hash(options = nil) options = options.try(:clone) || {} diff --git a/activerecord/lib/active_record/serializers/xml_serializer.rb b/activerecord/lib/active_record/serializers/xml_serializer.rb index 0e7f57aa43..2da836ef0c 100644 --- a/activerecord/lib/active_record/serializers/xml_serializer.rb +++ b/activerecord/lib/active_record/serializers/xml_serializer.rb @@ -3,7 +3,7 @@ require 'active_support/core_ext/hash/conversions' module ActiveRecord #:nodoc: module Serialization - include ActiveModel::Serializers::Xml + include ActiveModel::Serializable::XML # Builds an XML document to represent the model. Some configuration is # available through +options+. However more complicated cases should @@ -176,13 +176,13 @@ module ActiveRecord #:nodoc: end end - class XmlSerializer < ActiveModel::Serializers::Xml::Serializer #:nodoc: + class XmlSerializer < ActiveModel::Serializable::XML::Serializer #:nodoc: def initialize(*args) super options[:except] = Array.wrap(options[:except]) | Array.wrap(@serializable.class.inheritance_column) end - class Attribute < ActiveModel::Serializers::Xml::Serializer::Attribute #:nodoc: + class Attribute < ActiveModel::Serializable::XML::Serializer::Attribute #:nodoc: def compute_type klass = @serializable.class type = if klass.serialized_attributes.key?(name) diff --git a/activesupport/lib/active_support/json/encoding.rb b/activesupport/lib/active_support/json/encoding.rb index 469ae69258..b0fd3a3c0e 100644 --- a/activesupport/lib/active_support/json/encoding.rb +++ b/activesupport/lib/active_support/json/encoding.rb @@ -50,9 +50,9 @@ module ActiveSupport end # like encode, but only calls as_json, without encoding to string - def as_json(value) + def as_json(value, use_options = true) check_for_circular_references(value) do - value.as_json(options_for(value)) + use_options ? value.as_json(options_for(value)) : value.as_json end end @@ -212,7 +212,7 @@ class Array def as_json(options = nil) #:nodoc: # use encoder as a proxy to call as_json on all elements, to protect from circular references encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options) - map { |v| encoder.as_json(v) } + map { |v| encoder.as_json(v, options) } end def encode_json(encoder) #:nodoc: @@ -239,7 +239,7 @@ class Hash # use encoder as a proxy to call as_json on all values in the subset, to protect from circular references encoder = options && options[:encoder] || ActiveSupport::JSON::Encoding::Encoder.new(options) result = self.is_a?(ActiveSupport::OrderedHash) ? ActiveSupport::OrderedHash : Hash - result[subset.map { |k, v| [k.to_s, encoder.as_json(v)] }] + result[subset.map { |k, v| [k.to_s, encoder.as_json(v, options)] }] end def encode_json(encoder) -- cgit v1.2.3 From 6da52c617e2a075b9d9e7142786f316d8f2c7930 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 23 Nov 2011 23:22:46 +0000 Subject: Remove listings from the serialization guide that won't make 3.2. --- railties/guides/source/serializers.textile | 37 ------------------------------ 1 file changed, 37 deletions(-) diff --git a/railties/guides/source/serializers.textile b/railties/guides/source/serializers.textile index 86a5e5ac8d..efc7cbf248 100644 --- a/railties/guides/source/serializers.textile +++ b/railties/guides/source/serializers.textile @@ -459,25 +459,6 @@ In other words, if a +PostSerializer+ is trying to serialize comments, it will f look for +PostSerializer::CommentSerializer+ before falling back to +CommentSerializer+ and finally +comment.as_json+. -h3. Optional Associations - -In some cases, you will want to allow a front-end to decide whether to include associated -content or not. You can achieve this easily by making an association *optional*. - - -class PostSerializer < ActiveModel::Serializer - attributes :title. :body - has_many :comments, :optional => true - - # ... -end - - -If an association is optional, it will not be included unless the request asks for it -with an +including+ parameter. The +including+ parameter is a comma-separated list of -optional associations to include. If the +including+ parameter includes an association -you did not specify in your serializer, it will receive a +401 Forbidden+ response. - h3. Overriding the Defaults h4. Authorization Scope @@ -500,24 +481,6 @@ which allows you to define a dynamic authorization scope based on the current re WARNING: If you use different objects as authorization scopes, make sure that they all implement whatever interface you use in your serializers to control what the outputted JSON looks like. -h4. Parameter to Specify Included Optional Associations - -In most cases, you should be able to use the default +including+ parameter to specify -which optional associations to include. If you are already using that parameter name or -want to reserve it for some reason, you can specify a different name by using the -+serialization_includes_param+ class method. - - -class PostsController < ApplicationController - serialization_includes_param :associations_to_include -end - - -You can also implement a +serialization_includes+ instance method, which should return an -Array of optional includes. - -WARNING: If you implement +serialization_includes+ and return an invalid association, your user will receive a +401 Forbidden+ exception. - h3. Using Serializers Outside of a Request The serialization API encapsulates the concern of generating a JSON representation of -- cgit v1.2.3 From 0e7443060b298d080292b18a5888aaf554cce344 Mon Sep 17 00:00:00 2001 From: Michael Pearson Date: Thu, 24 Nov 2011 10:33:04 +1100 Subject: Add step to getting_started to install JS Runtime. abstrakt on #rubyonrails found that the guide, when followed step by step, does fails at the 'rails server' command due to a lacking JS runtime. --- railties/guides/source/getting_started.textile | 12 ++++++++++++ 1 file changed, 12 insertions(+) diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile index fde83ae730..55415dd41b 100644 --- a/railties/guides/source/getting_started.textile +++ b/railties/guides/source/getting_started.textile @@ -250,6 +250,18 @@ $ rails --version If it says something like "Rails 3.1.1" you are ready to continue. +h5. Installing a JavaScript Runtime + +By default, Rails requires a JavaScript interpreter to compile CoffeeScript to JavaScript. + +You can install one by running: + + +# gem install therubyracer + + +Or investigate the list of alternatives give by "ExecJS":https://github.com/sstephenson/execjs. + h4. Creating the Blog Application To begin, open a terminal, navigate to a folder where you have rights to create -- cgit v1.2.3 From 7fcc8c0a1f38c77b12cb6ffe81fb2887e6c60b85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 23 Nov 2011 23:45:27 +0000 Subject: Rely solely on active_model_serializer and remove the fancy constant lookup. --- .../lib/action_controller/metal/serialization.rb | 4 +- actionpack/test/controller/render_json_test.rb | 16 +++++++- activemodel/lib/active_model/serializable.rb | 12 +++--- activemodel/lib/active_model/serializer.rb | 45 ++++------------------ activemodel/test/cases/serializer_test.rb | 37 ++---------------- activesupport/lib/active_support/dependencies.rb | 1 + activesupport/test/class_cache_test.rb | 5 +++ 7 files changed, 41 insertions(+), 79 deletions(-) diff --git a/actionpack/lib/action_controller/metal/serialization.rb b/actionpack/lib/action_controller/metal/serialization.rb index 9bb665a9ae..9fb49f512e 100644 --- a/actionpack/lib/action_controller/metal/serialization.rb +++ b/actionpack/lib/action_controller/metal/serialization.rb @@ -13,7 +13,9 @@ module ActionController end def _render_option_json(json, options) - json = json.active_model_serializer.new(json, serialization_scope) if json.respond_to?(:active_model_serializer) + if json.respond_to?(:active_model_serializer) && (serializer = json.active_model_serializer) + json = serializer.new(json, serialization_scope) + end super end diff --git a/actionpack/test/controller/render_json_test.rb b/actionpack/test/controller/render_json_test.rb index f886af1a95..dc09812ba3 100644 --- a/actionpack/test/controller/render_json_test.rb +++ b/actionpack/test/controller/render_json_test.rb @@ -26,8 +26,12 @@ class RenderJsonTest < ActionController::TestCase end class JsonSerializable + def initialize(skip=false) + @skip = skip + end + def active_model_serializer - JsonSerializer + JsonSerializer unless @skip end def as_json(*) @@ -89,6 +93,11 @@ class RenderJsonTest < ActionController::TestCase @current_user = Struct.new(:as_json).new(:current_user => true) render :json => JsonSerializable.new end + + def render_json_with_serializer_api_but_without_serializer + @current_user = Struct.new(:as_json).new(:current_user => true) + render :json => JsonSerializable.new(true) + end end tests TestController @@ -166,4 +175,9 @@ class RenderJsonTest < ActionController::TestCase assert_match '"scope":{"current_user":true}', @response.body assert_match '"object":{"serializable_object":true}', @response.body end + + def test_render_json_with_serializer_api_but_without_serializer + get :render_json_with_serializer_api_but_without_serializer + assert_match '{"serializable_object":true}', @response.body + end end diff --git a/activemodel/lib/active_model/serializable.rb b/activemodel/lib/active_model/serializable.rb index 769e934dbe..70e27c5683 100644 --- a/activemodel/lib/active_model/serializable.rb +++ b/activemodel/lib/active_model/serializable.rb @@ -1,6 +1,7 @@ require 'active_support/core_ext/hash/except' require 'active_support/core_ext/hash/slice' require 'active_support/core_ext/array/wrap' +require 'active_support/core_ext/string/inflections' module ActiveModel # == Active Model Serializable @@ -72,11 +73,10 @@ module ActiveModel autoload :JSON, "active_model/serializable/json" autoload :XML, "active_model/serializable/xml" - include ActiveModel::Serializer::Scope - module ClassMethods #:nodoc: - def _model_serializer - @_model_serializer ||= ActiveModel::Serializer::Finder.find(self, self) + def active_model_serializer + return @active_model_serializer if defined?(@active_model_serializer) + @active_model_serializer = "#{self.name}Serializer".safe_constantize end end @@ -108,8 +108,8 @@ module ActiveModel end # Returns a model serializer for this object considering its namespace. - def model_serializer - self.class._model_serializer + def active_model_serializer + self.class.active_model_serializer end private diff --git a/activemodel/lib/active_model/serializer.rb b/activemodel/lib/active_model/serializer.rb index a541a1053d..5478da15c8 100644 --- a/activemodel/lib/active_model/serializer.rb +++ b/activemodel/lib/active_model/serializer.rb @@ -1,7 +1,6 @@ require "active_support/core_ext/class/attribute" require "active_support/core_ext/string/inflections" require "active_support/core_ext/module/anonymous" -require "active_support/core_ext/module/introspection" require "set" module ActiveModel @@ -15,7 +14,7 @@ module ActiveModel def serializable_array @object.map do |item| - if serializer = Serializer::Finder.find(item, scope) + if item.respond_to?(:active_model_serializer) && (serializer = item.active_model_serializer) serializer.new(item, scope) else item @@ -30,41 +29,6 @@ module ActiveModel # Active Model Serializer class Serializer - module Finder - mattr_accessor :constantizer - @@constantizer = ActiveSupport::Inflector - - # Finds a serializer for the given object in the given scope. - # If the object implements a +model_serializer+ method, it does - # not do a scope lookup but uses the model_serializer method instead. - def self.find(object, scope) - if object.respond_to?(:model_serializer) - object.model_serializer - else - scope = scope.class unless scope.respond_to?(:const_defined?) - object = object.class unless object.respond_to?(:name) - serializer = "#{object.name.demodulize}Serializer" - - begin - scope.const_get serializer - rescue NameError => e - raise unless e.message =~ /uninitialized constant ([\w_]+::)*#{serializer}$/ - scope.parents.each do |parent| - return parent.const_get(serializer) if parent.const_defined?(serializer) - end - nil - end - end - end - end - - # Defines the serialization scope. Core extension serializers - # are defined in this module so a scoped lookup is able to find - # core extension serializers. - module Scope - ArraySerializer = ::ActiveModel::ArraySerializer - end - module Associations class Config < Struct.new(:name, :options) def serializer @@ -216,3 +180,10 @@ module ActiveModel end end end + +class Array + # Array uses ActiveModel::ArraySerializer. + def active_model_serializer + ActiveModel::ArraySerializer + end +end \ No newline at end of file diff --git a/activemodel/test/cases/serializer_test.rb b/activemodel/test/cases/serializer_test.rb index e99b3692ec..f6c4282d39 100644 --- a/activemodel/test/cases/serializer_test.rb +++ b/activemodel/test/cases/serializer_test.rb @@ -19,10 +19,8 @@ class SerializerTest < ActiveModel::TestCase include ActiveModel::Serializable attr_accessor :superuser - attr_writer :model_serializer def initialize(hash={}) - @model_serializer = nil @attributes = hash.merge(:first_name => "Jose", :last_name => "Valim", :password => "oh noes yugive my password") end @@ -33,17 +31,15 @@ class SerializerTest < ActiveModel::TestCase def super_user? @superuser end - - def model_serializer - @model_serializer || super - end end class Post < Model attr_accessor :comments + def active_model_serializer; PostSerializer; end end class Comment < Model + def active_model_serializer; CommentSerializer; end end class UserSerializer < ActiveModel::Serializer @@ -428,38 +424,11 @@ class SerializerTest < ActiveModel::TestCase post.comments = [] array = [model, post, comments] - serializer = ActiveModel::Serializer::Finder.find(array, user).new(array, user) + serializer = array.active_model_serializer.new(array, user) assert_equal([ { :model => "Model" }, { :post => { :body => "Body of new post", :comments => [], :title => "New Post" } }, { :comment => { :title => "Comment1" } } ], serializer.as_json) end - - def test_array_serializer_respects_model_serializer - user = User.new(:first_name => "Jose", :last_name => "Valim") - user.model_serializer = User2Serializer - - array = [user] - serializer = ActiveModel::Serializer::Finder.find(array, user).new(array, {}) - assert_equal([ - { :user2 => { :last_name => "Valim", :first_name => "Jose", :ok => true } }, - ], serializer.as_json) - end - - def test_finder_respects_model_serializer - user = User.new(:first_name => "Jose", :last_name => "Valim") - assert_equal UserSerializer, user.model_serializer - - serializer = ActiveModel::Serializer::Finder.find(user, self).new(user, {}) - assert_equal({ - :user => { :last_name => "Valim", :first_name => "Jose"}, - }, serializer.as_json) - - user.model_serializer = User2Serializer - serializer = ActiveModel::Serializer::Finder.find(user, self).new(user, {}) - assert_equal({ - :user2 => { :last_name => "Valim", :first_name => "Jose", :ok => true }, - }, serializer.as_json) - end end \ No newline at end of file diff --git a/activesupport/lib/active_support/dependencies.rb b/activesupport/lib/active_support/dependencies.rb index 2f1066016b..1372e71a61 100644 --- a/activesupport/lib/active_support/dependencies.rb +++ b/activesupport/lib/active_support/dependencies.rb @@ -542,6 +542,7 @@ module ActiveSupport #:nodoc: key = key.name if key.respond_to?(:name) @store[key] ||= Inflector.constantize(key) end + alias :[] :get def safe_get(key) key = key.name if key.respond_to?(:name) diff --git a/activesupport/test/class_cache_test.rb b/activesupport/test/class_cache_test.rb index 87f61dcfc8..b96f476ce6 100644 --- a/activesupport/test/class_cache_test.rb +++ b/activesupport/test/class_cache_test.rb @@ -49,6 +49,11 @@ module ActiveSupport end end + def test_get_alias + assert @cache.empty? + assert_equal @cache[ClassCacheTest.name], @cache.get(ClassCacheTest.name) + end + def test_safe_get_constantizes assert @cache.empty? assert_equal ClassCacheTest, @cache.safe_get(ClassCacheTest.name) -- cgit v1.2.3 From 28bcda4098985ae5eb2d18c5214d95cf04f3ea93 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Wed, 23 Nov 2011 23:53:20 +0000 Subject: Rename UserSerializer to DefaultUserSerializer in tests. --- activemodel/test/cases/serializer_test.rb | 28 +++++++++++++--------------- 1 file changed, 13 insertions(+), 15 deletions(-) diff --git a/activemodel/test/cases/serializer_test.rb b/activemodel/test/cases/serializer_test.rb index f6c4282d39..2b2e21540f 100644 --- a/activemodel/test/cases/serializer_test.rb +++ b/activemodel/test/cases/serializer_test.rb @@ -44,16 +44,16 @@ class SerializerTest < ActiveModel::TestCase class UserSerializer < ActiveModel::Serializer attributes :first_name, :last_name - end - - class User2Serializer < ActiveModel::Serializer - attributes :first_name, :last_name def serializable_hash attributes.merge(:ok => true).merge(scope) end end + class DefaultUserSerializer < ActiveModel::Serializer + attributes :first_name, :last_name + end + class MyUserSerializer < ActiveModel::Serializer attributes :first_name, :last_name @@ -85,34 +85,34 @@ class SerializerTest < ActiveModel::TestCase def test_attributes user = User.new - user_serializer = UserSerializer.new(user, nil) + user_serializer = DefaultUserSerializer.new(user, {}) hash = user_serializer.as_json assert_equal({ - :user => { :first_name => "Jose", :last_name => "Valim" } + :default_user => { :first_name => "Jose", :last_name => "Valim" } }, hash) end def test_attributes_method user = User.new - user_serializer = User2Serializer.new(user, {}) + user_serializer = UserSerializer.new(user, {}) hash = user_serializer.as_json assert_equal({ - :user2 => { :first_name => "Jose", :last_name => "Valim", :ok => true } + :user => { :first_name => "Jose", :last_name => "Valim", :ok => true } }, hash) end def test_serializer_receives_scope user = User.new - user_serializer = User2Serializer.new(user, {:scope => true}) + user_serializer = UserSerializer.new(user, {:scope => true}) hash = user_serializer.as_json assert_equal({ - :user2 => { + :user => { :first_name => "Jose", :last_name => "Valim", :ok => true, @@ -419,15 +419,13 @@ class SerializerTest < ActiveModel::TestCase def test_array_serializer model = Model.new user = User.new - post = Post.new(:title => "New Post", :body => "Body of new post", :email => "tenderlove@tenderlove.com") comments = Comment.new(:title => "Comment1", :id => 1) - post.comments = [] - array = [model, post, comments] - serializer = array.active_model_serializer.new(array, user) + array = [model, user, comments] + serializer = array.active_model_serializer.new(array, {:scope => true}) assert_equal([ { :model => "Model" }, - { :post => { :body => "Body of new post", :comments => [], :title => "New Post" } }, + { :user => { :last_name=>"Valim", :ok=>true, :first_name=>"Jose", :scope => true } }, { :comment => { :title => "Comment1" } } ], serializer.as_json) end -- cgit v1.2.3 From ef38c3089e1269e2b315aff503e61c0b23c95f00 Mon Sep 17 00:00:00 2001 From: Michael Pearson Date: Thu, 24 Nov 2011 10:59:29 +1100 Subject: Move JS runtime instructions to where the error would be encountered, remove gem install. --- railties/guides/source/getting_started.textile | 18 ++++++------------ 1 file changed, 6 insertions(+), 12 deletions(-) diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile index 55415dd41b..5774eba5ae 100644 --- a/railties/guides/source/getting_started.textile +++ b/railties/guides/source/getting_started.textile @@ -250,18 +250,6 @@ $ rails --version If it says something like "Rails 3.1.1" you are ready to continue. -h5. Installing a JavaScript Runtime - -By default, Rails requires a JavaScript interpreter to compile CoffeeScript to JavaScript. - -You can install one by running: - - -# gem install therubyracer - - -Or investigate the list of alternatives give by "ExecJS":https://github.com/sstephenson/execjs. - h4. Creating the Blog Application To begin, open a terminal, navigate to a folder where you have rights to create @@ -462,6 +450,12 @@ start a web server on your development machine. You can do this by running: $ rails server +TIP: Some environments require that you install a JavaScript runtime to compile +CoffeeScript to JavaScript and will give you an +execjs+ error the first time +you run +rails server+. +therubyracer+ and +therubyrhino+ are commonly used +runtimes for Ruby and JRuby respectively. You can also investigate a list +of runtimes at "ExecJS":https://github.com/sstephenson/execjs. + This will fire up an instance of the WEBrick web server by default (Rails can also use several other web servers). To see your application in action, open a browser window and navigate to "http://localhost:3000":http://localhost:3000. -- cgit v1.2.3 From 510502ee3abfc8feacdf82868013f2f81676c8ff Mon Sep 17 00:00:00 2001 From: rpq Date: Wed, 23 Nov 2011 23:23:48 -0500 Subject: comma --- railties/guides/source/active_support_core_extensions.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile index ff6c5f967f..da70f9206d 100644 --- a/railties/guides/source/active_support_core_extensions.textile +++ b/railties/guides/source/active_support_core_extensions.textile @@ -571,7 +571,7 @@ NOTE: Defined in +active_support/core_ext/module/attr_accessor_with_default.rb+. h5. Internal Attributes -When you are defining an attribute in a class that is meant to be subclassed name collisions are a risk. That's remarkably important for libraries. +When you are defining an attribute in a class that is meant to be subclassed, name collisions are a risk. That's remarkably important for libraries. Active Support defines the macros +attr_internal_reader+, +attr_internal_writer+, and +attr_internal_accessor+. They behave like their Ruby built-in +attr_*+ counterparts, except they name the underlying instance variable in a way that makes collisions less likely. -- cgit v1.2.3 From a478389b7d70536f1629d080a50a9ecd87c005d0 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Thu, 24 Nov 2011 01:59:01 +0100 Subject: Forgot to add CHANGELOG entry for config.railties_order --- railties/CHANGELOG.md | 4 ++++ 1 file changed, 4 insertions(+) diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index b05ac21b49..2d7a1db2be 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,5 +1,9 @@ ## Rails 3.2.0 (unreleased) ## +* Allow to change the loading order of railties with `config.railties_order=`. Example: + + config.railties_order = [Blog::Engine, :main_app, :all] + * Scaffold returns 204 No Content for API requests without content. This makes scaffold work with jQuery out of the box. *José Valim* * Updated Rails::Rack::Logger middleware to apply any tags set in config.log_tags to the newly ActiveSupport::TaggedLogging Rails.logger. This makes it easy to tag log lines with debug information like subdomain and request id -- both very helpful in debugging multi-user production applications *DHH* -- cgit v1.2.3 From dc39af0a9a998938a969b214554db624dcdd9c85 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jakub=20Ku=C5=BAma?= Date: Thu, 24 Nov 2011 15:50:21 +0100 Subject: make ActiveModel::Name fail gracefully with anonymous classes --- activemodel/lib/active_model/naming.rb | 3 +++ activemodel/test/cases/naming_test.rb | 13 +++++++++++++ 2 files changed, 16 insertions(+) diff --git a/activemodel/lib/active_model/naming.rb b/activemodel/lib/active_model/naming.rb index 2566920d63..953d24a3b2 100644 --- a/activemodel/lib/active_model/naming.rb +++ b/activemodel/lib/active_model/naming.rb @@ -12,6 +12,9 @@ module ActiveModel def initialize(klass, namespace = nil, name = nil) name ||= klass.name + + raise ArgumentError, "Class name cannot be blank. You need to supply a name argument when anonymous class given" if name.blank? + super(name) @unnamespaced = self.sub(/^#{namespace.name}::/, '') if namespace diff --git a/activemodel/test/cases/naming_test.rb b/activemodel/test/cases/naming_test.rb index a5ee2d6090..6f9004da7f 100644 --- a/activemodel/test/cases/naming_test.rb +++ b/activemodel/test/cases/naming_test.rb @@ -247,3 +247,16 @@ class NamingHelpersTest < Test::Unit::TestCase ActiveModel::Naming.send(method, *args) end end + +class NameWithAnonymousClassTest < Test::Unit::TestCase + def test_anonymous_class_without_name_argument + assert_raises(ArgumentError) do + model_name = ActiveModel::Name.new(Class.new) + end + end + + def test_anonymous_class_with_name_argument + model_name = ActiveModel::Name.new(Class.new, nil, "Anonymous") + assert_equal "Anonymous", model_name + end +end -- cgit v1.2.3 From 0cd3bf84068dd2b2d0bbb26062f2cdc7093a1b04 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Thu, 24 Nov 2011 01:45:50 +0100 Subject: Allow to display engine's routes when running `rake routes ENGINES=true` --- railties/CHANGELOG.md | 7 +++-- railties/lib/rails/application/route_inspector.rb | 37 +++++++++++++++++++++-- railties/lib/rails/tasks/routes.rake | 2 +- railties/test/application/route_inspect_test.rb | 30 ++++++++++++++++++ 4 files changed, 70 insertions(+), 6 deletions(-) diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 2d7a1db2be..a7afb72562 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,8 +1,11 @@ ## Rails 3.2.0 (unreleased) ## -* Allow to change the loading order of railties with `config.railties_order=`. Example: +* Added displaying of mounted engine's routes with `rake routes ENGINES=true`. *Piotr Sarnacki* - config.railties_order = [Blog::Engine, :main_app, :all] +* Allow to change the loading order of railties with `config.railties_order=`. *Piotr Sarnacki* + + Example: + config.railties_order = [Blog::Engine, :main_app, :all] * Scaffold returns 204 No Content for API requests without content. This makes scaffold work with jQuery out of the box. *José Valim* diff --git a/railties/lib/rails/application/route_inspector.rb b/railties/lib/rails/application/route_inspector.rb index 8252f21aa7..8da7de2584 100644 --- a/railties/lib/rails/application/route_inspector.rb +++ b/railties/lib/rails/application/route_inspector.rb @@ -4,12 +4,23 @@ module Rails # This class is just used for displaying route information when someone # executes `rake routes`. People should not use this class. class RouteInspector # :nodoc: + def initialize + @engines = ActiveSupport::OrderedHash.new + end + def format all_routes, filter = nil if filter all_routes = all_routes.select{ |route| route.defaults[:controller] == filter } end - routes = all_routes.collect do |route| + routes = collect_routes(all_routes) + + formatted_routes(routes) + + formatted_routes_for_engines + end + + def collect_routes(routes) + routes = routes.collect do |route| route_reqs = route.requirements rack_app = route.app unless route.app.class.name.to_s =~ /^ActionDispatch::Routing/ @@ -25,12 +36,32 @@ module Rails verb = route.verb.source.gsub(/[$^]/, '') - {:name => route.name.to_s, :verb => verb, :path => route.path.spec.to_s, :reqs => reqs} + collect_engine_routes(reqs, rack_app) + + {:name => route.name.to_s, :verb => verb, :path => route.path.spec.to_s, :reqs => reqs } end # Skip the route if it's internal info route - routes.reject! { |r| r[:path] =~ %r{/rails/info/properties|^/assets} } + routes.reject { |r| r[:path] =~ %r{/rails/info/properties|^/assets} } + end + + def collect_engine_routes(name, rack_app) + return unless rack_app && ENV["ENGINES"] && rack_app.respond_to?(:routes) + return if @engines[name] + + routes = rack_app.routes + if routes.is_a?(ActionDispatch::Routing::RouteSet) + @engines[name] = collect_routes(routes.routes) + end + end + + def formatted_routes_for_engines + @engines.map do |name, routes| + ["\nRoutes for #{name}:"] + formatted_routes(routes) + end.flatten + end + def formatted_routes(routes) name_width = routes.map{ |r| r[:name].length }.max verb_width = routes.map{ |r| r[:verb].length }.max path_width = routes.map{ |r| r[:path].length }.max diff --git a/railties/lib/rails/tasks/routes.rake b/railties/lib/rails/tasks/routes.rake index 7dc54144da..df72baef67 100644 --- a/railties/lib/rails/tasks/routes.rake +++ b/railties/lib/rails/tasks/routes.rake @@ -1,4 +1,4 @@ -desc 'Print out all defined routes in match order, with names. Target specific controller with CONTROLLER=x.' +desc 'Print out all defined routes in match order, with names. Target specific controller with CONTROLLER=x. Include engine\'s routes with ENGINES=true' task :routes => :environment do Rails.application.reload_routes! all_routes = Rails.application.routes.routes diff --git a/railties/test/application/route_inspect_test.rb b/railties/test/application/route_inspect_test.rb index 78980705ed..130d4e52f8 100644 --- a/railties/test/application/route_inspect_test.rb +++ b/railties/test/application/route_inspect_test.rb @@ -1,6 +1,7 @@ require 'test/unit' require 'rails/application/route_inspector' require 'action_controller' +require 'rails/engine' module ApplicationTests class RouteInspectTest < Test::Unit::TestCase @@ -9,6 +10,35 @@ module ApplicationTests @inspector = Rails::Application::RouteInspector.new end + def test_displaying_routes_for_engines + ENV["ENGINES"] = "true" + + engine = Class.new(Rails::Engine) do + def self.to_s + "Blog::Engine" + end + end + engine.routes.draw do + get '/cart', :to => 'cart#show' + end + + @set.draw do + get '/custom/assets', :to => 'custom_assets#show' + mount engine => "/blog", :as => "blog" + end + + output = @inspector.format @set.routes + expected = [ + "custom_assets GET /custom/assets(.:format) custom_assets#show", + " blog /blog Blog::Engine", + "\nRoutes for Blog::Engine:", + "cart GET /cart(.:format) cart#show" + ] + assert_equal expected, output + ensure + ENV["ENGINES"] = nil + end + def test_cart_inspect @set.draw do get '/cart', :to => 'cart#show' -- cgit v1.2.3 From ebb8ea22f30dc029f4a868a550828443b160f639 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 25 Nov 2011 09:13:19 +0000 Subject: Add generators for serializers. --- .../lib/rails/generators/rails/serializer/USAGE | 9 ++++ .../rails/serializer/serializer_generator.rb | 39 ++++++++++++++ .../rails/serializer/templates/serializer.rb | 9 ++++ .../test_unit/serializer/serializer_generator.rb | 13 +++++ .../test_unit/serializer/templates/unit_test.rb | 9 ++++ .../test/generators/serializer_generator_test.rb | 63 ++++++++++++++++++++++ 6 files changed, 142 insertions(+) create mode 100644 railties/lib/rails/generators/rails/serializer/USAGE create mode 100644 railties/lib/rails/generators/rails/serializer/serializer_generator.rb create mode 100644 railties/lib/rails/generators/rails/serializer/templates/serializer.rb create mode 100644 railties/lib/rails/generators/test_unit/serializer/serializer_generator.rb create mode 100644 railties/lib/rails/generators/test_unit/serializer/templates/unit_test.rb create mode 100644 railties/test/generators/serializer_generator_test.rb diff --git a/railties/lib/rails/generators/rails/serializer/USAGE b/railties/lib/rails/generators/rails/serializer/USAGE new file mode 100644 index 0000000000..a49f7ea1fb --- /dev/null +++ b/railties/lib/rails/generators/rails/serializer/USAGE @@ -0,0 +1,9 @@ +Description: + Generates a serializer for the given resource with tests. + +Example: + `rails generate serializer Account name created_at` + + For TestUnit it creates: + Serializer: app/serializers/account_serializer.rb + TestUnit: test/unit/account_serializer_test.rb diff --git a/railties/lib/rails/generators/rails/serializer/serializer_generator.rb b/railties/lib/rails/generators/rails/serializer/serializer_generator.rb new file mode 100644 index 0000000000..2118906227 --- /dev/null +++ b/railties/lib/rails/generators/rails/serializer/serializer_generator.rb @@ -0,0 +1,39 @@ +module Rails + module Generators + class SerializerGenerator < NamedBase + check_class_collision :suffix => "Serializer" + + argument :attributes, :type => :array, :default => [], :banner => "field:type field:type" + + class_option :parent, :type => :string, :desc => "The parent class for the generated serializer" + + def create_serializer_file + template 'serializer.rb', File.join('app/serializers', class_path, "#{file_name}_serializer.rb") + end + + hook_for :test_framework + + private + + def attributes_names + attributes.select { |attr| !attr.reference? }.map { |a| a.name.to_sym } + end + + def association_names + attributes.select { |attr| attr.reference? }.map { |a| a.name.to_sym } + end + + def parent_class_name + if options[:parent] + options[:parent] + elsif (n = Rails::Generators.namespace) && n.const_defined?(:ApplicationSerializer) + "ApplicationSerializer" + elsif Object.const_defined?(:ApplicationSerializer) + "ApplicationSerializer" + else + "ActiveModel::Serializer" + end + end + end + end +end diff --git a/railties/lib/rails/generators/rails/serializer/templates/serializer.rb b/railties/lib/rails/generators/rails/serializer/templates/serializer.rb new file mode 100644 index 0000000000..30c058c7e9 --- /dev/null +++ b/railties/lib/rails/generators/rails/serializer/templates/serializer.rb @@ -0,0 +1,9 @@ +<% module_namespacing do -%> +class <%= class_name %>Serializer < <%= parent_class_name %> +<% if attributes.any? -%> attributes <%= attributes_names.map(&:inspect).join(", ") %> +<% end -%> +<% association_names.each do |attribute| -%> + has_one :<%= attribute %> +<% end -%> +end +<% end -%> \ No newline at end of file diff --git a/railties/lib/rails/generators/test_unit/serializer/serializer_generator.rb b/railties/lib/rails/generators/test_unit/serializer/serializer_generator.rb new file mode 100644 index 0000000000..533c032c3b --- /dev/null +++ b/railties/lib/rails/generators/test_unit/serializer/serializer_generator.rb @@ -0,0 +1,13 @@ +require 'rails/generators/test_unit' + +module TestUnit + module Generators + class SerializerGenerator < Base + check_class_collision :suffix => "SerializerTest" + + def create_test_files + template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_serializer_test.rb") + end + end + end +end diff --git a/railties/lib/rails/generators/test_unit/serializer/templates/unit_test.rb b/railties/lib/rails/generators/test_unit/serializer/templates/unit_test.rb new file mode 100644 index 0000000000..0b1bbdcaa5 --- /dev/null +++ b/railties/lib/rails/generators/test_unit/serializer/templates/unit_test.rb @@ -0,0 +1,9 @@ +require 'test_helper' + +<% module_namespacing do -%> +class <%= class_name %>SerializerTest < ActiveSupport::TestCase + # test "the truth" do + # assert true + # end +end +<% end -%> diff --git a/railties/test/generators/serializer_generator_test.rb b/railties/test/generators/serializer_generator_test.rb new file mode 100644 index 0000000000..2afaa65693 --- /dev/null +++ b/railties/test/generators/serializer_generator_test.rb @@ -0,0 +1,63 @@ +require 'generators/generators_test_helper' +require 'rails/generators/rails/serializer/serializer_generator' + +class SerializerGeneratorTest < Rails::Generators::TestCase + include GeneratorsTestHelper + arguments %w(account name:string description:text business:references) + + def test_generates_a_serializer + run_generator + assert_file "app/serializers/account_serializer.rb", /class AccountSerializer < ActiveModel::Serializer/ + end + + def test_generates_a_namespaced_serializer + run_generator ["admin/account"] + assert_file "app/serializers/admin/account_serializer.rb", /class Admin::AccountSerializer < ActiveModel::Serializer/ + end + + def test_uses_application_serializer_if_one_exists + Object.const_set(:ApplicationSerializer, Class.new) + run_generator + assert_file "app/serializers/account_serializer.rb", /class AccountSerializer < ApplicationSerializer/ + ensure + Object.send :remove_const, :ApplicationSerializer + end + + def test_uses_namespace_application_serializer_if_one_exists + Object.const_set(:SerializerNamespace, Module.new) + SerializerNamespace.const_set(:ApplicationSerializer, Class.new) + Rails::Generators.namespace = SerializerNamespace + run_generator + assert_file "app/serializers/serializer_namespace/account_serializer.rb", + /module SerializerNamespace\n class AccountSerializer < ApplicationSerializer/ + ensure + Object.send :remove_const, :SerializerNamespace + Rails::Generators.namespace = nil + end + + def test_uses_given_parent + Object.const_set(:ApplicationSerializer, Class.new) + run_generator ["Account", "--parent=MySerializer"] + assert_file "app/serializers/account_serializer.rb", /class AccountSerializer < MySerializer/ + ensure + Object.send :remove_const, :ApplicationSerializer + end + + def test_generates_attributes_and_associations + run_generator + assert_file "app/serializers/account_serializer.rb" do |serializer| + assert_match(/^ attributes :name, :description$/, serializer) + assert_match(/^ has_one :business$/, serializer) + end + end + + def test_with_no_attributes_does_not_add_extra_space + run_generator ["account"] + assert_file "app/serializers/account_serializer.rb", /class AccountSerializer < ActiveModel::Serializer\nend/ + end + + def test_invokes_default_test_framework + run_generator + assert_file "test/unit/account_serializer_test.rb", /class AccountSerializerTest < ActiveSupport::TestCase/ + end +end -- cgit v1.2.3 From 6d9f9b3b05f6ea55aad9853888912bcca4d81c9f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 25 Nov 2011 09:17:32 +0000 Subject: Add a hook for serializers in the scaffold generator (off for now). --- railties/lib/rails/generators.rb | 4 +++- .../lib/rails/generators/rails/scaffold/scaffold_generator.rb | 1 + railties/test/generators/scaffold_generator_test.rb | 9 +++++++++ 3 files changed, 13 insertions(+), 1 deletion(-) diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb index 27f8d13ce8..f1ca9080ff 100644 --- a/railties/lib/rails/generators.rb +++ b/railties/lib/rails/generators.rb @@ -33,7 +33,8 @@ module Rails :stylesheets => '-y', :stylesheet_engine => '-se', :template_engine => '-e', - :test_framework => '-t' + :test_framework => '-t', + :serializer => '-z' }, :test_unit => { @@ -58,6 +59,7 @@ module Rails :performance_tool => nil, :resource_controller => :controller, :scaffold_controller => :scaffold_controller, + :serializer => false, :stylesheets => true, :stylesheet_engine => :css, :test_framework => false, diff --git a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb index 03a61a035e..7353a67c83 100644 --- a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb +++ b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb @@ -10,6 +10,7 @@ module Rails class_option :stylesheet_engine, :desc => "Engine for Stylesheets" hook_for :scaffold_controller, :required => true + hook_for :serializer hook_for :assets do |assets| invoke assets, [controller_name] diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb index 2db8090621..2e8b03fbef 100644 --- a/railties/test/generators/scaffold_generator_test.rb +++ b/railties/test/generators/scaffold_generator_test.rb @@ -264,6 +264,15 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase assert_file "app/assets/stylesheets/posts.css" end + def test_scaffold_also_generators_serializer + run_generator [ "posts", "name:string", "author:references", "--serializer" ] + assert_file "app/serializers/post_serializer.rb" do |serializer| + assert_match /class PostSerializer < ActiveModel::Serializer/, serializer + assert_match /^ attributes :name$/, serializer + assert_match /^ has_one :author$/, serializer + end + end + def test_scaffold_generator_outputs_error_message_on_missing_attribute_type run_generator ["post", "title", "body:text", "author"] -- cgit v1.2.3 From c8d7291b6b5736562b112007365317648ae2b790 Mon Sep 17 00:00:00 2001 From: ganesh Date: Fri, 25 Nov 2011 14:55:28 +0530 Subject: Added tests for #3751 --- railties/lib/rails/generators/actions.rb | 2 +- railties/test/generators/actions_test.rb | 2 +- 2 files changed, 2 insertions(+), 2 deletions(-) diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb index b26839644e..184cb2866b 100644 --- a/railties/lib/rails/generators/actions.rb +++ b/railties/lib/rails/generators/actions.rb @@ -68,7 +68,7 @@ module Rails end in_root do - str = "gem #{parts.join(", ")}\n" + str = "\ngem #{parts.join(", ")}\n" str = " " + str if @in_group append_file "Gemfile", str, :verbose => false end diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb index 51fa2fe16f..917b196777 100644 --- a/railties/test/generators/actions_test.rb +++ b/railties/test/generators/actions_test.rb @@ -113,7 +113,7 @@ class ActionsTest < Rails::Generators::TestCase gem 'fakeweb' end - assert_file 'Gemfile', /\ngroup :development, :test do\n gem "rspec-rails"\nend\n\ngroup :test do\n gem "fakeweb"\nend/ + assert_file 'Gemfile', /\ngroup :development, :test do\n \ngem "rspec-rails"\nend\n\ngroup :test do\n \ngem "fakeweb"\nend/ end def test_environment_should_include_data_in_environment_initializer_block -- cgit v1.2.3 From 696d01f7f4a8ed787924a41cce6df836cd73c46f Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 25 Nov 2011 09:49:54 +0000 Subject: Add docs to serializers. Update CHANGELOGs. --- .../lib/action_controller/metal/serialization.rb | 23 +++++++ activemodel/CHANGELOG.md | 14 +++- activemodel/lib/active_model/serializer.rb | 78 ++++++++++++++++++++-- railties/CHANGELOG.md | 12 ++-- 4 files changed, 114 insertions(+), 13 deletions(-) diff --git a/actionpack/lib/action_controller/metal/serialization.rb b/actionpack/lib/action_controller/metal/serialization.rb index 9fb49f512e..628d5996d7 100644 --- a/actionpack/lib/action_controller/metal/serialization.rb +++ b/actionpack/lib/action_controller/metal/serialization.rb @@ -1,4 +1,27 @@ module ActionController + # Action Controller Serialization + # + # Overrides render :json to check if the given object implements +active_model_serializer+ + # as a method. If so, use the returned serializer instead of calling +to_json+ in the object. + # + # This module also provides a serialization_scope method that allows you to configure the + # +serialization_scope+ of the serializer. Most apps will likely set the +serialization_scope+ + # to the current user: + # + # class ApplicationController < ActionController::Base + # serialization_scope :current_user + # end + # + # If you need more complex scope rules, you can simply override the serialization_scope: + # + # class ApplicationController < ActionController::Base + # private + # + # def serialization_scope + # current_user + # end + # end + # module Serialization extend ActiveSupport::Concern diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md index 71a737caff..53934f08e7 100644 --- a/activemodel/CHANGELOG.md +++ b/activemodel/CHANGELOG.md @@ -1,4 +1,16 @@ -* Added ActiveModel::Errors#added? to check if a specific error has been added *Martin Svalin* +## Rails 3.2.0 (unreleased) ## + +* Add ActiveModel::Serializer that encapsulates an ActiveModel object serialization *José Valim* + +* Renamed (with a deprecation the following constants): + + ActiveModel::Serialization => ActiveModel::Serializable + ActiveModel::Serializers::JSON => ActiveModel::Serializable::JSON + ActiveModel::Serializers::Xml => ActiveModel::Serializable::XML + + *José Valim* + +* Add ActiveModel::Errors#added? to check if a specific error has been added *Martin Svalin* * Add ability to define strict validation(with :strict => true option) that always raises exception when fails *Bogdan Gusiev* diff --git a/activemodel/lib/active_model/serializer.rb b/activemodel/lib/active_model/serializer.rb index 5478da15c8..0e23df2f2b 100644 --- a/activemodel/lib/active_model/serializer.rb +++ b/activemodel/lib/active_model/serializer.rb @@ -5,6 +5,9 @@ require "set" module ActiveModel # Active Model Array Serializer + # + # It serializes an array checking if each element that implements + # the +active_model_serializer+ method passing down the current scope. class ArraySerializer attr_reader :object, :scope @@ -28,15 +31,46 @@ module ActiveModel end # Active Model Serializer + # + # Provides a basic serializer implementation that allows you to easily + # control how a given object is going to be serialized. On initialization, + # it expects to object as arguments, a resource and a scope. For example, + # one may do in a controller: + # + # PostSerializer.new(@post, current_user).to_json + # + # The object to be serialized is the +@post+ and the scope is +current_user+. + # + # We use the scope to check if a given attribute should be serialized or not. + # For example, some attributes maybe only be returned if +current_user+ is the + # author of the post: + # + # class PostSerializer < ActiveModel::Serializer + # attributes :title, :body + # has_many :comments + # + # private + # + # def attributes + # hash = super + # hash.merge!(:email => post.email) if author? + # hash + # end + # + # def author? + # post.author == scope + # end + # end + # class Serializer - module Associations - class Config < Struct.new(:name, :options) + module Associations #:nodoc: + class Config < Struct.new(:name, :options) #:nodoc: def serializer options[:serializer] end end - class HasMany < Config + class HasMany < Config #:nodoc: def serialize(collection, scope) collection.map do |item| serializer.new(item, scope).serializable_hash @@ -45,7 +79,7 @@ module ActiveModel def serialize_ids(collection, scope) # use named scopes if they are present - #return collection.ids if collection.respond_to?(:ids) + # return collection.ids if collection.respond_to?(:ids) collection.map do |item| item.read_attribute_for_serialization(:id) @@ -53,7 +87,7 @@ module ActiveModel end end - class HasOne < Config + class HasOne < Config #:nodoc: def serialize(object, scope) object && serializer.new(object, scope).serializable_hash end @@ -76,11 +110,12 @@ module ActiveModel class_attribute :_root_embed class << self + # Define attributes to be used in the serialization. def attributes(*attrs) self._attributes += attrs end - def associate(klass, attrs) + def associate(klass, attrs) #:nodoc: options = attrs.extract_options! self._associations += attrs.map do |attr| unless method_defined?(attr) @@ -92,24 +127,43 @@ module ActiveModel end end + # Defines an association in the object should be rendered. + # + # The serializer object should implement the association name + # as a method which should return an array when invoked. If a method + # with the association name does not exist, the association name is + # dispatched to the serialized object. def has_many(*attrs) associate(Associations::HasMany, attrs) end + # Defines an association in the object should be rendered. + # + # The serializer object should implement the association name + # as a method which should return an object when invoked. If a method + # with the association name does not exist, the association name is + # dispatched to the serialized object. def has_one(*attrs) associate(Associations::HasOne, attrs) end + # Define how associations should be embedded. + # + # embed :objects # Embed associations as full objects + # embed :ids # Embed only the association ids + # embed :ids, :include => true # Embed the association ids and include objects in the root + # def embed(type, options={}) self._embed = type self._root_embed = true if options[:include] end + # Defines the root used on serialization. If false, disables the root. def root(name) self._root = name end - def inherited(klass) + def inherited(klass) #:nodoc: return if klass.anonymous? name = klass.name.demodulize.underscore.sub(/_serializer$/, '') @@ -127,6 +181,8 @@ module ActiveModel @object, @scope = object, scope end + # Returns a json representation of the serializable + # object including the root. def as_json(*) if _root hash = { _root => serializable_hash } @@ -137,6 +193,8 @@ module ActiveModel end end + # Returns a hash representation of the serializable + # object without the root. def serializable_hash if _embed == :ids attributes.merge(association_ids) @@ -147,6 +205,8 @@ module ActiveModel end end + # Returns a hash representation of the serializable + # object associations. def associations hash = {} @@ -158,6 +218,8 @@ module ActiveModel hash end + # Returns a hash representation of the serializable + # object associations ids. def association_ids hash = {} @@ -169,6 +231,8 @@ module ActiveModel hash end + # Returns a hash representation of the serializable + # object attributes. def attributes hash = {} diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index b05ac21b49..b055b051be 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,20 +1,22 @@ ## Rails 3.2.0 (unreleased) ## +* Add a serializer generator and add a hook for it in the scaffold generators *José Valim* + * Scaffold returns 204 No Content for API requests without content. This makes scaffold work with jQuery out of the box. *José Valim* -* Updated Rails::Rack::Logger middleware to apply any tags set in config.log_tags to the newly ActiveSupport::TaggedLogging Rails.logger. This makes it easy to tag log lines with debug information like subdomain and request id -- both very helpful in debugging multi-user production applications *DHH* +* Update Rails::Rack::Logger middleware to apply any tags set in config.log_tags to the newly ActiveSupport::TaggedLogging Rails.logger. This makes it easy to tag log lines with debug information like subdomain and request id -- both very helpful in debugging multi-user production applications *DHH* * Default options to `rails new` can be set in ~/.railsrc *Guillermo Iguaran* -* Added destroy alias to Rails engines. *Guillermo Iguaran* +* Add destroy alias to Rails engines *Guillermo Iguaran* -* Added destroy alias for Rails command line. This allows the following: `rails d model post`. *Andrey Ognevsky* +* Add destroy alias for Rails command line. This allows the following: `rails d model post` *Andrey Ognevsky* * Attributes on scaffold and model generators default to string. This allows the following: "rails g scaffold Post title body:text author" *José Valim* -* Removed old plugin generator (`rails generate plugin`) in favor of `rails plugin new` command. *Guillermo Iguaran* +* Remove old plugin generator (`rails generate plugin`) in favor of `rails plugin new` command *Guillermo Iguaran* -* Removed old 'config.paths.app.controller' API in favor of 'config.paths["app/controller"]' API. *Guillermo Iguaran* +* Remove old 'config.paths.app.controller' API in favor of 'config.paths["app/controller"]' API *Guillermo Iguaran* * Rails 3.1.1 -- cgit v1.2.3 From cd9d28d6fdff6819dac3c6643fe882eb568b5a39 Mon Sep 17 00:00:00 2001 From: lest Date: Thu, 24 Nov 2011 22:37:48 +0300 Subject: middlewares should use logger from env --- .../lib/action_dispatch/middleware/params_parser.rb | 6 +++--- .../lib/action_dispatch/middleware/show_exceptions.rb | 16 ++++++++++------ actionpack/test/abstract_unit.rb | 4 ++-- .../test/dispatch/request/json_params_parsing_test.rb | 16 ++++++---------- .../test/dispatch/request/xml_params_parsing_test.rb | 16 ++++++---------- actionpack/test/dispatch/show_exceptions_test.rb | 7 +++++++ railties/lib/rails/application.rb | 3 ++- railties/test/application/configuration_test.rb | 1 + 8 files changed, 37 insertions(+), 32 deletions(-) diff --git a/actionpack/lib/action_dispatch/middleware/params_parser.rb b/actionpack/lib/action_dispatch/middleware/params_parser.rb index 84e3dd16dd..6ded9dbfed 100644 --- a/actionpack/lib/action_dispatch/middleware/params_parser.rb +++ b/actionpack/lib/action_dispatch/middleware/params_parser.rb @@ -52,7 +52,7 @@ module ActionDispatch false end rescue Exception => e # YAML, XML or Ruby code block errors - logger.debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}" + logger(env).debug "Error occurred while parsing request parameters.\nContents:\n\n#{request.raw_post}" raise e end @@ -68,8 +68,8 @@ module ActionDispatch nil end - def logger - defined?(Rails.logger) ? Rails.logger : Logger.new($stderr) + def logger(env) + env['action_dispatch.logger'] || Logger.new($stderr) end end end diff --git a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb index 52dce4cc81..8dc2820d37 100644 --- a/actionpack/lib/action_dispatch/middleware/show_exceptions.rb +++ b/actionpack/lib/action_dispatch/middleware/show_exceptions.rb @@ -63,7 +63,7 @@ module ActionDispatch private def render_exception(env, exception) - log_error(exception) + log_error(env, exception) exception = original_exception(exception) if env['action_dispatch.show_detailed_exceptions'] == true @@ -124,14 +124,14 @@ module ActionDispatch defined?(Rails.public_path) ? Rails.public_path : 'public_path' end - def log_error(exception) - return unless logger + def log_error(env, exception) + return unless logger(env) ActiveSupport::Deprecation.silence do message = "\n#{exception.class} (#{exception.message}):\n" message << exception.annoted_source_code.to_s if exception.respond_to?(:annoted_source_code) message << " " << application_trace(exception).join("\n ") - logger.fatal("#{message}\n\n") + logger(env).fatal("#{message}\n\n") end end @@ -153,8 +153,12 @@ module ActionDispatch exception.backtrace end - def logger - defined?(Rails.logger) ? Rails.logger : Logger.new($stderr) + def logger(env) + env['action_dispatch.logger'] || stderr_logger + end + + def stderr_logger + Logger.new($stderr) end def original_exception(exception) diff --git a/actionpack/test/abstract_unit.rb b/actionpack/test/abstract_unit.rb index 24d071df39..cbb8968496 100644 --- a/actionpack/test/abstract_unit.rb +++ b/actionpack/test/abstract_unit.rb @@ -333,9 +333,9 @@ module ActionDispatch "#{FIXTURE_LOAD_PATH}/public" end - remove_method :logger + remove_method :stderr_logger # Silence logger - def logger + def stderr_logger nil end end diff --git a/actionpack/test/dispatch/request/json_params_parsing_test.rb b/actionpack/test/dispatch/request/json_params_parsing_test.rb index d481a2df13..ad44b4b16a 100644 --- a/actionpack/test/dispatch/request/json_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/json_params_parsing_test.rb @@ -32,16 +32,12 @@ class JsonParamsParsingTest < ActionDispatch::IntegrationTest test "logs error if parsing unsuccessful" do with_test_routing do - begin - $stderr = StringIO.new - json = "[\"person]\": {\"name\": \"David\"}}" - post "/parse", json, {'CONTENT_TYPE' => 'application/json', 'action_dispatch.show_exceptions' => true} - assert_response :error - $stderr.rewind && err = $stderr.read - assert err =~ /Error occurred while parsing request parameters/ - ensure - $stderr = STDERR - end + output = StringIO.new + json = "[\"person]\": {\"name\": \"David\"}}" + post "/parse", json, {'CONTENT_TYPE' => 'application/json', 'action_dispatch.show_exceptions' => true, 'action_dispatch.logger' => Logger.new(output)} + assert_response :error + output.rewind && err = output.read + assert err =~ /Error occurred while parsing request parameters/ end end diff --git a/actionpack/test/dispatch/request/xml_params_parsing_test.rb b/actionpack/test/dispatch/request/xml_params_parsing_test.rb index 65a25557b7..d8fa751548 100644 --- a/actionpack/test/dispatch/request/xml_params_parsing_test.rb +++ b/actionpack/test/dispatch/request/xml_params_parsing_test.rb @@ -54,16 +54,12 @@ class XmlParamsParsingTest < ActionDispatch::IntegrationTest test "logs error if parsing unsuccessful" do with_test_routing do - begin - $stderr = StringIO.new - xml = "David#{ActiveSupport::Base64.encode64('ABC')}" - post "/parse", xml, default_headers.merge('action_dispatch.show_exceptions' => true) - assert_response :error - $stderr.rewind && err = $stderr.read - assert err =~ /Error occurred while parsing request parameters/ - ensure - $stderr = STDERR - end + output = StringIO.new + xml = "David#{ActiveSupport::Base64.encode64('ABC')}" + post "/parse", xml, default_headers.merge('action_dispatch.show_exceptions' => true, 'action_dispatch.logger' => Logger.new(output)) + assert_response :error + output.rewind && err = output.read + assert err =~ /Error occurred while parsing request parameters/ end end diff --git a/actionpack/test/dispatch/show_exceptions_test.rb b/actionpack/test/dispatch/show_exceptions_test.rb index 9f4d6c530f..5875725b5d 100644 --- a/actionpack/test/dispatch/show_exceptions_test.rb +++ b/actionpack/test/dispatch/show_exceptions_test.rb @@ -128,4 +128,11 @@ class ShowExceptionsTest < ActionDispatch::IntegrationTest get "/", {}, {'action_dispatch.show_exceptions' => true} assert_equal "text/html; charset=utf-8", response.headers["Content-Type"] end + + test 'uses logger from env' do + @app = ProductionApp + output = StringIO.new + get "/", {}, {'action_dispatch.show_exceptions' => true, 'action_dispatch.logger' => Logger.new(output)} + assert_match(/puke/, output.rewind && output.read) + end end diff --git a/railties/lib/rails/application.rb b/railties/lib/rails/application.rb index 847445d29f..25ff74506a 100644 --- a/railties/lib/rails/application.rb +++ b/railties/lib/rails/application.rb @@ -123,7 +123,8 @@ module Rails @env_config ||= super.merge({ "action_dispatch.parameter_filter" => config.filter_parameters, "action_dispatch.secret_token" => config.secret_token, - "action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions + "action_dispatch.show_exceptions" => config.action_dispatch.show_exceptions, + "action_dispatch.logger" => Rails.logger }) end diff --git a/railties/test/application/configuration_test.rb b/railties/test/application/configuration_test.rb index f356805d6e..f37a024a0b 100644 --- a/railties/test/application/configuration_test.rb +++ b/railties/test/application/configuration_test.rb @@ -524,6 +524,7 @@ module ApplicationTests assert_equal app.env_config['action_dispatch.parameter_filter'], app.config.filter_parameters assert_equal app.env_config['action_dispatch.secret_token'], app.config.secret_token assert_equal app.env_config['action_dispatch.show_exceptions'], app.config.action_dispatch.show_exceptions + assert_equal app.env_config['action_dispatch.logger'], Rails.logger end end end -- cgit v1.2.3 From c9bb099318dd3bc293a6cb4672333147c1cce4b9 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Fri, 25 Nov 2011 12:42:11 +0100 Subject: Display mounted engines in `rake routes` by default --- railties/CHANGELOG.md | 2 +- railties/lib/rails/application/route_inspector.rb | 2 +- railties/test/application/route_inspect_test.rb | 4 ---- 3 files changed, 2 insertions(+), 6 deletions(-) diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 25d45d886c..1b89bfa6f9 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -1,6 +1,6 @@ ## Rails 3.2.0 (unreleased) ## -* Add displaying of mounted engine's routes with `rake routes ENGINES=true` *Piotr Sarnacki* +* Display mounted engine's routes in `rake routes`. *Piotr Sarnacki* * Allow to change the loading order of railties with `config.railties_order=` *Piotr Sarnacki* diff --git a/railties/lib/rails/application/route_inspector.rb b/railties/lib/rails/application/route_inspector.rb index 8da7de2584..26652a8e5e 100644 --- a/railties/lib/rails/application/route_inspector.rb +++ b/railties/lib/rails/application/route_inspector.rb @@ -46,7 +46,7 @@ module Rails end def collect_engine_routes(name, rack_app) - return unless rack_app && ENV["ENGINES"] && rack_app.respond_to?(:routes) + return unless rack_app && rack_app.respond_to?(:routes) return if @engines[name] routes = rack_app.routes diff --git a/railties/test/application/route_inspect_test.rb b/railties/test/application/route_inspect_test.rb index 130d4e52f8..2ad5ee6c4c 100644 --- a/railties/test/application/route_inspect_test.rb +++ b/railties/test/application/route_inspect_test.rb @@ -11,8 +11,6 @@ module ApplicationTests end def test_displaying_routes_for_engines - ENV["ENGINES"] = "true" - engine = Class.new(Rails::Engine) do def self.to_s "Blog::Engine" @@ -35,8 +33,6 @@ module ApplicationTests "cart GET /cart(.:format) cart#show" ] assert_equal expected, output - ensure - ENV["ENGINES"] = nil end def test_cart_inspect -- cgit v1.2.3 From 8ff8fa5e5fd9a615f2c63f809a2747a55cd4da15 Mon Sep 17 00:00:00 2001 From: Piotr Sarnacki Date: Fri, 25 Nov 2011 12:54:39 +0100 Subject: I suck, forgot to also change rake's task description --- railties/lib/rails/tasks/routes.rake | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/lib/rails/tasks/routes.rake b/railties/lib/rails/tasks/routes.rake index df72baef67..7dc54144da 100644 --- a/railties/lib/rails/tasks/routes.rake +++ b/railties/lib/rails/tasks/routes.rake @@ -1,4 +1,4 @@ -desc 'Print out all defined routes in match order, with names. Target specific controller with CONTROLLER=x. Include engine\'s routes with ENGINES=true' +desc 'Print out all defined routes in match order, with names. Target specific controller with CONTROLLER=x.' task :routes => :environment do Rails.application.reload_routes! all_routes = Rails.application.routes.routes -- cgit v1.2.3 From 9a45867b094c0a41303e24b68414bc69d2263bc8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?U=C4=A3is=20Ozols?= Date: Fri, 25 Nov 2011 14:22:36 +0200 Subject: Remove unnecessary comment. --- actionpack/lib/action_dispatch/routing/url_for.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb index 8fc8dc191b..334ddd5c2f 100644 --- a/actionpack/lib/action_dispatch/routing/url_for.rb +++ b/actionpack/lib/action_dispatch/routing/url_for.rb @@ -42,7 +42,7 @@ module ActionDispatch # url_for(:controller => 'users', # :action => 'new', # :message => 'Welcome!', - # :host => 'www.example.com') # Changed this. + # :host => 'www.example.com') # # => "http://www.example.com/users/new?message=Welcome%21" # # By default, all controllers and views have access to a special version of url_for, -- cgit v1.2.3 From a607a9d978c4a84935fa23556de1dde5aea274d8 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?U=C4=A3is=20Ozols?= Date: Fri, 25 Nov 2011 14:24:14 +0200 Subject: what's -> that's --- actionpack/lib/action_dispatch/routing/url_for.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_dispatch/routing/url_for.rb b/actionpack/lib/action_dispatch/routing/url_for.rb index 334ddd5c2f..39ba83fb9a 100644 --- a/actionpack/lib/action_dispatch/routing/url_for.rb +++ b/actionpack/lib/action_dispatch/routing/url_for.rb @@ -52,7 +52,7 @@ module ActionDispatch # # For convenience reasons, mailers provide a shortcut for ActionController::UrlFor#url_for. # So within mailers, you only have to type 'url_for' instead of 'ActionController::UrlFor#url_for' - # in full. However, mailers don't have hostname information, and what's why you'll still + # in full. However, mailers don't have hostname information, and that's why you'll still # have to specify the :host argument when generating URLs in mailers. # # -- cgit v1.2.3 From 95e9b5fbfd87b81bf2d2b16a6df6d9ddbfdb686d Mon Sep 17 00:00:00 2001 From: John Donahue Date: Fri, 25 Nov 2011 09:06:14 -0800 Subject: Updating newline fix to maintain existing linebreaks and indentation and test for b/eol on inserts --- railties/lib/rails/generators/actions.rb | 7 ++++--- railties/test/generators/actions_test.rb | 10 ++++++---- 2 files changed, 10 insertions(+), 7 deletions(-) diff --git a/railties/lib/rails/generators/actions.rb b/railties/lib/rails/generators/actions.rb index 184cb2866b..ca93f9ef9d 100644 --- a/railties/lib/rails/generators/actions.rb +++ b/railties/lib/rails/generators/actions.rb @@ -68,8 +68,9 @@ module Rails end in_root do - str = "\ngem #{parts.join(", ")}\n" + str = "gem #{parts.join(", ")}" str = " " + str if @in_group + str = "\n" + str append_file "Gemfile", str, :verbose => false end end @@ -87,13 +88,13 @@ module Rails log :gemfile, "group #{name}" in_root do - append_file "Gemfile", "\ngroup #{name} do\n", :force => true + append_file "Gemfile", "\ngroup #{name} do", :force => true @in_group = true instance_eval(&block) @in_group = false - append_file "Gemfile", "end\n", :force => true + append_file "Gemfile", "\nend\n", :force => true end end diff --git a/railties/test/generators/actions_test.rb b/railties/test/generators/actions_test.rb index 917b196777..c1fd6a38f1 100644 --- a/railties/test/generators/actions_test.rb +++ b/railties/test/generators/actions_test.rb @@ -95,11 +95,13 @@ class ActionsTest < Rails::Generators::TestCase def test_gem_should_insert_on_separate_lines run_generator + File.open('Gemfile', 'a') {|f| f.write('# Some content...') } + action :gem, 'rspec' action :gem, 'rspec-rails' - assert_file 'Gemfile', /gem "rspec"$/ - assert_file 'Gemfile', /gem "rspec-rails"$/ + assert_file 'Gemfile', /^gem "rspec"$/ + assert_file 'Gemfile', /^gem "rspec-rails"$/ end def test_gem_group_should_wrap_gems_in_a_group @@ -112,8 +114,8 @@ class ActionsTest < Rails::Generators::TestCase action :gem_group, :test do gem 'fakeweb' end - - assert_file 'Gemfile', /\ngroup :development, :test do\n \ngem "rspec-rails"\nend\n\ngroup :test do\n \ngem "fakeweb"\nend/ + + assert_file 'Gemfile', /\ngroup :development, :test do\n gem "rspec-rails"\nend\n\ngroup :test do\n gem "fakeweb"\nend/ end def test_environment_should_include_data_in_environment_initializer_block -- cgit v1.2.3 From 0a4035b12a6c59253cb60f9e3456513c6a6a9d33 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Jos=C3=A9=20Valim?= Date: Fri, 25 Nov 2011 19:29:39 +0000 Subject: Revert the serializers API as other alternatives are now also under discussion --- actionpack/lib/action_controller.rb | 1 - .../lib/action_controller/metal/serialization.rb | 51 -- actionpack/test/controller/render_json_test.rb | 48 -- activemodel/CHANGELOG.md | 2 - activemodel/lib/active_model.rb | 2 - activemodel/lib/active_model/serializable.rb | 12 - activemodel/test/cases/serializer_test.rb | 432 ---------------- railties/CHANGELOG.md | 2 - railties/guides/source/serializers.textile | 563 --------------------- railties/lib/rails/generators.rb | 4 +- .../rails/scaffold/scaffold_generator.rb | 1 - .../lib/rails/generators/rails/serializer/USAGE | 9 - .../rails/serializer/serializer_generator.rb | 39 -- .../rails/serializer/templates/serializer.rb | 9 - .../test_unit/serializer/serializer_generator.rb | 13 - .../test_unit/serializer/templates/unit_test.rb | 9 - .../test/generators/scaffold_generator_test.rb | 9 - .../test/generators/serializer_generator_test.rb | 63 --- 18 files changed, 1 insertion(+), 1268 deletions(-) delete mode 100644 actionpack/lib/action_controller/metal/serialization.rb delete mode 100644 activemodel/test/cases/serializer_test.rb delete mode 100644 railties/guides/source/serializers.textile delete mode 100644 railties/lib/rails/generators/rails/serializer/USAGE delete mode 100644 railties/lib/rails/generators/rails/serializer/serializer_generator.rb delete mode 100644 railties/lib/rails/generators/rails/serializer/templates/serializer.rb delete mode 100644 railties/lib/rails/generators/test_unit/serializer/serializer_generator.rb delete mode 100644 railties/lib/rails/generators/test_unit/serializer/templates/unit_test.rb delete mode 100644 railties/test/generators/serializer_generator_test.rb diff --git a/actionpack/lib/action_controller.rb b/actionpack/lib/action_controller.rb index 3829e60bb0..f4eaa2fd1b 100644 --- a/actionpack/lib/action_controller.rb +++ b/actionpack/lib/action_controller.rb @@ -31,7 +31,6 @@ module ActionController autoload :RequestForgeryProtection autoload :Rescue autoload :Responder - autoload :Serialization autoload :SessionManagement autoload :Streaming autoload :Testing diff --git a/actionpack/lib/action_controller/metal/serialization.rb b/actionpack/lib/action_controller/metal/serialization.rb deleted file mode 100644 index 628d5996d7..0000000000 --- a/actionpack/lib/action_controller/metal/serialization.rb +++ /dev/null @@ -1,51 +0,0 @@ -module ActionController - # Action Controller Serialization - # - # Overrides render :json to check if the given object implements +active_model_serializer+ - # as a method. If so, use the returned serializer instead of calling +to_json+ in the object. - # - # This module also provides a serialization_scope method that allows you to configure the - # +serialization_scope+ of the serializer. Most apps will likely set the +serialization_scope+ - # to the current user: - # - # class ApplicationController < ActionController::Base - # serialization_scope :current_user - # end - # - # If you need more complex scope rules, you can simply override the serialization_scope: - # - # class ApplicationController < ActionController::Base - # private - # - # def serialization_scope - # current_user - # end - # end - # - module Serialization - extend ActiveSupport::Concern - - include ActionController::Renderers - - included do - class_attribute :_serialization_scope - end - - def serialization_scope - send(_serialization_scope) - end - - def _render_option_json(json, options) - if json.respond_to?(:active_model_serializer) && (serializer = json.active_model_serializer) - json = serializer.new(json, serialization_scope) - end - super - end - - module ClassMethods - def serialization_scope(scope) - self._serialization_scope = scope - end - end - end -end diff --git a/actionpack/test/controller/render_json_test.rb b/actionpack/test/controller/render_json_test.rb index dc09812ba3..fc604a2db3 100644 --- a/actionpack/test/controller/render_json_test.rb +++ b/actionpack/test/controller/render_json_test.rb @@ -15,36 +15,9 @@ class RenderJsonTest < ActionController::TestCase end end - class JsonSerializer - def initialize(object, scope) - @object, @scope = object, scope - end - - def as_json(*) - { :object => @object.as_json, :scope => @scope.as_json } - end - end - - class JsonSerializable - def initialize(skip=false) - @skip = skip - end - - def active_model_serializer - JsonSerializer unless @skip - end - - def as_json(*) - { :serializable_object => true } - end - end - class TestController < ActionController::Base protect_from_forgery - serialization_scope :current_user - attr_reader :current_user - def self.controller_path 'test' end @@ -88,16 +61,6 @@ class RenderJsonTest < ActionController::TestCase def render_json_without_options render :json => JsonRenderable.new end - - def render_json_with_serializer - @current_user = Struct.new(:as_json).new(:current_user => true) - render :json => JsonSerializable.new - end - - def render_json_with_serializer_api_but_without_serializer - @current_user = Struct.new(:as_json).new(:current_user => true) - render :json => JsonSerializable.new(true) - end end tests TestController @@ -169,15 +132,4 @@ class RenderJsonTest < ActionController::TestCase get :render_json_without_options assert_equal '{"a":"b"}', @response.body end - - def test_render_json_with_serializer - get :render_json_with_serializer - assert_match '"scope":{"current_user":true}', @response.body - assert_match '"object":{"serializable_object":true}', @response.body - end - - def test_render_json_with_serializer_api_but_without_serializer - get :render_json_with_serializer_api_but_without_serializer - assert_match '{"serializable_object":true}', @response.body - end end diff --git a/activemodel/CHANGELOG.md b/activemodel/CHANGELOG.md index 53934f08e7..caea0b86bd 100644 --- a/activemodel/CHANGELOG.md +++ b/activemodel/CHANGELOG.md @@ -1,7 +1,5 @@ ## Rails 3.2.0 (unreleased) ## -* Add ActiveModel::Serializer that encapsulates an ActiveModel object serialization *José Valim* - * Renamed (with a deprecation the following constants): ActiveModel::Serialization => ActiveModel::Serializable diff --git a/activemodel/lib/active_model.rb b/activemodel/lib/active_model.rb index 6c4fb44b0f..7ea04344f0 100644 --- a/activemodel/lib/active_model.rb +++ b/activemodel/lib/active_model.rb @@ -29,7 +29,6 @@ require 'active_model/version' module ActiveModel extend ActiveSupport::Autoload - autoload :ArraySerializer, 'active_model/serializer' autoload :AttributeMethods autoload :BlockValidator, 'active_model/validator' autoload :Callbacks @@ -46,7 +45,6 @@ module ActiveModel autoload :SecurePassword autoload :Serializable autoload :Serialization - autoload :Serializer autoload :TestCase autoload :Translation autoload :Validations diff --git a/activemodel/lib/active_model/serializable.rb b/activemodel/lib/active_model/serializable.rb index 70e27c5683..86770a25e4 100644 --- a/activemodel/lib/active_model/serializable.rb +++ b/activemodel/lib/active_model/serializable.rb @@ -73,13 +73,6 @@ module ActiveModel autoload :JSON, "active_model/serializable/json" autoload :XML, "active_model/serializable/xml" - module ClassMethods #:nodoc: - def active_model_serializer - return @active_model_serializer if defined?(@active_model_serializer) - @active_model_serializer = "#{self.name}Serializer".safe_constantize - end - end - def serializable_hash(options = nil) options ||= {} @@ -107,11 +100,6 @@ module ActiveModel hash end - # Returns a model serializer for this object considering its namespace. - def active_model_serializer - self.class.active_model_serializer - end - private # Hook method defining how an attribute value should be retrieved for diff --git a/activemodel/test/cases/serializer_test.rb b/activemodel/test/cases/serializer_test.rb deleted file mode 100644 index 2b2e21540f..0000000000 --- a/activemodel/test/cases/serializer_test.rb +++ /dev/null @@ -1,432 +0,0 @@ -require "cases/helper" - -class SerializerTest < ActiveModel::TestCase - class Model - def initialize(hash={}) - @attributes = hash - end - - def read_attribute_for_serialization(name) - @attributes[name] - end - - def as_json(*) - { :model => "Model" } - end - end - - class User - include ActiveModel::Serializable - - attr_accessor :superuser - - def initialize(hash={}) - @attributes = hash.merge(:first_name => "Jose", :last_name => "Valim", :password => "oh noes yugive my password") - end - - def read_attribute_for_serialization(name) - @attributes[name] - end - - def super_user? - @superuser - end - end - - class Post < Model - attr_accessor :comments - def active_model_serializer; PostSerializer; end - end - - class Comment < Model - def active_model_serializer; CommentSerializer; end - end - - class UserSerializer < ActiveModel::Serializer - attributes :first_name, :last_name - - def serializable_hash - attributes.merge(:ok => true).merge(scope) - end - end - - class DefaultUserSerializer < ActiveModel::Serializer - attributes :first_name, :last_name - end - - class MyUserSerializer < ActiveModel::Serializer - attributes :first_name, :last_name - - def serializable_hash - hash = attributes - hash = hash.merge(:super_user => true) if my_user.super_user? - hash - end - end - - class CommentSerializer - def initialize(comment, scope) - @comment, @scope = comment, scope - end - - def serializable_hash - { :title => @comment.read_attribute_for_serialization(:title) } - end - - def as_json - { :comment => serializable_hash } - end - end - - class PostSerializer < ActiveModel::Serializer - attributes :title, :body - has_many :comments, :serializer => CommentSerializer - end - - def test_attributes - user = User.new - user_serializer = DefaultUserSerializer.new(user, {}) - - hash = user_serializer.as_json - - assert_equal({ - :default_user => { :first_name => "Jose", :last_name => "Valim" } - }, hash) - end - - def test_attributes_method - user = User.new - user_serializer = UserSerializer.new(user, {}) - - hash = user_serializer.as_json - - assert_equal({ - :user => { :first_name => "Jose", :last_name => "Valim", :ok => true } - }, hash) - end - - def test_serializer_receives_scope - user = User.new - user_serializer = UserSerializer.new(user, {:scope => true}) - - hash = user_serializer.as_json - - assert_equal({ - :user => { - :first_name => "Jose", - :last_name => "Valim", - :ok => true, - :scope => true - } - }, hash) - end - - def test_pretty_accessors - user = User.new - user.superuser = true - user_serializer = MyUserSerializer.new(user, nil) - - hash = user_serializer.as_json - - assert_equal({ - :my_user => { - :first_name => "Jose", :last_name => "Valim", :super_user => true - } - }, hash) - end - - def test_has_many - user = User.new - - post = Post.new(:title => "New Post", :body => "Body of new post", :email => "tenderlove@tenderlove.com") - comments = [Comment.new(:title => "Comment1"), Comment.new(:title => "Comment2")] - post.comments = comments - - post_serializer = PostSerializer.new(post, user) - - assert_equal({ - :post => { - :title => "New Post", - :body => "Body of new post", - :comments => [ - { :title => "Comment1" }, - { :title => "Comment2" } - ] - } - }, post_serializer.as_json) - end - - class Blog < Model - attr_accessor :author - end - - class AuthorSerializer < ActiveModel::Serializer - attributes :first_name, :last_name - end - - class BlogSerializer < ActiveModel::Serializer - has_one :author, :serializer => AuthorSerializer - end - - def test_has_one - user = User.new - blog = Blog.new - blog.author = user - - json = BlogSerializer.new(blog, user).as_json - assert_equal({ - :blog => { - :author => { - :first_name => "Jose", - :last_name => "Valim" - } - } - }, json) - end - - def test_implicit_serializer - author_serializer = Class.new(ActiveModel::Serializer) do - attributes :first_name - end - - blog_serializer = Class.new(ActiveModel::Serializer) do - const_set(:AuthorSerializer, author_serializer) - has_one :author - end - - user = User.new - blog = Blog.new - blog.author = user - - json = blog_serializer.new(blog, user).as_json - assert_equal({ - :author => { - :first_name => "Jose" - } - }, json) - end - - def test_overridden_associations - author_serializer = Class.new(ActiveModel::Serializer) do - attributes :first_name - end - - blog_serializer = Class.new(ActiveModel::Serializer) do - const_set(:PersonSerializer, author_serializer) - - def person - object.author - end - - has_one :person - end - - user = User.new - blog = Blog.new - blog.author = user - - json = blog_serializer.new(blog, user).as_json - assert_equal({ - :person => { - :first_name => "Jose" - } - }, json) - end - - def post_serializer(type) - Class.new(ActiveModel::Serializer) do - attributes :title, :body - has_many :comments, :serializer => CommentSerializer - - if type != :super - define_method :serializable_hash do - post_hash = attributes - post_hash.merge!(send(type)) - post_hash - end - end - end - end - - def test_associations - post = Post.new(:title => "New Post", :body => "Body of new post", :email => "tenderlove@tenderlove.com") - comments = [Comment.new(:title => "Comment1"), Comment.new(:title => "Comment2")] - post.comments = comments - - serializer = post_serializer(:associations).new(post, nil) - - assert_equal({ - :title => "New Post", - :body => "Body of new post", - :comments => [ - { :title => "Comment1" }, - { :title => "Comment2" } - ] - }, serializer.as_json) - end - - def test_association_ids - serializer = post_serializer(:association_ids) - - serializer.class_eval do - def as_json(*) - { :post => serializable_hash }.merge(associations) - end - end - - post = Post.new(:title => "New Post", :body => "Body of new post", :email => "tenderlove@tenderlove.com") - comments = [Comment.new(:title => "Comment1", :id => 1), Comment.new(:title => "Comment2", :id => 2)] - post.comments = comments - - serializer = serializer.new(post, nil) - - assert_equal({ - :post => { - :title => "New Post", - :body => "Body of new post", - :comments => [1, 2] - }, - :comments => [ - { :title => "Comment1" }, - { :title => "Comment2" } - ] - }, serializer.as_json) - end - - def test_associations_with_nil_association - user = User.new - blog = Blog.new - - json = BlogSerializer.new(blog, user).as_json - assert_equal({ - :blog => { :author => nil } - }, json) - - serializer = Class.new(BlogSerializer) do - root :blog - - def serializable_hash - attributes.merge(association_ids) - end - end - - json = serializer.new(blog, user).as_json - assert_equal({ :blog => { :author => nil } }, json) - end - - def test_custom_root - user = User.new - blog = Blog.new - - serializer = Class.new(BlogSerializer) do - root :my_blog - end - - assert_equal({ :my_blog => { :author => nil } }, serializer.new(blog, user).as_json) - end - - def test_false_root - user = User.new - blog = Blog.new - - serializer = Class.new(BlogSerializer) do - root false - end - - assert_equal({ :author => nil }, serializer.new(blog, user).as_json) - - # test inherited false root - serializer = Class.new(serializer) - assert_equal({ :author => nil }, serializer.new(blog, user).as_json) - end - - def test_embed_ids - serializer = post_serializer(:super) - - serializer.class_eval do - root :post - embed :ids - end - - post = Post.new(:title => "New Post", :body => "Body of new post", :email => "tenderlove@tenderlove.com") - comments = [Comment.new(:title => "Comment1", :id => 1), Comment.new(:title => "Comment2", :id => 2)] - post.comments = comments - - serializer = serializer.new(post, nil) - - assert_equal({ - :post => { - :title => "New Post", - :body => "Body of new post", - :comments => [1, 2] - } - }, serializer.as_json) - end - - def test_embed_ids_include_true - serializer = post_serializer(:super) - - serializer.class_eval do - root :post - embed :ids, :include => true - end - - post = Post.new(:title => "New Post", :body => "Body of new post", :email => "tenderlove@tenderlove.com") - comments = [Comment.new(:title => "Comment1", :id => 1), Comment.new(:title => "Comment2", :id => 2)] - post.comments = comments - - serializer = serializer.new(post, nil) - - assert_equal({ - :post => { - :title => "New Post", - :body => "Body of new post", - :comments => [1, 2] - }, - :comments => [ - { :title => "Comment1" }, - { :title => "Comment2" } - ] - }, serializer.as_json) - end - - def test_embed_objects - serializer = post_serializer(:super) - - serializer.class_eval do - root :post - embed :objects - end - - post = Post.new(:title => "New Post", :body => "Body of new post", :email => "tenderlove@tenderlove.com") - comments = [Comment.new(:title => "Comment1", :id => 1), Comment.new(:title => "Comment2", :id => 2)] - post.comments = comments - - serializer = serializer.new(post, nil) - - assert_equal({ - :post => { - :title => "New Post", - :body => "Body of new post", - :comments => [ - { :title => "Comment1" }, - { :title => "Comment2" } - ] - } - }, serializer.as_json) - end - - def test_array_serializer - model = Model.new - user = User.new - comments = Comment.new(:title => "Comment1", :id => 1) - - array = [model, user, comments] - serializer = array.active_model_serializer.new(array, {:scope => true}) - assert_equal([ - { :model => "Model" }, - { :user => { :last_name=>"Valim", :ok=>true, :first_name=>"Jose", :scope => true } }, - { :comment => { :title => "Comment1" } } - ], serializer.as_json) - end -end \ No newline at end of file diff --git a/railties/CHANGELOG.md b/railties/CHANGELOG.md index 1b89bfa6f9..6b0be4c096 100644 --- a/railties/CHANGELOG.md +++ b/railties/CHANGELOG.md @@ -7,8 +7,6 @@ Example: config.railties_order = [Blog::Engine, :main_app, :all] -* Add a serializer generator and add a hook for it in the scaffold generators *José Valim* - * Scaffold returns 204 No Content for API requests without content. This makes scaffold work with jQuery out of the box. *José Valim* * Update Rails::Rack::Logger middleware to apply any tags set in config.log_tags to the newly ActiveSupport::TaggedLogging Rails.logger. This makes it easy to tag log lines with debug information like subdomain and request id -- both very helpful in debugging multi-user production applications *DHH* diff --git a/railties/guides/source/serializers.textile b/railties/guides/source/serializers.textile deleted file mode 100644 index efc7cbf248..0000000000 --- a/railties/guides/source/serializers.textile +++ /dev/null @@ -1,563 +0,0 @@ -h2. Rails Serializers - -This guide describes how to use Active Model serializers to build non-trivial JSON services in Rails. By reading this guide, you will learn: - -* When to use the built-in Active Model serialization -* When to use a custom serializer for your models -* How to use serializers to encapsulate authorization concerns -* How to create serializer templates to describe the application-wide structure of your serialized JSON -* How to build resources not backed by a single database table for use with JSON services - -This guide covers an intermediate topic and assumes familiarity with Rails conventions. It is suitable for applications that expose a -JSON API that may return different results based on the authorization status of the user. - -endprologue. - -h3. Serialization - -By default, Active Record objects can serialize themselves into JSON by using the `to_json` method. This method takes a series of additional -parameter to control which properties and associations Rails should include in the serialized output. - -When building a web application that uses JavaScript to retrieve JSON data from the server, this mechanism has historically been the primary -way that Rails developers prepared their responses. This works great for simple cases, as the logic for serializing an Active Record object -is neatly encapsulated in Active Record itself. - -However, this solution quickly falls apart in the face of serialization requirements based on authorization. For instance, a web service -may choose to expose additional information about a resource only if the user is entitled to access it. In addition, a JavaScript front-end -may want information that is not neatly described in terms of serializing a single Active Record object, or in a different format than. - -In addition, neither the controller nor the model seems like the correct place for logic that describes how to serialize an model object -*for the current user*. - -Serializers solve these problems by encapsulating serialization in an object designed for this purpose. If the default +to_json+ semantics, -with at most a few configuration options serve your needs, by all means continue to use the built-in +to_json+. If you find yourself doing -hash-driven-development in your controllers, juggling authorization logic and other concerns, serializers are for you! - -h3. The Most Basic Serializer - -A basic serializer is a simple Ruby object named after the model class it is serializing. - - -class PostSerializer - def initialize(post, scope) - @post, @scope = post, scope - end - - def as_json - { post: { title: @post.name, body: @post.body } } - end -end - - -A serializer is initialized with two parameters: the model object it should serialize and an authorization scope. By default, the -authorization scope is the current user (+current_user+) but you can use a different object if you want. The serializer also -implements an +as_json+ method, which returns a Hash that will be sent to the JSON encoder. - -Rails will transparently use your serializer when you use +render :json+ in your controller. - - -class PostsController < ApplicationController - def show - @post = Post.find(params[:id]) - render json: @post - end -end - - -Because +respond_with+ uses +render :json+ under the hood for JSON requests, Rails will automatically use your serializer when -you use +respond_with+ as well. - -h4. +serializable_hash+ - -In general, you will want to implement +serializable_hash+ and +as_json+ to allow serializers to embed associated content -directly. The easiest way to implement these two methods is to have +as_json+ call +serializable_hash+ and insert the root. - - -class PostSerializer - def initialize(post, scope) - @post, @scope = post, scope - end - - def serializable_hash - { title: @post.name, body: @post.body } - end - - def as_json - { post: serializable_hash } - end -end - - -h4. Authorization - -Let's update our serializer to include the email address of the author of the post, but only if the current user has superuser -access. - - -class PostSerializer - def initialize(post, scope) - @post, @scope = post, scope - end - - def as_json - { post: serializable_hash } - end - - def serializable_hash - hash = post - hash.merge!(super_data) if super? - hash - end - -private - def post - { title: @post.name, body: @post.body } - end - - def super_data - { email: @post.email } - end - - def super? - @scope.superuser? - end -end - - -h4. Testing - -One benefit of encapsulating our objects this way is that it becomes extremely straight-forward to test the serialization -logic in isolation. - - -require "ostruct" - -class PostSerializerTest < ActiveSupport::TestCase - # For now, we use a very simple authorization structure. These tests will need - # refactoring if we change that. - plebe = OpenStruct.new(super?: false) - god = OpenStruct.new(super?: true) - - post = OpenStruct.new(title: "Welcome to my blog!", body: "Blah blah blah", email: "tenderlove@gmail.com") - - test "a regular user sees just the title and body" do - json = PostSerializer.new(post, plebe).to_json - hash = JSON.parse(json) - - assert_equal post.title, hash.delete("title") - assert_equal post.body, hash.delete("body") - assert_empty hash - end - - test "a superuser sees the title, body and email" do - json = PostSerializer.new(post, god).to_json - hash = JSON.parse(json) - - assert_equal post.title, hash.delete("title") - assert_equal post.body, hash.delete("body") - assert_equal post.email, hash.delete("email") - assert_empty hash - end -end - - -It's important to note that serializer objects define a clear interface specifically for serializing an existing object. -In this case, the serializer expects to receive a post object with +name+, +body+ and +email+ attributes and an authorization -scope with a +super?+ method. - -By defining a clear interface, it's must easier to ensure that your authorization logic is behaving correctly. In this case, -the serializer doesn't need to concern itself with how the authorization scope decides whether to set the +super?+ flag, just -whether it is set. In general, you should document these requirements in your serializer files and programatically via tests. -The documentation library +YARD+ provides excellent tools for describing this kind of requirement: - - -class PostSerializer - # @param [~body, ~title, ~email] post the post to serialize - # @param [~super] scope the authorization scope for this serializer - def initialize(post, scope) - @post, @scope = post, scope - end - - # ... -end - - -h3. Attribute Sugar - -To simplify this process for a number of common cases, Rails provides a default superclass named +ActiveModel::Serializer+ -that you can use to implement your serializers. - -For example, you will sometimes want to simply include a number of existing attributes from the source model into the outputted -JSON. In the above example, the +title+ and +body+ attributes were always included in the JSON. Let's see how to use -+ActiveModel::Serializer+ to simplify our post serializer. - - -class PostSerializer < ActiveModel::Serializer - attributes :title, :body - - def initialize(post, scope) - @post, @scope = post, scope - end - - def serializable_hash - hash = attributes - hash.merge!(super_data) if super? - hash - end - -private - def super_data - { email: @post.email } - end - - def super? - @scope.superuser? - end -end - - -First, we specified the list of included attributes at the top of the class. This will create an instance method called -+attributes+ that extracts those attributes from the post model. - -NOTE: Internally, +ActiveModel::Serializer+ uses +read_attribute_for_serialization+, which defaults to +read_attribute+, which defaults to +send+. So if you're rolling your own models for use with the serializer, you can use simple Ruby accessors for your attributes if you like. - -Next, we use the attributes methood in our +serializable_hash+ method, which allowed us to eliminate the +post+ method we hand-rolled -earlier. We could also eliminate the +as_json+ method, as +ActiveModel::Serializer+ provides a default +as_json+ method for -us that calls our +serializable_hash+ method and inserts a root. But we can go a step further! - - -class PostSerializer < ActiveModel::Serializer - attributes :title, :body - -private - def attributes - hash = super - hash.merge!(email: post.email) if super? - hash - end - - def super? - @scope.superuser? - end -end - - -The superclass provides a default +initialize+ method as well as a default +serializable_hash+ method, which uses -+attributes+. We can call +super+ to get the hash based on the attributes we declared, and then add in any additional -attributes we want to use. - -NOTE: +ActiveModel::Serializer+ will create an accessor matching the name of the current class for the resource you pass in. In this case, because we have defined a PostSerializer, we can access the resource with the +post+ accessor. - -h3. Associations - -In most JSON APIs, you will want to include associated objects with your serialized object. In this case, let's include -the comments with the current post. - - -class PostSerializer < ActiveModel::Serializer - attributes :title, :body - has_many :comments - -private - def attributes - hash = super - hash.merge!(email: post.email) if super? - hash - end - - def super? - @scope.superuser? - end -end - - -The default +serializable_hash+ method will include the comments as embedded objects inside the post. - - -{ - post: { - title: "Hello Blog!", - body: "This is my first post. Isn't it fabulous!", - comments: [ - { - title: "Awesome", - body: "Your first post is great" - } - ] - } -} - - -Rails uses the same logic to generate embedded serializations as it does when you use +render :json+. In this case, -because you didn't define a +CommentSerializer+, Rails used the default +as_json+ on your comment object. - -If you define a serializer, Rails will automatically instantiate it with the existing authorization scope. - - -class CommentSerializer - def initialize(comment, scope) - @comment, @scope = comment, scope - end - - def serializable_hash - { title: @comment.title } - end - - def as_json - { comment: serializable_hash } - end -end - - -If we define the above comment serializer, the outputted JSON will change to: - - -{ - post: { - title: "Hello Blog!", - body: "This is my first post. Isn't it fabulous!", - comments: [{ title: "Awesome" }] - } -} - - -Let's imagine that our comment system allows an administrator to kill a comment, and we only want to allow -users to see the comments they're entitled to see. By default, +has_many :comments+ will simply use the -+comments+ accessor on the post object. We can override the +comments+ accessor to limit the comments used -to just the comments we want to allow for the current user. - - -class PostSerializer < ActiveModel::Serializer - attributes :title. :body - has_many :comments - -private - def attributes - hash = super - hash.merge!(email: post.email) if super? - hash - end - - def comments - post.comments_for(scope) - end - - def super? - @scope.superuser? - end -end - - -+ActiveModel::Serializer+ will still embed the comments, but this time it will use just the comments -for the current user. - -NOTE: The logic for deciding which comments a user should see still belongs in the model layer. In general, you should encapsulate concerns that require making direct Active Record queries in scopes or public methods on your models. - -h3. Customizing Associations - -Not all front-ends expect embedded documents in the same form. In these cases, you can override the -default +serializable_hash+, and use conveniences provided by +ActiveModel::Serializer+ to avoid having to -build up the hash manually. - -For example, let's say our front-end expects the posts and comments in the following format: - - -{ - post: { - id: 1 - title: "Hello Blog!", - body: "This is my first post. Isn't it fabulous!", - comments: [1,2] - }, - comments: [ - { - id: 1 - title: "Awesome", - body: "Your first post is great" - }, - { - id: 2 - title: "Not so awesome", - body: "Why is it so short!" - } - ] -} - - -We could achieve this with a custom +as_json+ method. We will also need to define a serializer for comments. - - -class CommentSerializer < ActiveModel::Serializer - attributes :id, :title, :body - - # define any logic for dealing with authorization-based attributes here -end - -class PostSerializer < ActiveModel::Serializer - attributes :title, :body - has_many :comments - - def as_json - { post: serializable_hash }.merge!(associations) - end - - def serializable_hash - post_hash = attributes - post_hash.merge!(association_ids) - post_hash - end - -private - def attributes - hash = super - hash.merge!(email: post.email) if super? - hash - end - - def comments - post.comments_for(scope) - end - - def super? - @scope.superuser? - end -end - - -Here, we used two convenience methods: +associations+ and +association_ids+. The first, -+associations+, creates a hash of all of the define associations, using their defined -serializers. The second, +association_ids+, generates a hash whose key is the association -name and whose value is an Array of the association's keys. - -The +association_ids+ helper will use the overridden version of the association, so in -this case, +association_ids+ will only include the ids of the comments provided by the -+comments+ method. - -h3. Special Association Serializers - -So far, associations defined in serializers use either the +as_json+ method on the model -or the defined serializer for the association type. Sometimes, you may want to serialize -associated models differently when they are requested as part of another resource than -when they are requested on their own. - -For instance, we might want to provide the full comment when it is requested directly, -but only its title when requested as part of the post. To achieve this, you can define -a serializer for associated objects nested inside the main serializer. - - -class PostSerializer < ActiveModel::Serializer - class CommentSerializer < ActiveModel::Serializer - attributes :id, :title - end - - # same as before - # ... -end - - -In other words, if a +PostSerializer+ is trying to serialize comments, it will first -look for +PostSerializer::CommentSerializer+ before falling back to +CommentSerializer+ -and finally +comment.as_json+. - -h3. Overriding the Defaults - -h4. Authorization Scope - -By default, the authorization scope for serializers is +:current_user+. This means -that when you call +render json: @post+, the controller will automatically call -its +current_user+ method and pass that along to the serializer's initializer. - -If you want to change that behavior, simply use the +serialization_scope+ class -method. - - -class PostsController < ApplicationController - serialization_scope :current_app -end - - -You can also implement an instance method called (no surprise) +serialization_scope+, -which allows you to define a dynamic authorization scope based on the current request. - -WARNING: If you use different objects as authorization scopes, make sure that they all implement whatever interface you use in your serializers to control what the outputted JSON looks like. - -h3. Using Serializers Outside of a Request - -The serialization API encapsulates the concern of generating a JSON representation of -a particular model for a particular user. As a result, you should be able to easily use -serializers, whether you define them yourself or whether you use +ActiveModel::Serializer+ -outside a request. - -For instance, if you want to generate the JSON representation of a post for a user outside -of a request: - - -user = get_user # some logic to get the user in question -PostSerializer.new(post, user).to_json # reliably generate JSON output - - -If you want to generate JSON for an anonymous user, you should be able to use whatever -technique you use in your application to generate anonymous users outside of a request. -Typically, that means creating a new user and not saving it to the database: - - -user = User.new # create a new anonymous user -PostSerializer.new(post, user).to_json - - -In general, the better you encapsulate your authorization logic, the more easily you -will be able to use the serializer outside of the context of a request. For instance, -if you use an authorization library like Cancan, which uses a uniform +user.can?(action, model)+, -the authorization interface can very easily be replaced by a plain Ruby object for -testing or usage outside the context of a request. - -h3. Collections - -So far, we've talked about serializing individual model objects. By default, Rails -will serialize collections, including when using the +associations+ helper, by -looping over each element of the collection, calling +serializable_hash+ on the element, -and then grouping them by their type (using the plural version of their class name -as the root). - -For example, an Array of post objects would serialize as: - - -{ - posts: [ - { - title: "FIRST POST!", - body: "It's my first pooooost" - }, - { title: "Second post!", - body: "Zomg I made it to my second post" - } - ] -} - - -If you want to change the behavior of serialized Arrays, you need to create -a custom Array serializer. - - -class ArraySerializer < ActiveModel::ArraySerializer - def serializable_array - serializers.map do |serializer| - serializer.serializable_hash - end - end - - def as_json - hash = { root => serializable_array } - hash.merge!(associations) - hash - end -end - - -When generating embedded associations using the +associations+ helper inside a -regular serializer, it will create a new ArraySerializer with the -associated content and call its +serializable_array+ method. In this case, those -embedded associations will not recursively include associations. - -When generating an Array using +render json: posts+, the controller will invoke -the +as_json+ method, which will include its associations and its root. diff --git a/railties/lib/rails/generators.rb b/railties/lib/rails/generators.rb index f1ca9080ff..27f8d13ce8 100644 --- a/railties/lib/rails/generators.rb +++ b/railties/lib/rails/generators.rb @@ -33,8 +33,7 @@ module Rails :stylesheets => '-y', :stylesheet_engine => '-se', :template_engine => '-e', - :test_framework => '-t', - :serializer => '-z' + :test_framework => '-t' }, :test_unit => { @@ -59,7 +58,6 @@ module Rails :performance_tool => nil, :resource_controller => :controller, :scaffold_controller => :scaffold_controller, - :serializer => false, :stylesheets => true, :stylesheet_engine => :css, :test_framework => false, diff --git a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb index 7353a67c83..03a61a035e 100644 --- a/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb +++ b/railties/lib/rails/generators/rails/scaffold/scaffold_generator.rb @@ -10,7 +10,6 @@ module Rails class_option :stylesheet_engine, :desc => "Engine for Stylesheets" hook_for :scaffold_controller, :required => true - hook_for :serializer hook_for :assets do |assets| invoke assets, [controller_name] diff --git a/railties/lib/rails/generators/rails/serializer/USAGE b/railties/lib/rails/generators/rails/serializer/USAGE deleted file mode 100644 index a49f7ea1fb..0000000000 --- a/railties/lib/rails/generators/rails/serializer/USAGE +++ /dev/null @@ -1,9 +0,0 @@ -Description: - Generates a serializer for the given resource with tests. - -Example: - `rails generate serializer Account name created_at` - - For TestUnit it creates: - Serializer: app/serializers/account_serializer.rb - TestUnit: test/unit/account_serializer_test.rb diff --git a/railties/lib/rails/generators/rails/serializer/serializer_generator.rb b/railties/lib/rails/generators/rails/serializer/serializer_generator.rb deleted file mode 100644 index 2118906227..0000000000 --- a/railties/lib/rails/generators/rails/serializer/serializer_generator.rb +++ /dev/null @@ -1,39 +0,0 @@ -module Rails - module Generators - class SerializerGenerator < NamedBase - check_class_collision :suffix => "Serializer" - - argument :attributes, :type => :array, :default => [], :banner => "field:type field:type" - - class_option :parent, :type => :string, :desc => "The parent class for the generated serializer" - - def create_serializer_file - template 'serializer.rb', File.join('app/serializers', class_path, "#{file_name}_serializer.rb") - end - - hook_for :test_framework - - private - - def attributes_names - attributes.select { |attr| !attr.reference? }.map { |a| a.name.to_sym } - end - - def association_names - attributes.select { |attr| attr.reference? }.map { |a| a.name.to_sym } - end - - def parent_class_name - if options[:parent] - options[:parent] - elsif (n = Rails::Generators.namespace) && n.const_defined?(:ApplicationSerializer) - "ApplicationSerializer" - elsif Object.const_defined?(:ApplicationSerializer) - "ApplicationSerializer" - else - "ActiveModel::Serializer" - end - end - end - end -end diff --git a/railties/lib/rails/generators/rails/serializer/templates/serializer.rb b/railties/lib/rails/generators/rails/serializer/templates/serializer.rb deleted file mode 100644 index 30c058c7e9..0000000000 --- a/railties/lib/rails/generators/rails/serializer/templates/serializer.rb +++ /dev/null @@ -1,9 +0,0 @@ -<% module_namespacing do -%> -class <%= class_name %>Serializer < <%= parent_class_name %> -<% if attributes.any? -%> attributes <%= attributes_names.map(&:inspect).join(", ") %> -<% end -%> -<% association_names.each do |attribute| -%> - has_one :<%= attribute %> -<% end -%> -end -<% end -%> \ No newline at end of file diff --git a/railties/lib/rails/generators/test_unit/serializer/serializer_generator.rb b/railties/lib/rails/generators/test_unit/serializer/serializer_generator.rb deleted file mode 100644 index 533c032c3b..0000000000 --- a/railties/lib/rails/generators/test_unit/serializer/serializer_generator.rb +++ /dev/null @@ -1,13 +0,0 @@ -require 'rails/generators/test_unit' - -module TestUnit - module Generators - class SerializerGenerator < Base - check_class_collision :suffix => "SerializerTest" - - def create_test_files - template 'unit_test.rb', File.join('test/unit', class_path, "#{file_name}_serializer_test.rb") - end - end - end -end diff --git a/railties/lib/rails/generators/test_unit/serializer/templates/unit_test.rb b/railties/lib/rails/generators/test_unit/serializer/templates/unit_test.rb deleted file mode 100644 index 0b1bbdcaa5..0000000000 --- a/railties/lib/rails/generators/test_unit/serializer/templates/unit_test.rb +++ /dev/null @@ -1,9 +0,0 @@ -require 'test_helper' - -<% module_namespacing do -%> -class <%= class_name %>SerializerTest < ActiveSupport::TestCase - # test "the truth" do - # assert true - # end -end -<% end -%> diff --git a/railties/test/generators/scaffold_generator_test.rb b/railties/test/generators/scaffold_generator_test.rb index 2e8b03fbef..2db8090621 100644 --- a/railties/test/generators/scaffold_generator_test.rb +++ b/railties/test/generators/scaffold_generator_test.rb @@ -264,15 +264,6 @@ class ScaffoldGeneratorTest < Rails::Generators::TestCase assert_file "app/assets/stylesheets/posts.css" end - def test_scaffold_also_generators_serializer - run_generator [ "posts", "name:string", "author:references", "--serializer" ] - assert_file "app/serializers/post_serializer.rb" do |serializer| - assert_match /class PostSerializer < ActiveModel::Serializer/, serializer - assert_match /^ attributes :name$/, serializer - assert_match /^ has_one :author$/, serializer - end - end - def test_scaffold_generator_outputs_error_message_on_missing_attribute_type run_generator ["post", "title", "body:text", "author"] diff --git a/railties/test/generators/serializer_generator_test.rb b/railties/test/generators/serializer_generator_test.rb deleted file mode 100644 index 2afaa65693..0000000000 --- a/railties/test/generators/serializer_generator_test.rb +++ /dev/null @@ -1,63 +0,0 @@ -require 'generators/generators_test_helper' -require 'rails/generators/rails/serializer/serializer_generator' - -class SerializerGeneratorTest < Rails::Generators::TestCase - include GeneratorsTestHelper - arguments %w(account name:string description:text business:references) - - def test_generates_a_serializer - run_generator - assert_file "app/serializers/account_serializer.rb", /class AccountSerializer < ActiveModel::Serializer/ - end - - def test_generates_a_namespaced_serializer - run_generator ["admin/account"] - assert_file "app/serializers/admin/account_serializer.rb", /class Admin::AccountSerializer < ActiveModel::Serializer/ - end - - def test_uses_application_serializer_if_one_exists - Object.const_set(:ApplicationSerializer, Class.new) - run_generator - assert_file "app/serializers/account_serializer.rb", /class AccountSerializer < ApplicationSerializer/ - ensure - Object.send :remove_const, :ApplicationSerializer - end - - def test_uses_namespace_application_serializer_if_one_exists - Object.const_set(:SerializerNamespace, Module.new) - SerializerNamespace.const_set(:ApplicationSerializer, Class.new) - Rails::Generators.namespace = SerializerNamespace - run_generator - assert_file "app/serializers/serializer_namespace/account_serializer.rb", - /module SerializerNamespace\n class AccountSerializer < ApplicationSerializer/ - ensure - Object.send :remove_const, :SerializerNamespace - Rails::Generators.namespace = nil - end - - def test_uses_given_parent - Object.const_set(:ApplicationSerializer, Class.new) - run_generator ["Account", "--parent=MySerializer"] - assert_file "app/serializers/account_serializer.rb", /class AccountSerializer < MySerializer/ - ensure - Object.send :remove_const, :ApplicationSerializer - end - - def test_generates_attributes_and_associations - run_generator - assert_file "app/serializers/account_serializer.rb" do |serializer| - assert_match(/^ attributes :name, :description$/, serializer) - assert_match(/^ has_one :business$/, serializer) - end - end - - def test_with_no_attributes_does_not_add_extra_space - run_generator ["account"] - assert_file "app/serializers/account_serializer.rb", /class AccountSerializer < ActiveModel::Serializer\nend/ - end - - def test_invokes_default_test_framework - run_generator - assert_file "test/unit/account_serializer_test.rb", /class AccountSerializerTest < ActiveSupport::TestCase/ - end -end -- cgit v1.2.3 From 3f1a4c3415113f2d365eb1a5531757699afec605 Mon Sep 17 00:00:00 2001 From: gregolsen Date: Sun, 6 Nov 2011 21:42:03 +0200 Subject: beginning_of_week extended in both Time and Date so that to return week start based on start day that is monday by default --- .../active_support/core_ext/date/calculations.rb | 24 ++++++++++++++-------- .../active_support/core_ext/time/calculations.rb | 24 ++++++++++++++-------- activesupport/test/core_ext/date_time_ext_test.rb | 18 ++++++++++++++++ activesupport/test/core_ext/time_ext_test.rb | 20 ++++++++++++++++++ .../source/active_support_core_extensions.textile | 10 +++++---- 5 files changed, 76 insertions(+), 20 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index c6d5f29690..a322075c4e 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -174,19 +174,27 @@ class Date months_since(1) end unless method_defined?(:next_month) - # Returns a new Date/DateTime representing the "start" of this week (i.e, Monday; DateTime objects will have time set to 0:00). - def beginning_of_week - days_to_monday = self.wday!=0 ? self.wday-1 : 6 - result = self - days_to_monday + # Returns number of days to start of this week, week starts on start_day (default is Monday) + def days_to_week_start(start_day = :monday) + start_day_number = DAYS_INTO_WEEK[start_day] + current_day_number = wday != 0 ? wday - 1 : 6 + days_span = current_day_number - start_day_number + days_span >= 0 ? days_span : 7 + days_span + end + + # Returns a new Date/DateTime representing the "start" of this week, week starts on start_day (i.e, Monday; DateTime objects will have time set to 0:00). + def beginning_of_week(start_day = :monday) + days_to_start = days_to_week_start(start_day) + result = self - days_to_start self.acts_like?(:time) ? result.midnight : result end alias :monday :beginning_of_week alias :at_beginning_of_week :beginning_of_week - # Returns a new Date/DateTime representing the end of this week (Sunday, DateTime objects will have time set to 23:59:59). - def end_of_week - days_to_sunday = self.wday!=0 ? 7-self.wday : 0 - result = self + days_to_sunday.days + # Returns a new Date/DateTime representing the end of this week, week starts on start_day (Sunday, DateTime objects will have time set to 23:59:59). + def end_of_week(start_day = :monday) + days_to_end = 6 - days_to_week_start(start_day) + result = self + days_to_end.days self.acts_like?(:time) ? result.end_of_day : result end alias :sunday :end_of_week diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index 1e15529569..154ba78646 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -161,18 +161,26 @@ class Time months_since(1) end - # Returns a new Time representing the "start" of this week (Monday, 0:00) - def beginning_of_week - days_to_monday = wday!=0 ? wday-1 : 6 - (self - days_to_monday.days).midnight + # Returns number of days to start of this week, week starts on start_day (default is Monday) + def days_to_week_start(start_day = :monday) + start_day_number = DAYS_INTO_WEEK[start_day] + current_day_number = wday != 0 ? wday - 1 : 6 + days_span = current_day_number - start_day_number + days_span >= 0 ? days_span : 7 + days_span + end + + # Returns a new Time representing the "start" of this week, week starts on start_day (deafult is Monday, e.g. Monday, 0:00) + def beginning_of_week(start_day = :monday) + days_to_start = days_to_week_start(start_day) + (self - days_to_start.days).midnight end alias :monday :beginning_of_week alias :at_beginning_of_week :beginning_of_week - # Returns a new Time representing the end of this week, (end of Sunday) - def end_of_week - days_to_sunday = wday!=0 ? 7-wday : 0 - (self + days_to_sunday.days).end_of_day + # Returns a new Time representing the end of this week, week starts on start_day (deafult is Monday, end of Sunday) + def end_of_week(start_day = :monday) + days_to_end = 6 - days_to_week_start(start_day) + (self + days_to_end.days).end_of_day end alias :at_end_of_week :end_of_week diff --git a/activesupport/test/core_ext/date_time_ext_test.rb b/activesupport/test/core_ext/date_time_ext_test.rb index 9be28272f7..0087163faf 100644 --- a/activesupport/test/core_ext/date_time_ext_test.rb +++ b/activesupport/test/core_ext/date_time_ext_test.rb @@ -54,6 +54,24 @@ class DateTimeExtCalculationsTest < Test::Unit::TestCase assert_equal 86399,DateTime.civil(2005,1,1,23,59,59).seconds_since_midnight end + def test_days_to_week_start + assert_equal 0, Time.local(2011,11,01,0,0,0).days_to_week_start(:tuesday) + assert_equal 1, Time.local(2011,11,02,0,0,0).days_to_week_start(:tuesday) + assert_equal 2, Time.local(2011,11,03,0,0,0).days_to_week_start(:tuesday) + assert_equal 3, Time.local(2011,11,04,0,0,0).days_to_week_start(:tuesday) + assert_equal 4, Time.local(2011,11,05,0,0,0).days_to_week_start(:tuesday) + assert_equal 5, Time.local(2011,11,06,0,0,0).days_to_week_start(:tuesday) + assert_equal 6, Time.local(2011,11,07,0,0,0).days_to_week_start(:tuesday) + + assert_equal 3, Time.local(2011,11,03,0,0,0).days_to_week_start(:monday) + assert_equal 3, Time.local(2011,11,04,0,0,0).days_to_week_start(:tuesday) + assert_equal 3, Time.local(2011,11,05,0,0,0).days_to_week_start(:wednesday) + assert_equal 3, Time.local(2011,11,06,0,0,0).days_to_week_start(:thursday) + assert_equal 3, Time.local(2011,11,07,0,0,0).days_to_week_start(:friday) + assert_equal 3, Time.local(2011,11,8,0,0,0).days_to_week_start(:saturday) + assert_equal 3, Time.local(2011,11,9,0,0,0).days_to_week_start(:sunday) + end + def test_beginning_of_week assert_equal DateTime.civil(2005,1,31), DateTime.civil(2005,2,4,10,10,10).beginning_of_week assert_equal DateTime.civil(2005,11,28), DateTime.civil(2005,11,28,0,0,0).beginning_of_week #monday diff --git a/activesupport/test/core_ext/time_ext_test.rb b/activesupport/test/core_ext/time_ext_test.rb index d2ff44e1b4..6cc63851e9 100644 --- a/activesupport/test/core_ext/time_ext_test.rb +++ b/activesupport/test/core_ext/time_ext_test.rb @@ -59,8 +59,28 @@ class TimeExtCalculationsTest < ActiveSupport::TestCase assert_equal Time.local(2005,11,28), Time.local(2005,12,02,0,0,0).beginning_of_week #friday assert_equal Time.local(2005,11,28), Time.local(2005,12,03,0,0,0).beginning_of_week #saturday assert_equal Time.local(2005,11,28), Time.local(2005,12,04,0,0,0).beginning_of_week #sunday + end + def test_days_to_week_start + assert_equal 0, Time.local(2011,11,01,0,0,0).days_to_week_start(:tuesday) + assert_equal 1, Time.local(2011,11,02,0,0,0).days_to_week_start(:tuesday) + assert_equal 2, Time.local(2011,11,03,0,0,0).days_to_week_start(:tuesday) + assert_equal 3, Time.local(2011,11,04,0,0,0).days_to_week_start(:tuesday) + assert_equal 4, Time.local(2011,11,05,0,0,0).days_to_week_start(:tuesday) + assert_equal 5, Time.local(2011,11,06,0,0,0).days_to_week_start(:tuesday) + assert_equal 6, Time.local(2011,11,07,0,0,0).days_to_week_start(:tuesday) + + assert_equal 3, Time.local(2011,11,03,0,0,0).days_to_week_start(:monday) + assert_equal 3, Time.local(2011,11,04,0,0,0).days_to_week_start(:tuesday) + assert_equal 3, Time.local(2011,11,05,0,0,0).days_to_week_start(:wednesday) + assert_equal 3, Time.local(2011,11,06,0,0,0).days_to_week_start(:thursday) + assert_equal 3, Time.local(2011,11,07,0,0,0).days_to_week_start(:friday) + assert_equal 3, Time.local(2011,11,8,0,0,0).days_to_week_start(:saturday) + assert_equal 3, Time.local(2011,11,9,0,0,0).days_to_week_start(:sunday) + end + + def test_beginning_of_day assert_equal Time.local(2005,2,4,0,0,0), Time.local(2005,2,4,10,10,10).beginning_of_day with_env_tz 'US/Eastern' do diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile index ff6c5f967f..c6130f4f62 100644 --- a/railties/guides/source/active_support_core_extensions.textile +++ b/railties/guides/source/active_support_core_extensions.textile @@ -3039,12 +3039,14 @@ Active Support defines these methods as well for Ruby 1.8. h6. +beginning_of_week+, +end_of_week+ -The methods +beginning_of_week+ and +end_of_week+ return the dates for the beginning and end of week, assuming weeks start on Monday: +The methods +beginning_of_week+ and +end_of_week+ receive a symbol with a day name in English (in lowercase, default is :monday) and return the dates for the beginning and end of week, assuming weeks start on day, passed as parameter: -d = Date.new(2010, 5, 8) # => Sat, 08 May 2010 -d.beginning_of_week # => Mon, 03 May 2010 -d.end_of_week # => Sun, 09 May 2010 +d = Date.new(2010, 5, 8) # => Sat, 08 May 2010 +d.beginning_of_week # => Mon, 03 May 2010 +d.beginning_of_week(:sunday) # => Sun, 02 May 2010 +d.end_of_week # => Sun, 09 May 2010 +d.end_of_week(:sunday) # => Sat, 08 May 2010 +beginning_of_week+ is aliased to +monday+ and +at_beginning_of_week+. +end_of_week+ is aliased to +sunday+ and +at_end_of_week+. -- cgit v1.2.3 From 7a33a005d732e80f5a6832b67df6dec5a6faaa1e Mon Sep 17 00:00:00 2001 From: gregolsen Date: Mon, 7 Nov 2011 13:26:56 +0200 Subject: API docstrings updated with default value info --- activesupport/lib/active_support/core_ext/date/calculations.rb | 6 +++--- activesupport/lib/active_support/core_ext/time/calculations.rb | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index a322075c4e..ea9a1c2c0d 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -174,7 +174,7 @@ class Date months_since(1) end unless method_defined?(:next_month) - # Returns number of days to start of this week, week starts on start_day (default is Monday) + # Returns number of days to start of this week, week starts on start_day (default is :monday). def days_to_week_start(start_day = :monday) start_day_number = DAYS_INTO_WEEK[start_day] current_day_number = wday != 0 ? wday - 1 : 6 @@ -182,7 +182,7 @@ class Date days_span >= 0 ? days_span : 7 + days_span end - # Returns a new Date/DateTime representing the "start" of this week, week starts on start_day (i.e, Monday; DateTime objects will have time set to 0:00). + # Returns a new Date/DateTime representing the "start" of this week, week starts on start_day (default is :monday, i.e. Monday; DateTime objects will have time set to 0:00). def beginning_of_week(start_day = :monday) days_to_start = days_to_week_start(start_day) result = self - days_to_start @@ -191,7 +191,7 @@ class Date alias :monday :beginning_of_week alias :at_beginning_of_week :beginning_of_week - # Returns a new Date/DateTime representing the end of this week, week starts on start_day (Sunday, DateTime objects will have time set to 23:59:59). + # Returns a new Date/DateTime representing the end of this week, week starts on start_day (default is :monday, i.e. Sunday, DateTime objects will have time set to 23:59:59). def end_of_week(start_day = :monday) days_to_end = 6 - days_to_week_start(start_day) result = self + days_to_end.days diff --git a/activesupport/lib/active_support/core_ext/time/calculations.rb b/activesupport/lib/active_support/core_ext/time/calculations.rb index 154ba78646..2cc5c82265 100644 --- a/activesupport/lib/active_support/core_ext/time/calculations.rb +++ b/activesupport/lib/active_support/core_ext/time/calculations.rb @@ -161,7 +161,7 @@ class Time months_since(1) end - # Returns number of days to start of this week, week starts on start_day (default is Monday) + # Returns number of days to start of this week, week starts on start_day (default is :monday). def days_to_week_start(start_day = :monday) start_day_number = DAYS_INTO_WEEK[start_day] current_day_number = wday != 0 ? wday - 1 : 6 @@ -169,7 +169,7 @@ class Time days_span >= 0 ? days_span : 7 + days_span end - # Returns a new Time representing the "start" of this week, week starts on start_day (deafult is Monday, e.g. Monday, 0:00) + # Returns a new Time representing the "start" of this week, week starts on start_day (default is :monday, i.e. Monday, 0:00). def beginning_of_week(start_day = :monday) days_to_start = days_to_week_start(start_day) (self - days_to_start.days).midnight @@ -177,7 +177,7 @@ class Time alias :monday :beginning_of_week alias :at_beginning_of_week :beginning_of_week - # Returns a new Time representing the end of this week, week starts on start_day (deafult is Monday, end of Sunday) + # Returns a new Time representing the end of this week, week starts on start_day (default is :monday, i.e. end of Sunday). def end_of_week(start_day = :monday) days_to_end = 6 - days_to_week_start(start_day) (self + days_to_end.days).end_of_day -- cgit v1.2.3 From a5b362df567ed4a0167a83e9b8f00b9f614ac38b Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Fri, 25 Nov 2011 12:01:58 -0800 Subject: some tweaks to PR#3547. [Closes #3547] --- activesupport/CHANGELOG.md | 3 +++ .../lib/active_support/core_ext/date/calculations.rb | 19 ++++++++++++------- .../source/active_support_core_extensions.textile | 4 +++- 3 files changed, 18 insertions(+), 8 deletions(-) diff --git a/activesupport/CHANGELOG.md b/activesupport/CHANGELOG.md index 18a115b369..465b0c1e16 100644 --- a/activesupport/CHANGELOG.md +++ b/activesupport/CHANGELOG.md @@ -1,5 +1,8 @@ ## Rails 3.2.0 (unreleased) ## +* (Date|DateTime|Time)#beginning_of_week accept an optional argument to + be able to set the day at which weeks are assumed to start. + * Deprecated ActiveSupport::MessageEncryptor#encrypt and decrypt. *José Valim* * ActiveSupport::Notifications.subscribed provides subscriptions to events while a block runs. *fxn* diff --git a/activesupport/lib/active_support/core_ext/date/calculations.rb b/activesupport/lib/active_support/core_ext/date/calculations.rb index ea9a1c2c0d..0c4081aa8d 100644 --- a/activesupport/lib/active_support/core_ext/date/calculations.rb +++ b/activesupport/lib/active_support/core_ext/date/calculations.rb @@ -174,24 +174,28 @@ class Date months_since(1) end unless method_defined?(:next_month) - # Returns number of days to start of this week, week starts on start_day (default is :monday). + # Returns number of days to start of this week. Week is assumed to start on + # +start_day+, default is +:monday+. def days_to_week_start(start_day = :monday) start_day_number = DAYS_INTO_WEEK[start_day] current_day_number = wday != 0 ? wday - 1 : 6 - days_span = current_day_number - start_day_number - days_span >= 0 ? days_span : 7 + days_span + (current_day_number - start_day_number) % 7 end - # Returns a new Date/DateTime representing the "start" of this week, week starts on start_day (default is :monday, i.e. Monday; DateTime objects will have time set to 0:00). + # Returns a new +Date+/+DateTime+ representing the start of this week. Week is + # assumed to start on +start_day+, default is +:monday+. +DateTime+ objects + # have their time set to 0:00. def beginning_of_week(start_day = :monday) days_to_start = days_to_week_start(start_day) result = self - days_to_start - self.acts_like?(:time) ? result.midnight : result + acts_like?(:time) ? result.midnight : result end alias :monday :beginning_of_week alias :at_beginning_of_week :beginning_of_week - # Returns a new Date/DateTime representing the end of this week, week starts on start_day (default is :monday, i.e. Sunday, DateTime objects will have time set to 23:59:59). + # Returns a new +Date+/+DateTime+ representing the end of this week, week + # starts on +start_day+, default is +:monday+. +DateTime+ objects have their + # time set to 23:59:59). def end_of_week(start_day = :monday) days_to_end = 6 - days_to_week_start(start_day) result = self + days_to_end.days @@ -200,7 +204,8 @@ class Date alias :sunday :end_of_week alias :at_end_of_week :end_of_week - # Returns a new Date/DateTime representing the start of the given day in the previous week (default is :monday). + # Returns a new +Date+/+DateTime+ representing the given +day+ in the previous + # week. Default is +:monday+. +DateTime+ objects have their time set to 0:00. def prev_week(day = :monday) result = (self - 7).beginning_of_week + DAYS_INTO_WEEK[day] self.acts_like?(:time) ? result.change(:hour => 0) : result diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile index c6130f4f62..09f931050d 100644 --- a/railties/guides/source/active_support_core_extensions.textile +++ b/railties/guides/source/active_support_core_extensions.textile @@ -3039,7 +3039,9 @@ Active Support defines these methods as well for Ruby 1.8. h6. +beginning_of_week+, +end_of_week+ -The methods +beginning_of_week+ and +end_of_week+ receive a symbol with a day name in English (in lowercase, default is :monday) and return the dates for the beginning and end of week, assuming weeks start on day, passed as parameter: +The methods +beginning_of_week+ and +end_of_week+ return the dates for the +beginning and end of the week, respectively. Weeks are assumed to start on +Monday, but that can be changed passing an argument, see examples: d = Date.new(2010, 5, 8) # => Sat, 08 May 2010 -- cgit v1.2.3 From 1be9830d4d99e2bf56f1cadf74b843f22d66da35 Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Fri, 25 Nov 2011 14:29:34 -0800 Subject: add the query to AR::Relation#explain output Rationale: this is more readable if serveral queries are involved in one call. Also, it will be possible to let AR log EXPLAINs automatically in production mode, where queries are not even around. --- activerecord/lib/active_record/relation.rb | 5 +++-- activerecord/test/cases/adapters/mysql2/explain_test.rb | 3 +++ activerecord/test/cases/adapters/postgresql/explain_test.rb | 3 +++ activerecord/test/cases/adapters/sqlite3/explain_test.rb | 3 +++ railties/guides/source/active_record_querying.textile | 5 +++++ 5 files changed, 17 insertions(+), 2 deletions(-) diff --git a/activerecord/lib/active_record/relation.rb b/activerecord/lib/active_record/relation.rb index f0891440a6..0c32ad5139 100644 --- a/activerecord/lib/active_record/relation.rb +++ b/activerecord/lib/active_record/relation.rb @@ -1,3 +1,4 @@ +# -*- coding: utf-8 -*- require 'active_support/core_ext/object/blank' require 'active_support/core_ext/module/delegation' @@ -155,8 +156,8 @@ module ActiveRecord end queries.map do |sql| - @klass.connection.explain(sql) - end.join + "EXPLAIN for: #{sql}\n#{@klass.connection.explain(sql)}" + end.join("\n") end def to_a diff --git a/activerecord/test/cases/adapters/mysql2/explain_test.rb b/activerecord/test/cases/adapters/mysql2/explain_test.rb index 8ea777b72b..68ed361aeb 100644 --- a/activerecord/test/cases/adapters/mysql2/explain_test.rb +++ b/activerecord/test/cases/adapters/mysql2/explain_test.rb @@ -9,12 +9,15 @@ module ActiveRecord def test_explain_for_one_query explain = Developer.where(:id => 1).explain + assert_match %(EXPLAIN for: SELECT `developers`.* FROM `developers` WHERE `developers`.`id` = 1), explain assert_match %(developers | const), explain end def test_explain_with_eager_loading explain = Developer.where(:id => 1).includes(:audit_logs).explain + assert_match %(EXPLAIN for: SELECT `developers`.* FROM `developers` WHERE `developers`.`id` = 1), explain assert_match %(developers | const), explain + assert_match %(EXPLAIN for: SELECT `audit_logs`.* FROM `audit_logs` WHERE `audit_logs`.`developer_id` IN (1)), explain assert_match %(audit_logs | ALL), explain end end diff --git a/activerecord/test/cases/adapters/postgresql/explain_test.rb b/activerecord/test/cases/adapters/postgresql/explain_test.rb index 0d599ed37f..0b61f61572 100644 --- a/activerecord/test/cases/adapters/postgresql/explain_test.rb +++ b/activerecord/test/cases/adapters/postgresql/explain_test.rb @@ -9,6 +9,7 @@ module ActiveRecord def test_explain_for_one_query explain = Developer.where(:id => 1).explain + assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = 1), explain assert_match %(QUERY PLAN), explain assert_match %(Index Scan using developers_pkey on developers), explain end @@ -16,7 +17,9 @@ module ActiveRecord def test_explain_with_eager_loading explain = Developer.where(:id => 1).includes(:audit_logs).explain assert_match %(QUERY PLAN), explain + assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = 1), explain assert_match %(Index Scan using developers_pkey on developers), explain + assert_match %(EXPLAIN for: SELECT "audit_logs".* FROM "audit_logs" WHERE "audit_logs"."developer_id" IN (1)), explain assert_match %(Seq Scan on audit_logs), explain end end diff --git a/activerecord/test/cases/adapters/sqlite3/explain_test.rb b/activerecord/test/cases/adapters/sqlite3/explain_test.rb index e18892821d..b227bce680 100644 --- a/activerecord/test/cases/adapters/sqlite3/explain_test.rb +++ b/activerecord/test/cases/adapters/sqlite3/explain_test.rb @@ -9,12 +9,15 @@ module ActiveRecord def test_explain_for_one_query explain = Developer.where(:id => 1).explain + assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = 1), explain assert_match(/(SEARCH )?TABLE developers USING (INTEGER )?PRIMARY KEY/, explain) end def test_explain_with_eager_loading explain = Developer.where(:id => 1).includes(:audit_logs).explain + assert_match %(EXPLAIN for: SELECT "developers".* FROM "developers" WHERE "developers"."id" = 1), explain assert_match(/(SEARCH )?TABLE developers USING (INTEGER )?PRIMARY KEY/, explain) + assert_match %(EXPLAIN for: SELECT "audit_logs".* FROM "audit_logs" WHERE "audit_logs"."developer_id" IN (1)), explain assert_match(/(SCAN )?TABLE audit_logs/, explain) end end diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile index ad12dca7e8..0f1f6eba4c 100644 --- a/railties/guides/source/active_record_querying.textile +++ b/railties/guides/source/active_record_querying.textile @@ -1287,6 +1287,7 @@ User.where(:id => 1).joins(:posts).explain may yield +EXPLAIN for: SELECT `users`.* FROM `users` INNER JOIN `posts` ON `posts`.`user_id` = `users`.`id` WHERE `users`.`id` = 1 ------------------------------------------------------------------------------------------ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | ------------------------------------------------------------------------------------------ @@ -1302,6 +1303,7 @@ Active Record performs a pretty printing that emulates the one of the database shells. So, the same query running with the PostgreSQL adapter would yield instead +EXPLAIN for: SELECT "users".* FROM "users" INNER JOIN "posts" ON "posts"."user_id" = "users"."id" WHERE "users"."id" = 1 QUERY PLAN ------------------------------------------------------------------------------ Nested Loop Left Join (cost=0.00..37.24 rows=8 width=0) @@ -1324,12 +1326,15 @@ User.where(:id => 1).includes(:posts).explain yields +EXPLAIN for: SELECT `users`.* FROM `users` WHERE `users`.`id` = 1 ------------------------------------------------------------------------------------ | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | ------------------------------------------------------------------------------------ | 1 | SIMPLE | users | const | PRIMARY | PRIMARY | 4 | const | 1 | | ------------------------------------------------------------------------------------ 1 row in set (0.00 sec) + +EXPLAIN for: SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` IN (1) ------------------------------------------------------------------------------------- | id | select_type | table | type | possible_keys | key | key_len | ref | rows | Extra | ------------------------------------------------------------------------------------- -- cgit v1.2.3 From b30b932447d3ae059c0dac2c5bf0a3a79e0fa54e Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Fri, 25 Nov 2011 14:58:43 -0800 Subject: finders guide: adds some pointers to help users interpret the output of EXPLAIN --- railties/guides/source/active_record_querying.textile | 11 +++++++++++ 1 file changed, 11 insertions(+) diff --git a/railties/guides/source/active_record_querying.textile b/railties/guides/source/active_record_querying.textile index 0f1f6eba4c..c4724f182e 100644 --- a/railties/guides/source/active_record_querying.textile +++ b/railties/guides/source/active_record_querying.textile @@ -1344,3 +1344,14 @@ EXPLAIN for: SELECT `posts`.* FROM `posts` WHERE `posts`.`user_id` IN (1) under MySQL. + +h4. Interpreting EXPLAIN + +Interpretation of the output of EXPLAIN is beyond the scope of this guide. The +following pointers may be helpful: + +* SQLite3: "EXPLAIN QUERY PLAN":http://www.sqlite.org/eqp.html + +* MySQL: "EXPLAIN Output Format":http://dev.mysql.com/doc/refman/5.6/en/explain-output.html + +* PostgreSQL: "Using EXPLAIN":http://www.postgresql.org/docs/current/static/using-explain.html -- cgit v1.2.3 From b6916e0b925d4c8b4487d574fe07b11440f2ec5e Mon Sep 17 00:00:00 2001 From: Xavier Noria Date: Fri, 25 Nov 2011 15:06:30 -0800 Subject: removes a Serialization constant left in the previous revert --- actionpack/lib/action_controller/base.rb | 1 - 1 file changed, 1 deletion(-) diff --git a/actionpack/lib/action_controller/base.rb b/actionpack/lib/action_controller/base.rb index cfb9cf5e6e..98bfe72fef 100644 --- a/actionpack/lib/action_controller/base.rb +++ b/actionpack/lib/action_controller/base.rb @@ -190,7 +190,6 @@ module ActionController Redirecting, Rendering, Renderers::All, - Serialization, ConditionalGet, RackDelegation, SessionManagement, -- cgit v1.2.3 From c4af5f00f602b9e1c3166872902265836daf321b Mon Sep 17 00:00:00 2001 From: Vijay Dev Date: Sat, 26 Nov 2011 18:52:10 +0530 Subject: use any? to check for size --- actionpack/lib/action_view/helpers/atom_feed_helper.rb | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/actionpack/lib/action_view/helpers/atom_feed_helper.rb b/actionpack/lib/action_view/helpers/atom_feed_helper.rb index 73824dc1f8..90589d2238 100644 --- a/actionpack/lib/action_view/helpers/atom_feed_helper.rb +++ b/actionpack/lib/action_view/helpers/atom_feed_helper.rb @@ -32,7 +32,7 @@ module ActionView # app/views/posts/index.atom.builder: # atom_feed do |feed| # feed.title("My great blog!") - # feed.updated(@posts[0].created_at) if @posts.length > 0 + # feed.updated(@posts.first.created_at) if @posts.any? # # @posts.each do |post| # feed.entry(post) do |entry| -- cgit v1.2.3 From 5c2a2ee76e2af591a997b71eec0e35143c07f9e1 Mon Sep 17 00:00:00 2001 From: Vijay Dev Date: Sat, 26 Nov 2011 19:06:53 +0530 Subject: rephrased ef38c3089e1269e2b315aff503e61c0b23c95f00 and mentioned about OS X and windows usually having a JS runtime --- railties/guides/source/getting_started.textile | 6 +----- 1 file changed, 1 insertion(+), 5 deletions(-) diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile index 5774eba5ae..ca6a404212 100644 --- a/railties/guides/source/getting_started.textile +++ b/railties/guides/source/getting_started.textile @@ -450,11 +450,7 @@ start a web server on your development machine. You can do this by running: $ rails server -TIP: Some environments require that you install a JavaScript runtime to compile -CoffeeScript to JavaScript and will give you an +execjs+ error the first time -you run +rails server+. +therubyracer+ and +therubyrhino+ are commonly used -runtimes for Ruby and JRuby respectively. You can also investigate a list -of runtimes at "ExecJS":https://github.com/sstephenson/execjs. +TIP: Compiling CoffeeScript to JavaScript requires a JavaScript runtime and the absence of a runtime will give you an +execjs+ error. Usually Mac OS X and Windows come with a JavaScript runtime installed. +therubyracer+ and +therubyrhino+ are the commonly used runtimes for Ruby and JRuby respectively. You can also investigate a list of runtimes at "ExecJS":https://github.com/sstephenson/execjs. This will fire up an instance of the WEBrick web server by default (Rails can also use several other web servers). To see your application in action, open a -- cgit v1.2.3 From fc20d6b6815032935f02b53b4b928cd514ed66ac Mon Sep 17 00:00:00 2001 From: Arun Agrawal Date: Sat, 26 Nov 2011 22:38:58 +0530 Subject: Let's say this 3.1.3 instead of 3.1.1 in docs --- railties/guides/source/getting_started.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile index ca6a404212..fe43c9db36 100644 --- a/railties/guides/source/getting_started.textile +++ b/railties/guides/source/getting_started.textile @@ -248,7 +248,7 @@ the following: $ rails --version -If it says something like "Rails 3.1.1" you are ready to continue. +If it says something like "Rails 3.1.3" you are ready to continue. h4. Creating the Blog Application -- cgit v1.2.3 From 448df2d100feccf1d5df9db2b02b732a775d8406 Mon Sep 17 00:00:00 2001 From: Alexey Vakhov Date: Sun, 27 Nov 2011 10:23:40 +0400 Subject: Cosmetic fixes in AM validatations docs --- activemodel/lib/active_model/validations/presence.rb | 6 +++--- activemodel/lib/active_model/validations/with.rb | 2 +- 2 files changed, 4 insertions(+), 4 deletions(-) diff --git a/activemodel/lib/active_model/validations/presence.rb b/activemodel/lib/active_model/validations/presence.rb index 35af7152db..9a643a6f5c 100644 --- a/activemodel/lib/active_model/validations/presence.rb +++ b/activemodel/lib/active_model/validations/presence.rb @@ -25,14 +25,14 @@ module ActiveModel # This is due to the way Object#blank? handles boolean values: false.blank? # => true. # # Configuration options: - # * message - A custom error message (default is: "can't be blank"). + # * :message - A custom error message (default is: "can't be blank"). # * :on - Specifies when this validation is active. Runs in all # validation contexts by default (+nil+), other options are :create # and :update. - # * if - Specifies a method, proc or string to call to determine if the validation should + # * :if - Specifies a method, proc or string to call to determine if the validation should # occur (e.g. :if => :allow_validation, or :if => Proc.new { |user| user.signup_step > 2 }). # The method, proc or string should return or evaluate to a true or false value. - # * unless - Specifies a method, proc or string to call to determine if the validation should + # * :unless - Specifies a method, proc or string to call to determine if the validation should # not occur (e.g. :unless => :skip_validation, or :unless => Proc.new { |user| user.signup_step <= 2 }). # The method, proc or string should return or evaluate to a true or false value. # * :strict - Specifies whether validation should be strict. diff --git a/activemodel/lib/active_model/validations/with.rb b/activemodel/lib/active_model/validations/with.rb index 93a340eb39..72b8562b93 100644 --- a/activemodel/lib/active_model/validations/with.rb +++ b/activemodel/lib/active_model/validations/with.rb @@ -56,7 +56,7 @@ module ActiveModel # if the validation should occur (e.g. :if => :allow_validation, # or :if => Proc.new { |user| user.signup_step > 2 }). # The method, proc or string should return or evaluate to a true or false value. - # * unless - Specifies a method, proc or string to call to + # * :unless - Specifies a method, proc or string to call to # determine if the validation should not occur # (e.g. :unless => :skip_validation, or # :unless => Proc.new { |user| user.signup_step <= 2 }). -- cgit v1.2.3 From 0ce64ba9aa93c01ee69c45493ad8090e76f95ff4 Mon Sep 17 00:00:00 2001 From: Arun Agrawal Date: Sun, 27 Nov 2011 12:34:14 +0530 Subject: Documentation about config.log_level config options --- railties/guides/source/configuring.textile | 2 ++ 1 file changed, 2 insertions(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index 809948b41e..beb623757b 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -88,6 +88,8 @@ NOTE. The +config.asset_path+ configuration is ignored if the asset pipeline is * +config.log_level+ defines the verbosity of the Rails logger. This option defaults to +:debug+ for all modes except production, where it defaults to +:info+. +* +config.log_tags+ accepts a list of methods that respond to +request+ object. This makes it easy to tag log lines with debug information like subdomain and request id -- both very helpful in debugging multi-user production applications. + * +config.logger+ accepts a logger conforming to the interface of Log4r or the default Ruby +Logger+ class. Defaults to an instance of +ActiveSupport::BufferedLogger+, with auto flushing off in production mode. * +config.middleware+ allows you to configure the application's middleware. This is covered in depth in the "Configuring Middleware":#configuring-middleware section below. -- cgit v1.2.3 From 28e3935360c9392685c4954791cc320ed02e15c0 Mon Sep 17 00:00:00 2001 From: Arun Agrawal Date: Sun, 27 Nov 2011 12:53:08 +0530 Subject: [Docs] Adding RequestId middleware in configuring middleware --- railties/guides/source/configuring.textile | 1 + 1 file changed, 1 insertion(+) diff --git a/railties/guides/source/configuring.textile b/railties/guides/source/configuring.textile index beb623757b..d91011c370 100644 --- a/railties/guides/source/configuring.textile +++ b/railties/guides/source/configuring.textile @@ -190,6 +190,7 @@ Every Rails application comes with a standard set of middleware which it uses in * +Rack::Runtime+ sets an +X-Runtime+ header, containing the time (in seconds) taken to execute the request. * +Rails::Rack::Logger+ notifies the logs that the request has began. After request is complete, flushes all the logs. * +ActionDispatch::ShowExceptions+ rescues any exception returned by the application and renders nice exception pages if the request is local or if +config.consider_all_requests_local+ is set to +true+. If +config.action_dispatch.show_exceptions+ is set to +false+, exceptions will be raised regardless. +* +ActionDispatch::RequestId+ makes a unique X-Request-Id header available to the response and enables the +ActionDispatch::Request#uuid+ method. * +ActionDispatch::RemoteIp+ checks for IP spoofing attacks. Configurable with the +config.action_dispatch.ip_spoofing_check+ and +config.action_dispatch.trusted_proxies+ settings. * +Rack::Sendfile+ intercepts responses whose body is being served from a file and replaces it with a server specific X-Sendfile header. Configurable with +config.action_dispatch.x_sendfile_header+. * +ActionDispatch::Callbacks+ runs the prepare callbacks before serving the request. -- cgit v1.2.3 From 9cf285599c1e99736f5dd9d01f986c19dcf3d4da Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Tadas=20Tamo=C5=A1auskas?= Date: Sun, 27 Nov 2011 12:44:14 +0000 Subject: Update Object#in? description for https://github.com/rails/rails/pull/3767 --- railties/guides/source/active_support_core_extensions.textile | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/railties/guides/source/active_support_core_extensions.textile b/railties/guides/source/active_support_core_extensions.textile index d601e9ea29..c1046a3b63 100644 --- a/railties/guides/source/active_support_core_extensions.textile +++ b/railties/guides/source/active_support_core_extensions.textile @@ -440,14 +440,16 @@ NOTE: Defined in +active_support/core_ext/kernel/reporting.rb+. h4. +in?+ -The predicate +in?+ tests if an object is included in another object. An +ArgumentError+ exception will be raised if the argument passed does not respond to +include?+. +The predicate +in?+ tests if an object is included in another object or a list of objects. An +ArgumentError+ exception will be raised if a single argument is passed and it does not respond to +include?+. Examples of +in?+: +1.in?(1,2) # => true 1.in?([1,2]) # => true "lo".in?("hello") # => true 25.in?(30..50) # => false +1.in?(1) # => ArgumentError NOTE: Defined in +active_support/core_ext/object/inclusion.rb+. -- cgit v1.2.3 From 2f428a6245a18017c7942f8fef3da4a85bec8cc5 Mon Sep 17 00:00:00 2001 From: Tim Reischmann Date: Mon, 28 Nov 2011 09:27:42 +0100 Subject: fixed typo in getting started form_for for comments --- railties/guides/source/getting_started.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile index fe43c9db36..3ff6603a4c 100644 --- a/railties/guides/source/getting_started.textile +++ b/railties/guides/source/getting_started.textile @@ -1297,7 +1297,7 @@ So first, we'll wire up the Post show template

Add a comment:

-<%= form_for([@post, @post.comments.build]) do |f| %> +<%= form_for([@post, @post.comment.build]) do |f| %>
<%= f.label :commenter %>
<%= f.text_field :commenter %> -- cgit v1.2.3 From e7dff9c1f1e3e8a73c1b5bc5fd4263126813489c Mon Sep 17 00:00:00 2001 From: Arun Agrawal Date: Mon, 28 Nov 2011 14:10:23 +0530 Subject: Revert "fixed typo in getting started form_for for comments" This reverts commit 2f428a6245a18017c7942f8fef3da4a85bec8cc5. See comments here 2f428a6245a18017c7942f8fef3da4a85bec8cc5 --- railties/guides/source/getting_started.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/getting_started.textile b/railties/guides/source/getting_started.textile index 3ff6603a4c..fe43c9db36 100644 --- a/railties/guides/source/getting_started.textile +++ b/railties/guides/source/getting_started.textile @@ -1297,7 +1297,7 @@ So first, we'll wire up the Post show template

Add a comment:

-<%= form_for([@post, @post.comment.build]) do |f| %> +<%= form_for([@post, @post.comments.build]) do |f| %>
<%= f.label :commenter %>
<%= f.text_field :commenter %> -- cgit v1.2.3 From 6d05c793cafe79860bcbb469d6c46c83c531ab34 Mon Sep 17 00:00:00 2001 From: Rodrigo Rosenfeld Rosas Date: Mon, 28 Nov 2011 13:15:07 -0200 Subject: Update information about foreign key plugins support in the guides There is not "a number" of up-to-date maintained plugins for dealing with foreign keys. The foreign_key_migrations does not have any update since 2009 and is also about to be removed: Extracted from http://www.harukizaemon.com/2009/09/plugins-grab-em-while-theyre-stale.html, referenced in the plugin reference of the old guide version: "And so it is that I will very shortly (within the next month) delete most of the plugins from my GitHub account (harukizaemon)" --- railties/guides/source/migrations.textile | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/railties/guides/source/migrations.textile b/railties/guides/source/migrations.textile index 5b52a93853..c63f2aa119 100644 --- a/railties/guides/source/migrations.textile +++ b/railties/guides/source/migrations.textile @@ -670,4 +670,4 @@ The Active Record way claims that intelligence belongs in your models, not in th Validations such as +validates :foreign_key, :uniqueness => true+ are one way in which models can enforce data integrity. The +:dependent+ option on associations allows models to automatically destroy child objects when the parent is destroyed. Like anything which operates at the application level, these cannot guarantee referential integrity and so some people augment them with foreign key constraints. -Although Active Record does not provide any tools for working directly with such features, the +execute+ method can be used to execute arbitrary SQL. There are also a number of plugins such as "foreign_key_migrations":https://github.com/harukizaemon/redhillonrails/tree/master/foreign_key_migrations/ which add foreign key support to Active Record (including support for dumping foreign keys in +db/schema.rb+). +Although Active Record does not provide any tools for working directly with such features, the +execute+ method can be used to execute arbitrary SQL. You could also use some plugin like "foreigner":https://github.com/matthuhiggins/foreigner which add foreign key support to Active Record (including support for dumping foreign keys in +db/schema.rb+). -- cgit v1.2.3