Francis's Octopress Blog

A blogging framework for hackers.

通过mongodump和mongorestore实现Mongodb备份和恢复 –by Chenzhou123520

通过mongodump和mongorestore实现Mongodb备份和恢复 —by chenzhou123520

from http://chenzhou123520.iteye.com/blog/1630993

Mongodb自带了mongodump和mongorestore这两个工具来实现对数据的备份和恢复。

mongodump能够在Mongodb运行时进行备份,它的工作原理是对运行的Mongodb做查询,然后将所有查到的文档写入磁盘。但是存在的问题时使用mongodump产生的备份不一定是数据库的实时快照,如果我们在备份时对数据库进行了写入操作,则备份出来的文件可能不完全和Mongodb实时数据相等。另外在备份时可能会对其它客户端性能产生不利的影响。

mongodump用法如下:

[root@localhost mongodb]# ./bin/mongodump --help
Export MongoDB data to BSON files.

options:
  --help                   produce help message
  -v [ --verbose ]         be more verbose (include multiple times for more
                           verbosity e.g. -vvvvv)
  --version                print the program's version and exit
  -h [ --host ] arg        mongo host to connect to ( <set name>/s1,s2 for
                           sets)
  --port arg               server port. Can also use --host hostname:port
  --ipv6                   enable IPv6 support (disabled by default)
  -u [ --username ] arg    username
  -p [ --password ] arg    password
  --dbpath arg             directly access mongod database files in the given
                           path, instead of connecting to a mongod  server -
                           needs to lock the data directory, so cannot be used
                           if a mongod is currently accessing the same path
  --directoryperdb         if dbpath specified, each db is in a separate
                           directory
  --journal                enable journaling
  -d [ --db ] arg          database to use
  -c [ --collection ] arg  collection to use (some commands)
  -o [ --out ] arg (=dump) output directory or "-" for stdout
  -q [ --query ] arg       json query
  --oplog                  Use oplog for point-in-time snapshotting
  --repair                 try to recover a crashed database
  --forceTableScan         force a table scan (do not use $snapshot)

参数说明:

-h:指明数据库宿主机的IP

-u:指明数据库的用户名

-p:指明数据库的密码

-d:指明数据库的名字

-c:指明collection的名字

-o:指明到要导出的文件名

-q:指明导出数据的过滤条件

具体使用示例如下:

[root@localhost mongodb]# ./bin/mongodump -d test -o data/backup
connected to: 127.0.0.1
DATABASE: test   to     data/backup/test
    test.system.indexes to data/backup/test/system.indexes.bson
         9 objects
    test.users to data/backup/test/users.bson
         3 objects
    test.games to data/backup/test/games.bson
         1 objects
    test.blog.post to data/backup/test/blog.post.bson
         1 objects
    test.lists to data/backup/test/lists.bson
         1 objects
    test.math to data/backup/test/math.bson
         1 objects
    test.map to data/backup/test/map.bson
         8 objects
    test.my_collection to data/backup/test/my_collection.bson
         0 objects
    test.foo to data/backup/test/foo.bson
         6 objects
    test.system.users to data/backup/test/system.users.bson
         1 objects

mongorestore是Mongodb从备份中恢复数据的工具,它主要用来获取mongodump的输出结果,并将备份的数据插入到运行的Mongodb中。

mongorestore命令使用方法如下:

[root@localhost mongodb]# ./bin/mongorestore --help
usage: ./bin/mongorestore [options] [directory or filename to restore from]
options:
  --help                  produce help message
  -v [ --verbose ]        be more verbose (include multiple times for more
                          verbosity e.g. -vvvvv)
  --version               print the program's version and exit
  -h [ --host ] arg       mongo host to connect to ( <set name>/s1,s2 for sets)
  --port arg              server port. Can also use --host hostname:port
  --ipv6                  enable IPv6 support (disabled by default)
  -u [ --username ] arg   username
  -p [ --password ] arg   password
  --dbpath arg            directly access mongod database files in the given
                          path, instead of connecting to a mongod  server -
                          needs to lock the data directory, so cannot be used
                          if a mongod is currently accessing the same path
  --directoryperdb        if dbpath specified, each db is in a separate
                          directory
  --journal               enable journaling
  -d [ --db ] arg         database to use
  -c [ --collection ] arg collection to use (some commands)
  --objcheck              validate object before inserting
  --filter arg            filter to apply before inserting
  --drop                  drop each collection before import
  --oplogReplay           replay oplog for point-in-time restore
  --keepIndexVersion      don't upgrade indexes to newest version

参数说明:

-h:指明数据库宿主机的IP

-u:指明数据库的用户名

-p:指明数据库的密码

-d:指明数据库的名字

-c:指明collection的名字

-o:指明到要备份的文件名

-q:指明备份数据的过滤条件

 

具体使用示例如下:

[root@localhost mongodb]# ./bin/mongorestore -d test --drop data/backup/test/
connected to: 127.0.0.1
Tue Aug 14 01:18:17 data/backup/test/games.bson
Tue Aug 14 01:18:17      going into namespace [test.games]
Tue Aug 14 01:18:17      dropping
1 objects found
Tue Aug 14 01:18:17 data/backup/test/foo.bson
Tue Aug 14 01:18:17      going into namespace [test.foo]
Tue Aug 14 01:18:17      dropping
6 objects found
Tue Aug 14 01:18:17 data/backup/test/blog.post.bson
Tue Aug 14 01:18:17      going into namespace [test.blog.post]
Tue Aug 14 01:18:17      dropping
1 objects found
Tue Aug 14 01:18:17 data/backup/test/lists.bson
Tue Aug 14 01:18:17      going into namespace [test.lists]
Tue Aug 14 01:18:17      dropping
1 objects found
Tue Aug 14 01:18:17 data/backup/test/map.bson
Tue Aug 14 01:18:17      going into namespace [test.map]
Tue Aug 14 01:18:17      dropping
8 objects found
Tue Aug 14 01:18:17 data/backup/test/math.bson
Tue Aug 14 01:18:17      going into namespace [test.math]
Tue Aug 14 01:18:17      dropping
1 objects found
Tue Aug 14 01:18:17 data/backup/test/system.users.bson
Tue Aug 14 01:18:17      going into namespace [test.system.users]
1 objects found
Tue Aug 14 01:18:17 data/backup/test/my_collection.bson
Tue Aug 14 01:18:17      going into namespace [test.my_collection]
Tue Aug 14 01:18:17      dropping
Tue Aug 14 01:18:17 file data/backup/test/my_collection.bson empty, skipping
Tue Aug 14 01:18:17 data/backup/test/users.bson
Tue Aug 14 01:18:17      going into namespace [test.users]
Tue Aug 14 01:18:17      dropping
3 objects found
Tue Aug 14 01:18:17 data/backup/test/system.indexes.bson
Tue Aug 14 01:18:17      going into namespace [test.system.indexes]
Tue Aug 14 01:18:17      dropping
Tue Aug 14 01:18:17 { key: { _id: 1 }, ns: "test.users", name: "_id_" }
Tue Aug 14 01:18:17 { key: { _id: 1 }, ns: "test.games", name: "_id_" }
Tue Aug 14 01:18:17 { key: { _id: 1 }, ns: "test.blog.post", name: "_id_" }
Tue Aug 14 01:18:17 { key: { _id: 1 }, ns: "test.lists", name: "_id_" }
Tue Aug 14 01:18:17 { key: { _id: 1 }, ns: "test.math", name: "_id_" }
Tue Aug 14 01:18:17 { key: { _id: 1 }, ns: "test.map", name: "_id_" }
Tue Aug 14 01:18:17 { key: { gps: "2d" }, ns: "test.map", name: "gps_", min: -180.0, max: 181.0 }
Tue Aug 14 01:18:17 { key: { _id: 1 }, ns: "test.foo", name: "_id_" }
Tue Aug 14 01:18:17 { key: { _id: 1 }, ns: "test.system.users", name: "_id_" }
9 objects found

 

Ruby on Rails 4.0 Release Notes

Ruby on Rails 4.0 Release Notes

sources:

https://github.com/rails/rails/blob/master/guides/source/4_0_release_notes.md

http://edgeguides.rubyonrails.org/4_0_release_notes.html

Ruby on Rails 4.0 Release Notes

Highlights in Rails 4.0: (WIP)

  • Ruby 1.9.3 only
  • Strong Parameters
  • Queue API
  • Caching Improvements

These release notes cover the major changes, but do not include each bug-fix and changes. If you want to see everything, check out the list of commits in the main Rails repository on GitHub.


Upgrading to Rails 4.0

TODO. This is a WIP guide.

If you’re upgrading an existing application, it’s a great idea to have good test coverage before going in. You should also first upgrade to Rails 3.2 in case you haven’t and make sure your application still runs as expected before attempting an update to Rails 4.0. Then take heed of the following changes:

Rails 4.0 requires at least Ruby 1.9.3

Rails 4.0 requires Ruby 1.9.3 or higher. Support for all of the previous Ruby versions has been dropped officially and you should upgrade as early as possible.

What to update in your apps

  • Update your Gemfile to depend on
    • rails = 4.0.0
    • sass-rails ~> 3.2.3
    • coffee-rails ~> 3.2.1
    • uglifier >= 1.0.3

TODO: Update the versions above.

  • Rails 4.0 removes vendor/plugins completely. You have to replace these plugins by extracting them as gems and adding them in your Gemfile. If you choose not to make them gems, you can move them into, say, lib/my_plugin/* and add an appropriate initializer in config/initializers/my_plugin.rb.

TODO: Configuration changes in environment files

Creating a Rails 4.0 application

<code> You should have the 'rails' rubygem installed $ rails new myapp $ cd myapp </code>

Vendoring Gems

Rails now uses a Gemfile in the application root to determine the gems you require for your application to start. This Gemfile is processed by the Bundler gem, which then installs all your dependencies. It can even install all the dependencies locally to your application so that it doesn’t depend on the system gems.

More information: Bundler homepage

Living on the Edge

Bundler and Gemfile makes freezing your Rails application easy as pie with the new dedicated bundle command. If you want to bundle straight from the Git repository, you can pass the —edge flag:

<code>$ rails new myapp --edge </code>

If you have a local checkout of the Rails repository and want to generate an application using that, you can pass the —dev flag:

<code>$ ruby /path/to/rails/railties/bin/rails new myapp --dev </code>

Major Features

Documentation

  • Guides are rewritten in GitHub Flavored Markdown.

Railties

  • Allow scaffold/model/migration generators to accept a polymorphic modifier for references/belongs_to, for instance
    <code>rails g model Product supplier:references{polymorphic} </code>
    will generate the model with belongs_to :supplier, polymorphic: true association and appropriate migration.
  • Set config.active_record.migration_error to :page_load for development.
  • Add runner to Rails::Railtie as a hook called just after runner starts.
  • Add /rails/info/routes path which displays the same information as rake routes.
  • Improved rake routes output for redirects.
  • Load all environments available in config.paths["config/environments"].
  • Add config.queue_consumer to allow the default consumer to be configurable.
  • Add Rails.queue as an interface with a default implementation that consumes jobs in a separate thread.
  • Remove Rack::SSL in favour of ActionDispatch::SSL.
  • Allow to set class that will be used to run as a console, other than IRB, with Rails.application.config.console=. It’s best to add it to console block.
    # it can be added to config/application.rb
    console do
      # this block is called only when running console,
      # so we can safely require pry here
      require "pry"
      config.console = Pry
    end
  • Add a convenience method hide! to Rails generators to hide the current generator namespace from showing when runningrails generate.
  • Scaffold now uses content_tag_for in index.html.erb.
  • Rails::Plugin is removed. Instead of adding plugins to vendor/plugins, use gems or bundler with path or git dependencies.

Deprecations

Action Mailer

  • Allow to set default Action Mailer options via config.action_mailer.default_options=.
  • Raise an ActionView::MissingTemplate exception when no implicit template could be found.
  • Asynchronously send messages via the Rails Queue.
  • Delivery Options (such as SMTP Settings) can now be set dynamically per mailer action.Delivery options are set via :delivery_method_options key on mail.
    def welcome_mailer(user,company)
      delivery_options = { user_name: company.smtp_user, password: company.smtp_password, address: company.smtp_host }
      mail(to: user.email, subject: "Welcome!", delivery_method_options: delivery_options)
    end

Action Pack

Action Controller

  • Add ActionController::Flash.add_flash_types method to allow people to register their own flash types. e.g.:
    class ApplicationController
      add_flash_types :error, :warning
    end
    If you add the above code, you can use <%= error %> in an erb, and redirect_to /foo, :error => 'message' in a controller.
  • Remove Active Model dependency from Action Pack.
  • Support unicode characters in routes. Route will be automatically escaped, so instead of manually escaping:
    get Rack::Utils.escape('こんにちは') => 'home#index'
    You just have to write the unicode route:
    get 'こんにちは' => 'home#index'
  • Return proper format on exceptions.
  • Extracted redirect logic from ActionController::ForceSSL::ClassMethods.force_ssl intoActionController::ForceSSL#force_ssl_redirect.
  • URL path parameters with invalid encoding now raise ActionController::BadRequest.
  • Malformed query and request parameter hashes now raise ActionController::BadRequest.
  • respond_to and respond_with now raise ActionController::UnknownFormat instead of directly returning head 406. The exception is rescued and converted to 406 in the exception handling middleware.
  • JSONP now uses application/javascript instead of application/json as the MIME type.
  • Session arguments passed to process calls in functional tests are now merged into the existing session, whereas previously they would replace the existing session. This change may break some existing tests if they are asserting the exact contents of the session but should not break existing tests that only assert individual keys.
  • Forms of persisted records use always PATCH (via the _method hack).
  • For resources, both PATCH and PUT are routed to the update action.
  • Don’t ignore force_ssl in development. This is a change of behavior - use an :if condition to recreate the old behavior.
    class AccountsController < ApplicationController
      force_ssl :if => :ssl_configured?
    
      def ssl_configured?
        !Rails.env.development?
      end
    end

Deprecations

  • Deprecated ActionController::Integration in favour of ActionDispatch::Integration.
  • Deprecated ActionController::IntegrationTest in favour of ActionDispatch::IntegrationTest.
  • Deprecated ActionController::PerformanceTest in favour of ActionDispatch::PerformanceTest.
  • Deprecated ActionController::AbstractRequest in favour of ActionDispatch::Request.
  • Deprecated ActionController::Request in favour of ActionDispatch::Request.
  • Deprecated ActionController::AbstractResponse in favour of ActionDispatch::Response.
  • Deprecated ActionController::Response in favour of ActionDispatch::Response.
  • Deprecated ActionController::Routing in favour of ActionDispatch::Routing.

Action Dispatch

  • Add Routing Concerns to declare common routes that can be reused inside others resources and routes.Code before:
    resources :messages do
      resources :comments
    end
    
    resources :posts do
      resources :comments
      resources :images, only: :index
    end
    Code after:
    concern :commentable do
      resources :comments
    end
    
    concern :image_attachable do
      resources :images, only: :index
    end
    
    resources :messages, concerns: :commentable
    
    resources :posts, concerns: [:commentable, :image_attachable]
  • Show routes in exception page while debugging a RoutingError in development.
  • Include mounted_helpers (helpers for accessing mounted engines) in ActionDispatch::IntegrationTest by default.
  • Added ActionDispatch::SSL middleware that when included force all the requests to be under HTTPS protocol.
  • Copy literal route constraints to defaults so that url generation know about them. The copied constraints are :protocol,:subdomain:domain:host and :port.
  • Allows assert_redirected_to to match against a regular expression.
  • Adds a backtrace to the routing error page in development.
  • assert_generatesassert_recognizes, and assert_routing all raise Assertion instead of RoutingError.
  • Allows the route helper root to take a string argument. For example, root 'pages#main' as a shortcut forroot to: 'pages#main'.
  • Adds support for the PATCH verb: Request objects respond to patch?. Routes now have a new patch method, and understand :patch in the existing places where a verb is configured, like :via. Functional tests have a new method patchand integration tests have a new method patch_via_redirect. If :patch is the default verb for updates, edits are tunneled asPATCH rather than as PUT and routing acts accordingly.
  • Integration tests support the OPTIONS method.
  • expires_in accepts a must_revalidate flag. If true, “must-revalidate” is added to the Cache-Control header.
  • Default responder will now always use your overridden block in respond_with to render your response.
  • Turn off verbose mode of rack-cache, we still have X-Rack-Cache to check that info.

Deprecations

Action View

  • Remove Active Model dependency from Action Pack.
  • Allow to use mounted_helpers (helpers for accessing mounted engines) in ActionView::TestCase.
  • Make current object and counter (when it applies) variables accessible when rendering templates with :object or:collection.
  • Allow to lazy load default_form_builder by passing a string instead of a constant.
  • Add index method to FormBuilder class.
  • Adds support for layouts when rendering a partial with a given collection.
  • Remove :disable_with in favor of data-disable-with option from submit_tagbutton_tag and button_to helpers.
  • Remove :mouseover option from image_tag helper.
  • Templates without a handler extension now raises a deprecation warning but still defaults to ERb. In future releases, it will simply return the template content.
  • Add a divider option to grouped_options_for_select to generate a separator optgroup automatically, and deprecate prompt as third argument, in favor of using an options hash.
  • Add time_field and time_field_tag helpers which render an input[type="time"] tag.
  • Removed old text_helper apis for highlightexcerpt and word_wrap.
  • Remove the leading \n added by textarea on assert_select.
  • Changed default value for config.action_view.embed_authenticity_token_in_remote_forms to false. This change breaks remote forms that need to work also without JavaScript, so if you need such behavior, you can either set it to true or explicitly pass :authenticity_token => true in form options.
  • Make possible to use a block in button_to helper if button text is hard to fit into the name parameter:
    <%= button_to [:make_happy, @user] do %>
      Make happy <strong><%= @user.name %></strong>
    <% end %>
    # => "<form method="post" action="/users/1/make_happy"n" style="margin: 0px; padding: 0px; border: 0px; ">button_to">
    #      <div>
    #        <button type="submit">
    #          Make happy <strong>Name</strong>
    #        </button>
    #      </div>
    #    </form>"
  • Replace include_seconds boolean argument with :include_seconds => true option in distance_of_time_in_words andtime_ago_in_words signature.
  • Remove button_to_function and link_to_function helpers.
  • truncate now always returns an escaped HTML-safe string. The option :escape can be used as false to not escape the result.
  • truncate now accepts a block to show extra content when the text is truncated.
  • Add week_fieldweek_field_tagmonth_fieldmonth_field_tagdatetime_local_field,datetime_local_field_tagdatetime_field and datetime_field_tag helpers.
  • Add color_field and color_field_tag helpers.
  • Add include_hidden option to select tag. With :include_hidden => false select with multiple attribute doesn’t generate hidden input with blank value.
  • Removed default size option from the text_fieldsearch_fieldtelephone_fieldurl_fieldemail_field helpers.
  • Removed default cols and rows options from the text_area helper.
  • Adds image_urljavascript_urlstylesheet_urlaudio_urlvideo_url, and font_url to assets tag helper. These URL helpers will return the full path to your assets. This is useful when you are going to reference this asset from external host.
  • Allow value_method and text_method arguments from collection_select and options_from_collection_for_select to receive an object that responds to :call such as a proc, to evaluate the option in the current element context. This works the same way with collection_radio_buttons and collection_check_boxes.
  • Add date_field and date_field_tag helpers which render an input[type="date"] tag.
  • Add collection_check_boxes form helper, similar to collection_select:
    collection_check_boxes :post, :author_ids, Author.all, :id, :name
    # Outputs something like:
    <input id="post_author_ids_1" name="post[author_ids][]" type="checkbox" value="1" />
    <label for="post_author_ids_1">D. Heinemeier Hansson</label>
    <input id="post_author_ids_2" name="post[author_ids][]" type="checkbox" value="2" />
    <label for="post_author_ids_2">D. Thomas</label>
    <input name="post[author_ids][]" type="hidden" value="" />
    The label/check_box pairs can be customized with a block.
  • Add collection_radio_buttons form helper, similar to collection_select:
    collection_radio_buttons :post, :author_id, Author.all, :id, :name
    # Outputs something like:
    <input id="post_author_id_1" name="post[author_id]" type="radio" value="1" />
    <label for="post_author_id_1">D. Heinemeier Hansson</label>
    <input id="post_author_id_2" name="post[author_id]" type="radio" value="2" />
    <label for="post_author_id_2">D. Thomas</label>
    The label/radio_button pairs can be customized with a block.
  • check_box with an HTML5 attribute :form will now replicate the :form attribute to the hidden field as well.
  • label form helper accepts :for => nil to not generate the attribute.
  • Add :format option to number_to_percentage.
  • Add config.action_view.logger to configure logger for Action View.
  • check_box helper with :disabled => true will generate a disabled hidden field to conform with the HTML convention where disabled fields are not submitted with the form. This is a behavior change, previously the hidden tag had a value of the disabled checkbox.
  • favicon_link_tag helper will now use the favicon in app/assets by default.
  • ActionView::Helpers::TextHelper#highlight now defaults to the HTML5 mark element.

Deprecations

Sprockets

Moved into a separate gem sprockets-rails.

Active Record

  • Add add_reference and remove_reference schema statements. Aliases, add_belongs_to and remove_belongs_to are acceptable. References are reversible.
    # Create a user_id column
    add_reference(:products, :user)
    
    # Create a supplier_id, supplier_type columns and appropriate index
    add_reference(:products, :supplier, polymorphic: true, index: true)
    
    # Remove polymorphic reference
    remove_reference(:products, :supplier, polymorphic: true)
  • Add :default and :null options to column_exists?.
    column_exists?(:testings, :taggable_id, :integer, null: false)
    column_exists?(:testings, :taggable_type, :string, default: 'Photo')
  • ActiveRecord::Relation#inspect now makes it clear that you are dealing with a Relation object rather than an array:
    User.where(:age => 30).inspect
    # => <ActiveRecord::Relation [#<User ...>, #<User ...>]>
    
    User.where(:age => 30).to_a.inspect
    # => [#<User ...>, #<User ...>]
    if more than 10 items are returned by the relation, inspect will only show the first 10 followed by ellipsis.
  • Add :collation and :ctype support to PostgreSQL. These are available for PostgreSQL 8.4 or later.
    development:
      adapter: postgresql
      host: localhost
      database: rails_development
      username: foo
      password: bar
      encoding: UTF8
      collation: ja_JP.UTF8
      ctype: ja_JP.UTF8
  • FinderMethods#exists? now returns false with the false argument.
  • Added support for specifying the precision of a timestamp in the postgresql adapter. So, instead of having to incorrectly specify the precision using the :limit option, you may use :precision, as intended. For example, in a migration:
    def change
      create_table :foobars do |t|
        t.timestamps :precision => 0
      end
    end
  • Allow ActiveRecord::Relation#pluck to accept multiple columns. Returns an array of arrays containing the typecasted values:
    Person.pluck(:id, :name)
    # SELECT people.id, people.name FROM people
    # => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]
  • Improve the derivation of HABTM join table name to take account of nesting. It now takes the table names of the two models, sorts them lexically and then joins them, stripping any common prefix from the second table name. Some examples:
    <code>Top level models (Category &lt;=&gt; Product) Old: categories_products New: categories_products Top level models with a global table_name_prefix (Category &lt;=&gt; Product) Old: site_categories_products New: site_categories_products Nested models in a module without a table_name_prefix method (Admin::Category &lt;=&gt; Admin::Product) Old: categories_products New: categories_products Nested models in a module with a table_name_prefix method (Admin::Category &lt;=&gt; Admin::Product) Old: categories_products New: admin_categories_products Nested models in a parent model (Catalog::Category &lt;=&gt; Catalog::Product) Old: categories_products New: catalog_categories_products Nested models in different parent models (Catalog::Category &lt;=&gt; Content::Page) Old: categories_pages New: catalog_categories_content_pages </code>
  • Move HABTM validity checks to ActiveRecord::Reflection. One side effect of this is to move when the exceptions are raised from the point of declaration to when the association is built. This is consistant with other association validity checks.
  • Added stored_attributes hash which contains the attributes stored using ActiveRecord::Store. This allows you to retrieve the list of attributes you’ve defined.
    class User < ActiveRecord::Base
      store :settings, accessors: [:color, :homepage]
    end
    
    User.stored_attributes[:settings] # [:color, :homepage]
  • PostgreSQL default log level is now ‘warning’, to bypass the noisy notice messages. You can change the log level using themin_messages option available in your config/database.yml.
  • Add uuid datatype support to PostgreSQL adapter.
  • Added ActiveRecord::Migration.check_pending! that raises an error if migrations are pending.
  • Added #destroy! which acts like #destroy but will raise an ActiveRecord::RecordNotDestroyed exception instead of returning false.
  • Allow blocks for count with ActiveRecord::Relation, to work similar as Array#count:Person.where("age > 26").count { |person| person.gender == 'female' }
  • Added support to CollectionAssociation#delete for passing fixnum or string values as record ids. This finds the records responding to the ids and deletes them.
    class Person < ActiveRecord::Base
      has_many :pets
    end
    
    person.pets.delete("1")  # => [#<Pet id: 1>]
    person.pets.delete(2, 3) # => [#<Pet id: 2>, #<Pet id: 3>]
  • It’s not possible anymore to destroy a model marked as read only.
  • Added ability to ActiveRecord::Relation#from to accept other ActiveRecord::Relation objects.
  • Added custom coders support for ActiveRecord::Store. Now you can set your custom coder like this:
    store :settings, accessors: [ :color, :homepage ], coder: JSON
  • mysql and mysql2 connections will set SQL_MODE=STRICT_ALL_TABLES by default to avoid silent data loss. This can be disabled by specifying strict: false in config/database.yml.
  • Added default order to ActiveRecord::Base#first to assure consistent results among different database engines. IntroducedActiveRecord::Base#take as a replacement to the old behavior.
  • Added an :index option to automatically create indexes for references and belongs_to statements in migrations. This can be either a boolean or a hash that is identical to options available to the add_index method:
    create_table :messages do |t|
      t.references :person, :index => true
    end
    Is the same as:
    create_table :messages do |t|
      t.references :person
    end
    add_index :messages, :person_id
    Generators have also been updated to use the new syntax.
  • Added bang methods for mutating ActiveRecord::Relation objects. For example, while foo.where(:bar) will return a new object leaving foo unchanged, foo.where!(:bar) will mutate the foo object.
  • Added #find_by and #find_by! to mirror the functionality provided by dynamic finders in a way that allows dynamic input more easily:
    Post.find_by name: 'Spartacus', rating: 4
    Post.find_by "published_at < ?", 2.weeks.ago
    Post.find_by! name: 'Spartacus'
  • Added ActiveRecord::Base#slice to return a hash of the given methods with their names as keys and returned values as values.
  • Remove IdentityMap - IdentityMap has never graduated to be an “enabled-by-default” feature, due to some inconsistencies with associations, as described in this commit. Hence the removal from the codebase, until such issues are fixed.
  • Added a feature to dump/load internal state of SchemaCache instance because we want to boot more quickly when we have many models.
    # execute rake task.
    RAILS_ENV=production bundle exec rake db:schema:cache:dump
    => generate db/schema_cache.dump
    
    # add config.use_schema_cache_dump = true in config/production.rb. BTW, true is default.
    
    # boot rails.
    RAILS_ENV=production bundle exec rails server
    => use db/schema_cache.dump
    
    # If you remove clear dumped cache, execute rake task.
    RAILS_ENV=production bundle exec rake db:schema:cache:clear
    => remove db/schema_cache.dump
  • Added support for partial indices to PostgreSQL adapter.
  • The add_index method now supports a where option that receives a string with the partial index criteria.
  • Added the ActiveRecord::NullRelation class implementing the null object pattern for the Relation class.
  • Implemented ActiveRecord::Relation#none method which returns a chainable relation with zero records (an instance of theNullRelation class). Any subsequent condition chained to the returned relation will continue generating an empty relation and will not fire any query to the database.
  • Added create_join_table migration helper to create HABTM join tables.
    create_join_table :products, :categories
    # =>
    # create_table :categories_products, :id => false do |td|
    #   td.integer :product_id, :null => false
    #   td.integer :category_id, :null => false
    # end
  • The primary key is always initialized in the @attributes hash to nil (unless another value has been specified).
  • In previous releases, the following would generate a single query with an OUTER JOIN comments, rather than two separate queries:
    Post.includes(:comments).where("comments.name = 'foo'")
    This behaviour relies on matching SQL string, which is an inherently flawed idea unless we write an SQL parser, which we do not wish to do. Therefore, it is now deprecated. To avoid deprecation warnings and for future compatibility, you must explicitly state which tables you reference, when using SQL snippets:
    Post.includes(:comments).where("comments.name = 'foo'").references(:comments)
    Note that you do not need to explicitly specify references in the following cases, as they can be automatically inferred:
    Post.where(comments: { name: 'foo' })
    Post.where('comments.name' => 'foo')
    Post.order('comments.name')
    You also do not need to worry about this unless you are doing eager loading. Basically, don’t worry unless you see a deprecation warning or (in future releases) an SQL error due to a missing JOIN.
  • Support for the schema_info table has been dropped. Please switch to schema_migrations.
  • Connections must be closed at the end of a thread. If not, your connection pool can fill and an exception will be raised.
  • Added the ActiveRecord::Model module which can be included in a class as an alternative to inheriting fromActiveRecord::Base:
    class Post
      include ActiveRecord::Model
    end
  • PostgreSQL hstore records can be created.
  • PostgreSQL hstore types are automatically deserialized from the database.
  • Added #update_columns method which updates the attributes from the passed-in hash without calling save, hence skipping validations and callbacks. ActiveRecordError will be raised when called on new objects or when at least one of the attributes is marked as read only.
    post.attributes # => {"id"=>2, "title"=>"My title", "body"=>"My content", "author"=>"Peter"}
    post.update_columns({title: 'New title', author: 'Sebastian'}) # => true
    post.attributes # => {"id"=>2, "title"=>"New title", "body"=>"My content", "author"=>"Sebastian"}

Deprecations

  • Deprecated most of the ‘dynamic finder’ methods. All dynamic methods except for find_by_... and find_by_...! are deprecated. Here’s how you can rewrite the code:
    find_all_by_... can be rewritten using where(...)
    find_last_by_... can be rewritten using where(...).last
    scoped_by_... can be rewritten using where(...)
    find_or_initialize_by_... can be rewritten using where(...).first_or_initialize
    find_or_create_by_... can be rewritten using where(...).first_or_create
    find_or_create_by_...! can be rewritten using where(...).first_or_create!
    The implementation of the deprecated dynamic finders has been moved to the active_record_deprecated_finders gem.
  • Deprecated the old-style hash based finder API. This means that methods which previously accepted “finder options” no longer do. For example this:
    Post.find(:all, :conditions => { :comments_count => 10 }, :limit => 5)
    should be rewritten in the new style which has existed since Rails 3:
    Post.where(comments_count: 10).limit(5)
    Note that as an interim step, it is possible to rewrite the above as:
    Post.scoped(:where => { :comments_count => 10 }, :limit => 5)
    This could save you a lot of work if there is a lot of old-style finder usage in your application. Calling Post.scoped(options) is a shortcut for Post.scoped.merge(options)Relation#merge now accepts a hash of options, but they must be identical to the names of the equivalent finder method. These are mostly identical to the old-style finder option names, except in the following cases:
    <code>:conditions becomes :where :include becomes :includes :extend becomes :extending </code>
    The code to implement the deprecated features has been moved out to the active_record_deprecated_finders gem. This gem is a dependency of Active Record in Rails 4.0. It will no longer be a dependency from Rails 4.1, but if your app relies on the deprecated features then you can add it to your own Gemfile. It will be maintained by the Rails core team until Rails 5.0 is released.
  • Deprecate eager-evaluated scopes.Don’t use this:
    scope :red, where(color: 'red')
    default_scope where(color: 'red')
    Use this:
    scope :red, -> { where(color: 'red') }
    default_scope { where(color: 'red') }
    The former has numerous issues. It is a common newbie gotcha to do the following:
    scope :recent, where(published_at: Time.now - 2.weeks)
    Or a more subtle variant:
    scope :recent, -> { where(published_at: Time.now - 2.weeks) }
    scope :recent_red, recent.where(color: 'red')
    Eager scopes are also very complex to implement within Active Record, and there are still bugs. For example, the following does not do what you expect:
    scope :remove_conditions, except(:where)
    where(...).remove_conditions # => still has conditions
  • Added deprecation for the :dependent => :restrict association option.
  • Up until now has_many and has_one, :dependent => :restrict option raised a DeleteRestrictionError at the time of destroying the object. Instead, it will add an error on the model.
  • To fix this warning, make sure your code isn’t relying on a DeleteRestrictionError and then addconfig.active_record.dependent_restrict_raises = false to your application config.
  • New rails application would be generated with the config.active_record.dependent_restrict_raises = false in the application config.
  • The migration generator now creates a join table with (commented) indexes every time the migration name contains the word “join_table”.
  • ActiveRecord::SessionStore is removed from Rails 4.0 and is now a separate gem.

Active Model

  • Changed AM::Serializers::JSON.include_root_in_json default value to false. Now, AM Serializers and AR objects have the same default behaviour.
    class User < ActiveRecord::Base; end
    
    class Person
      include ActiveModel::Model
      include ActiveModel::AttributeMethods
      include ActiveModel::Serializers::JSON
    
      attr_accessor :name, :age
    
      def attributes
        instance_values
      end
    end
    
    user.as_json
    => {"id"=>1, "name"=>"Konata Izumi", "age"=>16, "awesome"=>true}
    # root is not included
    
    person.as_json
    => {"name"=>"Francesco", "age"=>22}
    # root is not included
  • Passing false hash values to validates will no longer enable the corresponding validators.
  • ConfirmationValidator error messages will attach to :#{attribute}_confirmation instead of attribute.
  • Added ActiveModel::Model, a mixin to make Ruby objects work with Action Pack out of the box.
  • ActiveModel::Errors#to_json supports a new parameter :full_messages.
  • Trims down the API by removing valid? and errors.full_messages.

Deprecations

Active Resource

  • Active Resource is removed from Rails 4.0 and is now a separate gem.

Active Support

  • Add default values to all ActiveSupport::NumberHelper methods, to avoid errors with empty locales or missing values.
  • Time#change now works with time values with offsets other than UTC or the local time zone.
  • Add Time#prev_quarter and Time#next_quarter short-hands for months_ago(3) and months_since(3).
  • Remove obsolete and unused require_association method from dependencies.
  • Add :instance_accessor option for config_accessor.
    class User
      include ActiveSupport::Configurable
      config_accessor :allowed_access, instance_accessor: false
    end
    
    User.new.allowed_access = true # => NoMethodError
    User.new.allowed_access        # => NoMethodError
  • ActionView::Helpers::NumberHelper methods have been moved to ActiveSupport::NumberHelper and are now available viaNumeric#to_s.
  • Numeric#to_s now accepts the formatting options :phone, :currency, :percentage, :delimited, :rounded, :human, and :human_size.
  • Add Hash#transform_keysHash#transform_keys!Hash#deep_transform_keys and Hash#deep_transform_keys!.
  • Changed xml type datetime to dateTime (with upper case letter T).
  • Add :instance_accessor option for class_attribute.
  • constantize now looks in the ancestor chain.
  • Add Hash#deep_stringify_keys and Hash#deep_stringify_keys! to convert all keys from a Hash instance into strings.
  • Add Hash#deep_symbolize_keys and Hash#deep_symbolize_keys! to convert all keys from a Hash instance into symbols.
  • Object#try can’t call private methods.
  • AS::Callbacks#run_callbacks remove key argument.
  • deep_dup works more expectedly now and duplicates also values in Hash instances and elements in Array instances.
  • Inflector no longer applies ice -> ouse to words like slice, police.
  • Add ActiveSupport::Deprecations.behavior = :silence to completely ignore Rails runtime deprecations.
  • Make Module#delegate stop using send - can no longer delegate to private methods.
  • AS::Callbacks deprecate :rescuable option.
  • Adds Integer#ordinal to get the ordinal suffix string of an integer.
  • AS::Callbacks :per_key option is no longer supported.
  • AS::Callbacks#define_callbacks add :skip_after_callbacks_if_terminated option.
  • Add html_escape_once to ERB::Util, and delegate escape_once tag helper to it.
  • Remove ActiveSupport::TestCase#pending method, use skip instead.
  • Deletes the compatibility method Module#method_names, use Module#methods from now on (which returns symbols).
  • Deletes the compatibility method Module#instance_method_names, use Module#instance_methods from now on (which returns symbols).
  • Unicode database updated to 6.1.0.
  • Adds encode_big_decimal_as_string option to force JSON serialization of BigDecimals as numeric instead of wrapping them in strings for safety.

Deprecations

  • ActiveSupport::Callbacks: deprecate usage of filter object with #before and #after methods as around callback.
  • BufferedLogger is deprecated. Use ActiveSupport::Logger or the logger from Ruby stdlib.
  • Deprecates the compatibility method Module#local_constant_names and use Module#local_constants instead (which returns symbols).

Credits

See the full list of contributors to Rails for the many people who spent many hours making Rails, the stable and robust framework it is. Kudos to all of them.

Rails Active Record Named Scopes

Rails Active Record Named Scopes

Active Record Named Scopes

Instance Public methods

with_scope(scope = {}, action = :merge, &block) with_scope lets you apply options to inner block incrementally. It takes a hash and the keys must be :find or :create:find parameter is Relation while :create parameters are an attributes hash.

class Article < ActiveRecord::Base
  def self.create_with_scope
    with_scope(:find => where(:blog_id => 1), :create => { :blog_id => 1 }) do
      find(1) # => SELECT * from articles WHERE blog_id = 1 AND id = 1
      a = create(1)
      a.blog_id # => 1
    end
  end
end

In nested scopings, all previous parameters are overwritten by the innermost rule, with the exception of where, includes, and joins operations in Relation, which are merged.

joins operations are uniqued so multiple scopes can join in the same table without table aliasing problems. If you need to join multiple tables, but still want one of the tables to be uniqued, use the array of strings format for your joins.

class Article < ActiveRecord::Base
  def self.find_with_scope
    with_scope(:find => where(:blog_id => 1).limit(1), :create => { :blog_id => 1 }) do
      with_scope(:find => limit(10)) do
        all # => SELECT * from articles WHERE blog_id = 1 LIMIT 10
      end
      with_scope(:find => where(:author_id => 3)) do
        all # => SELECT * from articles WHERE blog_id = 1 AND author_id = 3 LIMIT 1
      end
    end
  end
end

You can ignore any previous scopings by using the with_exclusive_scope method.

class Article < ActiveRecord::Base
  def self.find_with_exclusive_scope
    with_scope(:find => where(:blog_id => 1).limit(1)) do
      with_exclusive_scope(:find => limit(10)) do
        all # => SELECT * from articles LIMIT 10
      end
    end
  end
end

Note: the :find scope also has effect on update and deletion methods, like update_all and delete_all.

default_scope

default_scope(scope = {}) Use this macro in your model to set a default scope for all operations on the model.

class Article < ActiveRecord::Base
  default_scope where(:published => true)
end

Article.all # => SELECT * FROM articles WHERE published = true

The default_scope is also applied while creating/building a record. It is not applied while updating a record.

Article.new.published    # => true
Article.create.published # => true

You can also use default_scope with a block, in order to have it lazily evaluated:

class Article < ActiveRecord::Base
  default_scope { where(:published_at => Time.now - 1.week) }
end

(You can also pass any object which responds to call to the default_scope macro, and it will be called when building the default scope.)

If you use multiple default_scope declarations in your model then they will be merged together:

class Article < ActiveRecord::Base
  default_scope where(:published => true)
  default_scope where(:rating => 'G')
end

Article.all # => SELECT * FROM articles WHERE published = true AND rating = 'G'

This is also the case with inheritance and module includes where the parent or module defines a default_scope and the child or including class defines a second one.

If you need to do more complex things with a default scope, you can alternatively define it as a class method:

class Article < ActiveRecord::Base
  def self.default_scope
    # Should return a scope, you can call 'super' here etc.
  end
end

activerecord/lib/active_record/scoping/named.rb

scope(name, scope_options = {}) Adds a class method for retrieving and querying objects. A scope represents a narrowing of a database query, such as where(:color => :red).select(‘shirts.*’).includes(:washing_instructions).

class Shirt < ActiveRecord::Base
  scope :red, where(:color => 'red')
  scope :dry_clean_only, joins(:washing_instructions).where('washing_instructions.dry_clean_only = ?', true)
end

The above calls to scope define class methods Shirt.red and Shirt.dry_clean_onlyShirt.red, in effect, represents the query Shirt.where(:color => ‘red’).

Note that this is simply ‘syntactic sugar’ for defining an actual class method:

class Shirt < ActiveRecord::Base
  def self.red
    where(:color => 'red')
  end
end

Unlike Shirt.find(…), however, the object returned by Shirt.red is not an Array; it resembles the association object constructed by a has_many declaration. For instance, you can invoke Shirt.red.firstShirt.red.count,Shirt.red.where(:size => ‘small’). Also, just as with the association objects, named scopes act like an Array, implementing Enumerable; Shirt.red.each(&block)Shirt.red.first, and Shirt.red.inject(memo, &block) all behave as ifShirt.red really was an Array.

These named scopes are composable. For instance, Shirt.red.dry_clean_only will produce all shirts that are both red and dry clean only. Nested finds and calculations also work with these compositions: Shirt.red.dry_clean_only.count returns the number of garments for which these criteria obtain. Similarly with Shirt.red.dry_clean_only.average(:thread_count).

All scopes are available as class methods on the ActiveRecord::Base descendant upon which the scopes were defined. But they are also available to has_many associations. If,

class Person < ActiveRecord::Base
  has_many :shirts
end

then elton.shirts.red.dry_clean_only will return all of Elton’s red, dry clean only shirts.

Named scopes can also be procedural:

class Shirt < ActiveRecord::Base
  scope :colored, lambda { |color| where(:color => color) }
end

In this example, Shirt.colored(‘puce’) finds all puce shirts.

On Ruby 1.9 you can use the ‘stabby lambda’ syntax:

scope :colored, ->(color) { where(:color => color) }

Note that scopes defined with scope will be evaluated when they are defined, rather than when they are used. For example, the following would be incorrect:

class Post < ActiveRecord::Base
  scope :recent, where('published_at >= ?', Time.current - 1.week)
end

The example above would be ‘frozen’ to the Time.current value when the Post class was defined, and so the resultant SQL query would always be the same. The correct way to do this would be via a lambda, which will re-evaluate the scope each time it is called:

class Post < ActiveRecord::Base
  scope :recent, lambda { where('published_at >= ?', Time.current - 1.week) }
end

Named scopes can also have extensions, just as with has_many declarations:

class Shirt < ActiveRecord::Base
  scope :red, where(:color => 'red') do
    def dom_id
      'red_shirts'
    end
  end
end

Scopes can also be used while creating/building a record.

class Article < ActiveRecord::Base
  scope :published, where(:published => true)
end

Article.published.new.published    # => true
Article.published.create.published # => true

Class methods on your model are automatically available on scopes. Assuming the following setup:

class Article < ActiveRecord::Base
  scope :published, where(:published => true)
  scope :featured, where(:featured => true)

  def self.latest_article
    order('published_at desc').first
  end

  def self.titles
    map(&:title)
  end

end

We are able to call the methods like this:

Article.published.featured.latest_article
Article.featured.titles

scoped(options = nil) Returns an anonymous scope.

posts = Post.scoped
posts.size # Fires "select count(*) from  posts" and returns the count
posts.each {|p| puts p.name } # Fires "select * from posts" and loads post objects

fruits = Fruit.scoped
fruits = fruits.where(:color => 'red') if options[:red_only]
fruits = fruits.limit(10) if limited?

Anonymous scopes tend to be useful when procedurally generating complex queries, where passing intermediate values (scopes) around as first-class objects is convenient.

You can define a scope that applies to all finders using ActiveRecord::Base.default_scope.

晒晒我们的开源项目 ITEYE开源代码

晒晒我们的开源项目 ITEYE开源代码

我们的研发团队是一支mini型研发团队,目前共有研发人员13人。由于网站产品维护的历史原因,这13人的研发团队分为4支小组,分别是:Ruby研发小组5人;PHP研发小组4人;.net研发小组2人,Java搜索小组2人。

别看我们研发人员这么少,但是我们研发人员战斗力很强,我们维护和开发着十多条产品线。在我们开发自身产品的过程中,也积累了一些比较通用的组件,一些对大家来说有用的工具。因此从今年下半年开始,我们陆续将一些组件和工具开源出来,哪怕这些项目的质量并不是那么高,也希望能够给社区带来一点点自己的贡献。

以下简要介绍一下我们已经开源的项目,今后我们还会努力开源更多的东西和大家交流和分享:

1、Ansj中文分词 – 开源的高准确率Java中文分词器

项目Github地址:https://github.com/ansjsun/ansj_seg 这是基于大名鼎鼎中科院的Ictclas中文分词算法编写的Java实现版本,比常用的开源mmseg4j的分词准确率高。目前我们自己站内的搜索将逐渐从mmseg4j算法替换成Ansj中文分词算法。

2、ServiceFramework – 开源的羽量级Java Web服务框架

项目Github地址:https://github.com/allwefantasy/ServiceFramework 我们基于Java的开源搜索框架lucene编写了网站的分布式搜索和Tag文章分类服务。因为需要向前端的Web应用程序提供搜索和Tag服务接口API,所以我们编写了这个羽量级的Java框架软件。它的优点就是羽量级,自身集成了Jetty服务器,MVC,IoC和ORM,让你只需要编写非常少的代码,就可以快速将你的业务逻辑组件以Web API的方式提供服务。

3、ExportBlog – 开源的通用博客导出工具

项目Github地址:https://github.com/sqzhuyi/ExportBlog 这是一个基于.net Winforms编写的通用博客导出工具,支持导出网站包括:CSDN、ITEYE、博客园、新浪、搜狐、和讯、ChinaUnix、网易、51CTO、开源中国、百度空间、QQ空间等等。导出格式支持CHM、PDF、HTML、TXT和EPUB 5种格式文档。详细介绍:http://blog.csdn.net/sq_zhuyi/article/details/7924776

4、Secode_level_cache – 开源的Rails对象缓存插件

项目Github地址:https://github.com/csdn-dev/second_level_cache Rails的ActiveRecord自身没有带强大的对象缓存功能,这是AR的一个重大的遗憾。早在2008年开始,我们就借鉴了Java强大的ORM框架Hibernate的二级对象缓存编写了这个Rails的AR对象缓存插件,并且一直作为JavaEye网站缓存优化的秘密武器来使用,取得了非常理想的效果。 现在我们将这个插件从Rails2.x的版本升级到了3.x版本,并且抽取成了一个通用插件,开始应用于新的Rails3.2的项目之上。有志于AR对象缓存优化的ruby程序员不容错过。

5、limiter - 网站反爬虫和DOS攻击的利器

项目Github地址:https://github.com/csdn-dev/limiter 早年的JavaEye网站曾经深受DOS攻击和爬虫海量抓取造成的负载过高的困扰,我曾经和这个问题进行了为期几年不懈的斗争,并且在总结几年斗争经验后写了一篇总结性博客文章:互联网网站的反爬虫策略浅析 。当时我基于这个反爬虫策略编写了JavaEye网站的智能防火墙插件,取得了良好的效果。 现在我们将这个插件从JavaEye的源代码中剥离出来,抽取成一个通用的rackware,便于应用于普通的Rails3.x的项目当中。

Single Sign-on 单点登录 Sso

Single sign-on 单点登录 sso

from wiki

Single sign-on (SSO) is a property of access control of multiple related, but independent software systems. With this property a user logs in once and gains access to all systems without being prompted to log in again at each of them. Conversely, Single sign-off is the property whereby a single action of signing out terminates access to multiple software systems.

As different applications and resources support different authentication mechanisms, single sign-on has to internally translate to and store different credentials compared to what is used for initial authentication.

from baike

单点登录(Single Sign On),简称为 SSO,是目前比较流行的企业业务整合的解决方案之一。SSO的定义是在多个应用系统中,用户只需要登录一次就可以访问所有相互信任的应用系统。

企业应用集成(EAI, Enterprise Application Integration)。企业应用集成可以在不同层面上进行:例如在数据存储层面上的“数据大集中”,在传输层面上的“通用数据交换平台”,在应用层面上的“业务流程整合”,和用户界面上的“通用企业门户”等等。事实上,还有一个层面上的集成变得越来越重要,那就是“身份认证”的整合,也就是“单点登录”。

现在普遍使用 oauth 来实现 多个系统的授权 认证

https://github.com/songkick/oauth2-provider

Sharing a Devise User Session Across Subdomains With Rails 3

Sharing A Devise User Session Across Subdomains With Rails 3

to andersen
  仔细测了一下 Askjane::Application.config.session_store :active_record_store, key: '_askjane_session', :domain => ".bbtang.com"在 server上
  是ok的(效果上也是能共享bbtang.com 和www.bbtang.com的会话的只要端口一致)就是不知道在本地如何因为本地一般不会设置host绑定域名(经测试答案是不能的),如何(所以需要:all这个设置项)
to 客服 和 jojo
 提一点 你们一些 首页源码的时候 不要把 域名加上去  注意 尼玛 端口不一致 也是不能share 会话的
 简而言之 你们写一些html静态源码的时候除非不在 bbtang.com的 项目上 没有必要加 域名的 这会加大 大家的成本(如果说端口不一致就会导致会话丢失,如果没有做多域名兼容也会导致会话丢失能免则免)
over



Francis.J(864248765) 13:57:28
https://github.com/rails/rails/issues/2483
Francis.J(864248765) 13:57:55
尼玛 rails 大爷又 踩雷了
Francis.J(864248765) 13:59:42
https://github.com/rails/rails/issues/2483
Francis.J(864248765) 14:02:27
https://github.com/rails/rails/pull/7316
Francis.J(864248765) 14:08:37
然后 我们看看 究竟用 :cookie_store + :domain => :all
还是  :active_record_store + ".bbtang.com" (这里垮子域名估计不会很合适)
还是 升一下 rails  用  :active_record_store + :domain => :all

Recently I’ve been working on a Rails application that supports subdomains. I’m using Devise for user authentication and need the user to choose a subdomain to use upon registration.

Similar to the 37signals applications, I want a single sign-on to be persistent across subdomains. Since I didn’t have a clue where to begin with subdomains, I followed this tutorial on my new Rails 3.1 beta 1 application. This tutorial worked like a charm and I omitted the friendly_id and tweaked a few things to my liking.

The gist of it is simple. Create a User model like you would normally do with Devise. You add a Subdomain model that is linked to the Users (in my case I only wanted a single subdomain per user). Configuring the routes is pretty simple as you can simply create a constraint that will match the root and fire it off to the right action and let the rest fall through.

The trick comes into sharing the session between domains. Browsers, of course, will separate out the cookies and store them by separated out by subdomain. What you want to do is edit your config/initializers/session_store.rb file to look like this

APPNAMEGOESHERE::Application.config.session_store :cookie_store, :key => '_tourlyapp_session', :domain => "lvh.me"

The trick here is the :domain option. What this does is sets the level of the TLD (top level domain) and tells Rails how long the domain is. The part you want to watch out for here is that if you set :domain => :all like is recommend in some places, it simply won’t work unless you’re using localhost. :all defaults to a TLD length of 1, which means if you’re testing with Pow (myapp.dev) it won’t work either because that is a TLD of length 2.

You might get weird things like halfway Devise sessions sharing, but only allowing you to create and destroy the session on the root domain. Using :all works great if you’re using localhost, but when I started using lvh.me:3000 for testing I had those problems (lvh.me stands for local vhost me and is a domain that simply points to localhost which makes for zero-config subdomain development. It’s super handy.).

The best option might be to comment out this line and put it into your individual environment configurations. This way you can keep things configured easily as the :all option. Once you’ve got your domain string added everything should work like a charm.

BONUS PROTIP: The normal route variables you see used end with _path. These don’t include the domain and therefore ignore the :subdomain option you pass into them. url_for, on the other hand, does support subdomains so you should get into the habit of using root_url instead of root_path and so on.

IE, Iframe, P3P, Cookies, Oh My

IE, iframe, P3P, Cookies, oh my

测试新浪微游戏接口时,发现一个问题:当使用IE浏览器的时候,rails的session无法保存。之前在自己开发服务器上测试时,验证过IE和Firefox都能正常使用session的,觉得很是奇怪。

通 过抓包发现,正常情况下,服务器端在响应客户端访问请求后,在返回的http头中会有Set-Cookies这样的参数,同时在接下来的客户端的http 请求头中,会加上Cookie这样的参数;上述不能正常保存session情况下的抓包分析发现,客户端的http请求头中浏览器没有设置Cookie参 数。

解决方法一:修改IE的默认Cookie设置,设置IE隐私设置中的高级隐私设置,勾选“总是允许回话Cookie”,这样session的值就能正常保存了。不过这样肯定不是最好的解决办法,对大多数用户来说,这样做不合理。

解决方法二:参考方法来自: http://www.sympact.net/2008/07/rails-and-ifram.html ,文中描述了具体原因,是因为IFrame中打开的链接和主页面的链接不在同一个域,所以IE默认会认为是不可信任的,则不允许使用Cookie。解决办 法正如文中所示,在controller中的before_filter中增加一个方法,此方法中设置响应的http相应头中增加P3P参数,问题即可解 决。 大致代码如下:




[rails IE, IFRAME, P3P, COOKIES](https://gist.github.com/3719568)
#userful gem [rack-p3p](https://github.com/hoopla/rack-p3p)
# encoding: utf-8
class ApplicationController < ActionController::Base
  protect_from_forgery
  before_filter :set_p3p
  def set_p3p
    headers['P3P'] = "policyref=\"/w3c/p3p.xml\", CP=\"ALL DSP COR CURa ADMa DEVa TAIa OUR BUS IND UNI COM NAV INT\""
  end
end

I was just banging my head against the wall trying to figure out why internet explorer wasn’t remembering my user’s sessions. Turns out it’s something that has bitten me in the past.

IE doesn’t allow you to set cookies when your site is in an iframe unless your site has set P3P headers. Also, ordering matters – the P3P header must be set before the cookie is set.

If you’re using ruby, this gem works pretty well: https://github.com/hoopla/rack-p3p

Further reading: http://stackoverflow.com/questions/389456/cookie-blocked-not-saved-in-iframe-in-internet-explorer

All the articles I read about setting headers, etags, etc were all really old. Hopefully, if you’re using rails you found this article. Just install the gem and add the line from the README to your application.rb – no monkey patching. Good luck.

rails IE frame ActionController::InvalidAuthenticityToken

Same problem here with a rails application launched in an iframe I get:

“the change you wanted was rejected”

In log:

ActionController::InvalidAuthenticityToken

Seems that the problem occur in IE when you are developing in an iframe situation where the master page is at a different domain than the inner page. (es: iframed Facebook applications)

This is because IE’s default “medium” privacy setting has an issue with cookies in that situation.

A possible solution is to set a P3P header (try to google: p3p iframe internet explorer) Example, in application_controller.rb:

<code>before_filter  <wbr>:set_p3p

def set_p3p
 <wbr> response.headers["P3P"]='CP="CAO PSA OUR"'
end</wbr></wbr></code>

 

php版站内应用在ie浏览器下获取到session值(失效)[已解决]

php版站内应用在ie浏览器下获取到session值(失效) 首页授权证后保存的session在其它页面获取不到。例如: 在站内应用的iframe下: a.php <?php session_start(); $_SESSION[“user”]=”abc”; echo $_SESSION[“user”]; ?> <a href=”b.php”>b.php</a> b.php <?php session_start(); echo $_SESSION[“user”]; ?> 运行a.php后正常显示abc,跳转到b.php显示为空。 这问题纠缠了我很久了,不继地调试,直到今晚终于找到答案了: 原来这也属于是跨域访问的问题。 以下是解决方案: “用P3P header解决iframe跨域访问cookie/session”的问题 理论很简单,而且模式也和大多请求返回状态的SSO差不多.但是有几个地方是要注意一下的. 1.页面里的COOKIE不能是浏览器进程的COOKIE(包括验证票和不设置超时时间的COOKIE),否则跨域会取不到.这点做跨域COOKIE的人比较少提到.不过实际上留意下几家大学做的方案,有细微的提到他们的验证模块里的COOKIE是有设置超时时间的. 2.当利用IFRAME时,记得要在相应的动态页的页头添加一下P3P的信息,否则IE会自觉的把IFRAME框里的COOKIE给阻止掉,产生问题.本身不保存自然就取不到了.这个其实是FRAMESET和COOKIE的问题,用FRAME或者IFRAME都会遇到. 3.测试时输出TRACE,会减少很多测试的工作量. 只需要设置 P3P HTTP Header,在隐含 iframe 里面跨域设置 cookie 就可以成功。他们所用的内容是: P3P: CP=’CURa ADMa DEVa PSAo PSDo OUR BUS UNI PUR INT DEM STA PRE COM NAV OTC NOI DSP COR’ ASP直接在头部加了头部申明,测试有效。 <%Response.AddHeader “P3P”, “CP=CAO PSA OUR”%> php的话,我没去试,应该是如下写法: header(‘P3P: CP=CAO PSA OUR’); ASP.NET的话 通过在代码上加Response.AddHeader(“P3P”, “CP=CAO PSA OUR”)或者在Window服务中将ASP.NET State Service 启动。 JSP:response.setHeader(“P3P”,”CP=CAO PSA OUR”) 如何在静态页面加头信息 P3P: CP=”CAO PSA OUR 来解决框架与cookie的问题? IIS站点管理器允许你给所有输出的内容都加上任意HTTP Header,你只要在Custom Header里面加上P3P: CP=”CAO PSA OUR” 就ok了

在 Cloud Foundry 上使用 JRuby for Rails 应用程序

在 Cloud Foundry 上使用 JRuby for Rails 应用程序

[译注]本文翻译自Cloud Foundry英文博客站点,原文题为“Using JRuby for Rails Applications on Cloud Foundry”,文章发表时间是 2012 年 4 月 19 日。

如今,只需进行一些简单的配置更改,即可将 JRuby Rails 应用程序部署到 CloudFoundry.com。JRuby 应用程序常常通过创建一个包含 Rails 应用程序的 .war 文件来部署到 servlet 容器中。对于 Cloud Foundry,我们将采取同样的做法,同时对数据库配置进行一些更改,以便应用程序还可以访问 CloudFoundry.com 上的数据库服务。

将 JRuby on Rails 应用程序部署到 Cloud Foundry 上时需进行的更改

要使 JRuby 应用程序在 CloudFoundry.com 上运行,我们需要完成两项任务。首先,我们需通过修改 configuration 目录中的 database.yml 文件将该应用程序配置为连接到 CloudFoundry.com 上的数据库服务。当我们部署该应用程序时,我们还需要运行“rake db:migrate”的等效命令,以便创建数据库表。我们可以通过在 config/initializers 目录中添加一个初始化程序来做到这一点。

环境变量 VCAP_SERVICES 中提供了我们在配置数据库连接时所需的信息。我们可以通过编程方式分析该变量,也可以使用方便的 Cloud Foundry 运行时 gem(请参见 Cloud Foundry 服务与 Ruby 搭配使用:第 2 部分 –  Ruby 应用程序的实时支持博文),在本篇博文中我们将采用后者。要使用该 gem,我们需要将它包含在我们的 Gemfile 中:

...
gem  'cf-runtime'
...

既然我们已经添加了该 gem,我们就可以向 database.yml 文件中添加一些代码段,用以访问生产环境的数据库服务信息。下面是一个 database.yml 文件中的 production 部分,在这个文件中我们使用的是 MySQL 数据库:

config/database.yml

production:
    adapter: mysql
    <% require 'cfruntime/properties' %>
    <% db_svc = CFRuntime::CloudApp.service_props('mysql') %>
    database: <%= db_svc[:database] rescue 'bookshelf_production' %>
    username: <%= db_svc[:username] rescue 'root' %>
    password: <%= db_svc[:password] rescue '' %>
    host: <%= db_svc[:host] rescue 'localhost' %>
    port: <%= db_svc[:port] rescue '3306' %>

正如您可以看到的那样,我们添加了一个 require 语句来获取 cfruntime/properties,然后我们通过调用 service_props 方法并在调用时传入我们所使用的服务类型来获取服务属性的哈希值。如果仅有一项属于该类型的服务绑定到该应用程序,则无需指定此服务的实际名称。如果您将多项属于同一类型的服务绑定到您的应用程序,您将需要指定实际服务名称。服务属性的哈希值存储在一个名为 db_svc 的变量中,代码会将对应的值提取出来以用作数据库、用户名、密码、主机和端口。其中的每一条语句都有一个 rescue 子句,如果我们并非在 Cloud Foundry 环境中操作,则该语句将提供要使用的值,这种情况下 db_svc 将为 Nil。

另外,如果使用的是 PostgreSQL,则该 database.yml 文件的 production 部分将大致如下:

production:
   adapter: postgresql
   encoding: unicode
   <% require 'cfruntime/properties' %>
   <% db_svc = CFRuntime::CloudApp.service_props('postgresql') %>
   database: <%= db_svc[:database] rescue 'bookshelf_production' %>
   username: <%= db_svc[:username] rescue 'bookshelf' %>
   password: <%= db_svc[:password] rescue '' %>
   host: <%= db_svc[:host] rescue 'localhost' %>
   port: <%= db_svc[:port] rescue '5432' %>

接下来,我们将注意力转向创建供我们的应用程序使用的表上面。为此,我们在部署该应用程序时需向 config/initializers 目录中添加以下初始化程序。我将该初始化程序命名为 cf_db_migrate.rb:

config/initializers/cf_db_migrate.rb

require 'cfruntime/properties'
# Run the equivalent of rake db:migrate on startup
if CFRuntime::CloudApp.running_in_cloud?
  migrations = Rails.root.join('db','migrate')
  if migrations.directory?
    ActiveRecord::Migrator.migrate(migrations)
  end
end

我们再次使用 cfruntime/properties 来检查我们当前是否在云中运行。接下来,我们将检查 db/migrate 目录是否存在;如果它存在,我们将使用该目录中的迁移文件来运行数据库迁移 (ActiveRecord::Migrator.migrate(migrations))。

我们还必须进行的一项更改就是对 warble 配置的更改。默认情况下此配置在生成的 war 文件中不包含 db/migrate 目录,因此我们需要通过指定 config.includes = FileList[“db/migrate/*”] 将此目录添加到此配置中。下面是 config/warble.rb 文件的相关内容:

config/warble.rb

# Warbler web application assembly configuration file
Warbler::Config.new do |config|
  # Application directories to be included in the webapp.
  config.dirs = %w(app config lib log vendor tmp)
  # Additional files/directories to include, above those in config.dirs
  config.includes = FileList["db/migrate/*"]
end

一个完整示例

我们从上文中已经了解到需做出哪些更改,那我们就来快速生成一个 Rails 应用程序并做出所需的更改,然后将该应用程序部署到 CloudFoundry.com。如果您尚未安装 JRuby,建议首先参阅 JRuby 入门

创建 JRuby Rails 应用程序

首先,我们创建新应用程序,并创建具有完整基架的第一个域对象。

<code>jruby -S rails new bookshelf -d mysql cd bookshelf jruby -S rails generate scaffold Book title:string category:string published:integer price:decimal{10.2} isbn:string </code>

接下来,我们删除生成的 public/index.html,然后修改 config/routes.rb 以使用“books”作为根目录:

<code>rm public/index.html vi config/routes.rb</code>

下面是我在 config/routes.rb 中添加的路由:

Bookshelf::Application.routes.draw do
  resources :books
  # You can have the root of your site routed with "root"
  # just remember to delete public/index.html.
  # root :to => 'welcome#index'
  root :to => 'books#index'
  # See how all your routes lay out with "rake routes"
end

现在,我们将在本地运行此应用程序以确保它正常运行:

<code>jruby -S rake db:create jruby -S rake db:migrate jruby -S rails server</code>

Book 实体的 Rails 空列表视图表明它确实正常运行。现在,我可以向我的书架添加新书。

修改 JRuby Rails 应用程序以便部署到 CloudFoundry

我们来首先进行我们在上文中提到的以下更改:

  • 将 gem cf-runtime 添加到 Gemfile
  • 修改 config/database.yml 文件的“production:”节,具体见上文
  • 添加一个名为 config/initializers/cf_db_migrate.rb 的文件,文件内容见上文

接下来我们需要生成 Warbler 配置文件,因此我们将运行:

<code>jruby -S warble config </code>

现在,我们就可以:

  • 修改 config/warble.rb 以添加 db/migrate 目录,具体见上文

这些便是需要做出的全部更改,我们现在就已万事俱备,可以打包和部署此应用程序了。

打包 JRuby Rails 应用程序并将其部署到 CloudFoundry

我们将使用 Warbler 来将此应用程序打包成一个 war,并使用 CloudFoundry vmc 命令行实用程序来部署此应用程序。

我们用来将此应用程序打包成 war 文件的流程十分简单:捆绑,预编译资产,然后运行 Warbler:

<code>jruby -S bundle install jruby -S rake assets:precompile jruby -S warble</code>

这会在我们的 Rails 应用程序的根目录中创建一个 bookshelf.war。目前,将 vmc 命令与 JRuby 一起运行时会存在一些问题,不过我们正在致力于加以修复。同时,我们还可以将此 war 文件移至其他目录,这样我就可以更轻松地改用常规的“C”Ruby。我将创建一个“deploy”目录,并将该目录配置为使用 Ruby 1.9.2-p290(我当前使用的是 rbenv,但您也可以使用 RVM):

<code>mkdir deploy mv bookshelf.war deploy/. cd deploy rbenv local 1.9.2-p290 # (if you use RVM the command should be 'rvm ruby-1.9.2-p290')</code>

现在,我们已准备就绪,可以登录 CloudFoundry 并部署我们的应用程序了。对于此部分,您需要安装vmc

<code>vmc target api.cloudfoundry.com vmc login cloud@mycompany.com Password: ***** Successfully logged into [http://api.cloudfoundry.com] vmc push bookshelf Would you like to deploy from the current directory? [Yn]: Y Application Deployed URL [bookshelf.cloudfoundry.com]: mybookshelf.cloudfoundry.com Detected a Java Web Application, is this correct? [Yn]: Y Memory reservation (128M, 256M, 512M, 1G, 2G) [512M]: 512M How many instances? [1]: 1 Bind existing services to 'bookshelf'? [yN]: N Create services to bind to 'bookshelf'? [yN]: Y 1: mongodb 2: mysql 3: postgresql 4: rabbitmq 5: redis What kind of service?: 2 Specify the name of the service [mysql-a4fd7]: mysql-books Create another? [yN]: N Would you like to save this configuration? [yN]: N Creating Application: OK Creating Service [mysql-books]: OK Binding Service [mysql-books]: OK Uploading Application: Checking for available resources: OK Processing resources: OK Packing application: OK Uploading (707K): OK Push Status: OK Staging Application 'bookshelf': OK Starting Application 'bookshelf': OK</code>

上面已将 vmc 命令突出显示出来。除 URL 以及是否应创建服务外,已接受大部分默认设置。我使用的 URL 为“mybookshelf.cloudfoundry.com”而非默认 URL,以免与现有的书架应用程序冲突。对于是否创建新服务的问题,我回答“Y”;此外我还选择了 (2) mysql 并将其命名为“mysql-books”。

现在我们应看到此应用程序正在运行:

<code>vmc apps +-------------+----+---------+---------------------------------+---------------+ | Application | # | Health | URLS | Services | +-------------+----+---------+---------------------------------+---------------+ | bookshelf | 1 | RUNNING | mybookshelf.cloudfoundry.com | mysql-books | +-------------+----+---------+---------------------------------+---------------+</code>

因此,我们现在输入“http://mybookshelf.cloudfoundry.com/”,就可以看到此 Bookshelf 应用程序惟妙惟肖地展示出来,我们可以添加一些书籍。

您可以在 cloudfoundry-samples/jruby-rails-bookshelf 位置查看和下载用于此示例的完整源代码;如果您只想了解为在 Cloud Foundry 中部署而需进行的更改,请查看此提交

结论

我们已经证明了可以将一个简单的 JRuby on Rails 应用程序部署到 cloudfoundry 并使用 MySQL 服务作为支持它的数据存储。只需对数据库的应用程序配置进行一些修改,即可做到。

我们将发布一篇博文来阐述对于使用 DataMapper 实现持久保留的 JRuby Sinatra 应用程序我们需要进行的类似更改。

- Cloud Foundry 团队 Thomas Risberg

你可能错过的 Rails 技巧 –Posted by Bigcircle

你可能错过的 Rails 技巧 —Posted by bigcircle

记得前段时间 RailsConf2012 之后看过一个不错的pdf,10 things you didn’t know rails could do

说是10个,但是给出了42个实例,这几天抽空又回味了下,料很多,写的很好,顺便总结学习下

Pass 掉第一个 fridayhug,我们是开心拥抱每一天

 

%w(action_controller/railtie coderay markaby).map &method(:require)

run TheSmallestRailsApp ||= Class.new(Rails::Application) {
  config.secret_token = routes.append {
    root to: proc {
      [200, {"Content-Type" => "text/html"}, [Markaby::Builder.new.html {
        title @title = "The Smallest Rails App"
        h3 "I am #@title!"
        p "Here is my source code:"
        div { CodeRay.scan_file(__FILE__).div(line_numbers: :table) }
        p { a "Make me smaller", href: "//goo.gl/YdRpy" }
      }]]
    }
  }.to_s
  initialize!
}

2 – 提醒功能 TODO

class UsersController < ApplicationController
  # TODO:  Make it possible to create new users.
end

class User < ActiveRecord::Base
  # FIXME: Should token really  be accessible?
  attr_accessible :bil, :email, :name, :token
end

执行 rake notes

 

app/controllers/users_controller.rb:
  * [ 2] [TODO] Make it possible to create new users.

app/models/user.rb:
  * [ 2] [FIXME] Should token really be accessible?

app/views/articles/index.html.erb:
  * [ 1] [OPTIMIZE] Paginate this listing.

查看单独的 TODO / FIXME / OPTIMIZE

rake notes:todo

app/controllers/users_controller.rb:
  * [ 2] Make it possible to create new users.

可以自定义提醒名称

class Article < ActiveRecord::Base
  belongs_to :user
  attr_accessible :body, :subject
  # JEG2: Add that code from your blog here.
end

不过需要敲一长串,可以alias个快捷键

rake notes:custom ANNOTATION=JEG2

app/models/article.rb:
  * [ 4]Add that code from your blog here.

3 – 沙箱模式执行 rails c

rails c --sandbox

沙箱模式会有回滚事务机制,对数据库的操作在退出之前都会自动回滚到之前未修改的数据

4 – 在 rails c 控制台中使用 rails helper 方法

$ rails c
Loading development environment (Rails 3.2.3)
>> helper.number_to_currency(100)
=> "$100.00"
>> helper.time_ago_in_words(3.days.ago)
=> "3 days"

5 – 开发模式用 thin 代替 webrick

group :development do
  gem 'thin'
end

rails s thin / thin start

6 – 允许自定义配置

 - lib/custom/railtie.rb

 module Custom
   class Railtie < Rails::Railtie
     config.custom = ActiveSupport::OrderedOptions.new
   end
 end

 - config/application.rb

 require_relative "../lib/custom/railtie"

 module Blog
   class Application < Rails::Application
     # ...
     config.custom.setting = 42
   end
 end

7 – keep funny

作者给出了个介绍 ruby 以及一些相关 blog的网站 rubydramas,搞笑的是这个网站右上角标明

Powered by PHP

用 isitrails.com 检查了下,果然不是用 rails 做的,这个应该是作者分享 ppt 过程中的一个小插曲吧

8 -理解简写的迁移文件

rails g resources user name:string email:string token:string bio:text

字段会被默认为 string 属性,查看了下 源码,果然有初始化定义

rails g resources user name email token:string{6} bio:text

会生成用样的 migration 文件

class CreateUsers < ActiveRecord::Migration   def change     create_table :users do |t|       t.string :name       t.string :email       t.string :token, :limit => 6
      t.text :bio
      t.timestamps
    end
  end
end

9 – 给 migration 添加索引

rails g resource user name:index email:uniq token:string{6} bio:text

会生成 name 和 email 的索引,同时限定 email 唯一

class CreateUsers < ActiveRecord::Migration   def change     create_table :users do |t|       t.string :name       t.string :email       t.string :token, :limit => 6
      t.text :bio
      t.timestamps
    end

    add_index :users, :name
    add_index :users, :email, :unique => true
  end
end

10 – 添加关联关系

rails g resource article user:references subject body:text

会自动关联生成对应的 belongs_to 和 外键,并添加索引

class CreateArticles < ActiveRecord::Migration
  def change
    create_table :articles do |t|
      t.references :user
      t.string :subject
      t.text :body
      t.timestamps
    end
    add_index :articles, :user_id
  end
end
class Article < ActiveRecord::Base
  belongs_to :user
  attr_accessible :body, :subject
end

11 – 显示数据迁移记录

rake db:migrate:status

会输出 migration 的状态,这在解决迁移版本冲突的时候很有用

database: db/development.sqlite3

 status   Migration ID    Migration Name
 ---------------------------------------
   up     20120414155612  Create users
   up     20120414160528  Create articles
  down    20120414161355  Create comments

12 – 导入 csv 文件

csv 文件内容如下

Name,Email
James,james@example.com
Dana,dana@example.com
Summer,summer@example.com

创建 rake 任务导入 users 表

require 'csv'
namespace :users do
  desc "Import users from a CSV file"
  task :import => :environment do
    path = ENV.fetch("CSV_FILE") {
      File.join(File.dirname(__FILE__), *%w[.. .. db data users.csv])
    }
    CSV.foreach(path, headers: true, header_converters: :symbol) do |row|
      User.create(row.to_hash)
    end
  end
end

13 – 数据库中储存 csv

class Article <  ActiveRecord::Base
  require 'csv'
  module CSVSerializer
    module_function
    def load(field); field.to_s.parse_csv; end
    def dump(object); Array(object).to_csv; end
  end
  serialize :categories, CSVSerializer

  attr_accessible :body, :subject, :categories
end

serialize 用于在文本字段储存序列化的值,如序列,Hash,Array等,例如

user = User.create(:preferences => { "background" => "black", "display" => large })
User.find(user.id).preferences # => { "background" => "black", "display" => large }

这个例子中将 CSVSerializer to_csv序列化之后储存在 categories 这个文本类型字段中

14 – 用 pluck 查询

$ rails c
loading development environment(Rails 3.2.3)

>> User.select(:email).map(&:email)
  User Load(0.1ms) SELECT email FROM "users"
=> ["james@example.com", "dana@example.com", "summer@example.com"]
>> User.pluck(:email)
   (0.2ms) SELECT email FROM "users"
=> ["james@example.com", "dana@example.com", "summer@example.com"]
>> User.uniq.pluck(:email)
   (0.2ms) SELECT DISTINCT email FROM "users"
=> ["james@example.com", "dana@example.com", "summer@example.com"]

pluck 的实现方式其实也是 select 之后再 map

def pluck(column_name)
  column_name = column_name.to_s
  klass.connection.select_all(select(column_name).arel).map! do |attributes|
    klass.type_cast_attribute(attributes.keys.first, klass.initialize_attributes(attributes))
  end
end

15 – 使用 group count

创建 article 关联 model event

rails g resource event article:belongs_to triggle

创建3条 edit 记录和10条 view 记录。 Event.count 标明有13条记录, group(:triggle).count 表示统计 triggle group 之后的数量,也可以用 count(:group => :trigger)

$ rails c

&gt;&gt; article = Article.last
=&gt; #<article id:1="">&gt;&gt; {edit:3, view:10}.each do |trigger, count| ?&gt; count.times do ?&gt; Event.new(trigger: trigger).tap{ |e| e.article= article; e.save! } ?&gt; end =&gt; {:edit =&gt; 3, :view =&gt; 10} &gt;&gt; Event.count =&gt; 13 &gt;&gt; Event.group(:trigger).count =&gt; {"edit" =&gt; 3, "view" =&gt; 10}</article>

16 – 覆盖关联关系

class Car < ActiveRecord::Base
  belongs_to :owner
  belongs_to :previous_owner, class_name: "Owner"

  def owner=(new_owner)
    self.previous_owner = owner
    super
  end
end

car更改 owner 时,如果有了 new_owner,就把原 owner 赋给 previous_owner,注意要加上 super

17 – 构造示例数据

$ rails c
Loading development environment (Rails 3.2.3)
>> User.find(1)
=> #
>> jeg2 = User.instantiate("id" => 1, "email" => "
=> #
>> jeg2.name = "James Edward Gray II"
=> "James Edward Gray II"
>> jeg2.save!
=> true
>> User.find(1)
james@example.com", ...>

伪造一条记录,并不是数据库中的真实数据,也不会把原有数据覆盖

18 – PostgreSQL 中使用无限制的 string

去掉适配器中对 string 长度的限制,这个应该是 PostgreSQL 数据库的特性

module PsqlApp
  class Application < Rails::Application
    # Switch to limitless strings
    initializer "postgresql.no_default_string_limit" do
      ActiveSupport.on_load(:active_record) do
        adapter = ActiveRecord::ConnectionAdapters::PostgreSQLAdapter
        adapter::NATIVE_DATABASE_TYPES[:string].delete(:limit)
      end
    end
 end
end

创建 user 表,bio 字符串

rails g resource user bio
$ rails c
Loading development environment (Rails 3.2.3)
>> very_long_bio = "X" * 10_000; :set
=> :set
>> User.create!(bio: very_long_bio)
=> #
>> User.last.bio.size
=> 10000

19 – PostgreSQL 中使用全文搜

rails g resource article subject body:text

更改迁移文件,添加索引和 articles_search_update 触发器

class CreateArticles < ActiveRecord::Migration
  def change
    create_table :articles do |t|
      t.string :subject
      t.text   :body
      t.column :search, "tsvector"
      t.timestamps
    end
    execute <

Model 中添加 search 方法

class Article < ActiveRecord::Base
  attr_accessible :body, :subject
  def self.search(query)
    sql = sanitize_sql_array(["plainto_tsquery('english', ?)", query])
    where(
      "search @@ #{sql}"
    ).order(
      "ts_rank_cd(search, #{sql}) DESC"
    )
  end
end

PostgreSQL 数据库没用过,这段看例子吧

$ rails c
Loading development environment (Rails 3.2.3)
&gt;&gt; Article.create!(subject: "Full Text Search")
=&gt; #<article id:="" 1="">&gt;&gt; Article.create!(body: "A stemmed search.") =&gt; #<article id:="" 2="">&gt;&gt; Article.create!(body: "You won't find me!") =&gt; #<article id:="" 3="">&gt;&gt; Article.search("search").map { |a| a.subject || a.body } =&gt; ["Full Text Search", "A stemmed search."] &gt;&gt; Article.search("stemming").map { |a| a.subject || a.body } =&gt; ["A stemmed search."]</article></article></article>

21 – 每个用户使用不同的数据库

- user_database.rb

def connect_to_user_database(name)
  config = ActiveRecord::Base.configurations["development"].merge("database" => "db/#{name}.sqlite3")
  ActiveRecord::Base.establish_connection(config)
end

创建 rake 任务:新增用户数据库和创建

require "user_database"

namespace :db do
  desc "Add a new user database"
  task :add => %w[environment load_config] do
    name = ENV.fetch("DB_NAME") { fail "DB_NAME is required" }
    connect_to_user_database(name)
    ActiveRecord::Base.connection
  end

  namespace :migrate do
    desc "Migrate all user databases"
    task :all => %w[environment load_config] do
      ActiveRecord::Migration.verbose = ENV.fetch("VERBOSE", "true") == "true"
      Dir.glob("db/*.sqlite3") do |file|
        next if file == "db/test.sqlite3"
        connect_to_user_database(File.basename(file, ".sqlite3"))
        ActiveRecord::Migrator.migrate(
          ActiveRecord::Migrator.migrations_paths,
          ENV["VERSION"] && ENV["VERSION"].to_i
        ) do |migration|
          ENV["SCOPE"].blank? || (ENV["SCOPE"] == migration.scope)
        end
      end
    end
  end
end

执行几个rake 任务创建不同的数据库

$ rails g resource user name
$ rake db:add DB_NAME=ruby_rogues
$ rake db:add DB_NAME=grays
$ rake db:migrate:all
==  CreateUsers: migrating ==================================
-- create_table(:users)
   -> 0.0008s
==  CreateUsers: migrated (0.0008s) =========================
==  CreateUsers: migrating ==================================
-- create_table(:users)
   -> 0.0007s
==  CreateUsers: migrated (0.0008s) =========================

创建几条记录,为不同的数据库创建不同的数据

$ rails c
>> connect_to_user_database("ruby_rogues")
=> #
>> User.create!(name: "Chuck")
=> #
>> User.create!(name: "Josh")
=> #
>> User.create!(name: "Avdi")
=> #
...
>> connect_to_user_database("grays")
=> #
>> User.create!(name: "James")
=> #
>> User.create!(name: "Dana")
=> #
>> User.create!(name: "Summer")
=> #

ApplicationController 里面添加 before_filter 根据登陆的二级域名判断连接哪个数据库

class ApplicationController < ActionController::Base
  protect_from_forgery
  before_filter :connect_to_database
private
  def connect_to_database
    connect_to_user_database(request.subdomains.first)
  end
end

中场休息时,去找了下 RailsConf 2012 的视频看了看,刚好看到关于 这篇 的介绍,片子还挺长,41分钟,演讲者长相和声音都很不符合大家对 Rails 的认知,大家有兴趣的可以去听听

21 – 自动写文件

class Comment < ActiveRecord::Base
  # ...
  Q_DIR = (Rails.root + "comment_queue").tap(&:mkpath)
  after_save :queue_comment
  def queue_comment
    File.atomic_write(Q_DIR + "#{id}.txt") do |f|
      f.puts "Article: #{article.subject}"
      f.puts "User:    #{user.name}"
      f.puts body
    end
  end
end

找了下 Api 是 Rails 对 Ruby 基础类的扩展

22 – 合并嵌套 Hash

$ rails c
Loading development environment (Rails 3.2.3)
>> {nested: {one: 1}}.merge(nested: {two: 2})
=> {:nested=>{:two=>2}}
>> {nested: {one: 1}}.deep_merge(nested: {two: 2})
=> {:nested=>{:one=>1, :two=>2}}

主要是用到了 deep_merge 合并相同的 key

23 – Hash except

$ rails c
Loading development environment (Rails 3.2.3)
>> params = {controller: "home", action: "index", from: "Google"}
=> {:controller=>"home", :action=>"index", :from=>"Google"}
>> params.except(:controller, :action)
=> {:from=>"Google"}

这个方法经常会用到,可能用的人也很多

24 – add defaults to Hash

$ rails c
Loading development environment (Rails 3.2.3)
>> {required: true}.merge(optional: true)
=> {:required=>true, :optional=>true}
>> {required: true}.reverse_merge(optional: true)
=> {:optional=>true, :required=>true}
>> {required: true, optional: false}.merge(optional: true)
=> {:required=>true, :optional=>true}
>> {required: true, optional: false}.reverse_merge(optional: true)
=> {:optional=>false, :required=>true}

这几个都是对 Hash 类的增强,merge 会替换原有相同 key 的值,但 reverse_merge 不会

从源码就可以看出,会事先 copy 一份 default hash

def reverse_merge(other_hash)
  super
  self.class.new_from_hash_copying_default(other_hash)
end

25 – String.value? 方法

看下面的几个例子

$ rails c Loading development environment (Rails 3.2.3) >> env = Rails.env => “development” >> env.development? => true >> env.test? => false >> “magic”.inquiry.magic? => true >> article = Article.first => #

>> article.draft? => true >> article.published? => false
 

env, “magic” 可以直接使用 value? 的方法,这个扩展是 String#inquiry 方法

def inquiry
  ActiveSupport::StringInquirer.new(self)
end

# 用method_missing 实现
def method_missing(method_name, *arguments)
  if method_name.to_s[-1,1] == "?"
    self == method_name.to_s[0..-2]
  else
    super
  end
end

类型的一个例子,同样用到了 inquiry 方法

class Article < ActiveRecord::Base
  # ...
  STATUSES = %w[Draft Published]
  validates_inclusion_of :status, in: STATUSES
  def method_missing(method, *args, &block)
    if method =~ /\A#{STATUSES.map(&:downcase).join("|")}\?\z/
      status.downcase.inquiry.send(method)
    else
      super
    end
  end
end

 

26 - 让你成为杂志的封面 (暖场之用)

搞笑哥拿出了 DHH 当选 Linux journal 杂志封面的图片,会场也是哄堂大笑 ^.^

27 – 隐藏注释

<h1>Home Page</h1>

# 生成的 html<!-- HTML comments stay in the rendered content --> 

<h1>Home Page</h1>

这个一下没看懂。。试了下项目里面的代码,原来是隐藏的意思。。 28 – 理解更短的 erb 语法

# ...
module Blog
  class Application < Rails::Application

    # Broken:  config.action_view.erb_trim_mode = '%'
    ActionView::Template::Handlers::ERB.erb_implementation =
      Class.new(ActionView::Template::Handlers::Erubis) do
        include ::Erubis::PercentLineEnhancer
      end
    end
  end
end

接着在 view 页面替换用 % 表示原来,有点像 slim

% if current_user.try(:admin?)

% end

29 – 用 block 避免视图层赋值


<table>
  <% @cart.products.each do |product| %>
    <tr>
      <td><%= product.name %></td>
      <td><%= number_to_currency product.price %></td>
    </tr>
  <% end %>
  <tr>
    <td>Subtotal</td>
    <td><%= number_to_currency @cart.total %></td>
  </tr>
  <tr>
    <td>Tax</td>
    <td><%= number_to_currency(tax = calculate_tax(@cart.total)) %></td>
  </tr>
  <tr>
    <td>Total</td>
    <td><%= number_to_currency(@cart.total + tax) %></td>
  </tr>
</table>

tax = calculate_tax(@cart.total) 会先被赋值再被下面引用 用 block 重构下,把逻辑代码放到 helper 里面

module CartHelper
  def calculate_tax(total, user = current_user)
    tax = TaxTable.for(user).calculate(total)
    if block_given?
      yield tax
    else
      tax
    end
  end
end
<table>
  <% @cart.products.each do |product| %>
    <tr>
      <td><%= product.name %></td>
      <td><%= number_to_currency product.price %></td>
    </tr>
  <% end %>
  <tr>
    <td>Subtotal</td>
    <td><%= number_to_currency @cart.total %></td>
  </tr>
  <% calculate_tax @cart.total do |tax| %>
    <tr>
      <td>Tax</td>
      <td><%= number_to_currency tax %></td>
    </tr>
    <tr>
      <td>Total</td>
      <td><%= number_to_currency(@cart.total + tax) %></td>
    </tr>
  <% end %>
</table>


 

30 – 同时生成多个标签

<h1>Articles</h1>
  <% @articles.each do |article| %>
    <%= content_tag_for(:div, article) do %>
    <h2><%= article.subject %></h2>
  <% end %>
<% end %>
<%= content_tag_for(:div, @articles) do |article| %>
  <h2><%= article.subject %></h2>
<% end %>


 

 

content_tag_for 具体用法可以参考 Api,意思比较明白 to_partial_path 是 ActiveModel 內建的实例方法,返回一个和可识别关联对象路径的字符串,原文是这么说的,目前还没看明白这么用的目的在哪

这篇 blog 介绍了4个最喜欢的 Rails3.2 隐藏特性,

 

这4条都在这个系列中,作者可能也是从这学来的吧

31 – Render Any Object

class Event < ActiveRecord::Base
  # ...
  def to_partial_path
    "events/#{trigger}"  # events/edit or events/view
  end
end

to_partial_path 是 ActiveModel 內建的实例方法,返回一个和可识别关联对象路径的字符串,原文是这么说的,目前还没看明白这么用的目的在哪

 

Returns a string identifying the path associated with the object.
ActionPack uses this to find a suitable partial to represent the object.

32 – 生成 group option

 %w[One Two Three],
  "Group B" => %w[One Two Three]
) ) %>

这个其实就是用到了 grouped_options_for_select ,我在前面的 博文 提到过这几个 select 的用法

33 -定制你自己喜欢的 form 表单

class LabeledFieldsWithErrors < ActionView::Helpers::FormBuilder
  def errors_for(attribute)
    if (errors = object.errors[attribute]).any?
      @template.content_tag(:span, errors.to_sentence, class: "error")
    end
  end
  def method_missing(method, *args, &block)
    if %r{ \A (?labeled_)?
              (?\w+?)
              (?_with_errors)? \z }x =~ method and
       respond_to?(wrapped) and [labeled, with_errors].any?(&:present?)
      attribute, tags = args.first, [ ]
      tags           << label(attribute) if labeled.present?
      tags           << send(wrapped, *args, &block)
      tags           << errors_for(attribute) if with_errors.present?
      tags.join(" ").html_safe
    else
      super
    end
  end
end

定义了几个不想去看懂的 method_missing 方法。。 修改 application.rb,添加配置

class Application < Rails::Application
  # ...
  require "labeled_fields_with_errors"
  config.action_view.default_form_builder = LabeledFieldsWithErrors
  config.action_view.field_error_proc     = ->(field, _) { field }
end

创建 form 表单可以这样书写


<%= form_for @article do |f| %>
  <p><%= f.text_field
  <p><%= f.labeled_text_field
  <p><%= f.text_field_with_errors
  <p><%= f.labeled_text_field_with_errors :subject %></p>
  <%= f.submit %>
<% end %>

生成如下的 html 页面

<p><input id="article_subject" name="article[subject]" size="30" type="text" value="" /></p>
<p><label for="article_subject">Subject</label>
   <input id="article_subject" name="article[subject]" size="30" type="text" value="" /></p>
<p><input id="article_subject" name="article[subject]" size="30" type="text" value="" />
   <span class="error">can't be blank</span></p>
<p><label for="article_subject">Subject</label>
   <input id="article_subject" name="article[subject]" size="30" type="text" value="" />
   <span class="error">can't be blank</span></p>
<!-- ... -->

不是很喜欢这种方式,反而把简单的html搞复杂了,让后来维护的人增加额外的学习成本     不是很喜欢这种方式,反而把简单的html搞复杂了,让后来维护的人增加额外的学习成本

34 - Inspire theme songs about your work (再次暖场时刻)

2011年 Farmhouse Conf 上主持人 Ron Evans 专门用口琴演奏了为大神 Tenderlove 写的歌 - Ruby Hero Tenderlove! ,听了半天不知道唱的啥。。 想找下有没有美女 Rubist, 看了下貌似没有,都是大妈,这位 Meghann Millard 尚可远观,大姐装束妖娆,手握纸条,蚊蝇环绕,不时微笑,长的真有点像 gossip girl 里面的 Jenny Humphrey

35 - 灵活的异常操作

修改 application.rb 定义

class Application < Rails::Application
# ...
  config.exceptions_app = routes
end

每次有异常时路由都会被调用,你可以用下面的方法简单 render 404 页面

match "/404", :to => "errors#not_found"

这个例子也在开头提到的那篇博文里面,感兴趣可以去自己研究下

36 – 给 Sinatra 添加路由

- Gemfile

source 'https://rubygems.org'
# ...
gem "resque", require: "resque/server"

module AdminValidator

  def matches?(request)
    if (id = request.env["rack.session"]["user_id"])
      current_user = User.find_by_id(id)
      current_user.try(:admin?)
    else
      false
    end
  end
end

挂载 Resque::Server 至 /admin/resqu

Blog::Application.routes.draw do
  # ...
  require "admin_validator"
  constraints AdminValidator do
    mount Resque::Server, at: "/admin/resque"
  end
end

这个也没有试验,不清楚具体用法,sinatra 平时也基本不用

37 – 导出CSV流

class ArticlesController < ApplicationController
  def index
    respond_to do |format|
      format.html do
        @articles = Article.all
      end
      format.csv do
        headers["Content-Disposition"] = %Q{attachment; filename="articles.csv"}
        self.response_body = Enumerator.new do |response|
          csv  = CSV.new(response, row_sep: "\n")
          csv << %w[Subject Created Status]
          Article.find_each do |article|
            csv << [ article.subject,
                     article.created_at.to_s(:long),
                     article.status ]
          end
        end
      end
    end
  end
# ...
end

导出 csv 是很常用的功能,很多时候报表都需要,这个还是比较实用的

38 - do some work in the background

给 articles 添加文本类型 stats 字段
rails g migration add_stats_to_articles stats:text

添加一个计算 stats 方法 和 一个 after_create 方法,在创建一条记录后,会把 calculate_stats 添加到 Queue 队列,当队列中有任务时,后台创建一个线程执行该 job

class Article < ActiveRecord::Base
  # ...
  serialize :stats
  def calculate_stats
    words = Hash.new(0)
    body.to_s.scan(/\S+/) { |word| words[word] += 1 }
    sleep 10  # simulate a lot of work
    self.stats = {words: words}
  end

  require "thread"
  def self.queue; @queue ||= Queue.new end
  def self.thread
    @thread ||= Thread.new do
      while job = queue.pop
        job.call
      end
    end
  end
  thread  # start the Thread

  after_create :add_stats
  def add_stats
    self.class.queue << -> { calculate_stats; save }
  end
end

添加一条记录,10秒后会自动给该记录 stats 字段添加 words Hash

$ rails c
Loading development environment (Rails 3.2.3)
>> Article.create!(subject: "Stats", body: "Lorem ipsum...");
Time.now.strftime("%H:%M:%S")
=> "15:24:10"
>> [Article.last.stats, Time.now.strftime("%H:%M:%S")]
=> [nil, "15:24:13"]
>> [Article.last.stats, Time.now.strftime("%H:%M:%S")]
=>[{:words=>{"Lorem"=>1, "ipsum"=>1, ...}, "15:24:26"]

39 – 用 Rails 生成静态站点

修改 config/environment/development.rb

Static::Application.configure do
  # ...
  # Show full error reports and disable caching
  config.consider_all_requests_local       = true
  config.action_controller.perform_caching = !!ENV["GENERATING_SITE"]
  # ...
  # Don't fallback to assets pipeline if a precompiled asset is missed
  config.assets.compile = !ENV["GENERATING_SITE"]
  # Generate digests for assets URLs
  config.assets.digest = !!ENV["GENERATING_SITE"]
  # ...
end

class ApplicationController < ActionController::Base
  protect_from_forgery
  if ENV["GENERATING_SITE"]
    after_filter do |c|
      c.cache_page(nil, nil, Zlib::BEST_COMPRESSION)
    end
  end
end

修改 rake static:generate 任务

require "open-uri"
namespace :static do
  desc "Generate a static copy of the site"
  task :generate => %w[environment assets:precompile] do
    site = ENV.fetch("RSYNC_SITE_TO") { fail "Must set RSYNC_SITE_TO" }
    server = spawn( {"GENERATING_SITE" => "true"},
                    "bundle exec rails s thin -p 3001" )
    sleep 10  # FIXME: start when the server is up

    # FIXME: improve the following super crude spider
    paths = %w[/]
    files = [ ]
    while path = paths.shift
      files << File.join("public", path.sub(%r{/\z}, "/index") + ".html")
      File.unlink(files.last) if File.exist? files.last
      files << files.last + ".gz"
      File.unlink(files.last) if File.exist? files.last
      page = open("http://localhost:3001#{path}") { |url| url.read }
      page.scan(/]+href="([^"]+)"/) do |link|
        paths << link.first
      end
    end

    system("rsync -a public #{site}")

    Process.kill("INT", server)
    Process.wait(server)
    system("bundle exec rake assets:clean")
    files.each do |file|
      File.unlink(file)
    end
  end
end

生成到某个地方,去查看吧

rake static:generate RSYNC_SITE_TO=/Users/james/Desktop

后面几个都不感兴趣,没有测试,说好的42个,瞎扯了3个pass掉了,实在是吐血了

Over.