├── .gitignore ├── CHANGELOG.md ├── Dockerfile ├── Gemfile ├── Gemfile.lock ├── README.md ├── Rakefile ├── assets ├── images │ ├── akshaysurve.jpg │ ├── belongs_to.png │ ├── book_icon.gif │ ├── bullet.gif │ ├── chapters_icon.gif │ ├── check_bullet.gif │ ├── credits_pic_blank.gif │ ├── csrf.png │ ├── edge_badge.png │ ├── favicon.ico │ ├── feature_tile.gif │ ├── footer_tile.gif │ ├── fxn.png │ ├── getting_started │ │ ├── article_with_comments.png │ │ ├── challenge.png │ │ ├── confirm_dialog.png │ │ ├── forbidden_attributes_for_new_article.png │ │ ├── form_with_errors.png │ │ ├── index_action_with_edit_link.png │ │ ├── new_article.png │ │ ├── rails_welcome.png │ │ ├── routing_error_no_controller.png │ │ ├── routing_error_no_route_matches.png │ │ ├── show_action_for_articles.png │ │ ├── template_is_missing_articles_new.png │ │ ├── unknown_action_create_for_articles.png │ │ └── unknown_action_new_for_articles.png │ ├── grey_bullet.gif │ ├── habtm.png │ ├── has_many.png │ ├── has_many_through.png │ ├── has_one.png │ ├── has_one_through.png │ ├── header_backdrop.png │ ├── header_tile.gif │ ├── i18n │ │ ├── demo_html_safe.png │ │ ├── demo_localized_pirate.png │ │ ├── demo_translated_en.png │ │ ├── demo_translated_pirate.png │ │ ├── demo_translation_missing.png │ │ └── demo_untranslated.png │ ├── icons │ │ ├── README │ │ ├── callouts │ │ │ ├── 1.png │ │ │ ├── 10.png │ │ │ ├── 11.png │ │ │ ├── 12.png │ │ │ ├── 13.png │ │ │ ├── 14.png │ │ │ ├── 15.png │ │ │ ├── 2.png │ │ │ ├── 3.png │ │ │ ├── 4.png │ │ │ ├── 5.png │ │ │ ├── 6.png │ │ │ ├── 7.png │ │ │ ├── 8.png │ │ │ └── 9.png │ │ ├── caution.png │ │ ├── example.png │ │ ├── home.png │ │ ├── important.png │ │ ├── next.png │ │ ├── note.png │ │ ├── prev.png │ │ ├── tip.png │ │ ├── up.png │ │ └── warning.png │ ├── nav_arrow.gif │ ├── oscardelben.jpg │ ├── polymorphic.png │ ├── radar.png │ ├── rails4_features.png │ ├── rails_guides_kindle_cover.jpg │ ├── rails_guides_logo.gif │ ├── rails_logo_remix.gif │ ├── session_fixation.png │ ├── tab_grey.gif │ ├── tab_info.gif │ ├── tab_note.gif │ ├── tab_red.gif │ ├── tab_yellow.gif │ ├── tab_yellow.png │ └── vijaydev.jpg ├── javascripts │ ├── guides.js │ ├── jquery.min.js │ ├── responsive-tables.js │ └── syntaxhighlighter.js └── stylesheets │ ├── fixes.css │ ├── kindle.css │ ├── main.css │ ├── print.css │ ├── reset.css │ ├── responsive-tables.css │ ├── style.css │ └── syntaxhighlighter │ ├── shCore.css │ └── shThemeRailsGuides.css ├── bug_report_templates ├── action_controller_gem.rb ├── action_controller_master.rb ├── active_record_gem.rb ├── active_record_master.rb ├── generic_gem.rb └── generic_master.rb ├── rails_guides.rb ├── rails_guides ├── cn.rb ├── generator.rb ├── helpers.rb ├── indexer.rb ├── kindle.rb ├── levenshtein.rb ├── markdown.rb └── markdown │ └── renderer.rb ├── source ├── 2_2_release_notes.md ├── 2_3_release_notes.md ├── 3_0_release_notes.md ├── 3_1_release_notes.md ├── 3_2_release_notes.md ├── 4_0_release_notes.md ├── 4_1_release_notes.md ├── 4_2_release_notes.md ├── 5_0_release_notes.md ├── 5_1_release_notes.md ├── _license.html.erb ├── _welcome.html.erb ├── action_cable_overview.md ├── action_controller_overview.md ├── action_mailer_basics.md ├── action_view_overview.md ├── active_job_basics.md ├── active_model_basics.md ├── active_record_basics.md ├── active_record_callbacks.md ├── active_record_migrations.md ├── active_record_postgresql.md ├── active_record_querying.md ├── active_record_validations.md ├── active_support_core_extensions.md ├── active_support_instrumentation.md ├── api_app.md ├── api_documentation_guidelines.md ├── asset_pipeline.md ├── association_basics.md ├── autoloading_and_reloading_constants.md ├── caching_with_rails.md ├── command_line.md ├── configuring.md ├── contributing_to_ruby_on_rails.md ├── credits.html.erb ├── debugging_rails_applications.md ├── development_dependencies_install.md ├── documents.yaml ├── engines.md ├── form_helpers.md ├── generators.md ├── getting_started.md ├── i18n.md ├── index.html.erb ├── initialization.md ├── kindle │ ├── copyright.html.erb │ ├── layout.html.erb │ ├── rails_guides.opf.erb │ ├── toc.html.erb │ ├── toc.ncx.erb │ └── welcome.html.erb ├── layout.html.erb ├── layouts_and_rendering.md ├── maintenance_policy.md ├── nested_model_forms.md ├── plugins.md ├── profiling.md ├── rails_application_templates.md ├── rails_on_rack.md ├── routing.md ├── ruby_on_rails_guides_guidelines.md ├── security.md ├── testing.md ├── upgrading_ruby_on_rails.md ├── working_with_javascript_in_rails.md └── zh-CN │ ├── 2_2_release_notes.md │ ├── 2_3_release_notes.md │ ├── 3_0_release_notes.md │ ├── 3_1_release_notes.md │ ├── 3_2_release_notes.md │ ├── 4_0_release_notes.md │ ├── 4_1_release_notes.md │ ├── 4_2_release_notes.md │ ├── 5_0_release_notes.md │ ├── 5_1_release_notes.md │ ├── _license.html.erb │ ├── _welcome.html.erb │ ├── action_cable_overview.md │ ├── action_controller_overview.md │ ├── action_mailer_basics.md │ ├── action_view_overview.md │ ├── active_job_basics.md │ ├── active_model_basics.md │ ├── active_record_basics.md │ ├── active_record_callbacks.md │ ├── active_record_migrations.md │ ├── active_record_postgresql.md │ ├── active_record_querying.md │ ├── active_record_validations.md │ ├── active_support_core_extensions.md │ ├── active_support_instrumentation.md │ ├── api_app.md │ ├── api_documentation_guidelines.md │ ├── asset_pipeline.md │ ├── association_basics.md │ ├── autoloading_and_reloading_constants.md │ ├── caching_with_rails.md │ ├── command_line.md │ ├── configuring.md │ ├── contributing_to_ruby_on_rails.md │ ├── credits.html.erb │ ├── debugging_rails_applications.md │ ├── development_dependencies_install.md │ ├── documents.yaml │ ├── engines.md │ ├── form_helpers.md │ ├── generators.md │ ├── getting_started.md │ ├── i18n.md │ ├── index.html.erb │ ├── initialization.md │ ├── kindle │ ├── copyright.html.erb │ ├── layout.html.erb │ ├── rails_guides.opf.erb │ ├── toc.html.erb │ ├── toc.ncx.erb │ └── welcome.html.erb │ ├── layout.html.erb │ ├── layouts_and_rendering.md │ ├── maintenance_policy.md │ ├── plugins.md │ ├── rails_application_templates.md │ ├── rails_on_rack.md │ ├── routing.md │ ├── ruby_on_rails_guides_guidelines.md │ ├── security.md │ ├── testing.md │ ├── upgrading_ruby_on_rails.md │ └── working_with_javascript_in_rails.md └── w3c_validator.rb /.gitignore: -------------------------------------------------------------------------------- 1 | # Don't put *.swp, *.bak, etc here; those belong in a global ~/.gitignore. 2 | # Check out https://help.github.com/articles/ignoring-files for how to set that up. 3 | 4 | .bundle/ 5 | /output/ 6 | BASE_PATH 7 | .DS_Store 8 | -------------------------------------------------------------------------------- /CHANGELOG.md: -------------------------------------------------------------------------------- 1 | ## Rails 5.1.0 (April 27, 2017) ## 2 | 3 | * No changes. 4 | 5 | ## Rails 5.0.1 (December 21, 2016) ## 6 | 7 | * No changes. 8 | 9 | 10 | ## Rails 5.0.1.rc2 (December 10, 2016) ## 11 | 12 | * No changes. 13 | 14 | 15 | ## Rails 5.0.1.rc1 (December 01, 2016) ## 16 | 17 | * No changes. 18 | 19 | 20 | ## Rails 5.0.0 (June 30, 2016) ## 21 | 22 | * Update example of passing a proc to `:message` option for validating records. 23 | 24 | This behavior was recently changed in [Pull Request #24199](https://github.com/rails/rails/pull/24119) to 25 | pass the object being validated as first argument to the `:message` proc, 26 | instead of the key of the field being validated. 27 | 28 | *Prathamesh Sonpatki* 29 | 30 | * Added new guide: Action Cable Overview. 31 | 32 | *David Kuhta* 33 | 34 | * Add code of conduct to contributing guide. 35 | 36 | *Jon Moss* 37 | 38 | * New section in Configuring: Configuring Active Job. 39 | 40 | *Eliot Sykes* 41 | 42 | * New section in Active Record Association Basics: Single Table Inheritance. 43 | 44 | *Andrey Nering* 45 | 46 | * New section in Active Record Querying: Understanding The Method Chaining. 47 | 48 | *Andrey Nering* 49 | 50 | * New section in Configuring: Search Engines Indexing. 51 | 52 | *Andrey Nering* 53 | 54 | Please check [4-2-stable](https://github.com/rails/rails/blob/4-2-stable/guides/CHANGELOG.md) for previous changes. 55 | -------------------------------------------------------------------------------- /Dockerfile: -------------------------------------------------------------------------------- 1 | FROM ruby:2.4.1 2 | 3 | RUN mkdir /app 4 | WORKDIR /app 5 | 6 | RUN apt-get update && apt-get -y install imagemagick 7 | 8 | RUN mkdir /tmp/kindlegen && cd /tmp/kindlegen && \ 9 | wget http://kindlegen.s3.amazonaws.com/kindlegen_linux_2.6_i386_v2_9.tar.gz && \ 10 | tar xfz kindlegen_linux_2.6_i386_v2_9.tar.gz && \ 11 | mv kindlegen /usr/local/bin/ 12 | 13 | RUN gem install bundler 14 | 15 | COPY Gemfile /app 16 | COPY Gemfile.lock /app 17 | RUN bundle install 18 | 19 | ENV GUIDES_LANGUAGE=zh-CN 20 | ENV RAILS_VERSION=v5.1.1 21 | -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | # frozen_string_literal: true 2 | source 'https://rubygems.org' 3 | 4 | gem 'rails', '5.1.1' 5 | gem 'redcarpet' 6 | gem 'kindlerb', '1.2.0' 7 | -------------------------------------------------------------------------------- /Gemfile.lock: -------------------------------------------------------------------------------- 1 | GEM 2 | remote: https://rubygems.org/ 3 | specs: 4 | actioncable (5.1.1) 5 | actionpack (= 5.1.1) 6 | nio4r (~> 2.0) 7 | websocket-driver (~> 0.6.1) 8 | actionmailer (5.1.1) 9 | actionpack (= 5.1.1) 10 | actionview (= 5.1.1) 11 | activejob (= 5.1.1) 12 | mail (~> 2.5, >= 2.5.4) 13 | rails-dom-testing (~> 2.0) 14 | actionpack (5.1.1) 15 | actionview (= 5.1.1) 16 | activesupport (= 5.1.1) 17 | rack (~> 2.0) 18 | rack-test (~> 0.6.3) 19 | rails-dom-testing (~> 2.0) 20 | rails-html-sanitizer (~> 1.0, >= 1.0.2) 21 | actionview (5.1.1) 22 | activesupport (= 5.1.1) 23 | builder (~> 3.1) 24 | erubi (~> 1.4) 25 | rails-dom-testing (~> 2.0) 26 | rails-html-sanitizer (~> 1.0, >= 1.0.3) 27 | activejob (5.1.1) 28 | activesupport (= 5.1.1) 29 | globalid (>= 0.3.6) 30 | activemodel (5.1.1) 31 | activesupport (= 5.1.1) 32 | activerecord (5.1.1) 33 | activemodel (= 5.1.1) 34 | activesupport (= 5.1.1) 35 | arel (~> 8.0) 36 | activesupport (5.1.1) 37 | concurrent-ruby (~> 1.0, >= 1.0.2) 38 | i18n (~> 0.7) 39 | minitest (~> 5.1) 40 | tzinfo (~> 1.1) 41 | arel (8.0.0) 42 | builder (3.2.3) 43 | concurrent-ruby (1.0.5) 44 | erubi (1.6.0) 45 | globalid (0.4.0) 46 | activesupport (>= 4.2.0) 47 | i18n (0.8.1) 48 | kindlerb (1.2.0) 49 | mustache 50 | nokogiri 51 | loofah (2.0.3) 52 | nokogiri (>= 1.5.9) 53 | mail (2.6.5) 54 | mime-types (>= 1.16, < 4) 55 | method_source (0.8.2) 56 | mime-types (3.1) 57 | mime-types-data (~> 3.2015) 58 | mime-types-data (3.2016.0521) 59 | mini_portile2 (2.1.0) 60 | minitest (5.10.2) 61 | mustache (1.0.5) 62 | nio4r (2.0.0) 63 | nokogiri (1.7.2) 64 | mini_portile2 (~> 2.1.0) 65 | rack (2.0.3) 66 | rack-test (0.6.3) 67 | rack (>= 1.0) 68 | rails (5.1.1) 69 | actioncable (= 5.1.1) 70 | actionmailer (= 5.1.1) 71 | actionpack (= 5.1.1) 72 | actionview (= 5.1.1) 73 | activejob (= 5.1.1) 74 | activemodel (= 5.1.1) 75 | activerecord (= 5.1.1) 76 | activesupport (= 5.1.1) 77 | bundler (>= 1.3.0, < 2.0) 78 | railties (= 5.1.1) 79 | sprockets-rails (>= 2.0.0) 80 | rails-dom-testing (2.0.3) 81 | activesupport (>= 4.2.0) 82 | nokogiri (>= 1.6) 83 | rails-html-sanitizer (1.0.3) 84 | loofah (~> 2.0) 85 | railties (5.1.1) 86 | actionpack (= 5.1.1) 87 | activesupport (= 5.1.1) 88 | method_source 89 | rake (>= 0.8.7) 90 | thor (>= 0.18.1, < 2.0) 91 | rake (12.0.0) 92 | redcarpet (3.4.0) 93 | sprockets (3.7.1) 94 | concurrent-ruby (~> 1.0) 95 | rack (> 1, < 3) 96 | sprockets-rails (3.2.0) 97 | actionpack (>= 4.0) 98 | activesupport (>= 4.0) 99 | sprockets (>= 3.0.0) 100 | thor (0.19.4) 101 | thread_safe (0.3.6) 102 | tzinfo (1.2.3) 103 | thread_safe (~> 0.1) 104 | websocket-driver (0.6.5) 105 | websocket-extensions (>= 0.1.0) 106 | websocket-extensions (0.1.2) 107 | 108 | PLATFORMS 109 | ruby 110 | 111 | DEPENDENCIES 112 | kindlerb (= 1.2.0) 113 | rails (= 5.1.1) 114 | redcarpet 115 | 116 | BUNDLED WITH 117 | 1.14.6 118 | -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # Rails Guide 中文翻译 2 | 3 | ## 构建(基于 Docker) 4 | 5 | 为了管理依赖,建议使用 Docker。 6 | 7 | ### 安装 Docker 8 | 9 | https://www.docker.com/ 10 | 11 | ### 构建镜像 12 | 13 | ```bash 14 | $ docker build -t rails-guides . 15 | ``` 16 | 17 | ### 构建 18 | 19 | ```bash 20 | $ docker run -it -v $(pwd):/app rails-guides rake guides:generate:html 21 | $ docker run -it -v $(pwd):/app rails-guides rake guides:generate:kindle 22 | ``` 23 | 24 | ## 发布 25 | 26 | 另外 clone 一份 repo,checkout 到 `gh-pages` 分支,将 HTML 版内容拷贝进去,commit,push。 27 | 28 | Kindle 版通过 GitHub Release 发布。 29 | -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | namespace :guides do 2 | desc 'Generate guides (for authors), use ONLY=foo to process just "foo.md"' 3 | task generate: "generate:html" 4 | 5 | # Guides are written in UTF-8, but the environment may be configured for some 6 | # other locale, these tasks are responsible for ensuring the default external 7 | # encoding is UTF-8. 8 | # 9 | # Real use cases: Generation was reported to fail on a machine configured with 10 | # GBK (Chinese). The docs server once got misconfigured somehow and had "C", 11 | # which broke generation too. 12 | task :encoding do 13 | %w(LANG LANGUAGE LC_ALL).each do |env_var| 14 | ENV[env_var] = "en_US.UTF-8" 15 | end 16 | end 17 | 18 | namespace :generate do 19 | desc "Generate HTML guides" 20 | task html: :encoding do 21 | ENV["WARNINGS"] = "1" # authors can't disable this 22 | ruby "-E UTF-8 rails_guides.rb" 23 | end 24 | 25 | desc "Generate .mobi file. The kindlegen executable must be in your PATH. You can get it for free from http://www.amazon.com/gp/feature.html?docId=1000765211" 26 | task kindle: :encoding do 27 | require "kindlerb" 28 | unless Kindlerb.kindlegen_available? 29 | abort "Please run `setupkindlerb` to install kindlegen" 30 | end 31 | unless `convert` =~ /convert/ 32 | abort "Please install ImageMagick" 33 | end 34 | ENV["KINDLE"] = "1" 35 | Rake::Task["guides:generate:html"].invoke 36 | end 37 | end 38 | 39 | # Validate guides ------------------------------------------------------------------------- 40 | desc 'Validate guides, use ONLY=foo to process just "foo.html"' 41 | task validate: :encoding do 42 | ruby "w3c_validator.rb" 43 | end 44 | 45 | desc "Show help" 46 | task :help do 47 | puts < folder (such as source/es) 79 | 80 | Examples: 81 | $ rake guides:generate ALL=1 RAILS_VERSION=v5.1.0 82 | $ rake guides:generate ONLY=migrations 83 | $ rake guides:generate:kindle 84 | $ rake guides:generate GUIDES_LANGUAGE=es 85 | HELP 86 | end 87 | end 88 | 89 | task default: "guides:help" 90 | -------------------------------------------------------------------------------- /assets/images/akshaysurve.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/akshaysurve.jpg -------------------------------------------------------------------------------- /assets/images/belongs_to.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/belongs_to.png -------------------------------------------------------------------------------- /assets/images/book_icon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/book_icon.gif -------------------------------------------------------------------------------- /assets/images/bullet.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/bullet.gif -------------------------------------------------------------------------------- /assets/images/chapters_icon.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/chapters_icon.gif -------------------------------------------------------------------------------- /assets/images/check_bullet.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/check_bullet.gif -------------------------------------------------------------------------------- /assets/images/credits_pic_blank.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/credits_pic_blank.gif -------------------------------------------------------------------------------- /assets/images/csrf.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/csrf.png -------------------------------------------------------------------------------- /assets/images/edge_badge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/edge_badge.png -------------------------------------------------------------------------------- /assets/images/favicon.ico: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/favicon.ico -------------------------------------------------------------------------------- /assets/images/feature_tile.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/feature_tile.gif -------------------------------------------------------------------------------- /assets/images/footer_tile.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/footer_tile.gif -------------------------------------------------------------------------------- /assets/images/fxn.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/fxn.png -------------------------------------------------------------------------------- /assets/images/getting_started/article_with_comments.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/getting_started/article_with_comments.png -------------------------------------------------------------------------------- /assets/images/getting_started/challenge.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/getting_started/challenge.png -------------------------------------------------------------------------------- /assets/images/getting_started/confirm_dialog.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/getting_started/confirm_dialog.png -------------------------------------------------------------------------------- /assets/images/getting_started/forbidden_attributes_for_new_article.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/getting_started/forbidden_attributes_for_new_article.png -------------------------------------------------------------------------------- /assets/images/getting_started/form_with_errors.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/getting_started/form_with_errors.png -------------------------------------------------------------------------------- /assets/images/getting_started/index_action_with_edit_link.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/getting_started/index_action_with_edit_link.png -------------------------------------------------------------------------------- /assets/images/getting_started/new_article.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/getting_started/new_article.png -------------------------------------------------------------------------------- /assets/images/getting_started/rails_welcome.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/getting_started/rails_welcome.png -------------------------------------------------------------------------------- /assets/images/getting_started/routing_error_no_controller.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/getting_started/routing_error_no_controller.png -------------------------------------------------------------------------------- /assets/images/getting_started/routing_error_no_route_matches.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/getting_started/routing_error_no_route_matches.png -------------------------------------------------------------------------------- /assets/images/getting_started/show_action_for_articles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/getting_started/show_action_for_articles.png -------------------------------------------------------------------------------- /assets/images/getting_started/template_is_missing_articles_new.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/getting_started/template_is_missing_articles_new.png -------------------------------------------------------------------------------- /assets/images/getting_started/unknown_action_create_for_articles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/getting_started/unknown_action_create_for_articles.png -------------------------------------------------------------------------------- /assets/images/getting_started/unknown_action_new_for_articles.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/getting_started/unknown_action_new_for_articles.png -------------------------------------------------------------------------------- /assets/images/grey_bullet.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/grey_bullet.gif -------------------------------------------------------------------------------- /assets/images/habtm.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/habtm.png -------------------------------------------------------------------------------- /assets/images/has_many.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/has_many.png -------------------------------------------------------------------------------- /assets/images/has_many_through.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/has_many_through.png -------------------------------------------------------------------------------- /assets/images/has_one.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/has_one.png -------------------------------------------------------------------------------- /assets/images/has_one_through.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/has_one_through.png -------------------------------------------------------------------------------- /assets/images/header_backdrop.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/header_backdrop.png -------------------------------------------------------------------------------- /assets/images/header_tile.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/header_tile.gif -------------------------------------------------------------------------------- /assets/images/i18n/demo_html_safe.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/i18n/demo_html_safe.png -------------------------------------------------------------------------------- /assets/images/i18n/demo_localized_pirate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/i18n/demo_localized_pirate.png -------------------------------------------------------------------------------- /assets/images/i18n/demo_translated_en.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/i18n/demo_translated_en.png -------------------------------------------------------------------------------- /assets/images/i18n/demo_translated_pirate.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/i18n/demo_translated_pirate.png -------------------------------------------------------------------------------- /assets/images/i18n/demo_translation_missing.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/i18n/demo_translation_missing.png -------------------------------------------------------------------------------- /assets/images/i18n/demo_untranslated.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/i18n/demo_untranslated.png -------------------------------------------------------------------------------- /assets/images/icons/README: -------------------------------------------------------------------------------- 1 | Replaced the plain DocBook XSL admonition icons with Jimmac's DocBook 2 | icons (http://jimmac.musichall.cz/ikony.php3). I dropped transparency 3 | from the Jimmac icons to get round MS IE and FOP PNG incompatibilities. 4 | 5 | Stuart Rackham 6 | -------------------------------------------------------------------------------- /assets/images/icons/callouts/1.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/callouts/1.png -------------------------------------------------------------------------------- /assets/images/icons/callouts/10.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/callouts/10.png -------------------------------------------------------------------------------- /assets/images/icons/callouts/11.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/callouts/11.png -------------------------------------------------------------------------------- /assets/images/icons/callouts/12.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/callouts/12.png -------------------------------------------------------------------------------- /assets/images/icons/callouts/13.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/callouts/13.png -------------------------------------------------------------------------------- /assets/images/icons/callouts/14.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/callouts/14.png -------------------------------------------------------------------------------- /assets/images/icons/callouts/15.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/callouts/15.png -------------------------------------------------------------------------------- /assets/images/icons/callouts/2.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/callouts/2.png -------------------------------------------------------------------------------- /assets/images/icons/callouts/3.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/callouts/3.png -------------------------------------------------------------------------------- /assets/images/icons/callouts/4.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/callouts/4.png -------------------------------------------------------------------------------- /assets/images/icons/callouts/5.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/callouts/5.png -------------------------------------------------------------------------------- /assets/images/icons/callouts/6.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/callouts/6.png -------------------------------------------------------------------------------- /assets/images/icons/callouts/7.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/callouts/7.png -------------------------------------------------------------------------------- /assets/images/icons/callouts/8.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/callouts/8.png -------------------------------------------------------------------------------- /assets/images/icons/callouts/9.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/callouts/9.png -------------------------------------------------------------------------------- /assets/images/icons/caution.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/caution.png -------------------------------------------------------------------------------- /assets/images/icons/example.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/example.png -------------------------------------------------------------------------------- /assets/images/icons/home.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/home.png -------------------------------------------------------------------------------- /assets/images/icons/important.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/important.png -------------------------------------------------------------------------------- /assets/images/icons/next.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/next.png -------------------------------------------------------------------------------- /assets/images/icons/note.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/note.png -------------------------------------------------------------------------------- /assets/images/icons/prev.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/prev.png -------------------------------------------------------------------------------- /assets/images/icons/tip.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/tip.png -------------------------------------------------------------------------------- /assets/images/icons/up.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/up.png -------------------------------------------------------------------------------- /assets/images/icons/warning.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/icons/warning.png -------------------------------------------------------------------------------- /assets/images/nav_arrow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/nav_arrow.gif -------------------------------------------------------------------------------- /assets/images/oscardelben.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/oscardelben.jpg -------------------------------------------------------------------------------- /assets/images/polymorphic.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/polymorphic.png -------------------------------------------------------------------------------- /assets/images/radar.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/radar.png -------------------------------------------------------------------------------- /assets/images/rails4_features.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/rails4_features.png -------------------------------------------------------------------------------- /assets/images/rails_guides_kindle_cover.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/rails_guides_kindle_cover.jpg -------------------------------------------------------------------------------- /assets/images/rails_guides_logo.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/rails_guides_logo.gif -------------------------------------------------------------------------------- /assets/images/rails_logo_remix.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/rails_logo_remix.gif -------------------------------------------------------------------------------- /assets/images/session_fixation.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/session_fixation.png -------------------------------------------------------------------------------- /assets/images/tab_grey.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/tab_grey.gif -------------------------------------------------------------------------------- /assets/images/tab_info.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/tab_info.gif -------------------------------------------------------------------------------- /assets/images/tab_note.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/tab_note.gif -------------------------------------------------------------------------------- /assets/images/tab_red.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/tab_red.gif -------------------------------------------------------------------------------- /assets/images/tab_yellow.gif: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/tab_yellow.gif -------------------------------------------------------------------------------- /assets/images/tab_yellow.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/tab_yellow.png -------------------------------------------------------------------------------- /assets/images/vijaydev.jpg: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/ruby-china/rails-guides/25df24f26a188d0e68e47729fb711a2d52b2fbcc/assets/images/vijaydev.jpg -------------------------------------------------------------------------------- /assets/javascripts/guides.js: -------------------------------------------------------------------------------- 1 | $.fn.selectGuide = function(guide) { 2 | $("select", this).val(guide); 3 | }; 4 | 5 | var guidesIndex = { 6 | bind: function() { 7 | var currentGuidePath = window.location.pathname; 8 | var currentGuide = currentGuidePath.substring(currentGuidePath.lastIndexOf("/")+1); 9 | $(".guides-index-small"). 10 | on("change", "select", guidesIndex.navigate). 11 | selectGuide(currentGuide); 12 | $(document).on("click", ".more-info-button", function(e){ 13 | e.stopPropagation(); 14 | if ($(".more-info-links").is(":visible")) { 15 | $(".more-info-links").addClass("s-hidden").unwrap(); 16 | } else { 17 | $(".more-info-links").wrap("
").removeClass("s-hidden"); 18 | } 19 | }); 20 | $("#guidesMenu").on("click", function(e) { 21 | $("#guides").toggle(); 22 | return false; 23 | }); 24 | $(document).on("click", function(e){ 25 | e.stopPropagation(); 26 | var $button = $(".more-info-button"); 27 | var element; 28 | 29 | // Cross browser find the element that had the event 30 | if (e.target) element = e.target; 31 | else if (e.srcElement) element = e.srcElement; 32 | 33 | // Defeat the older Safari bug: 34 | // http://www.quirksmode.org/js/events_properties.html 35 | if (element.nodeType === 3) element = element.parentNode; 36 | 37 | var $element = $(element); 38 | 39 | var $container = $element.parents(".more-info-container"); 40 | 41 | // We've captured a click outside the popup 42 | if($container.length === 0){ 43 | $container = $button.next(".more-info-container"); 44 | $container.find(".more-info-links").addClass("s-hidden").unwrap(); 45 | } 46 | }); 47 | }, 48 | navigate: function(e){ 49 | var $list = $(e.target); 50 | var url = $list.val(); 51 | window.location = url; 52 | } 53 | }; 54 | -------------------------------------------------------------------------------- /assets/javascripts/responsive-tables.js: -------------------------------------------------------------------------------- 1 | $(document).ready(function() { 2 | var switched = false; 3 | $("table").not(".syntaxhighlighter").addClass("responsive"); 4 | var updateTables = function() { 5 | if (($(window).width() < 767) && !switched ){ 6 | switched = true; 7 | $("table.responsive").each(function(i, element) { 8 | splitTable($(element)); 9 | }); 10 | return true; 11 | } 12 | else if (switched && ($(window).width() > 767)) { 13 | switched = false; 14 | $("table.responsive").each(function(i, element) { 15 | unsplitTable($(element)); 16 | }); 17 | } 18 | }; 19 | 20 | $(window).load(updateTables); 21 | $(window).bind("resize", updateTables); 22 | 23 | 24 | function splitTable(original) 25 | { 26 | original.wrap("
"); 27 | 28 | var copy = original.clone(); 29 | copy.find("td:not(:first-child), th:not(:first-child)").css("display", "none"); 30 | copy.removeClass("responsive"); 31 | 32 | original.closest(".table-wrapper").append(copy); 33 | copy.wrap("
"); 34 | original.wrap("
"); 35 | } 36 | 37 | function unsplitTable(original) { 38 | original.closest(".table-wrapper").find(".pinned").remove(); 39 | original.unwrap(); 40 | original.unwrap(); 41 | } 42 | 43 | }); 44 | -------------------------------------------------------------------------------- /assets/stylesheets/fixes.css: -------------------------------------------------------------------------------- 1 | /* 2 | Fix a rendering issue affecting WebKits on Mac. 3 | See https://github.com/lifo/docrails/issues#issue/16 for more information. 4 | */ 5 | .syntaxhighlighter a, 6 | .syntaxhighlighter div, 7 | .syntaxhighlighter code, 8 | .syntaxhighlighter table, 9 | .syntaxhighlighter table td, 10 | .syntaxhighlighter table tr, 11 | .syntaxhighlighter table tbody, 12 | .syntaxhighlighter table thead, 13 | .syntaxhighlighter table caption, 14 | .syntaxhighlighter textarea { 15 | line-height: 1.25em !important; 16 | } 17 | -------------------------------------------------------------------------------- /assets/stylesheets/kindle.css: -------------------------------------------------------------------------------- 1 | p { text-indent: 0; } 2 | 3 | p, H1, H2, H3, H4, H5, H6, H7, H8, table { margin-top: 1em;} 4 | 5 | .pagebreak { page-break-before: always; } 6 | #toc H3 { 7 | text-indent: 1em; 8 | } 9 | #toc .document { 10 | text-indent: 2em; 11 | } -------------------------------------------------------------------------------- /assets/stylesheets/print.css: -------------------------------------------------------------------------------- 1 | /* Guides.rubyonrails.org */ 2 | /* Print.css */ 3 | /* Created January 30, 2009 */ 4 | /* Modified January 31, 2009 5 | --------------------------------------- */ 6 | 7 | body, .wrapper, .note, .info, code, #topNav, .L, .R, #frame, #container, #header, #navigation, #footer, #feature, #mainCol, #subCol, #extraCol, .content {position: static; text-align: left; text-indent: 0; background: White; color: Black; border-color: Black; width: auto; height: auto; display: block; float: none; min-height: 0; margin: 0; padding: 0;} 8 | 9 | body { 10 | background: #FFF; 11 | font-size: 10pt !important; 12 | font-family: "Helvetica Neue", Helvetica, Arial, sans-serif; 13 | line-height: 1.5; 14 | color: #000; 15 | padding: 0 3%; 16 | } 17 | 18 | .hide, .nav { 19 | display: none !important; 20 | } 21 | 22 | a:link, a:visited { 23 | background: transparent; 24 | font-weight: bold; 25 | text-decoration: underline; 26 | } 27 | 28 | hr { 29 | background:#ccc; 30 | color:#ccc; 31 | width:100%; 32 | height:2px; 33 | margin:2em 0; 34 | padding:0; 35 | border:none; 36 | } 37 | 38 | h1,h2,h3,h4,h5,h6 { font-family: "Helvetica Neue", Arial, "Lucida Grande", sans-serif; } 39 | code { font:.9em "Courier New", Monaco, Courier, monospace; display:inline} 40 | 41 | img { float:left; margin:1.5em 1.5em 1.5em 0; } 42 | a img { border:none; } 43 | 44 | blockquote { 45 | margin:1.5em; 46 | padding:1em; 47 | font-style:italic; 48 | font-size:.9em; 49 | } 50 | 51 | .small { font-size: .9em; } 52 | .large { font-size: 1.1em; } 53 | -------------------------------------------------------------------------------- /assets/stylesheets/reset.css: -------------------------------------------------------------------------------- 1 | /* Guides.rubyonrails.org */ 2 | /* Reset.css */ 3 | /* Created January 30, 2009 4 | --------------------------------------- */ 5 | 6 | html, body, div, span, applet, object, iframe, 7 | h1, h2, h3, h4, h5, h6, p, blockquote, pre, 8 | a, abbr, acronym, address, big, cite, code, 9 | del, dfn, em, font, img, ins, kbd, q, s, samp, 10 | small, strike, strong, sub, sup, tt, var, 11 | b, u, i, center, 12 | dl, dt, dd, ol, ul, li, 13 | fieldset, form, label, legend, 14 | table, caption, tbody, tfoot, thead, tr, th, td { 15 | margin: 0; 16 | padding: 0; 17 | border: 0; 18 | outline: 0; 19 | font-size: 100%; 20 | background: transparent; 21 | } 22 | 23 | body {line-height: 1; color: black; background: white;} 24 | a img {border:none;} 25 | ins {text-decoration: none;} 26 | del {text-decoration: line-through;} 27 | 28 | :focus { 29 | -moz-outline:0; 30 | outline:0; 31 | outline-offset:0; 32 | } 33 | 34 | /* tables still need 'cellspacing="0"' in the markup */ 35 | table {border-collapse: collapse; border-spacing: 0;} 36 | caption, th, td {text-align: left; font-weight: normal;} 37 | 38 | blockquote, q {quotes: none;} 39 | blockquote:before, blockquote:after, 40 | q:before, q:after { 41 | content: ''; 42 | content: none; 43 | } 44 | -------------------------------------------------------------------------------- /assets/stylesheets/responsive-tables.css: -------------------------------------------------------------------------------- 1 | /* Foundation v2.1.4 http://foundation.zurb.com */ 2 | /* Artfully masterminded by ZURB */ 3 | 4 | /* -------------------------------------------------- 5 | Table of Contents 6 | ----------------------------------------------------- 7 | :: Shared Styles 8 | :: Page Name 1 9 | :: Page Name 2 10 | */ 11 | 12 | 13 | /* ----------------------------------------- 14 | Shared Styles 15 | ----------------------------------------- */ 16 | 17 | table th { font-weight: bold; } 18 | table td, table th { padding: 9px 10px; text-align: left; } 19 | 20 | /* Mobile */ 21 | @media only screen and (max-width: 767px) { 22 | 23 | table { margin-bottom: 0; } 24 | 25 | .pinned { position: absolute; left: 0; top: 0; background: #fff; width: 35%; overflow: hidden; overflow-x: scroll; border-right: 1px solid #ccc; border-left: 1px solid #ccc; } 26 | .pinned table { border-right: none; border-left: none; width: 100%; } 27 | .pinned table th, .pinned table td { white-space: nowrap; } 28 | .pinned td:last-child { border-bottom: 0; } 29 | 30 | div.table-wrapper { position: relative; margin-bottom: 20px; overflow: hidden; border-right: 1px solid #ccc; } 31 | div.table-wrapper div.scrollable table { margin-left: 35%; } 32 | div.table-wrapper div.scrollable { overflow: scroll; overflow-y: hidden; } 33 | 34 | table td, table th { position: relative; white-space: nowrap; overflow: hidden; } 35 | table th:first-child, table td:first-child, table td:first-child, table.pinned td { display: none; } 36 | 37 | } 38 | 39 | /* ----------------------------------------- 40 | Page Name 1 41 | ----------------------------------------- */ 42 | 43 | 44 | 45 | 46 | /* ----------------------------------------- 47 | Page Name 2 48 | ----------------------------------------- */ 49 | 50 | 51 | -------------------------------------------------------------------------------- /assets/stylesheets/style.css: -------------------------------------------------------------------------------- 1 | /* Guides.rubyonrails.org */ 2 | /* Style.css */ 3 | /* Created January 30, 2009 4 | --------------------------------------- */ 5 | 6 | /* 7 | --------------------------------------- 8 | Import advanced style sheet 9 | --------------------------------------- 10 | */ 11 | 12 | @import url("reset.css"); 13 | @import url("main.css"); 14 | -------------------------------------------------------------------------------- /assets/stylesheets/syntaxhighlighter/shCore.css: -------------------------------------------------------------------------------- 1 | /** 2 | * SyntaxHighlighter 3 | * http://alexgorbatchev.com/SyntaxHighlighter 4 | * 5 | * SyntaxHighlighter is donationware. If you are using it, please donate. 6 | * http://alexgorbatchev.com/SyntaxHighlighter/donate.html 7 | * 8 | * @version 9 | * 3.0.83 (July 02 2010) 10 | * 11 | * @copyright 12 | * Copyright (C) 2004-2010 Alex Gorbatchev. 13 | * 14 | * @license 15 | * Dual licensed under the MIT and GPL licenses. 16 | */ 17 | .syntaxhighlighter a, 18 | .syntaxhighlighter div, 19 | .syntaxhighlighter code, 20 | .syntaxhighlighter table, 21 | .syntaxhighlighter table td, 22 | .syntaxhighlighter table tr, 23 | .syntaxhighlighter table tbody, 24 | .syntaxhighlighter table thead, 25 | .syntaxhighlighter table caption, 26 | .syntaxhighlighter textarea { 27 | -moz-border-radius: 0 0 0 0 !important; 28 | -webkit-border-radius: 0 0 0 0 !important; 29 | background: none !important; 30 | border: 0 !important; 31 | bottom: auto !important; 32 | float: none !important; 33 | height: auto !important; 34 | left: auto !important; 35 | line-height: 1.1em !important; 36 | margin: 0 !important; 37 | outline: 0 !important; 38 | overflow: visible !important; 39 | padding: 0 !important; 40 | position: static !important; 41 | right: auto !important; 42 | text-align: left !important; 43 | top: auto !important; 44 | vertical-align: baseline !important; 45 | width: auto !important; 46 | box-sizing: content-box !important; 47 | font-family: "Consolas", "Bitstream Vera Sans Mono", "Courier New", Courier, monospace !important; 48 | font-weight: normal !important; 49 | font-style: normal !important; 50 | font-size: 1em !important; 51 | min-height: inherit !important; 52 | min-height: auto !important; 53 | } 54 | 55 | .syntaxhighlighter { 56 | width: 100% !important; 57 | margin: 1em 0 1em 0 !important; 58 | position: relative !important; 59 | overflow: auto !important; 60 | font-size: 1em !important; 61 | } 62 | .syntaxhighlighter.source { 63 | overflow: hidden !important; 64 | } 65 | .syntaxhighlighter .bold { 66 | font-weight: bold !important; 67 | } 68 | .syntaxhighlighter .italic { 69 | font-style: italic !important; 70 | } 71 | .syntaxhighlighter .line { 72 | white-space: pre !important; 73 | } 74 | .syntaxhighlighter table { 75 | width: 100% !important; 76 | } 77 | .syntaxhighlighter table caption { 78 | text-align: left !important; 79 | padding: .5em 0 0.5em 1em !important; 80 | } 81 | .syntaxhighlighter table td.code { 82 | width: 100% !important; 83 | } 84 | .syntaxhighlighter table td.code .container { 85 | position: relative !important; 86 | } 87 | .syntaxhighlighter table td.code .container textarea { 88 | box-sizing: border-box !important; 89 | position: absolute !important; 90 | left: 0 !important; 91 | top: 0 !important; 92 | width: 100% !important; 93 | height: 100% !important; 94 | border: none !important; 95 | background: white !important; 96 | padding-left: 1em !important; 97 | overflow: hidden !important; 98 | white-space: pre !important; 99 | } 100 | .syntaxhighlighter table td.gutter .line { 101 | text-align: right !important; 102 | padding: 0 0.5em 0 1em !important; 103 | } 104 | .syntaxhighlighter table td.code .line { 105 | padding: 0 1em !important; 106 | } 107 | .syntaxhighlighter.nogutter td.code .container textarea, .syntaxhighlighter.nogutter td.code .line { 108 | padding-left: 0em !important; 109 | } 110 | .syntaxhighlighter.show { 111 | display: block !important; 112 | } 113 | .syntaxhighlighter.collapsed table { 114 | display: none !important; 115 | } 116 | .syntaxhighlighter.collapsed .toolbar { 117 | padding: 0.1em 0.8em 0em 0.8em !important; 118 | font-size: 1em !important; 119 | position: static !important; 120 | width: auto !important; 121 | height: auto !important; 122 | } 123 | .syntaxhighlighter.collapsed .toolbar span { 124 | display: inline !important; 125 | margin-right: 1em !important; 126 | } 127 | .syntaxhighlighter.collapsed .toolbar span a { 128 | padding: 0 !important; 129 | display: none !important; 130 | } 131 | .syntaxhighlighter.collapsed .toolbar span a.expandSource { 132 | display: inline !important; 133 | } 134 | .syntaxhighlighter .toolbar { 135 | position: absolute !important; 136 | right: 1px !important; 137 | top: 1px !important; 138 | width: 11px !important; 139 | height: 11px !important; 140 | font-size: 10px !important; 141 | z-index: 10 !important; 142 | } 143 | .syntaxhighlighter .toolbar span.title { 144 | display: inline !important; 145 | } 146 | .syntaxhighlighter .toolbar a { 147 | display: block !important; 148 | text-align: center !important; 149 | text-decoration: none !important; 150 | padding-top: 1px !important; 151 | } 152 | .syntaxhighlighter .toolbar a.expandSource { 153 | display: none !important; 154 | } 155 | .syntaxhighlighter.ie { 156 | font-size: .9em !important; 157 | padding: 1px 0 1px 0 !important; 158 | } 159 | .syntaxhighlighter.ie .toolbar { 160 | line-height: 8px !important; 161 | } 162 | .syntaxhighlighter.ie .toolbar a { 163 | padding-top: 0px !important; 164 | } 165 | .syntaxhighlighter.printing .line.alt1 .content, 166 | .syntaxhighlighter.printing .line.alt2 .content, 167 | .syntaxhighlighter.printing .line.highlighted .number, 168 | .syntaxhighlighter.printing .line.highlighted.alt1 .content, 169 | .syntaxhighlighter.printing .line.highlighted.alt2 .content { 170 | background: none !important; 171 | } 172 | .syntaxhighlighter.printing .line .number { 173 | color: #bbbbbb !important; 174 | } 175 | .syntaxhighlighter.printing .line .content { 176 | color: black !important; 177 | } 178 | .syntaxhighlighter.printing .toolbar { 179 | display: none !important; 180 | } 181 | .syntaxhighlighter.printing a { 182 | text-decoration: none !important; 183 | } 184 | .syntaxhighlighter.printing .plain, .syntaxhighlighter.printing .plain a { 185 | color: black !important; 186 | } 187 | .syntaxhighlighter.printing .comments, .syntaxhighlighter.printing .comments a { 188 | color: #008200 !important; 189 | } 190 | .syntaxhighlighter.printing .string, .syntaxhighlighter.printing .string a { 191 | color: blue !important; 192 | } 193 | .syntaxhighlighter.printing .keyword { 194 | color: #006699 !important; 195 | font-weight: bold !important; 196 | } 197 | .syntaxhighlighter.printing .preprocessor { 198 | color: gray !important; 199 | } 200 | .syntaxhighlighter.printing .variable { 201 | color: #aa7700 !important; 202 | } 203 | .syntaxhighlighter.printing .value { 204 | color: #009900 !important; 205 | } 206 | .syntaxhighlighter.printing .functions { 207 | color: #ff1493 !important; 208 | } 209 | .syntaxhighlighter.printing .constants { 210 | color: #0066cc !important; 211 | } 212 | .syntaxhighlighter.printing .script { 213 | font-weight: bold !important; 214 | } 215 | .syntaxhighlighter.printing .color1, .syntaxhighlighter.printing .color1 a { 216 | color: gray !important; 217 | } 218 | .syntaxhighlighter.printing .color2, .syntaxhighlighter.printing .color2 a { 219 | color: #ff1493 !important; 220 | } 221 | .syntaxhighlighter.printing .color3, .syntaxhighlighter.printing .color3 a { 222 | color: red !important; 223 | } 224 | .syntaxhighlighter.printing .break, .syntaxhighlighter.printing .break a { 225 | color: black !important; 226 | } 227 | -------------------------------------------------------------------------------- /assets/stylesheets/syntaxhighlighter/shThemeRailsGuides.css: -------------------------------------------------------------------------------- 1 | /** 2 | * Theme by fxn, took shThemeEclipse.css as starting point. 3 | */ 4 | .syntaxhighlighter { 5 | background-color: #eee !important; 6 | font-family: "Anonymous Pro", "Inconsolata", "Menlo", "Consolas", "Bitstream Vera Sans Mono", "Courier New", monospace !important; 7 | overflow-y: hidden !important; 8 | overflow-x: auto !important; 9 | } 10 | .syntaxhighlighter .line.alt1 { 11 | background-color: #eee !important; 12 | } 13 | .syntaxhighlighter .line.alt2 { 14 | background-color: #eee !important; 15 | } 16 | .syntaxhighlighter .line.highlighted.alt1, .syntaxhighlighter .line.highlighted.alt2 { 17 | background-color: #c3defe !important; 18 | } 19 | .syntaxhighlighter .line.highlighted.number { 20 | color: #eee !important; 21 | } 22 | .syntaxhighlighter table caption { 23 | color: #222 !important; 24 | } 25 | .syntaxhighlighter .gutter { 26 | color: #787878 !important; 27 | } 28 | .syntaxhighlighter .gutter .line { 29 | border-right: 3px solid #d4d0c8 !important; 30 | } 31 | .syntaxhighlighter .gutter .line.highlighted { 32 | background-color: #d4d0c8 !important; 33 | color: #eee !important; 34 | } 35 | .syntaxhighlighter.printing .line .content { 36 | border: none !important; 37 | } 38 | .syntaxhighlighter.collapsed { 39 | overflow: visible !important; 40 | } 41 | .syntaxhighlighter.collapsed .toolbar { 42 | color: #3f5fbf !important; 43 | background: #eee !important; 44 | border: 1px solid #d4d0c8 !important; 45 | } 46 | .syntaxhighlighter.collapsed .toolbar a { 47 | color: #3f5fbf !important; 48 | } 49 | .syntaxhighlighter.collapsed .toolbar a:hover { 50 | color: #aa7700 !important; 51 | } 52 | .syntaxhighlighter .toolbar { 53 | color: #a0a0a0 !important; 54 | background: #d4d0c8 !important; 55 | border: none !important; 56 | } 57 | .syntaxhighlighter .toolbar a { 58 | color: #a0a0a0 !important; 59 | } 60 | .syntaxhighlighter .toolbar a:hover { 61 | color: red !important; 62 | } 63 | .syntaxhighlighter .plain, .syntaxhighlighter .plain a { 64 | color: #222 !important; 65 | } 66 | .syntaxhighlighter .comments, .syntaxhighlighter .comments a { 67 | color: #708090 !important; 68 | } 69 | .syntaxhighlighter .string, .syntaxhighlighter .string a { 70 | font-style: italic !important; 71 | color: #6588A8 !important; 72 | } 73 | .syntaxhighlighter .keyword { 74 | color: #64434d !important; 75 | } 76 | .syntaxhighlighter .preprocessor { 77 | color: #646464 !important; 78 | } 79 | .syntaxhighlighter .variable { 80 | color: #222 !important; 81 | } 82 | .syntaxhighlighter .value { 83 | color: #009900 !important; 84 | } 85 | .syntaxhighlighter .functions { 86 | color: #ff1493 !important; 87 | } 88 | .syntaxhighlighter .constants { 89 | color: #0066cc !important; 90 | } 91 | .syntaxhighlighter .script { 92 | color: #222 !important; 93 | background-color: transparent !important; 94 | } 95 | .syntaxhighlighter .color1, .syntaxhighlighter .color1 a { 96 | color: gray !important; 97 | } 98 | .syntaxhighlighter .color2, .syntaxhighlighter .color2 a { 99 | color: #222 !important; 100 | font-weight: bold !important; 101 | } 102 | .syntaxhighlighter .color3, .syntaxhighlighter .color3 a { 103 | color: red !important; 104 | } 105 | 106 | .syntaxhighlighter .xml .keyword { 107 | color: #64434d !important; 108 | font-weight: normal !important; 109 | } 110 | .syntaxhighlighter .xml .color1, .syntaxhighlighter .xml .color1 a { 111 | color: #7f007f !important; 112 | } 113 | .syntaxhighlighter .xml .string { 114 | font-style: italic !important; 115 | color: #6588A8 !important; 116 | } 117 | -------------------------------------------------------------------------------- /bug_report_templates/action_controller_gem.rb: -------------------------------------------------------------------------------- 1 | begin 2 | require 'bundler/inline' 3 | rescue LoadError => e 4 | $stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler' 5 | raise e 6 | end 7 | 8 | gemfile(true) do 9 | source 'https://rubygems.org' 10 | # Activate the gem you are reporting the issue against. 11 | gem 'rails', '5.0.0' 12 | end 13 | 14 | require 'rack/test' 15 | require 'action_controller/railtie' 16 | 17 | class TestApp < Rails::Application 18 | config.root = File.dirname(__FILE__) 19 | config.session_store :cookie_store, key: 'cookie_store_key' 20 | secrets.secret_token = 'secret_token' 21 | secrets.secret_key_base = 'secret_key_base' 22 | 23 | config.logger = Logger.new($stdout) 24 | Rails.logger = config.logger 25 | 26 | routes.draw do 27 | get '/' => 'test#index' 28 | end 29 | end 30 | 31 | class TestController < ActionController::Base 32 | include Rails.application.routes.url_helpers 33 | 34 | def index 35 | render plain: 'Home' 36 | end 37 | end 38 | 39 | require 'minitest/autorun' 40 | 41 | # Ensure backward compatibility with Minitest 4 42 | Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test) 43 | 44 | class BugTest < Minitest::Test 45 | include Rack::Test::Methods 46 | 47 | def test_returns_success 48 | get '/' 49 | assert last_response.ok? 50 | end 51 | 52 | private 53 | def app 54 | Rails.application 55 | end 56 | end 57 | -------------------------------------------------------------------------------- /bug_report_templates/action_controller_master.rb: -------------------------------------------------------------------------------- 1 | begin 2 | require 'bundler/inline' 3 | rescue LoadError => e 4 | $stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler' 5 | raise e 6 | end 7 | 8 | gemfile(true) do 9 | source 'https://rubygems.org' 10 | gem 'rails', github: 'rails/rails' 11 | end 12 | 13 | require 'action_controller/railtie' 14 | 15 | class TestApp < Rails::Application 16 | config.root = File.dirname(__FILE__) 17 | config.session_store :cookie_store, key: 'cookie_store_key' 18 | secrets.secret_token = 'secret_token' 19 | secrets.secret_key_base = 'secret_key_base' 20 | 21 | config.logger = Logger.new($stdout) 22 | Rails.logger = config.logger 23 | 24 | routes.draw do 25 | get '/' => 'test#index' 26 | end 27 | end 28 | 29 | class TestController < ActionController::Base 30 | include Rails.application.routes.url_helpers 31 | 32 | def index 33 | render plain: 'Home' 34 | end 35 | end 36 | 37 | require 'minitest/autorun' 38 | require 'rack/test' 39 | 40 | class BugTest < Minitest::Test 41 | include Rack::Test::Methods 42 | 43 | def test_returns_success 44 | get '/' 45 | assert last_response.ok? 46 | end 47 | 48 | private 49 | def app 50 | Rails.application 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /bug_report_templates/active_record_gem.rb: -------------------------------------------------------------------------------- 1 | begin 2 | require 'bundler/inline' 3 | rescue LoadError => e 4 | $stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler' 5 | raise e 6 | end 7 | 8 | gemfile(true) do 9 | source 'https://rubygems.org' 10 | # Activate the gem you are reporting the issue against. 11 | gem 'activerecord', '5.0.0' 12 | gem 'sqlite3' 13 | end 14 | 15 | require 'active_record' 16 | require 'minitest/autorun' 17 | require 'logger' 18 | 19 | # Ensure backward compatibility with Minitest 4 20 | Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test) 21 | 22 | # This connection will do for database-independent bug reports. 23 | ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:') 24 | ActiveRecord::Base.logger = Logger.new(STDOUT) 25 | 26 | ActiveRecord::Schema.define do 27 | create_table :posts, force: true do |t| 28 | end 29 | 30 | create_table :comments, force: true do |t| 31 | t.integer :post_id 32 | end 33 | end 34 | 35 | class Post < ActiveRecord::Base 36 | has_many :comments 37 | end 38 | 39 | class Comment < ActiveRecord::Base 40 | belongs_to :post 41 | end 42 | 43 | class BugTest < Minitest::Test 44 | def test_association_stuff 45 | post = Post.create! 46 | post.comments << Comment.create! 47 | 48 | assert_equal 1, post.comments.count 49 | assert_equal 1, Comment.count 50 | assert_equal post.id, Comment.first.post.id 51 | end 52 | end 53 | -------------------------------------------------------------------------------- /bug_report_templates/active_record_master.rb: -------------------------------------------------------------------------------- 1 | begin 2 | require 'bundler/inline' 3 | rescue LoadError => e 4 | $stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler' 5 | raise e 6 | end 7 | 8 | gemfile(true) do 9 | source 'https://rubygems.org' 10 | gem 'rails', github: 'rails/rails' 11 | gem 'sqlite3' 12 | end 13 | 14 | require 'active_record' 15 | require 'minitest/autorun' 16 | require 'logger' 17 | 18 | # This connection will do for database-independent bug reports. 19 | ActiveRecord::Base.establish_connection(adapter: 'sqlite3', database: ':memory:') 20 | ActiveRecord::Base.logger = Logger.new(STDOUT) 21 | 22 | ActiveRecord::Schema.define do 23 | create_table :posts, force: true do |t| 24 | end 25 | 26 | create_table :comments, force: true do |t| 27 | t.integer :post_id 28 | end 29 | end 30 | 31 | class Post < ActiveRecord::Base 32 | has_many :comments 33 | end 34 | 35 | class Comment < ActiveRecord::Base 36 | belongs_to :post 37 | end 38 | 39 | class BugTest < Minitest::Test 40 | def test_association_stuff 41 | post = Post.create! 42 | post.comments << Comment.create! 43 | 44 | assert_equal 1, post.comments.count 45 | assert_equal 1, Comment.count 46 | assert_equal post.id, Comment.first.post.id 47 | end 48 | end 49 | -------------------------------------------------------------------------------- /bug_report_templates/generic_gem.rb: -------------------------------------------------------------------------------- 1 | begin 2 | require 'bundler/inline' 3 | rescue LoadError => e 4 | $stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler' 5 | raise e 6 | end 7 | 8 | gemfile(true) do 9 | source 'https://rubygems.org' 10 | # Activate the gem you are reporting the issue against. 11 | gem 'activesupport', '5.0.0' 12 | end 13 | 14 | require 'active_support/core_ext/object/blank' 15 | require 'minitest/autorun' 16 | 17 | # Ensure backward compatibility with Minitest 4 18 | Minitest::Test = MiniTest::Unit::TestCase unless defined?(Minitest::Test) 19 | 20 | class BugTest < Minitest::Test 21 | def test_stuff 22 | assert "zomg".present? 23 | refute "".present? 24 | end 25 | end 26 | -------------------------------------------------------------------------------- /bug_report_templates/generic_master.rb: -------------------------------------------------------------------------------- 1 | begin 2 | require 'bundler/inline' 3 | rescue LoadError => e 4 | $stderr.puts 'Bundler version 1.10 or later is required. Please update your Bundler' 5 | raise e 6 | end 7 | 8 | gemfile(true) do 9 | source 'https://rubygems.org' 10 | gem 'rails', github: 'rails/rails' 11 | end 12 | 13 | require 'active_support' 14 | require 'active_support/core_ext/object/blank' 15 | require 'minitest/autorun' 16 | 17 | class BugTest < Minitest::Test 18 | def test_stuff 19 | assert "zomg".present? 20 | refute "".present? 21 | end 22 | end 23 | -------------------------------------------------------------------------------- /rails_guides.rb: -------------------------------------------------------------------------------- 1 | $:.unshift __dir__ 2 | 3 | require "rails_guides/generator" 4 | require "rails_guides/cn" # PLEASE DO NOT FORGET ME 5 | require "active_support/core_ext/object/blank" 6 | 7 | env_value = ->(name) { ENV[name].presence } 8 | env_flag = ->(name) { "1" == env_value[name] } 9 | 10 | version = env_value["RAILS_VERSION"] 11 | edge = `git rev-parse HEAD`.strip unless version 12 | 13 | RailsGuides::Generator.new( 14 | edge: edge, 15 | version: version, 16 | all: env_flag["ALL"], 17 | only: env_value["ONLY"], 18 | kindle: env_flag["KINDLE"], 19 | language: env_value["GUIDES_LANGUAGE"] 20 | ).generate 21 | -------------------------------------------------------------------------------- /rails_guides/cn.rb: -------------------------------------------------------------------------------- 1 | # Patch for zh-CN generation 2 | 3 | module RailsGuides 4 | class Markdown 5 | private 6 | 7 | def dom_id(nodes) 8 | dom_id = dom_id_text(nodes.last) 9 | 10 | # Fix duplicate node by prefix with its parent node 11 | if @node_ids[dom_id] 12 | if @node_ids[dom_id].size > 1 13 | duplicate_nodes = @node_ids.delete(dom_id) 14 | new_node_id = "#{duplicate_nodes[-2][:id]}-#{duplicate_nodes.last[:id]}" 15 | duplicate_nodes.last[:id] = new_node_id 16 | @node_ids[new_node_id] = duplicate_nodes 17 | end 18 | 19 | dom_id = "#{nodes[-2][:id]}-#{dom_id}" 20 | end 21 | 22 | @node_ids[dom_id] = nodes 23 | dom_id 24 | end 25 | 26 | def dom_id_text(node) 27 | if node.previous_element && node.previous_element.inner_html.include?('class="anchor"') 28 | node.previous_element.children.first['id'] 29 | else 30 | escaped_chars = Regexp.escape('\\/`*_{}[]()#+-.!:,;|&<>^~=\'"') 31 | 32 | node.text.downcase.gsub(/\?/, "-questionmark") 33 | .gsub(/!/, "-bang") 34 | .gsub(/[#{escaped_chars}]+/, " ").strip 35 | .gsub(/\s+/, "-") 36 | end 37 | end 38 | 39 | def generate_index 40 | if @headings_for_index.present? 41 | raw_index = "" 42 | @headings_for_index.each do |level, node, label| 43 | if level == 1 44 | raw_index += "1. [#{label}](##{node[:id]})\n" 45 | elsif level == 2 46 | raw_index += " * [#{label}](##{node[:id]})\n" 47 | end 48 | end 49 | 50 | @index = Nokogiri::HTML.fragment(engine.render(raw_index)).tap do |doc| 51 | doc.at("ol")[:class] = "chapters" 52 | end.to_html 53 | 54 | # Only change `Chapters' to `目录' 55 | @index = <<-INDEX.html_safe 56 |
57 |

目录

58 | #{@index} 59 |
60 | INDEX 61 | end 62 | end 63 | 64 | end 65 | end 66 | -------------------------------------------------------------------------------- /rails_guides/generator.rb: -------------------------------------------------------------------------------- 1 | require "set" 2 | require "fileutils" 3 | 4 | require "active_support/core_ext/string/output_safety" 5 | require "active_support/core_ext/object/blank" 6 | require "action_controller" 7 | require "action_view" 8 | 9 | require "rails_guides/markdown" 10 | require "rails_guides/indexer" 11 | require "rails_guides/helpers" 12 | require "rails_guides/levenshtein" 13 | 14 | module RailsGuides 15 | class Generator 16 | GUIDES_RE = /\.(?:erb|md)\z/ 17 | 18 | def initialize(edge:, version:, all:, only:, kindle:, language:) 19 | @edge = edge 20 | @version = version 21 | @all = all 22 | @only = only 23 | @kindle = kindle 24 | @language = language 25 | 26 | if @kindle 27 | check_for_kindlegen 28 | register_kindle_mime_types 29 | end 30 | 31 | initialize_dirs 32 | create_output_dir_if_needed 33 | initialize_markdown_renderer 34 | end 35 | 36 | def generate 37 | generate_guides 38 | copy_assets 39 | generate_mobi if @kindle 40 | end 41 | 42 | private 43 | 44 | def register_kindle_mime_types 45 | Mime::Type.register_alias("application/xml", :opf, %w(opf)) 46 | Mime::Type.register_alias("application/xml", :ncx, %w(ncx)) 47 | end 48 | 49 | def check_for_kindlegen 50 | if `which kindlegen`.blank? 51 | raise "Can't create a kindle version without `kindlegen`." 52 | end 53 | end 54 | 55 | def generate_mobi 56 | require "rails_guides/kindle" 57 | out = "#{@output_dir}/kindlegen.out" 58 | Kindle.generate(@output_dir, mobi, out) 59 | puts "(kindlegen log at #{out})." 60 | end 61 | 62 | def mobi 63 | mobi = "ruby_on_rails_guides_#{@version || @edge[0, 7]}" 64 | mobi += ".#{@language}" if @language 65 | mobi += ".mobi" 66 | end 67 | 68 | def initialize_dirs 69 | @guides_dir = File.expand_path("..", __dir__) 70 | 71 | @source_dir = "#{@guides_dir}/source" 72 | @source_dir += "/#{@language}" if @language 73 | 74 | @output_dir = "#{@guides_dir}/output" 75 | @output_dir += "/kindle" if @kindle 76 | @output_dir += "/#{@language}" if @language 77 | end 78 | 79 | def create_output_dir_if_needed 80 | FileUtils.mkdir_p(@output_dir) 81 | end 82 | 83 | def initialize_markdown_renderer 84 | Markdown::Renderer.edge = @edge 85 | Markdown::Renderer.version = @version 86 | end 87 | 88 | def generate_guides 89 | guides_to_generate.each do |guide| 90 | output_file = output_file_for(guide) 91 | generate_guide(guide, output_file) if generate?(guide, output_file) 92 | end 93 | end 94 | 95 | def guides_to_generate 96 | guides = Dir.entries(@source_dir).grep(GUIDES_RE) 97 | 98 | if @kindle 99 | Dir.entries("#{@source_dir}/kindle").grep(GUIDES_RE).map do |entry| 100 | next if entry == "KINDLE.md" 101 | guides << "kindle/#{entry}" 102 | end 103 | end 104 | 105 | @only ? select_only(guides) : guides 106 | end 107 | 108 | def select_only(guides) 109 | prefixes = @only.split(",").map(&:strip) 110 | guides.select do |guide| 111 | guide.start_with?("kindle", *prefixes) 112 | end 113 | end 114 | 115 | def copy_assets 116 | FileUtils.cp_r(Dir.glob("#{@guides_dir}/assets/*"), @output_dir) 117 | end 118 | 119 | def output_file_for(guide) 120 | if guide.end_with?(".md") 121 | guide.sub(/md\z/, "html") 122 | else 123 | guide.sub(/\.erb\z/, "") 124 | end 125 | end 126 | 127 | def output_path_for(output_file) 128 | File.join(@output_dir, File.basename(output_file)) 129 | end 130 | 131 | def generate?(source_file, output_file) 132 | fin = File.join(@source_dir, source_file) 133 | fout = output_path_for(output_file) 134 | @all || !File.exist?(fout) || File.mtime(fout) < File.mtime(fin) 135 | end 136 | 137 | def generate_guide(guide, output_file) 138 | output_path = output_path_for(output_file) 139 | puts "Generating #{guide} as #{output_file}" 140 | layout = @kindle ? "kindle/layout" : "layout" 141 | 142 | File.open(output_path, "w") do |f| 143 | view = ActionView::Base.new( 144 | @source_dir, 145 | edge: @edge, 146 | version: @version, 147 | mobi: "kindle/#{mobi}", 148 | language: @language 149 | ) 150 | view.extend(Helpers) 151 | 152 | if guide =~ /\.(\w+)\.erb$/ 153 | # Generate the special pages like the home. 154 | # Passing a template handler in the template name is deprecated. So pass the file name without the extension. 155 | result = view.render(layout: layout, formats: [$1], file: $`) 156 | else 157 | body = File.read("#{@source_dir}/#{guide}") 158 | result = RailsGuides::Markdown.new( 159 | view: view, 160 | layout: layout, 161 | edge: @edge, 162 | version: @version 163 | ).render(body) 164 | 165 | warn_about_broken_links(result) 166 | end 167 | 168 | f.write(result) 169 | end 170 | end 171 | 172 | def warn_about_broken_links(html) 173 | anchors = extract_anchors(html) 174 | check_fragment_identifiers(html, anchors) 175 | end 176 | 177 | def extract_anchors(html) 178 | # Markdown generates headers with IDs computed from titles. 179 | anchors = Set.new 180 | html.scan(/ Levenshtein.distance(fragment_identifier, b) 200 | } 201 | puts "*** BROKEN LINK: ##{fragment_identifier}, perhaps you meant ##{guess}." 202 | end 203 | end 204 | end 205 | end 206 | end 207 | -------------------------------------------------------------------------------- /rails_guides/helpers.rb: -------------------------------------------------------------------------------- 1 | require "yaml" 2 | 3 | module RailsGuides 4 | module Helpers 5 | def guide(name, url, options = {}, &block) 6 | link = content_tag(:a, href: url) { name } 7 | result = content_tag(:dt, link) 8 | 9 | if options[:work_in_progress] 10 | result << content_tag(:dd, "Work in progress", class: "work-in-progress") 11 | end 12 | 13 | result << content_tag(:dd, capture(&block)) 14 | result 15 | end 16 | 17 | def documents_by_section 18 | @documents_by_section ||= YAML.load_file(File.expand_path("../../source/#{@language ? @language + '/' : ''}documents.yaml", __FILE__)) 19 | end 20 | 21 | def documents_flat 22 | documents_by_section.flat_map { |section| section["documents"] } 23 | end 24 | 25 | def finished_documents(documents) 26 | documents.reject { |document| document["work_in_progress"] } 27 | end 28 | 29 | def docs_for_menu(position = nil) 30 | if position.nil? 31 | documents_by_section 32 | elsif position == "L" 33 | documents_by_section.to(3) 34 | else 35 | documents_by_section.from(4) 36 | end 37 | end 38 | 39 | def author(name, nick, image = "credits_pic_blank.gif", &block) 40 | image = "images/#{image}" 41 | 42 | result = tag(:img, src: image, class: "left pic", alt: name, width: 91, height: 91) 43 | result << content_tag(:h3, name) 44 | result << content_tag(:p, capture(&block)) 45 | content_tag(:div, result, class: "clearfix", id: nick) 46 | end 47 | 48 | def code(&block) 49 | c = capture(&block) 50 | content_tag(:code, c) 51 | end 52 | end 53 | end 54 | -------------------------------------------------------------------------------- /rails_guides/indexer.rb: -------------------------------------------------------------------------------- 1 | require "active_support/core_ext/object/blank" 2 | require "active_support/core_ext/string/inflections" 3 | 4 | module RailsGuides 5 | class Indexer 6 | attr_reader :body, :result, :warnings, :level_hash 7 | 8 | def initialize(body, warnings) 9 | @body = body 10 | @result = @body.dup 11 | @warnings = warnings 12 | end 13 | 14 | def index 15 | @level_hash = process(body) 16 | end 17 | 18 | private 19 | 20 | def process(string, current_level = 3, counters = [1]) 21 | s = StringScanner.new(string) 22 | 23 | level_hash = {} 24 | 25 | while !s.eos? 26 | re = %r{^h(\d)(?:\((#.*?)\))?\s*\.\s*(.*)$} 27 | s.match?(re) 28 | if matched = s.matched 29 | matched =~ re 30 | level, idx, title = $1.to_i, $2, $3.strip 31 | 32 | if level < current_level 33 | # This is needed. Go figure. 34 | return level_hash 35 | elsif level == current_level 36 | index = counters.join(".") 37 | idx ||= "#" + title_to_idx(title) 38 | 39 | raise "Parsing Fail" unless @result.sub!(matched, "h#{level}(#{idx}). #{index} #{title}") 40 | 41 | key = { 42 | title: title, 43 | id: idx 44 | } 45 | # Recurse 46 | counters << 1 47 | level_hash[key] = process(s.post_match, current_level + 1, counters) 48 | counters.pop 49 | 50 | # Increment the current level 51 | last = counters.pop 52 | counters << last + 1 53 | end 54 | end 55 | s.getch 56 | end 57 | level_hash 58 | end 59 | 60 | def title_to_idx(title) 61 | idx = title.strip.parameterize.sub(/^\d+/, "") 62 | if warnings && idx.blank? 63 | puts "BLANK ID: please put an explicit ID for section #{title}, as in h5(#my-id)" 64 | end 65 | idx 66 | end 67 | end 68 | end 69 | -------------------------------------------------------------------------------- /rails_guides/kindle.rb: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "kindlerb" 4 | require "nokogiri" 5 | require "fileutils" 6 | require "yaml" 7 | require "date" 8 | 9 | module Kindle 10 | extend self 11 | 12 | def generate(output_dir, mobi_outfile, logfile) 13 | output_dir = File.absolute_path(output_dir) 14 | Dir.chdir output_dir do 15 | puts "=> Using output dir: #{output_dir}" 16 | puts "=> Arranging html pages in document order" 17 | toc = File.read("toc.ncx") 18 | doc = Nokogiri::XML(toc).xpath("//ncx:content", "ncx" => "http://www.daisy.org/z3986/2005/ncx/") 19 | html_pages = doc.select { |c| c[:src] }.map { |c| c[:src] }.uniq 20 | 21 | generate_front_matter(html_pages) 22 | 23 | generate_sections(html_pages) 24 | 25 | generate_document_metadata(mobi_outfile) 26 | 27 | puts "Creating MOBI document with kindlegen. This may take a while." 28 | if Kindlerb.run(output_dir) 29 | puts "MOBI document generated at #{File.expand_path(mobi_outfile, output_dir)}" 30 | end 31 | end 32 | end 33 | 34 | def generate_front_matter(html_pages) 35 | frontmatter = [] 36 | html_pages.delete_if { |x| 37 | if x =~ /(toc|welcome|credits|copyright).html/ 38 | frontmatter << x unless x =~ /toc/ 39 | true 40 | end 41 | } 42 | html = frontmatter.map { |x| 43 | Nokogiri::HTML(File.open(x)).at("body").inner_html 44 | }.join("\n") 45 | 46 | fdoc = Nokogiri::HTML(html) 47 | fdoc.search("h3").each do |h3| 48 | h3.name = "h4" 49 | end 50 | fdoc.search("h2").each do |h2| 51 | h2.name = "h3" 52 | h2["id"] = h2.inner_text.gsub(/\s/, "-") 53 | end 54 | add_head_section fdoc, "Front Matter" 55 | File.open("frontmatter.html", "w") { |f| f.puts fdoc.to_html } 56 | html_pages.unshift "frontmatter.html" 57 | end 58 | 59 | def generate_sections(html_pages) 60 | FileUtils::rm_rf("sections/") 61 | html_pages.each_with_index do |page, section_idx| 62 | FileUtils::mkdir_p("sections/%03d" % section_idx) 63 | doc = Nokogiri::HTML(File.open(page)) 64 | title = doc.at("title").inner_text.gsub("Ruby on Rails Guides: ", "") 65 | title = page.capitalize.gsub(".html", "") if title.strip == "" 66 | File.open("sections/%03d/_section.txt" % section_idx, "w") { |f| f.puts title } 67 | doc.xpath("//h3[@id]").each_with_index do |h3, item_idx| 68 | subsection = h3.inner_text 69 | content = h3.xpath("./following-sibling::*").take_while { |x| x.name != "h3" }.map(&:to_html) 70 | item = Nokogiri::HTML(h3.to_html + content.join("\n")) 71 | item_path = "sections/%03d/%03d.html" % [section_idx, item_idx] 72 | add_head_section(item, subsection) 73 | item.search("img").each do |img| 74 | img["src"] = "#{Dir.pwd}/#{img['src']}" 75 | end 76 | item.xpath("//li/p").each { |p| p.swap(p.children); p.remove } 77 | File.open(item_path, "w") { |f| f.puts item.to_html } 78 | end 79 | end 80 | end 81 | 82 | def generate_document_metadata(mobi_outfile) 83 | puts "=> Generating _document.yml" 84 | x = Nokogiri::XML(File.open("rails_guides.opf")).remove_namespaces! 85 | cover_jpg = "#{Dir.pwd}/images/rails_guides_kindle_cover.jpg" 86 | cover_gif = cover_jpg.sub(/jpg$/, "gif") 87 | puts `convert #{cover_jpg} #{cover_gif}` 88 | document = { 89 | "doc_uuid" => x.at("package")["unique-identifier"], 90 | "title" => x.at("title").inner_text.gsub(/\(.*$/, " v2"), 91 | "publisher" => x.at("publisher").inner_text, 92 | "author" => x.at("creator").inner_text, 93 | "subject" => x.at("subject").inner_text, 94 | "date" => x.at("date").inner_text, 95 | "cover" => cover_gif, 96 | "masthead" => nil, 97 | "mobi_outfile" => mobi_outfile 98 | } 99 | puts document.to_yaml 100 | File.open("_document.yml", "w") { |f| f.puts document.to_yaml } 101 | end 102 | 103 | def add_head_section(doc, title) 104 | head = Nokogiri::XML::Node.new "head", doc 105 | title_node = Nokogiri::XML::Node.new "title", doc 106 | title_node.content = title 107 | title_node.parent = head 108 | css = Nokogiri::XML::Node.new "link", doc 109 | css["rel"] = "stylesheet" 110 | css["type"] = "text/css" 111 | css["href"] = "#{Dir.pwd}/stylesheets/kindle.css" 112 | css.parent = head 113 | doc.at("body").before head 114 | end 115 | end 116 | -------------------------------------------------------------------------------- /rails_guides/levenshtein.rb: -------------------------------------------------------------------------------- 1 | module RailsGuides 2 | module Levenshtein 3 | # This code is based directly on the Text gem implementation. 4 | # Copyright (c) 2006-2013 Paul Battley, Michael Neumann, Tim Fletcher. 5 | # 6 | # Returns a value representing the "cost" of transforming str1 into str2 7 | def self.distance(str1, str2) 8 | s = str1 9 | t = str2 10 | n = s.length 11 | m = t.length 12 | 13 | return m if (0 == n) 14 | return n if (0 == m) 15 | 16 | d = (0..m).to_a 17 | x = nil 18 | 19 | # avoid duplicating an enumerable object in the loop 20 | str2_codepoint_enumerable = str2.each_codepoint 21 | 22 | str1.each_codepoint.with_index do |char1, i| 23 | e = i + 1 24 | 25 | str2_codepoint_enumerable.with_index do |char2, j| 26 | cost = (char1 == char2) ? 0 : 1 27 | x = [ 28 | d[j + 1] + 1, # insertion 29 | e + 1, # deletion 30 | d[j] + cost # substitution 31 | ].min 32 | d[j] = e 33 | e = x 34 | end 35 | 36 | d[m] = x 37 | end 38 | 39 | return x 40 | end 41 | end 42 | end 43 | -------------------------------------------------------------------------------- /rails_guides/markdown.rb: -------------------------------------------------------------------------------- 1 | require "redcarpet" 2 | require "nokogiri" 3 | require "rails_guides/markdown/renderer" 4 | 5 | module RailsGuides 6 | class Markdown 7 | def initialize(view:, layout:, edge:, version:) 8 | @view = view 9 | @layout = layout 10 | @edge = edge 11 | @version = version 12 | @index_counter = Hash.new(0) 13 | @raw_header = "" 14 | @node_ids = {} 15 | end 16 | 17 | def render(body) 18 | @raw_body = body 19 | extract_raw_header_and_body 20 | generate_header 21 | generate_title 22 | generate_body 23 | generate_structure 24 | generate_index 25 | render_page 26 | end 27 | 28 | private 29 | 30 | def dom_id(nodes) 31 | dom_id = dom_id_text(nodes.last.text) 32 | 33 | # Fix duplicate node by prefix with its parent node 34 | if @node_ids[dom_id] 35 | if @node_ids[dom_id].size > 1 36 | duplicate_nodes = @node_ids.delete(dom_id) 37 | new_node_id = "#{duplicate_nodes[-2][:id]}-#{duplicate_nodes.last[:id]}" 38 | duplicate_nodes.last[:id] = new_node_id 39 | @node_ids[new_node_id] = duplicate_nodes 40 | end 41 | 42 | dom_id = "#{nodes[-2][:id]}-#{dom_id}" 43 | end 44 | 45 | @node_ids[dom_id] = nodes 46 | dom_id 47 | end 48 | 49 | def dom_id_text(text) 50 | escaped_chars = Regexp.escape('\\/`*_{}[]()#+-.!:,;|&<>^~=\'"') 51 | 52 | text.downcase.gsub(/\?/, "-questionmark") 53 | .gsub(/!/, "-bang") 54 | .gsub(/[#{escaped_chars}]+/, " ").strip 55 | .gsub(/\s+/, "-") 56 | end 57 | 58 | def engine 59 | @engine ||= Redcarpet::Markdown.new(Renderer, 60 | no_intra_emphasis: true, 61 | fenced_code_blocks: true, 62 | autolink: true, 63 | strikethrough: true, 64 | superscript: true, 65 | tables: true 66 | ) 67 | end 68 | 69 | def extract_raw_header_and_body 70 | if @raw_body =~ /^\-{40,}$/ 71 | @raw_header, _, @raw_body = @raw_body.partition(/^\-{40,}$/).map(&:strip) 72 | end 73 | end 74 | 75 | def generate_body 76 | @body = engine.render(@raw_body) 77 | end 78 | 79 | def generate_header 80 | @header = engine.render(@raw_header).html_safe 81 | end 82 | 83 | def generate_structure 84 | @headings_for_index = [] 85 | if @body.present? 86 | @body = Nokogiri::HTML.fragment(@body).tap do |doc| 87 | hierarchy = [] 88 | 89 | doc.children.each do |node| 90 | if node.name =~ /^h[3-6]$/ 91 | case node.name 92 | when "h3" 93 | hierarchy = [node] 94 | @headings_for_index << [1, node, node.inner_html] 95 | when "h4" 96 | hierarchy = hierarchy[0, 1] + [node] 97 | @headings_for_index << [2, node, node.inner_html] 98 | when "h5" 99 | hierarchy = hierarchy[0, 2] + [node] 100 | when "h6" 101 | hierarchy = hierarchy[0, 3] + [node] 102 | end 103 | 104 | node[:id] = dom_id(hierarchy) 105 | node.inner_html = "#{node_index(hierarchy)} #{node.inner_html}" 106 | end 107 | end 108 | end.to_html 109 | end 110 | end 111 | 112 | def generate_index 113 | if @headings_for_index.present? 114 | raw_index = "" 115 | @headings_for_index.each do |level, node, label| 116 | if level == 1 117 | raw_index += "1. [#{label}](##{node[:id]})\n" 118 | elsif level == 2 119 | raw_index += " * [#{label}](##{node[:id]})\n" 120 | end 121 | end 122 | 123 | @index = Nokogiri::HTML.fragment(engine.render(raw_index)).tap do |doc| 124 | doc.at("ol")[:class] = "chapters" 125 | end.to_html 126 | 127 | @index = <<-INDEX.html_safe 128 |
129 |

Chapters

130 | #{@index} 131 |
132 | INDEX 133 | end 134 | end 135 | 136 | def generate_title 137 | if heading = Nokogiri::HTML.fragment(@header).at(:h2) 138 | @title = "#{heading.text} — Ruby on Rails Guides" 139 | else 140 | @title = "Ruby on Rails Guides" 141 | end 142 | end 143 | 144 | def node_index(hierarchy) 145 | case hierarchy.size 146 | when 1 147 | @index_counter[2] = @index_counter[3] = @index_counter[4] = 0 148 | "#{@index_counter[1] += 1}" 149 | when 2 150 | @index_counter[3] = @index_counter[4] = 0 151 | "#{@index_counter[1]}.#{@index_counter[2] += 1}" 152 | when 3 153 | @index_counter[4] = 0 154 | "#{@index_counter[1]}.#{@index_counter[2]}.#{@index_counter[3] += 1}" 155 | when 4 156 | "#{@index_counter[1]}.#{@index_counter[2]}.#{@index_counter[3]}.#{@index_counter[4] += 1}" 157 | end 158 | end 159 | 160 | def render_page 161 | @view.content_for(:header_section) { @header } 162 | @view.content_for(:page_title) { @title } 163 | @view.content_for(:index_section) { @index } 164 | @view.render(layout: @layout, html: @body.html_safe) 165 | end 166 | end 167 | end 168 | -------------------------------------------------------------------------------- /rails_guides/markdown/renderer.rb: -------------------------------------------------------------------------------- 1 | module RailsGuides 2 | class Markdown 3 | class Renderer < Redcarpet::Render::HTML 4 | cattr_accessor :edge, :version 5 | 6 | def block_code(code, language) 7 | <<-HTML 8 |
9 |
 10 | #{ERB::Util.h(code)}
 11 | 
12 |
13 | HTML 14 | end 15 | 16 | def link(url, title, content) 17 | if url.start_with?("http://api.rubyonrails.org") 18 | %(#{content}) 19 | elsif title 20 | %(#{content}) 21 | else 22 | %(#{content}) 23 | end 24 | end 25 | 26 | def header(text, header_level) 27 | # Always increase the heading level by 1, so we can use h1, h2 heading in the document 28 | header_level += 1 29 | 30 | %(#{text}) 31 | end 32 | 33 | def paragraph(text) 34 | if text =~ %r{^NOTE:\s+Defined\s+in\s+(.*?)\.?$} 35 | %(

Defined in #{$1}.

) 36 | elsif text =~ /^(TIP|IMPORTANT|CAUTION|WARNING|NOTE|INFO|TODO)[.:]/ 37 | convert_notes(text) 38 | elsif text.include?("DO NOT READ THIS FILE ON GITHUB") 39 | elsif text =~ /^\[(\d+)\]:<\/sup> (.+)$/ 40 | linkback = %(#{$1}) 41 | %(

#{linkback} #{$2}

) 42 | else 43 | text = convert_footnotes(text) 44 | "

#{text}

" 45 | end 46 | end 47 | 48 | private 49 | 50 | def convert_footnotes(text) 51 | text.gsub(/\[(\d+)\]<\/sup>/i) do 52 | %() + 53 | %(#{$1}) 54 | end 55 | end 56 | 57 | def brush_for(code_type) 58 | case code_type 59 | when "ruby", "sql", "plain" 60 | code_type 61 | when "erb", "html+erb" 62 | "ruby; html-script: true" 63 | when "html" 64 | "xml" # HTML is understood, but there are .xml rules in the CSS 65 | else 66 | "plain" 67 | end 68 | end 69 | 70 | def convert_notes(body) 71 | # The following regexp detects special labels followed by a 72 | # paragraph, perhaps at the end of the document. 73 | # 74 | # It is important that we do not eat more than one newline 75 | # because formatting may be wrong otherwise. For example, 76 | # if a bulleted list follows the first item is not rendered 77 | # as a list item, but as a paragraph starting with a plain 78 | # asterisk. 79 | body.gsub(/^(TIP|IMPORTANT|CAUTION|WARNING|NOTE|INFO|TODO)[.:](.*?)(\n(?=\n)|\Z)/m) do 80 | css_class = \ 81 | case $1 82 | when "CAUTION", "IMPORTANT" 83 | "warning" 84 | when "TIP" 85 | "info" 86 | else 87 | $1.downcase 88 | end 89 | %(

#{$2.strip}

) 90 | end 91 | end 92 | 93 | def github_file_url(file_path) 94 | tree = version || edge 95 | 96 | root = file_path[%r{(.+)/}, 1] 97 | path = \ 98 | case root 99 | when "abstract_controller", "action_controller", "action_dispatch" 100 | "actionpack/lib/#{file_path}" 101 | when /\A(action|active)_/ 102 | "#{root.sub("_", "")}/lib/#{file_path}" 103 | else 104 | file_path 105 | end 106 | 107 | "https://github.com/rails/rails/tree/#{tree}/#{path}" 108 | end 109 | 110 | def api_link(url) 111 | if url =~ %r{http://api\.rubyonrails\.org/v\d+\.} 112 | url 113 | elsif edge 114 | url.sub("api", "edgeapi") 115 | else 116 | url.sub(/(?<=\.org)/, "/#{version}") 117 | end 118 | end 119 | end 120 | end 121 | end 122 | -------------------------------------------------------------------------------- /source/_license.html.erb: -------------------------------------------------------------------------------- 1 |

This work is licensed under a Creative Commons Attribution-ShareAlike 4.0 International License

2 |

"Rails", "Ruby on Rails", and the Rails logo are trademarks of David Heinemeier Hansson. All rights reserved.

3 | -------------------------------------------------------------------------------- /source/_welcome.html.erb: -------------------------------------------------------------------------------- 1 |

Ruby on Rails Guides (<%= @edge ? @edge[0, 7] : @version %>)

2 | 3 | <% if @edge %> 4 |

5 | These are Edge Guides, based on master@<%= @edge[0, 7] %>. 6 |

7 |

8 | If you are looking for the ones for the stable version, please check 9 | http://guides.rubyonrails.org instead. 10 |

11 | <% else %> 12 |

13 | These are the new guides for Rails 5.1 based on <%= @version %>. 14 | These guides are designed to make you immediately productive with Rails, and to help you understand how all of the pieces fit together. 15 |

16 | <% end %> 17 |

18 | The guides for earlier releases: 19 | Rails 5.0, 20 | Rails 4.2, 21 | Rails 4.1, 22 | Rails 4.0, 23 | Rails 3.2, and 24 | Rails 2.3. 25 |

26 | -------------------------------------------------------------------------------- /source/credits.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :page_title do %> 2 | Ruby on Rails Guides: Credits 3 | <% end %> 4 | 5 | <% content_for :header_section do %> 6 |

Credits

7 | 8 |

We'd like to thank the following people for their tireless contributions to this project.

9 | 10 | <% end %> 11 | 12 |

Rails Guides Reviewers

13 | 14 | <%= author('Vijay Dev', 'vijaydev', 'vijaydev.jpg') do %> 15 | Vijayakumar, found as Vijay Dev on the web, is a web applications developer and an open source enthusiast who lives in Chennai, India. He started using Rails in 2009 and began actively contributing to Rails documentation in late 2010. He tweets a lot and also blogs. 16 | <% end %> 17 | 18 | <%= author('Xavier Noria', 'fxn', 'fxn.png') do %> 19 | Xavier Noria has been into Ruby on Rails since 2005. He is a Rails core team member and enjoys combining his passion for Rails and his past life as a proofreader of math textbooks. Xavier is currently an independent Ruby on Rails consultant. Oh, he also tweets and can be found everywhere as "fxn". 20 | <% end %> 21 | 22 |

Rails Guides Designers

23 | 24 | <%= author('Jason Zimdars', 'jz') do %> 25 | Jason Zimdars is an experienced creative director and web designer who has lead UI and UX design for numerous websites and web applications. You can see more of his design and writing at Thinkcage.com or follow him on Twitter. 26 | <% end %> 27 | 28 |

Rails Guides Authors

29 | 30 | <%= author('Ryan Bigg', 'radar', 'radar.png') do %> 31 | Ryan Bigg works as a Rails developer at Marketplacer and has been working with Rails since 2006. He's the author of Multi Tenancy With Rails and co-author of Rails 4 in Action. He's written many gems which can be seen on his GitHub page and he also tweets prolifically as @ryanbigg. 32 | <% end %> 33 | 34 | <%= author('Oscar Del Ben', 'oscardelben', 'oscardelben.jpg') do %> 35 | Oscar Del Ben is a software engineer at Wildfire. He's a regular open source contributor (GitHub account) and tweets regularly at @oscardelben. 36 | <% end %> 37 | 38 | <%= author('Frederick Cheung', 'fcheung') do %> 39 | Frederick Cheung is Chief Wizard at Texperts where he has been using Rails since 2006. He is based in Cambridge (UK) and when not consuming fine ales he blogs at spacevatican.org. 40 | <% end %> 41 | 42 | <%= author('Tore Darell', 'toretore') do %> 43 | Tore Darell is an independent developer based in Menton, France who specialises in cruft-free web applications using Ruby, Rails and unobtrusive JavaScript. You can follow him on Twitter. 44 | <% end %> 45 | 46 | <%= author('Jeff Dean', 'zilkey') do %> 47 | Jeff Dean is a software engineer with Pivotal Labs. 48 | <% end %> 49 | 50 | <%= author('Mike Gunderloy', 'mgunderloy') do %> 51 | Mike Gunderloy is a consultant with ActionRails. He brings 25 years of experience in a variety of languages to bear on his current work with Rails. His near-daily links and other blogging can be found at A Fresh Cup and he twitters too much. 52 | <% end %> 53 | 54 | <%= author('Mikel Lindsaar', 'raasdnil') do %> 55 | Mikel Lindsaar has been working with Rails since 2006 and is the author of the Ruby Mail gem and core contributor (he helped re-write Action Mailer's API). Mikel is the founder of RubyX, has a blog and tweets. 56 | <% end %> 57 | 58 | <%= author('Cássio Marques', 'cmarques') do %> 59 | Cássio Marques is a Brazilian software developer working with different programming languages such as Ruby, JavaScript, CPP and Java, as an independent consultant. He blogs at /* CODIFICANDO */, which is mainly written in Portuguese, but will soon get a new section for posts with English translation. 60 | <% end %> 61 | 62 | <%= author('James Miller', 'bensie') do %> 63 | James Miller is a software developer for JK Tech in San Diego, CA. You can find James on GitHub, Gmail, Twitter, and Freenode as "bensie". 64 | <% end %> 65 | 66 | <%= author('Pratik Naik', 'lifo') do %> 67 | Pratik Naik is a Ruby on Rails developer at Basecamp and maintains a blog at has_many :bugs, :through => :rails. He also has a semi-active twitter account. 68 | <% end %> 69 | 70 | <%= author('Emilio Tagua', 'miloops') do %> 71 | Emilio Tagua —a.k.a. miloops— is an Argentinian entrepreneur, developer, open source contributor and Rails evangelist. Cofounder of Eventioz. He has been using Rails since 2006 and contributing since early 2008. Can be found at gmail, twitter, freenode, everywhere as "miloops". 72 | <% end %> 73 | 74 | <%= author('Heiko Webers', 'hawe') do %> 75 | Heiko Webers is the founder of bauland42, a German web application security consulting and development company focused on Ruby on Rails. He blogs at the Ruby on Rails Security Project. After 10 years of desktop application development, Heiko has rarely looked back. 76 | <% end %> 77 | 78 | <%= author('Akshay Surve', 'startupjockey', 'akshaysurve.jpg') do %> 79 | Akshay Surve is the Founder at DeltaX, hackathon specialist, a midnight code junkie and occasionally writes prose. You can connect with him on Twitter, Linkedin, Personal Blog or Quora. 80 | <% end %> 81 | -------------------------------------------------------------------------------- /source/index.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :page_title do %> 2 | Ruby on Rails Guides 3 | <% end %> 4 | 5 | <% content_for :header_section do %> 6 | <%= render 'welcome' %> 7 | <% end %> 8 | 9 | <% content_for :index_section do %> 10 |
11 |
12 |
13 |
Rails Guides are also available for <%= link_to 'Kindle', @mobi %>.
14 |
Guides marked with this icon are currently being worked on and will not be available in the Guides Index menu. While still useful, they may contain incomplete information and even errors. You can help by reviewing them and posting your comments and corrections.
15 |
16 |
17 | <% end %> 18 | 19 | <% documents_by_section.each do |section| %> 20 |

<%= section['name'] %>

21 |
22 | <% section['documents'].each do |document| %> 23 | <%= guide(document['name'], document['url'], work_in_progress: document['work_in_progress']) do %> 24 |

<%= document['description'] %>

25 | <% end %> 26 | <% end %> 27 |
28 | <% end %> 29 | -------------------------------------------------------------------------------- /source/kindle/copyright.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'license' %> -------------------------------------------------------------------------------- /source/kindle/layout.html.erb: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | <%= yield(:page_title) || 'Ruby on Rails Guides' %> 9 | 10 | 11 | 12 | 13 | 14 | 15 | <% if content_for? :header_section %> 16 | <%= yield :header_section %> 17 |
18 | <% end %> 19 | 20 | <% if content_for? :index_section %> 21 | <%= yield :index_section %> 22 |
23 | <% end %> 24 | 25 | <%= yield.html_safe %> 26 | 27 | 28 | -------------------------------------------------------------------------------- /source/kindle/rails_guides.opf.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Ruby on Rails Guides (<%= @version || "master@#{@edge[0, 7]}" %>) 9 | 10 | en-us 11 | Ruby on Rails 12 | Ruby on Rails 13 | Reference 14 | <%= Time.now.strftime('%Y-%m-%d') %> 15 | 16 | These guides are designed to make you immediately productive with Rails, and to help you understand how all of the pieces fit together. 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | <% documents_flat.each do |document| %> 26 | 27 | <% end %> 28 | 29 | <% %w{toc.html credits.html welcome.html copyright.html}.each do |url| %> 30 | 31 | <% end %> 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | <% documents_flat.each do |document| %> 44 | 45 | <% end %> 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /source/kindle/toc.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :page_title do %> 2 | Ruby on Rails Guides 3 | <% end %> 4 | 5 |

Table of Contents

6 |
7 | 8 | <% documents_by_section.each_with_index do |section, i| %> 9 |

<%= "#{i + 1}." %> <%= section['name'] %>

10 |
    11 | <% section['documents'].each do |document| %> 12 |
  • 13 | <%= document['name'] %> 14 | <% if document['work_in_progress']%>(WIP)<% end %> 15 |
  • 16 | <% end %> 17 |
18 | <% end %> 19 |
20 | 24 |
25 | -------------------------------------------------------------------------------- /source/kindle/toc.ncx.erb: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Ruby on Rails Guides 13 | docrails 14 | 15 | 16 | 17 | Table of Contents 18 | 19 | 20 | 21 | 22 | 23 | Introduction 24 | 25 | 26 | 27 | 28 | 29 | Welcome 30 | 31 | 32 | 33 | 34 | Credits 35 | 36 | 37 | 38 | Copyright & License 39 | 40 | 41 | 42 | 43 | <% play_order = 4 %> 44 | <% documents_by_section.each_with_index do |section, section_no| %> 45 | 46 | 47 | <%= section['name'] %> 48 | 49 | 50 | 51 | <% section['documents'].each_with_index do |document, document_no| %> 52 | 53 | 54 | <%= document['name'] %> 55 | 56 | 57 | 58 | <% end %> 59 | 60 | <% end %> 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /source/kindle/welcome.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'welcome' %> 2 | 3 |

Kindle Edition

4 | 5 |
6 | The Kindle Edition of the Rails Guides should be considered a work in progress. Feedback is really welcome. Please see the "Feedback" section at the end of each guide for instructions. 7 |
8 | -------------------------------------------------------------------------------- /source/layout.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= yield(:page_title) || 'Ruby on Rails Guides' %> 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | <% if @edge %> 21 |
22 | edge-badge 23 |
24 | <% end %> 25 |
26 |
27 | More at rubyonrails.org: 28 | 29 | More Ruby on Rails 30 | 31 | 38 |
39 |
40 | 78 |
79 | 80 |
81 |
82 | <%= yield :header_section %> 83 | 84 | <%= yield :index_section %> 85 |
86 |
87 | 88 |
89 |
90 |
91 | <%= yield %> 92 | 93 |

Feedback

94 |

95 | You're encouraged to help improve the quality of this guide. 96 |

97 |

98 | Please contribute if you see any typos or factual errors. 99 | To get started, you can read our <%= link_to 'documentation contributions', 'http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation' %> section. 100 |

101 |

102 | You may also find incomplete content, or stuff that is not up to date. 103 | Please do add any missing documentation for master. Make sure to check 104 | <%= link_to 'Edge Guides','http://edgeguides.rubyonrails.org' %> first to verify 105 | if the issues are already fixed or not on the master branch. 106 | Check the <%= link_to 'Ruby on Rails Guides Guidelines', 'ruby_on_rails_guides_guidelines.html' %> 107 | for style and conventions. 108 |

109 |

110 | If for whatever reason you spot something to fix but cannot patch it yourself, please 111 | <%= link_to 'open an issue', 'https://github.com/rails/rails/issues' %>. 112 |

113 |

And last but not least, any kind of discussion regarding Ruby on Rails 114 | documentation is very welcome in the <%= link_to 'rubyonrails-docs mailing list', 'https://groups.google.com/forum/#!forum/rubyonrails-docs' %>. 115 |

116 |
117 |
118 |
119 | 120 |
121 | 126 | 127 | 128 | 129 | 130 | 131 | 137 | 138 | 139 | -------------------------------------------------------------------------------- /source/maintenance_policy.md: -------------------------------------------------------------------------------- 1 | **DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.** 2 | 3 | Maintenance Policy for Ruby on Rails 4 | ==================================== 5 | 6 | Support of the Rails framework is divided into four groups: New features, bug 7 | fixes, security issues, and severe security issues. They are handled as 8 | follows, all versions in `X.Y.Z` format. 9 | 10 | -------------------------------------------------------------------------------- 11 | 12 | Rails follows a shifted version of [semver](http://semver.org/): 13 | 14 | **Patch `Z`** 15 | 16 | Only bug fixes, no API changes, no new features. 17 | Except as necessary for security fixes. 18 | 19 | **Minor `Y`** 20 | 21 | New features, may contain API changes (Serve as major versions of Semver). 22 | Breaking changes are paired with deprecation notices in the previous minor 23 | or major release. 24 | 25 | **Major `X`** 26 | 27 | New features, will likely contain API changes. The difference between Rails' 28 | minor and major releases is the magnitude of breaking changes, and usually 29 | reserved for special occasions. 30 | 31 | New Features 32 | ------------ 33 | 34 | New features are only added to the master branch and will not be made available 35 | in point releases. 36 | 37 | Bug Fixes 38 | --------- 39 | 40 | Only the latest release series will receive bug fixes. When enough bugs are 41 | fixed and its deemed worthy to release a new gem, this is the branch it happens 42 | from. 43 | 44 | In special situations, where someone from the Core Team agrees to support more series, 45 | they are included in the list of supported series. 46 | 47 | **Currently included series:** `5.1.Z`. 48 | 49 | Security Issues 50 | --------------- 51 | 52 | The current release series and the next most recent one will receive patches 53 | and new versions in case of a security issue. 54 | 55 | These releases are created by taking the last released version, applying the 56 | security patches, and releasing. Those patches are then applied to the end of 57 | the x-y-stable branch. For example, a theoretical 1.2.3 security release would 58 | be built from 1.2.2, and then added to the end of 1-2-stable. This means that 59 | security releases are easy to upgrade to if you're running the latest version 60 | of Rails. 61 | 62 | **Currently included series:** `5.1.Z`, `5.0.Z`. 63 | 64 | Severe Security Issues 65 | ---------------------- 66 | 67 | For severe security issues we will provide new versions as above, and also the 68 | last major release series will receive patches and new versions. The 69 | classification of the security issue is judged by the core team. 70 | 71 | **Currently included series:** `5.1.Z`, `5.0.Z`, `4.2.Z`. 72 | 73 | Unsupported Release Series 74 | -------------------------- 75 | 76 | When a release series is no longer supported, it's your own responsibility to 77 | deal with bugs and security issues. We may provide backports of the fixes and 78 | publish them to git, however there will be no new versions released. If you are 79 | not comfortable maintaining your own versions, you should upgrade to a 80 | supported version. 81 | -------------------------------------------------------------------------------- /source/nested_model_forms.md: -------------------------------------------------------------------------------- 1 | **DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.** 2 | 3 | Rails Nested Model Forms 4 | ======================== 5 | 6 | Creating a form for a model _and_ its associations can become quite tedious. Therefore Rails provides helpers to assist in dealing with the complexities of generating these forms _and_ the required CRUD operations to create, update, and destroy associations. 7 | 8 | After reading this guide, you will know: 9 | 10 | * do stuff. 11 | 12 | -------------------------------------------------------------------------------- 13 | 14 | NOTE: This guide assumes the user knows how to use the [Rails form helpers](form_helpers.html) in general. Also, it's **not** an API reference. For a complete reference please visit [the Rails API documentation](http://api.rubyonrails.org/). 15 | 16 | 17 | Model setup 18 | ----------- 19 | 20 | To be able to use the nested model functionality in your forms, the model will need to support some basic operations. 21 | 22 | First of all, it needs to define a writer method for the attribute that corresponds to the association you are building a nested model form for. The `fields_for` form helper will look for this method to decide whether or not a nested model form should be built. 23 | 24 | If the associated object is an array, a form builder will be yielded for each object, else only a single form builder will be yielded. 25 | 26 | Consider a Person model with an associated Address. When asked to yield a nested FormBuilder for the `:address` attribute, the `fields_for` form helper will look for a method on the Person instance named `address_attributes=`. 27 | 28 | ### ActiveRecord::Base model 29 | 30 | For an ActiveRecord::Base model and association this writer method is commonly defined with the `accepts_nested_attributes_for` class method: 31 | 32 | #### has_one 33 | 34 | ```ruby 35 | class Person < ApplicationRecord 36 | has_one :address 37 | accepts_nested_attributes_for :address 38 | end 39 | ``` 40 | 41 | #### belongs_to 42 | 43 | ```ruby 44 | class Person < ApplicationRecord 45 | belongs_to :firm 46 | accepts_nested_attributes_for :firm 47 | end 48 | ``` 49 | 50 | #### has_many / has_and_belongs_to_many 51 | 52 | ```ruby 53 | class Person < ApplicationRecord 54 | has_many :projects 55 | accepts_nested_attributes_for :projects 56 | end 57 | ``` 58 | 59 | NOTE: For greater detail on associations see [Active Record Associations](association_basics.html). 60 | For a complete reference on associations please visit the API documentation for [ActiveRecord::Associations::ClassMethods](http://api.rubyonrails.org/classes/ActiveRecord/Associations/ClassMethods.html). 61 | 62 | ### Custom model 63 | 64 | As you might have inflected from this explanation, you _don't_ necessarily need an ActiveRecord::Base model to use this functionality. The following examples are sufficient to enable the nested model form behavior: 65 | 66 | #### Single associated object 67 | 68 | ```ruby 69 | class Person 70 | def address 71 | Address.new 72 | end 73 | 74 | def address_attributes=(attributes) 75 | # ... 76 | end 77 | end 78 | ``` 79 | 80 | #### Association collection 81 | 82 | ```ruby 83 | class Person 84 | def projects 85 | [Project.new, Project.new] 86 | end 87 | 88 | def projects_attributes=(attributes) 89 | # ... 90 | end 91 | end 92 | ``` 93 | 94 | NOTE: See (TODO) in the advanced section for more information on how to deal with the CRUD operations in your custom model. 95 | 96 | Views 97 | ----- 98 | 99 | ### Controller code 100 | 101 | A nested model form will _only_ be built if the associated object(s) exist. This means that for a new model instance you would probably want to build the associated object(s) first. 102 | 103 | Consider the following typical RESTful controller which will prepare a new Person instance and its `address` and `projects` associations before rendering the `new` template: 104 | 105 | ```ruby 106 | class PeopleController < ApplicationController 107 | def new 108 | @person = Person.new 109 | @person.build_address 110 | 2.times { @person.projects.build } 111 | end 112 | 113 | def create 114 | @person = Person.new(params[:person]) 115 | if @person.save 116 | # ... 117 | end 118 | end 119 | end 120 | ``` 121 | 122 | NOTE: Obviously the instantiation of the associated object(s) can become tedious and not DRY, so you might want to move that into the model itself. ActiveRecord::Base provides an `after_initialize` callback which is a good way to refactor this. 123 | 124 | ### Form code 125 | 126 | Now that you have a model instance, with the appropriate methods and associated object(s), you can start building the nested model form. 127 | 128 | #### Standard form 129 | 130 | Start out with a regular RESTful form: 131 | 132 | ```erb 133 | <%= form_for @person do |f| %> 134 | <%= f.text_field :name %> 135 | <% end %> 136 | ``` 137 | 138 | This will generate the following html: 139 | 140 | ```html 141 |
142 | 143 |
144 | ``` 145 | 146 | #### Nested form for a single associated object 147 | 148 | Now add a nested form for the `address` association: 149 | 150 | ```erb 151 | <%= form_for @person do |f| %> 152 | <%= f.text_field :name %> 153 | 154 | <%= f.fields_for :address do |af| %> 155 | <%= af.text_field :street %> 156 | <% end %> 157 | <% end %> 158 | ``` 159 | 160 | This generates: 161 | 162 | ```html 163 |
164 | 165 | 166 | 167 |
168 | ``` 169 | 170 | Notice that `fields_for` recognized the `address` as an association for which a nested model form should be built by the way it has namespaced the `name` attribute. 171 | 172 | When this form is posted the Rails parameter parser will construct a hash like the following: 173 | 174 | ```ruby 175 | { 176 | "person" => { 177 | "name" => "Eloy Duran", 178 | "address_attributes" => { 179 | "street" => "Nieuwe Prinsengracht" 180 | } 181 | } 182 | } 183 | ``` 184 | 185 | That's it. The controller will simply pass this hash on to the model from the `create` action. The model will then handle building the `address` association for you and automatically save it when the parent (`person`) is saved. 186 | 187 | #### Nested form for a collection of associated objects 188 | 189 | The form code for an association collection is pretty similar to that of a single associated object: 190 | 191 | ```erb 192 | <%= form_for @person do |f| %> 193 | <%= f.text_field :name %> 194 | 195 | <%= f.fields_for :projects do |pf| %> 196 | <%= pf.text_field :name %> 197 | <% end %> 198 | <% end %> 199 | ``` 200 | 201 | Which generates: 202 | 203 | ```html 204 |
205 | 206 | 207 | 208 | 209 |
210 | ``` 211 | 212 | As you can see it has generated 2 `project name` inputs, one for each new `project` that was built in the controller's `new` action. Only this time the `name` attribute of the input contains a digit as an extra namespace. This will be parsed by the Rails parameter parser as: 213 | 214 | ```ruby 215 | { 216 | "person" => { 217 | "name" => "Eloy Duran", 218 | "projects_attributes" => { 219 | "0" => { "name" => "Project 1" }, 220 | "1" => { "name" => "Project 2" } 221 | } 222 | } 223 | } 224 | ``` 225 | 226 | You can basically see the `projects_attributes` hash as an array of attribute hashes, one for each model instance. 227 | 228 | NOTE: The reason that `fields_for` constructed a hash instead of an array is that it won't work for any form nested deeper than one level deep. 229 | 230 | TIP: You _can_ however pass an array to the writer method generated by `accepts_nested_attributes_for` if you're using plain Ruby or some other API access. See (TODO) for more info and example. 231 | -------------------------------------------------------------------------------- /source/profiling.md: -------------------------------------------------------------------------------- 1 | *DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.** 2 | 3 | A Guide to Profiling Rails Applications 4 | ======================================= 5 | 6 | This guide covers built-in mechanisms in Rails for profiling your application. 7 | 8 | After reading this guide, you will know: 9 | 10 | * Rails profiling terminology. 11 | * How to write benchmark tests for your application. 12 | * Other benchmarking approaches and plugins. 13 | 14 | -------------------------------------------------------------------------------- 15 | 16 | 17 | -------------------------------------------------------------------------------- /source/rails_application_templates.md: -------------------------------------------------------------------------------- 1 | **DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.** 2 | 3 | Rails Application Templates 4 | =========================== 5 | 6 | Application templates are simple Ruby files containing DSL for adding gems/initializers etc. to your freshly created Rails project or an existing Rails project. 7 | 8 | After reading this guide, you will know: 9 | 10 | * How to use templates to generate/customize Rails applications. 11 | * How to write your own reusable application templates using the Rails template API. 12 | 13 | -------------------------------------------------------------------------------- 14 | 15 | Usage 16 | ----- 17 | 18 | To apply a template, you need to provide the Rails generator with the location of the template you wish to apply using the `-m` option. This can either be a path to a file or a URL. 19 | 20 | ```bash 21 | $ rails new blog -m ~/template.rb 22 | $ rails new blog -m http://example.com/template.rb 23 | ``` 24 | 25 | You can use the `app:template` Rake task to apply templates to an existing Rails application. The location of the template needs to be passed in via the LOCATION environment variable. Again, this can either be path to a file or a URL. 26 | 27 | ```bash 28 | $ bin/rails app:template LOCATION=~/template.rb 29 | $ bin/rails app:template LOCATION=http://example.com/template.rb 30 | ``` 31 | 32 | Template API 33 | ------------ 34 | 35 | The Rails templates API is easy to understand. Here's an example of a typical Rails template: 36 | 37 | ```ruby 38 | # template.rb 39 | generate(:scaffold, "person name:string") 40 | route "root to: 'people#index'" 41 | rails_command("db:migrate") 42 | 43 | after_bundle do 44 | git :init 45 | git add: "." 46 | git commit: %Q{ -m 'Initial commit' } 47 | end 48 | ``` 49 | 50 | The following sections outline the primary methods provided by the API: 51 | 52 | ### gem(*args) 53 | 54 | Adds a `gem` entry for the supplied gem to the generated application's `Gemfile`. 55 | 56 | For example, if your application depends on the gems `bj` and `nokogiri`: 57 | 58 | ```ruby 59 | gem "bj" 60 | gem "nokogiri" 61 | ``` 62 | 63 | Please note that this will NOT install the gems for you and you will have to run `bundle install` to do that. 64 | 65 | ```bash 66 | bundle install 67 | ``` 68 | 69 | ### gem_group(*names, &block) 70 | 71 | Wraps gem entries inside a group. 72 | 73 | For example, if you want to load `rspec-rails` only in the `development` and `test` groups: 74 | 75 | ```ruby 76 | gem_group :development, :test do 77 | gem "rspec-rails" 78 | end 79 | ``` 80 | 81 | ### add_source(source, options={}, &block) 82 | 83 | Adds the given source to the generated application's `Gemfile`. 84 | 85 | For example, if you need to source a gem from `"http://code.whytheluckystiff.net"`: 86 | 87 | ```ruby 88 | add_source "http://code.whytheluckystiff.net" 89 | ``` 90 | 91 | If block is given, gem entries in block are wrapped into the source group. 92 | 93 | ```ruby 94 | add_source "http://gems.github.com/" do 95 | gem "rspec-rails" 96 | end 97 | ``` 98 | 99 | ### environment/application(data=nil, options={}, &block) 100 | 101 | Adds a line inside the `Application` class for `config/application.rb`. 102 | 103 | If `options[:env]` is specified, the line is appended to the corresponding file in `config/environments`. 104 | 105 | ```ruby 106 | environment 'config.action_mailer.default_url_options = {host: "http://yourwebsite.example.com"}', env: 'production' 107 | ``` 108 | 109 | A block can be used in place of the `data` argument. 110 | 111 | ### vendor/lib/file/initializer(filename, data = nil, &block) 112 | 113 | Adds an initializer to the generated application's `config/initializers` directory. 114 | 115 | Let's say you like using `Object#not_nil?` and `Object#not_blank?`: 116 | 117 | ```ruby 118 | initializer 'bloatlol.rb', <<-CODE 119 | class Object 120 | def not_nil? 121 | !nil? 122 | end 123 | 124 | def not_blank? 125 | !blank? 126 | end 127 | end 128 | CODE 129 | ``` 130 | 131 | Similarly, `lib()` creates a file in the `lib/` directory and `vendor()` creates a file in the `vendor/` directory. 132 | 133 | There is even `file()`, which accepts a relative path from `Rails.root` and creates all the directories/files needed: 134 | 135 | ```ruby 136 | file 'app/components/foo.rb', <<-CODE 137 | class Foo 138 | end 139 | CODE 140 | ``` 141 | 142 | That'll create the `app/components` directory and put `foo.rb` in there. 143 | 144 | ### rakefile(filename, data = nil, &block) 145 | 146 | Creates a new rake file under `lib/tasks` with the supplied tasks: 147 | 148 | ```ruby 149 | rakefile("bootstrap.rake") do 150 | <<-TASK 151 | namespace :boot do 152 | task :strap do 153 | puts "i like boots!" 154 | end 155 | end 156 | TASK 157 | end 158 | ``` 159 | 160 | The above creates `lib/tasks/bootstrap.rake` with a `boot:strap` rake task. 161 | 162 | ### generate(what, *args) 163 | 164 | Runs the supplied rails generator with given arguments. 165 | 166 | ```ruby 167 | generate(:scaffold, "person", "name:string", "address:text", "age:number") 168 | ``` 169 | 170 | ### run(command) 171 | 172 | Executes an arbitrary command. Just like the backticks. Let's say you want to remove the `README.rdoc` file: 173 | 174 | ```ruby 175 | run "rm README.rdoc" 176 | ``` 177 | 178 | ### rails_command(command, options = {}) 179 | 180 | Runs the supplied task in the Rails application. Let's say you want to migrate the database: 181 | 182 | ```ruby 183 | rails_command "db:migrate" 184 | ``` 185 | 186 | You can also run tasks with a different Rails environment: 187 | 188 | ```ruby 189 | rails_command "db:migrate", env: 'production' 190 | ``` 191 | 192 | You can also run tasks as a super-user: 193 | 194 | ```ruby 195 | rails_command "log:clear", sudo: true 196 | ``` 197 | 198 | ### route(routing_code) 199 | 200 | Adds a routing entry to the `config/routes.rb` file. In the steps above, we generated a person scaffold and also removed `README.rdoc`. Now, to make `PeopleController#index` the default page for the application: 201 | 202 | ```ruby 203 | route "root to: 'person#index'" 204 | ``` 205 | 206 | ### inside(dir) 207 | 208 | Enables you to run a command from the given directory. For example, if you have a copy of edge rails that you wish to symlink from your new apps, you can do this: 209 | 210 | ```ruby 211 | inside('vendor') do 212 | run "ln -s ~/commit-rails/rails rails" 213 | end 214 | ``` 215 | 216 | ### ask(question) 217 | 218 | `ask()` gives you a chance to get some feedback from the user and use it in your templates. Let's say you want your user to name the new shiny library you're adding: 219 | 220 | ```ruby 221 | lib_name = ask("What do you want to call the shiny library ?") 222 | lib_name << ".rb" unless lib_name.index(".rb") 223 | 224 | lib lib_name, <<-CODE 225 | class Shiny 226 | end 227 | CODE 228 | ``` 229 | 230 | ### yes?(question) or no?(question) 231 | 232 | These methods let you ask questions from templates and decide the flow based on the user's answer. Let's say you want to Freeze Rails only if the user wants to: 233 | 234 | ```ruby 235 | rails_command("rails:freeze:gems") if yes?("Freeze rails gems?") 236 | # no?(question) acts just the opposite. 237 | ``` 238 | 239 | ### git(:command) 240 | 241 | Rails templates let you run any git command: 242 | 243 | ```ruby 244 | git :init 245 | git add: "." 246 | git commit: "-a -m 'Initial commit'" 247 | ``` 248 | 249 | ### after_bundle(&block) 250 | 251 | Registers a callback to be executed after the gems are bundled and binstubs 252 | are generated. Useful for all generated files to version control: 253 | 254 | ```ruby 255 | after_bundle do 256 | git :init 257 | git add: '.' 258 | git commit: "-a -m 'Initial commit'" 259 | end 260 | ``` 261 | 262 | The callbacks gets executed even if `--skip-bundle` and/or `--skip-spring` has 263 | been passed. 264 | 265 | Advanced Usage 266 | -------------- 267 | 268 | The application template is evaluated in the context of a 269 | `Rails::Generators::AppGenerator` instance. It uses the `apply` action 270 | provided by 271 | [Thor](https://github.com/erikhuda/thor/blob/master/lib/thor/actions.rb#L207). 272 | This means you can extend and change the instance to match your needs. 273 | 274 | For example by overwriting the `source_paths` method to contain the 275 | location of your template. Now methods like `copy_file` will accept 276 | relative paths to your template's location. 277 | 278 | ```ruby 279 | def source_paths 280 | [File.expand_path(File.dirname(__FILE__))] 281 | end 282 | ``` 283 | -------------------------------------------------------------------------------- /source/ruby_on_rails_guides_guidelines.md: -------------------------------------------------------------------------------- 1 | **DO NOT READ THIS FILE ON GITHUB, GUIDES ARE PUBLISHED ON http://guides.rubyonrails.org.** 2 | 3 | Ruby on Rails Guides Guidelines 4 | =============================== 5 | 6 | This guide documents guidelines for writing Ruby on Rails Guides. This guide follows itself in a graceful loop, serving itself as an example. 7 | 8 | After reading this guide, you will know: 9 | 10 | * About the conventions to be used in Rails documentation. 11 | * How to generate guides locally. 12 | 13 | -------------------------------------------------------------------------------- 14 | 15 | Markdown 16 | ------- 17 | 18 | Guides are written in [GitHub Flavored Markdown](https://help.github.com/articles/github-flavored-markdown). There is comprehensive [documentation for Markdown](http://daringfireball.net/projects/markdown/syntax), as well as a [cheatsheet](http://daringfireball.net/projects/markdown/basics). 19 | 20 | Prologue 21 | -------- 22 | 23 | Each guide should start with motivational text at the top (that's the little introduction in the blue area). The prologue should tell the reader what the guide is about, and what they will learn. As an example, see the [Routing Guide](routing.html). 24 | 25 | Headings 26 | ------ 27 | 28 | The title of every guide uses an `h1` heading; guide sections use `h2` headings; subsections use `h3` headings; etc. Note that the generated HTML output will use heading tags starting with `

`. 29 | 30 | ``` 31 | Guide Title 32 | =========== 33 | 34 | Section 35 | ------- 36 | 37 | ### Sub Section 38 | ``` 39 | 40 | When writing headings, capitalize all words except for prepositions, conjunctions, internal articles, and forms of the verb "to be": 41 | 42 | ``` 43 | #### Middleware Stack is an Array 44 | #### When are Objects Saved? 45 | ``` 46 | 47 | Use the same inline formatting as regular text: 48 | 49 | ``` 50 | ##### The `:content_type` Option 51 | ``` 52 | 53 | Linking to the API 54 | ------------------ 55 | 56 | Links to the API (`api.rubyonrails.org`) are processed by the guides generator in the following manner: 57 | 58 | Links that include a release tag are left untouched. For example 59 | 60 | ``` 61 | http://api.rubyonrails.org/v5.0.1/classes/ActiveRecord/Attributes/ClassMethods.html 62 | ``` 63 | 64 | is not modified. 65 | 66 | Please use these in release notes, since they should point to the corresponding version no matter the target being generated. 67 | 68 | If the link does not include a release tag and edge guides are being generated, the domain is replaced by `edgeapi.rubyonrails.org`. For example, 69 | 70 | ``` 71 | http://api.rubyonrails.org/classes/ActionDispatch/Response.html 72 | ``` 73 | 74 | becomes 75 | 76 | ``` 77 | http://edgeapi.rubyonrails.org/classes/ActionDispatch/Response.html 78 | ``` 79 | 80 | If the link does not include a release tag and release guides are being generated, the Rails version is injected. For example, if we are generating the guides for v5.1.0 the link 81 | 82 | ``` 83 | http://api.rubyonrails.org/classes/ActionDispatch/Response.html 84 | ``` 85 | 86 | becomes 87 | 88 | ``` 89 | http://api.rubyonrails.org/v5.1.0/classes/ActionDispatch/Response.html 90 | ``` 91 | 92 | Please don't link to `edgeapi.rubyonrails.org` manually. 93 | 94 | 95 | API Documentation Guidelines 96 | ---------------------------- 97 | 98 | The guides and the API should be coherent and consistent where appropriate. In particular, these sections of the [API Documentation Guidelines](api_documentation_guidelines.html) also apply to the guides: 99 | 100 | * [Wording](api_documentation_guidelines.html#wording) 101 | * [English](api_documentation_guidelines.html#english) 102 | * [Example Code](api_documentation_guidelines.html#example-code) 103 | * [Filenames](api_documentation_guidelines.html#file-names) 104 | * [Fonts](api_documentation_guidelines.html#fonts) 105 | 106 | HTML Guides 107 | ----------- 108 | 109 | Before generating the guides, make sure that you have the latest version of 110 | Bundler installed on your system. As of this writing, you must install Bundler 111 | 1.3.5 or later on your device. 112 | 113 | To install the latest version of Bundler, run `gem install bundler`. 114 | 115 | ### Generation 116 | 117 | To generate all the guides, just `cd` into the `guides` directory, run `bundle install`, and execute: 118 | 119 | ``` 120 | bundle exec rake guides:generate 121 | ``` 122 | 123 | or 124 | 125 | ``` 126 | bundle exec rake guides:generate:html 127 | ``` 128 | 129 | Resulting HTML files can be found in the `./output` directory. 130 | 131 | To process `my_guide.md` and nothing else use the `ONLY` environment variable: 132 | 133 | ``` 134 | touch my_guide.md 135 | bundle exec rake guides:generate ONLY=my_guide 136 | ``` 137 | 138 | By default, guides that have not been modified are not processed, so `ONLY` is rarely needed in practice. 139 | 140 | To force processing all the guides, pass `ALL=1`. 141 | 142 | If you want to generate guides in a language other than English, you can keep them in a separate directory under `source` (eg. `source/es`) and use the `GUIDES_LANGUAGE` environment variable: 143 | 144 | ``` 145 | bundle exec rake guides:generate GUIDES_LANGUAGE=es 146 | ``` 147 | 148 | If you want to see all the environment variables you can use to configure the generation script just run: 149 | 150 | ``` 151 | rake 152 | ``` 153 | 154 | ### Validation 155 | 156 | Please validate the generated HTML with: 157 | 158 | ``` 159 | bundle exec rake guides:validate 160 | ``` 161 | 162 | Particularly, titles get an ID generated from their content and this often leads to duplicates. Please set `WARNINGS=1` when generating guides to detect them. The warning messages suggest a solution. 163 | 164 | Kindle Guides 165 | ------------- 166 | 167 | ### Generation 168 | 169 | To generate guides for the Kindle, use the following rake task: 170 | 171 | ``` 172 | bundle exec rake guides:generate:kindle 173 | ``` 174 | -------------------------------------------------------------------------------- /source/zh-CN/_license.html.erb: -------------------------------------------------------------------------------- 1 |

本著作采用 创作共用 署名-相同方式共享 4.0 国际 授权

2 |

“Rails”,“Ruby on Rails”,以及 Rails Logo 为 David Heinemeier Hansson 的商标。版权所有

3 | -------------------------------------------------------------------------------- /source/zh-CN/_welcome.html.erb: -------------------------------------------------------------------------------- 1 |

Ruby on Rails 指南 (<%= @edge ? @edge[0, 7] : @version %>)

2 | 3 | <% if @edge %> 4 |

5 | These are Edge Guides, based on the current master branch. 6 |

7 |

8 | If you are looking for the ones for the stable version, please check 9 | http://guides.rubyonrails.org instead. 10 |

11 | <% else %> 12 |

13 | 这是 Rails 5.1 的最新指南,基于 <%= @version %>。 14 | 这份指南旨在使您立即获得 Rails 的生产力,并帮助您了解所有组件如何组合在一起。 15 |

16 | <% end %> 17 |

18 | 早前版本的指南: 19 | Rails 5.0中文), 20 | Rails 4.2, 21 | Rails 4.1中文), 22 | Rails 4.0, 23 | Rails 3.2,和 24 | Rails 2.3。 25 |

26 | -------------------------------------------------------------------------------- /source/zh-CN/active_job_basics.md: -------------------------------------------------------------------------------- 1 | # Active Job 基础 2 | 3 | 本文全面说明创建、入队和执行后台作业的基础知识。 4 | 5 | 读完本文后,您将学到: 6 | 7 | * 如何创建作业; 8 | * 如何入队作业; 9 | * 如何在后台运行作业; 10 | * 如何在应用中异步发送电子邮件。 11 | 12 | ----------------------------------------------------------------------------- 13 | 14 | 15 | 16 | ## 简介 17 | 18 | Active Job 框架负责声明作业,在各种队列后端中运行。作业各种各样,可以是定期清理、账单支付和寄信。其实,任何可以分解且并行运行的工作都可以。 19 | 20 | 21 | 22 | ## Active Job 的作用 23 | 24 | 主要作用是确保所有 Rails 应用都有作业基础设施。这样便可以在此基础上构建各种功能和其他 gem,而无需担心不同作业运行程序(如 Delayed Job 和 Resque)的 API 之间的差异。此外,选用哪个队列后端只是战术问题。而且,切换队列后端也不用重写作业。 25 | 26 | NOTE: Rails 自带了一个在进程内线程池中执行作业的异步队列。这些作业虽然是异步执行的,但是重启后队列中的作业就会丢失。 27 | 28 | 29 | 30 | ## 创建作业 31 | 32 | 本节逐步说明创建和入队作业的过程。 33 | 34 | 35 | 36 | ### 创建作业 37 | 38 | Active Job 提供了一个 Rails 生成器,用于创建作业。下述命令在 `app/jobs` 目录中创建一个作业(还在 `test/jobs` 目录中创建相关的测试用例): 39 | 40 | ```sh 41 | $ bin/rails generate job guests_cleanup 42 | invoke test_unit 43 | create test/jobs/guests_cleanup_job_test.rb 44 | create app/jobs/guests_cleanup_job.rb 45 | ``` 46 | 47 | 还可以创建在指定队列中运行的作业: 48 | 49 | ```sh 50 | $ bin/rails generate job guests_cleanup --queue urgent 51 | ``` 52 | 53 | 如果不想使用生成器,可以自己动手在 `app/jobs` 目录中新建文件,不过要确保继承自 `ApplicationJob`。 54 | 55 | 看一下作业: 56 | 57 | ```ruby 58 | class GuestsCleanupJob < ApplicationJob 59 | queue_as :default 60 | 61 | def perform(*guests) 62 | # 稍后做些事情 63 | end 64 | end 65 | ``` 66 | 67 | 注意,`perform` 方法的参数是任意个。 68 | 69 | 70 | 71 | ### 入队作业 72 | 73 | 像下面这样入队作业: 74 | 75 | ```ruby 76 | # 入队作业,作业在队列系统空闲时立即执行 77 | GuestsCleanupJob.perform_later guest 78 | ``` 79 | 80 | ```ruby 81 | # 入队作业,在明天中午执行 82 | GuestsCleanupJob.set(wait_until: Date.tomorrow.noon).perform_later(guest) 83 | ``` 84 | 85 | ```ruby 86 | # 入队作业,在一周以后执行 87 | GuestsCleanupJob.set(wait: 1.week).perform_later(guest) 88 | ``` 89 | 90 | ```ruby 91 | # `perform_now` 和 `perform_later` 会在幕后调用 `perform` 92 | # 因此可以传入任意个参数 93 | GuestsCleanupJob.perform_later(guest1, guest2, filter: 'some_filter') 94 | ``` 95 | 96 | 就这么简单! 97 | 98 | 99 | 100 | ## 执行作业 101 | 102 | 在生产环境中入队和执行作业需要使用队列后端,即要为 Rails 提供一个第三方队列库。Rails 本身只提供了一个进程内队列系统,把作业存储在 RAM 中。如果进程崩溃,或者设备重启了,默认的异步后端会丢失所有作业。这对小型应用或不重要的作业来说没什么,但是生产环境中的多数应用应该挑选一个持久后端。 103 | 104 | 105 | 106 | ### 后端 107 | 108 | Active Job 为多种队列后端(Sidekiq、Resque、Delayed Job,等等)内置了适配器。最新的适配器列表参见 [`ActiveJob::QueueAdapters` 的 API 文档](http://api.rubyonrails.org/classes/ActiveJob/QueueAdapters.html)。 109 | 110 | 111 | 112 | ### 设置后端 113 | 114 | 队列后端易于设置: 115 | 116 | ```ruby 117 | # config/application.rb 118 | module YourApp 119 | class Application < Rails::Application 120 | # 要把适配器的 gem 写入 Gemfile 121 | # 请参照适配器的具体安装和部署说明 122 | config.active_job.queue_adapter = :sidekiq 123 | end 124 | end 125 | ``` 126 | 127 | 也可以在各个作业中配置后端: 128 | 129 | ```ruby 130 | class GuestsCleanupJob < ApplicationJob 131 | self.queue_adapter = :resque 132 | #.... 133 | end 134 | 135 | # 现在,这个作业使用 `resque` 作为后端队列适配器 136 | # 把 `config.active_job.queue_adapter` 配置覆盖了 137 | ``` 138 | 139 | 140 | 141 | ### 启动后端 142 | 143 | Rails 应用中的作业并行运行,因此多数队列库要求为自己启动专用的队列服务(与启动 Rails 应用的服务不同)。启动队列后端的说明参见各个库的文档。 144 | 145 | 下面列出部分文档: 146 | 147 | * [Sidekiq](https://github.com/mperham/sidekiq/wiki/Active-Job) 148 | * [Resque](https://github.com/resque/resque/wiki/ActiveJob) 149 | * [Sucker Punch](https://github.com/brandonhilkert/sucker_punch#active-job) 150 | * [Queue Classic](https://github.com/QueueClassic/queue_classic#active-job) 151 | 152 | 153 | 154 | ## 队列 155 | 156 | 多数适配器支持多个队列。Active Job 允许把作业调度到具体的队列中: 157 | 158 | ```ruby 159 | class GuestsCleanupJob < ApplicationJob 160 | queue_as :low_priority 161 | #.... 162 | end 163 | ``` 164 | 165 | 队列名称可以使用 `application.rb` 文件中的 `config.active_job.queue_name_prefix` 选项配置前缀: 166 | 167 | ```ruby 168 | # config/application.rb 169 | module YourApp 170 | class Application < Rails::Application 171 | config.active_job.queue_name_prefix = Rails.env 172 | end 173 | end 174 | 175 | # app/jobs/guests_cleanup_job.rb 176 | class GuestsCleanupJob < ApplicationJob 177 | queue_as :low_priority 178 | #.... 179 | end 180 | 181 | # 在生产环境中,作业在 production_low_priority 队列中运行 182 | # 在交付准备环境中,作业在 staging_low_priority 队列中运行 183 | ``` 184 | 185 | 默认的队列名称前缀分隔符是 `'_'`。这个值可以使用 `application.rb` 文件中的 `config.active_job.queue_name_delimiter` 选项修改: 186 | 187 | ```ruby 188 | # config/application.rb 189 | module YourApp 190 | class Application < Rails::Application 191 | config.active_job.queue_name_prefix = Rails.env 192 | config.active_job.queue_name_delimiter = '.' 193 | end 194 | end 195 | 196 | # app/jobs/guests_cleanup_job.rb 197 | class GuestsCleanupJob < ApplicationJob 198 | queue_as :low_priority 199 | #.... 200 | end 201 | 202 | # 在生产环境中,作业在 production.low_priority 队列中运行 203 | # 在交付准备环境中,作业在 staging.low_priority 队列中运行 204 | ``` 205 | 206 | 如果想更进一步控制作业在哪个队列中运行,可以把 `:queue` 选项传给 `#set` 方法: 207 | 208 | ```ruby 209 | MyJob.set(queue: :another_queue).perform_later(record) 210 | ``` 211 | 212 | 如果想在作业层控制队列,可以把一个块传给 `#queue_as` 方法。那个块在作业的上下文中执行(因此可以访问 `self.arguments`),必须返回队列的名称: 213 | 214 | ```ruby 215 | class ProcessVideoJob < ApplicationJob 216 | queue_as do 217 | video = self.arguments.first 218 | if video.owner.premium? 219 | :premium_videojobs 220 | else 221 | :videojobs 222 | end 223 | end 224 | 225 | def perform(video) 226 | # 处理视频 227 | end 228 | end 229 | 230 | ProcessVideoJob.perform_later(Video.last) 231 | ``` 232 | 233 | NOTE: 确保队列后端“监听”着队列名称。某些后端要求指定要监听的队列。 234 | 235 | 236 | 237 | ## 回调 238 | 239 | Active Job 在作业的生命周期内提供了多个钩子。回调用于在作业的生命周期内触发逻辑。 240 | 241 | 242 | 243 | ### 可用的回调 244 | 245 | * `before_enqueue` 246 | * `around_enqueue` 247 | * `after_enqueue` 248 | * `before_perform` 249 | * `around_perform` 250 | * `after_perform` 251 | 252 | 253 | 254 | ### 用法 255 | 256 | ```ruby 257 | class GuestsCleanupJob < ApplicationJob 258 | queue_as :default 259 | 260 | before_enqueue do |job| 261 | # 对作业实例做些事情 262 | end 263 | 264 | around_perform do |job, block| 265 | # 在执行之前做些事情 266 | block.call 267 | # 在执行之后做些事情 268 | end 269 | 270 | def perform 271 | # 稍后做些事情 272 | end 273 | end 274 | ``` 275 | 276 | 277 | 278 | ## Action Mailer 279 | 280 | 对现代的 Web 应用来说,最常见的作业是在请求-响应循环之外发送电子邮件,这样用户无需等待。Active Job 与 Action Mailer 是集成的,因此可以轻易异步发送电子邮件: 281 | 282 | ```ruby 283 | # 如需想现在发送电子邮件,使用 #deliver_now 284 | UserMailer.welcome(@user).deliver_now 285 | 286 | # 如果想通过 Active Job 发送电子邮件,使用 #deliver_later 287 | UserMailer.welcome(@user).deliver_later 288 | ``` 289 | 290 | 291 | 292 | ## 国际化 293 | 294 | 创建作业时,使用 `I18n.locale` 设置。如果异步发送电子邮件,可能用得到: 295 | 296 | ```ruby 297 | I18n.locale = :eo 298 | 299 | UserMailer.welcome(@user).deliver_later # 使用世界语本地化电子邮件 300 | ``` 301 | 302 | 303 | 304 | ## GlobalID 305 | 306 | Active Job 支持参数使用 GlobalID。这样便可以把 Active Record 对象传给作业,而不用传递类和 ID,再自己反序列化。以前,要这么定义作业: 307 | 308 | ```ruby 309 | class TrashableCleanupJob < ApplicationJob 310 | def perform(trashable_class, trashable_id, depth) 311 | trashable = trashable_class.constantize.find(trashable_id) 312 | trashable.cleanup(depth) 313 | end 314 | end 315 | ``` 316 | 317 | 现在可以简化成这样: 318 | 319 | ```ruby 320 | class TrashableCleanupJob < ApplicationJob 321 | def perform(trashable, depth) 322 | trashable.cleanup(depth) 323 | end 324 | end 325 | ``` 326 | 327 | 为此,模型类要混入 `GlobalID::Identification`。Active Record 模型类默认都混入了。 328 | 329 | 330 | 331 | ## 异常 332 | 333 | Active Job 允许捕获执行作业过程中抛出的异常: 334 | 335 | ```ruby 336 | class GuestsCleanupJob < ApplicationJob 337 | queue_as :default 338 | 339 | rescue_from(ActiveRecord::RecordNotFound) do |exception| 340 | # 处理异常 341 | end 342 | 343 | def perform 344 | # 稍后做些事情 345 | end 346 | end 347 | ``` 348 | 349 | 350 | 351 | ### 反序列化 352 | 353 | 有了 GlobalID,可以序列化传给 `#perform` 方法的整个 Active Record 对象。 354 | 355 | 如果在作业入队之后、调用 `#perform` 方法之前删除了传入的记录,Active Job 会抛出 `ActiveJob::DeserializationError` 异常。 356 | 357 | 358 | 359 | ## 测试作业 360 | 361 | 测试作业的详细说明参见 [测试作业](testing.html#testing-jobs)。 362 | -------------------------------------------------------------------------------- /source/zh-CN/active_record_basics.md: -------------------------------------------------------------------------------- 1 | # Active Record 基础 2 | 3 | 本文简介 Active Record。 4 | 5 | 读完本文后,您将学到: 6 | 7 | * 对象关系映射(Object Relational Mapping,ORM)和 Active Record 是什么,以及如何在 Rails 中使用; 8 | * Active Record 在 MVC 中的作用; 9 | * 如何使用 Active Record 模型处理保存在关系型数据库中的数据; 10 | * Active Record 模式(schema)的命名约定; 11 | * 数据库迁移,数据验证和回调。 12 | 13 | ----------------------------------------------------------------------------- 14 | 15 | 16 | 17 | ## Active Record 是什么? 18 | 19 | Active Record 是 [MVC](http://en.wikipedia.org/wiki/Model%E2%80%93view%E2%80%93controller) 中的 M(模型),负责处理数据和业务逻辑。Active Record 负责创建和使用需要持久存入数据库中的数据。Active Record 实现了 Active Record 模式,是一种对象关系映射系统。 20 | 21 | 22 | 23 | ### Active Record 模式 24 | 25 | [Active Record 模式](http://www.martinfowler.com/eaaCatalog/activeRecord.html)出自 Martin Fowler 写的《[企业应用架构模式](https://book.douban.com/subject/4826290/)》一书。在 Active Record 模式中,对象中既有持久存储的数据,也有针对数据的操作。Active Record 模式把数据存取逻辑作为对象的一部分,处理对象的用户知道如何把数据写入数据库,还知道如何从数据库中读出数据。 26 | 27 | 28 | 29 | ### 对象关系映射 30 | 31 | 对象关系映射(ORM)是一种技术手段,把应用中的对象和关系型数据库中的数据表连接起来。使用 ORM,应用中对象的属性和对象之间的关系可以通过一种简单的方法从数据库中获取,无需直接编写 SQL 语句,也不过度依赖特定的数据库种类。 32 | 33 | 34 | 35 | ### 用作 ORM 框架的 Active Record 36 | 37 | Active Record 提供了很多功能,其中最重要的几个如下: 38 | 39 | * 表示模型和其中的数据; 40 | * 表示模型之间的关系; 41 | * 通过相关联的模型表示继承层次结构; 42 | * 持久存入数据库之前,验证模型; 43 | * 以面向对象的方式处理数据库操作。 44 | 45 | 46 | 47 | ## Active Record 中的“多约定少配置”原则 48 | 49 | 使用其他编程语言或框架开发应用时,可能必须要编写很多配置代码。大多数 ORM 框架都是这样。但是,如果遵循 Rails 的约定,创建 Active Record 模型时不用做多少配置(有时甚至完全不用配置)。Rails 的理念是,如果大多数情况下都要使用相同的方式配置应用,那么就应该把这定为默认的方式。所以,只有约定无法满足要求时,才要额外配置。 50 | 51 | 52 | 53 | ### 命名约定 54 | 55 | 默认情况下,Active Record 使用一些命名约定,查找模型和数据库表之间的映射关系。Rails 把模型的类名转换成复数,然后查找对应的数据表。例如,模型类名为 `Book`,数据表就是 `books`。Rails 提供的单复数转换功能很强大,常见和不常见的转换方式都能处理。如果类名由多个单词组成,应该按照 Ruby 的约定,使用驼峰式命名法,这时对应的数据库表将使用下划线分隔各单词。因此: 56 | 57 | * 数据库表名:复数,下划线分隔单词(例如 `book_clubs`) 58 | * 模型类名:单数,每个单词的首字母大写(例如 `BookClub`) 59 | 60 | | 模型/类 | 表/模式 | 61 | |---|---| 62 | | `Article` | `articles` | 63 | | `LineItem` | `line_items` | 64 | | `Deer` | `deers` | 65 | | `Mouse` | `mice` | 66 | | `Person` | `people` | 67 | 68 | 69 | 70 | ### 模式约定 71 | 72 | 根据字段的作用不同,Active Record 对数据库表中的字段命名也做了相应的约定: 73 | 74 | * **外键**:使用 `singularized_table_name_id` 形式命名,例如 `item_id`,`order_id`。创建模型关联后,Active Record 会查找这个字段; 75 | * **主键**:默认情况下,Active Record 使用整数字段 `id` 作为表的主键。使用 [Active Record 迁移](active_record_migrations.html)创建数据库表时,会自动创建这个字段; 76 | 77 | 还有一些可选的字段,能为 Active Record 实例添加更多的功能: 78 | 79 | * `created_at`:创建记录时,自动设为当前的日期和时间; 80 | * `updated_at`:更新记录时,自动设为当前的日期和时间; 81 | * `lock_version`:在模型中添加[乐观锁](http://api.rubyonrails.org/classes/ActiveRecord/Locking.html); 82 | * `type`:让模型使用[单表继承](http://api.rubyonrails.org/classes/ActiveRecord/Base.html#class-ActiveRecord::Base-label-Single+table+inheritance); 83 | * `(association_name)_type`:存储[多态关联](association_basics.html#polymorphic-associations)的类型; 84 | * `(table_name)_count`:缓存所关联对象的数量。比如说,一个 `Article` 有多个 `Comment`,那么 `comments_count` 列存储各篇文章现有的评论数量; 85 | 86 | NOTE: 虽然这些字段是可选的,但在 Active Record 中是被保留的。如果想使用相应的功能,就不要把这些保留字段用作其他用途。例如,`type` 这个保留字段是用来指定数据库表使用单表继承(Single Table Inheritance,STI)的。如果不用单表继承,请使用其他的名称,例如“context”,这也能表明数据的作用。 87 | 88 | 89 | 90 | 91 | ## 创建 Active Record 模型 92 | 93 | 创建 Active Record 模型的过程很简单,只要继承 `ApplicationRecord` 类就行了: 94 | 95 | ```ruby 96 | class Product < ApplicationRecord 97 | end 98 | ``` 99 | 100 | 上面的代码会创建 `Product` 模型,对应于数据库中的 `products` 表。同时,`products` 表中的字段也映射到 `Product` 模型实例的属性上。假如 `products` 表由下面的 SQL 语句创建: 101 | 102 | ```sql 103 | CREATE TABLE products ( 104 | id int(11) NOT NULL auto_increment, 105 | name varchar(255), 106 | PRIMARY KEY (id) 107 | ); 108 | ``` 109 | 110 | 按照这样的数据表结构,可以编写下面的代码: 111 | 112 | ```ruby 113 | p = Product.new 114 | p.name = "Some Book" 115 | puts p.name # "Some Book" 116 | ``` 117 | 118 | 119 | 120 | ## 覆盖命名约定 121 | 122 | 如果想使用其他的命名约定,或者在 Rails 应用中使用即有的数据库可以吗?没问题,默认的约定能轻易覆盖。 123 | 124 | `ApplicationRecord` 继承自 `ActiveRecord::Base`,后者定义了一系列有用的方法。使用 `ActiveRecord::Base.table_name=` 方法可以指定要使用的表名: 125 | 126 | ```ruby 127 | class Product < ApplicationRecord 128 | self.table_name = "my_products" 129 | end 130 | ``` 131 | 132 | 如果这么做,还要调用 `set_fixture_class` 方法,手动指定固件(my_products.yml)的类名: 133 | 134 | ```ruby 135 | class ProductTest < ActiveSupport::TestCase 136 | set_fixture_class my_products: Product 137 | fixtures :my_products 138 | ... 139 | end 140 | ``` 141 | 142 | 还可以使用 `ActiveRecord::Base.primary_key=` 方法指定表的主键: 143 | 144 | ```ruby 145 | class Product < ApplicationRecord 146 | self.primary_key = "product_id" 147 | end 148 | ``` 149 | 150 | 151 | 152 | ## CRUD:读写数据 153 | 154 | CURD 是四种数据操作的简称:C 表示创建,R 表示读取,U 表示更新,D 表示删除。Active Record 自动创建了处理数据表中数据的方法。 155 | 156 | 157 | 158 | ### 创建 159 | 160 | Active Record 对象可以使用散列创建,在块中创建,或者创建后手动设置属性。`new` 方法创建一个新对象,`create` 方法创建新对象,并将其存入数据库。 161 | 162 | 例如,`User` 模型中有两个属性,`name` 和 `occupation`。调用 `create` 方法会创建一个新记录,并将其存入数据库: 163 | 164 | ```ruby 165 | user = User.create(name: "David", occupation: "Code Artist") 166 | ``` 167 | 168 | `new` 方法实例化一个新对象,但不保存: 169 | 170 | ```ruby 171 | user = User.new 172 | user.name = "David" 173 | user.occupation = "Code Artist" 174 | ``` 175 | 176 | 调用 `user.save` 可以把记录存入数据库。 177 | 178 | 最后,如果在 `create` 和 `new` 方法中使用块,会把新创建的对象拉入块中,初始化对象: 179 | 180 | ```ruby 181 | user = User.new do |u| 182 | u.name = "David" 183 | u.occupation = "Code Artist" 184 | end 185 | ``` 186 | 187 | 188 | 189 | ### 读取 190 | 191 | Active Record 为读取数据库中的数据提供了丰富的 API。下面举例说明。 192 | 193 | ```ruby 194 | # 返回所有用户组成的集合 195 | users = User.all 196 | ``` 197 | 198 | ```ruby 199 | # 返回第一个用户 200 | user = User.first 201 | ``` 202 | 203 | ```ruby 204 | # 返回第一个名为 David 的用户 205 | david = User.find_by(name: 'David') 206 | ``` 207 | 208 | ```ruby 209 | # 查找所有名为 David,职业为 Code Artists 的用户,而且按照 created_at 反向排列 210 | users = User.where(name: 'David', occupation: 'Code Artist').order(created_at: :desc) 211 | ``` 212 | 213 | [Active Record 查询接口](active_record_querying.html)会详细介绍查询 Active Record 模型的方法。 214 | 215 | 216 | 217 | ### 更新 218 | 219 | 检索到 Active Record 对象后,可以修改其属性,然后再将其存入数据库。 220 | 221 | ```ruby 222 | user = User.find_by(name: 'David') 223 | user.name = 'Dave' 224 | user.save 225 | ``` 226 | 227 | 还有种使用散列的简写方式,指定属性名和属性值,例如: 228 | 229 | ```ruby 230 | user = User.find_by(name: 'David') 231 | user.update(name: 'Dave') 232 | ``` 233 | 234 | 一次更新多个属性时使用这种方法最方便。如果想批量更新多个记录,可以使用类方法 `update_all`: 235 | 236 | ```ruby 237 | User.update_all "max_login_attempts = 3, must_change_password = 'true'" 238 | ``` 239 | 240 | 241 | 242 | ### 删除 243 | 244 | 类似地,检索到 Active Record 对象后还可以将其销毁,从数据库中删除。 245 | 246 | ```ruby 247 | user = User.find_by(name: 'David') 248 | user.destroy 249 | ``` 250 | 251 | 252 | 253 | ## 数据验证 254 | 255 | 在存入数据库之前,Active Record 还可以验证模型。模型验证有很多方法,可以检查属性值是否不为空,是否是唯一的、没有在数据库中出现过,等等。 256 | 257 | 把数据存入数据库之前进行验证是十分重要的步骤,所以调用 `save` 和 `update` 方法时会做数据验证。验证失败时返回 `false`,此时不会对数据库做任何操作。这两个方法都有对应的爆炸方法(`save!` 和 `update!`)。爆炸方法要严格一些,如果验证失败,抛出 `ActiveRecord::RecordInvalid` 异常。下面举个简单的例子: 258 | 259 | ```ruby 260 | class User < ApplicationRecord 261 | validates :name, presence: true 262 | end 263 | 264 | user = User.new 265 | user.save # => false 266 | user.save! # => ActiveRecord::RecordInvalid: Validation failed: Name can't be blank 267 | ``` 268 | 269 | [Active Record 数据验证](active_record_validations.html)会详细介绍数据验证。 270 | 271 | 272 | 273 | ## 回调 274 | 275 | Active Record 回调用于在模型生命周期的特定事件上绑定代码,相应的事件发生时,执行绑定的代码。例如创建新纪录时、更新记录时、删除记录时,等等。[Active Record 回调](active_record_callbacks.html)会详细介绍回调。 276 | 277 | 278 | 279 | ## 迁移 280 | 281 | Rails 提供了一个 DSL(Domain-Specific Language)用来处理数据库模式,叫做“迁移”。迁移的代码存储在特定的文件中,通过 `rails` 命令执行,可以用在 Active Record 支持的所有数据库上。下面这个迁移新建一个表: 282 | 283 | ```ruby 284 | class CreatePublications < ActiveRecord::Migration[5.0] 285 | def change 286 | create_table :publications do |t| 287 | t.string :title 288 | t.text :description 289 | t.references :publication_type 290 | t.integer :publisher_id 291 | t.string :publisher_type 292 | t.boolean :single_issue 293 | 294 | t.timestamps 295 | end 296 | add_index :publications, :publication_type_id 297 | end 298 | end 299 | ``` 300 | 301 | Rails 会跟踪哪些迁移已经应用到数据库上,还提供了回滚功能。为了创建表,要执行 `rails db:migrate` 命令。如果想回滚,则执行 `rails db:rollback` 命令。 302 | 303 | 注意,上面的代码与具体的数据库种类无关,可用于 MySQL、PostgreSQL、Oracle 等数据库。关于迁移的详细介绍,参阅[Active Record 迁移](active_record_migrations.html)。 304 | -------------------------------------------------------------------------------- /source/zh-CN/credits.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :page_title do %> 2 | Ruby on Rails Guides: Credits 3 | <% end %> 4 | 5 | <% content_for :header_section do %> 6 |

Credits

7 | 8 |

We'd like to thank the following people for their tireless contributions to this project.

9 | 10 | <% end %> 11 | 12 |

Rails Guides Reviewers

13 | 14 | <%= author('Vijay Dev', 'vijaydev', 'vijaydev.jpg') do %> 15 | Vijayakumar, found as Vijay Dev on the web, is a web applications developer and an open source enthusiast who lives in Chennai, India. He started using Rails in 2009 and began actively contributing to Rails documentation in late 2010. He tweets a lot and also blogs. 16 | <% end %> 17 | 18 | <%= author('Xavier Noria', 'fxn', 'fxn.png') do %> 19 | Xavier Noria has been into Ruby on Rails since 2005. He is a Rails core team member and enjoys combining his passion for Rails and his past life as a proofreader of math textbooks. Xavier is currently an independent Ruby on Rails consultant. Oh, he also tweets and can be found everywhere as "fxn". 20 | <% end %> 21 | 22 |

Rails Guides Designers

23 | 24 | <%= author('Jason Zimdars', 'jz') do %> 25 | Jason Zimdars is an experienced creative director and web designer who has lead UI and UX design for numerous websites and web applications. You can see more of his design and writing at Thinkcage.com or follow him on Twitter. 26 | <% end %> 27 | 28 |

Rails Guides Authors

29 | 30 | <%= author('Ryan Bigg', 'radar', 'radar.png') do %> 31 | Ryan Bigg works as a Rails developer at Marketplacer and has been working with Rails since 2006. He's the author of Multi Tenancy With Rails and co-author of Rails 4 in Action. He's written many gems which can be seen on his GitHub page and he also tweets prolifically as @ryanbigg. 32 | <% end %> 33 | 34 | <%= author('Oscar Del Ben', 'oscardelben', 'oscardelben.jpg') do %> 35 | Oscar Del Ben is a software engineer at Wildfire. He's a regular open source contributor (GitHub account) and tweets regularly at @oscardelben. 36 | <% end %> 37 | 38 | <%= author('Frederick Cheung', 'fcheung') do %> 39 | Frederick Cheung is Chief Wizard at Texperts where he has been using Rails since 2006. He is based in Cambridge (UK) and when not consuming fine ales he blogs at spacevatican.org. 40 | <% end %> 41 | 42 | <%= author('Tore Darell', 'toretore') do %> 43 | Tore Darell is an independent developer based in Menton, France who specialises in cruft-free web applications using Ruby, Rails and unobtrusive JavaScript. You can follow him on Twitter. 44 | <% end %> 45 | 46 | <%= author('Jeff Dean', 'zilkey') do %> 47 | Jeff Dean is a software engineer with Pivotal Labs. 48 | <% end %> 49 | 50 | <%= author('Mike Gunderloy', 'mgunderloy') do %> 51 | Mike Gunderloy is a consultant with ActionRails. He brings 25 years of experience in a variety of languages to bear on his current work with Rails. His near-daily links and other blogging can be found at A Fresh Cup and he twitters too much. 52 | <% end %> 53 | 54 | <%= author('Mikel Lindsaar', 'raasdnil') do %> 55 | Mikel Lindsaar has been working with Rails since 2006 and is the author of the Ruby Mail gem and core contributor (he helped re-write Action Mailer's API). Mikel is the founder of RubyX, has a blog and tweets. 56 | <% end %> 57 | 58 | <%= author('Cássio Marques', 'cmarques') do %> 59 | Cássio Marques is a Brazilian software developer working with different programming languages such as Ruby, JavaScript, CPP and Java, as an independent consultant. He blogs at /* CODIFICANDO */, which is mainly written in Portuguese, but will soon get a new section for posts with English translation. 60 | <% end %> 61 | 62 | <%= author('James Miller', 'bensie') do %> 63 | James Miller is a software developer for JK Tech in San Diego, CA. You can find James on GitHub, Gmail, Twitter, and Freenode as "bensie". 64 | <% end %> 65 | 66 | <%= author('Pratik Naik', 'lifo') do %> 67 | Pratik Naik is a Ruby on Rails developer at Basecamp and also a member of the Rails core team. He maintains a blog at has_many :bugs, :through => :rails and has a semi-active twitter account. 68 | <% end %> 69 | 70 | <%= author('Emilio Tagua', 'miloops') do %> 71 | Emilio Tagua —a.k.a. miloops— is an Argentinian entrepreneur, developer, open source contributor and Rails evangelist. Cofounder of Eventioz. He has been using Rails since 2006 and contributing since early 2008. Can be found at gmail, twitter, freenode, everywhere as "miloops". 72 | <% end %> 73 | 74 | <%= author('Heiko Webers', 'hawe') do %> 75 | Heiko Webers is the founder of bauland42, a German web application security consulting and development company focused on Ruby on Rails. He blogs at the Ruby on Rails Security Project. After 10 years of desktop application development, Heiko has rarely looked back. 76 | <% end %> 77 | 78 | <%= author('Akshay Surve', 'startupjockey', 'akshaysurve.jpg') do %> 79 | Akshay Surve is the Founder at DeltaX, hackathon specialist, a midnight code junkie and occasionally writes prose. You can connect with him on Twitter, Linkedin, Personal Blog or Quora. 80 | <% end %> 81 | 82 |

Rails 指南中文译者

83 | 84 |
85 | Akshay Surve 86 |

安道

87 |

88 | 高校老师 / 自由翻译,翻译了大量 Ruby 资料。博客 89 |

90 |
91 | 92 |
93 | Akshay Surve 94 |

chinakr

95 |

96 | GitHub 97 |

98 |
99 | 100 |

其他贡献者

101 | -------------------------------------------------------------------------------- /source/zh-CN/development_dependencies_install.md: -------------------------------------------------------------------------------- 1 | # 安装开发依赖 2 | 3 | 本文说明如何搭建 Ruby on Rails 核心开发环境。 4 | 5 | 读完本文后,您将学到: 6 | 7 | * 如何设置你的设备供 Rails 开发; 8 | * 如何运行 Rails 测试组件中特定的单元测试组; 9 | * Rails 测试组件中的 Active Record 部分是如何运作的。 10 | 11 | ----------------------------------------------------------------------------- 12 | 13 | 14 | 15 | ## 简单方式 16 | 17 | 搭建开发环境最简单、也是推荐的方式是使用 [Rails 开发虚拟机](https://github.com/rails/rails-dev-box)。 18 | 19 | 20 | 21 | ## 笨拙方式 22 | 23 | 如果你不便使用 Rails 开发虚拟机,参见下述说明。这些步骤说明如何自己动手搭建开发环境,供 Ruby on Rails 核心开发使用。 24 | 25 | 26 | 27 | ### 安装 Git 28 | 29 | Ruby on Rails 使用 Git 做源码控制。Git 的安装说明参见[官网](http://git-scm.com/)。网上有很多学习 Git 的资源: 30 | 31 | * [Try Git](http://try.github.io/) 是个交互式课程,教你基本用法。 32 | * [官方文档](http://git-scm.com/documentation)十分全面,也有一些 Git 基本用法的视频。 33 | * [Everyday Git](http://schacon.github.io/git/everyday.html) 教你一些技能,足够日常使用。 34 | * [GitHub 帮助页面](http://help.github.com/)中有很多 Git 资源的链接。 35 | * [Pro Git](http://git-scm.com/book) 是一本讲解 Git 的书,基于知识共享许可证发布。 36 | 37 | 38 | 39 | ### 克隆 Ruby on Rails 仓库 40 | 41 | 进入你想保存 Ruby on Rails 源码的文件夹,然后执行(会创建 `rails` 子目录): 42 | 43 | ```sh 44 | $ git clone git://github.com/rails/rails.git 45 | $ cd rails 46 | ``` 47 | 48 | 49 | 50 | ### 准备工作和运行测试 51 | 52 | 提交的代码必须通过测试组件。不管你是编写新的补丁,还是评估别人的代码,都要运行测试。 53 | 54 | 首先,安装 `sqlite3` gem 所需的 SQLite3 及其开发文件 。macOS 用户这么做: 55 | 56 | ```sh 57 | $ brew install sqlite3 58 | ``` 59 | 60 | Ubuntu 用户这么做: 61 | 62 | ```sh 63 | $ sudo apt-get install sqlite3 libsqlite3-dev 64 | ``` 65 | 66 | Fedora 或 CentOS 用户这么做: 67 | 68 | ```sh 69 | $ sudo yum install sqlite3 sqlite3-devel 70 | ``` 71 | 72 | Arch Linux 用户要这么做: 73 | 74 | ```sh 75 | $ sudo pacman -S sqlite 76 | ``` 77 | 78 | FreeBSD 用户这么做: 79 | 80 | ```sh 81 | # pkg install sqlite3 82 | ``` 83 | 84 | 或者编译 `databases/sqlite3` port。 85 | 86 | 然后安装最新版 [Bundler](http://bundler.io/): 87 | 88 | ```sh 89 | $ gem install bundler 90 | $ gem update bundler 91 | ``` 92 | 93 | 再执行: 94 | 95 | ```sh 96 | $ bundle install --without db 97 | ``` 98 | 99 | 这个命令会安装除了 MySQL 和 PostgreSQL 的 Ruby 驱动之外的所有依赖。稍后再安装那两个驱动。 100 | 101 | NOTE: 如果想运行使用 memcached 的测试,要安装并运行 memcached。 102 | 103 | 在 macOS 中可以使用 [Homebrew](http://brew.sh/) 安装 memcached: 104 | 105 | ```sh 106 | $ brew install memcached 107 | ``` 108 | 109 | 在 Ubuntu 中可以使用 apt-get 安装 memcached: 110 | 111 | ```sh 112 | $ sudo apt-get install memcached 113 | ``` 114 | 115 | 在 Fedora 或 CentOS 中这么做: 116 | 117 | ```sh 118 | $ sudo yum install memcached 119 | ``` 120 | 121 | 在 Arch Linux 中这么做: 122 | 123 | ```sh 124 | $ sudo pacman -S memcached 125 | ``` 126 | 127 | 在 FreeBSD 中这么做: 128 | 129 | ```sh 130 | # pkg install memcached 131 | ``` 132 | 133 | 或者编译 `databases/memcached` port。 134 | 135 | 136 | 安装好依赖之后,可以执行下述命令运行测试组件: 137 | 138 | ```sh 139 | $ bundle exec rake test 140 | ``` 141 | 142 | 还可以运行某个组件(如 Action Pack)的测试,方法是进入组件所在的目录,然后执行相同的命令: 143 | 144 | ```sh 145 | $ cd actionpack 146 | $ bundle exec rake test 147 | ``` 148 | 149 | 如果想运行某个目录中的测试,使用 `TEST_DIR` 环境变量指定。例如,下述命令只运行 `railties/test/generators` 目录中的测试: 150 | 151 | ```sh 152 | $ cd railties 153 | $ TEST_DIR=generators bundle exec rake test 154 | ``` 155 | 156 | 可以像下面这样运行某个文件中的测试: 157 | 158 | ```sh 159 | $ cd actionpack 160 | $ bundle exec ruby -Itest test/template/form_helper_test.rb 161 | ``` 162 | 163 | 还可以运行某个文件中的某个测试: 164 | 165 | ```sh 166 | $ cd actionpack 167 | $ bundle exec ruby -Itest path/to/test.rb -n test_name 168 | ``` 169 | 170 | 171 | 172 | ### 为 Railties 做准备 173 | 174 | 有些 Railties 测试依赖 JavaScript 运行时环境,因此要安装 [Node.js](https://nodejs.org/)。 175 | 176 | 177 | 178 | ### 为 Active Record 做准备 179 | 180 | Active Record 的测试组件运行三次:一次针对 SQLite3,一次针对 MySQL,还有一次针对 PostgreSQL。下面说明如何为这三种数据库搭建环境。 181 | 182 | WARNING: 编写 Active Record 代码时,必须确保测试至少能在 MySQL、PostgreSQL 和 SQLite3 中通过。如果只使用 MySQL 测试,虽然测试能通过,但是不同适配器之间的差异没有考虑到。 183 | 184 | 185 | 186 | 187 | #### 数据库配置 188 | 189 | Active Record 测试组件需要一个配置文件:`activerecord/test/config.yml`。`activerecord/test/config.example.yml` 文件中有些示例。你可以复制里面的内容,然后根据你的环境修改。 190 | 191 | 192 | 193 | #### MySQL 和 PostgreSQL 194 | 195 | 为了运行针对 MySQL 和 PostgreSQL 的测试组件,要安装相应的 gem。首先安装服务器、客户端库和开发文件。 196 | 197 | 在 macOS 中可以这么做: 198 | 199 | ```sh 200 | $ brew install mysql 201 | $ brew install postgresql 202 | ``` 203 | 204 | 然后按照 Homebrew 给出的说明做。 205 | 206 | 在 Ubuntu 中只需这么做: 207 | 208 | ```sh 209 | $ sudo apt-get install mysql-server libmysqlclient-dev 210 | $ sudo apt-get install postgresql postgresql-client postgresql-contrib libpq-dev 211 | ``` 212 | 213 | 在 Fedora 或 CentOS 中只需这么做: 214 | 215 | ```sh 216 | $ sudo yum install mysql-server mysql-devel 217 | $ sudo yum install postgresql-server postgresql-devel 218 | ``` 219 | 220 | MySQL 不再支持 Arch Linux,因此你要使用 MariaDB(参见[这个声明](https://www.archlinux.org/news/mariadb-replaces-mysql-in-repositories/)): 221 | 222 | ```sh 223 | $ sudo pacman -S mariadb libmariadbclient mariadb-clients 224 | $ sudo pacman -S postgresql postgresql-libs 225 | ``` 226 | 227 | FreeBSD 用户要这么做: 228 | 229 | ```sh 230 | # pkg install mysql56-client mysql56-server 231 | # pkg install postgresql94-client postgresql94-server 232 | ``` 233 | 234 | 或者通过 port 安装(在 `databases` 文件夹中)。在安装 MySQL 的过程中如何遇到问题,请查阅 [MySQL 文档](http://dev.mysql.com/doc/refman/5.1/en/freebsd-installation.html)。 235 | 236 | 安装好之后,执行下述命令: 237 | 238 | ```sh 239 | $ rm .bundle/config 240 | $ bundle install 241 | ``` 242 | 243 | 首先,我们要删除 `.bundle/config` 文件,因为 Bundler 记得那个文件中的配置。我们前面配置了,不安装“db”分组(此外也可以修改那个文件)。 244 | 245 | 为了使用 MySQL 运行测试组件,我们要创建一个名为 `rails` 的用户,并且赋予它操作测试数据库的权限: 246 | 247 | ```sh 248 | $ mysql -uroot -p 249 | 250 | mysql> CREATE USER 'rails'@'localhost'; 251 | mysql> GRANT ALL PRIVILEGES ON activerecord_unittest.* 252 | to 'rails'@'localhost'; 253 | mysql> GRANT ALL PRIVILEGES ON activerecord_unittest2.* 254 | to 'rails'@'localhost'; 255 | mysql> GRANT ALL PRIVILEGES ON inexistent_activerecord_unittest.* 256 | to 'rails'@'localhost'; 257 | ``` 258 | 259 | 然后创建测试数据库: 260 | 261 | ```sh 262 | $ cd activerecord 263 | $ bundle exec rake db:mysql:build 264 | ``` 265 | 266 | PostgreSQL 的身份验证方式有所不同。为了使用开发账户搭建开发环境,在 Linux 或 BSD 中要这么做: 267 | 268 | ```sh 269 | $ sudo -u postgres createuser --superuser $USER 270 | ``` 271 | 272 | 在 macOS 中这么做: 273 | 274 | ```sh 275 | $ createuser --superuser $USER 276 | ``` 277 | 278 | 然后,执行下述命令创建测试数据库: 279 | 280 | ```sh 281 | $ cd activerecord 282 | $ bundle exec rake db:postgresql:build 283 | ``` 284 | 285 | 可以执行下述命令创建 PostgreSQL 和 MySQL 的测试数据库: 286 | 287 | ```sh 288 | $ cd activerecord 289 | $ bundle exec rake db:create 290 | ``` 291 | 292 | 可以使用下述命令清理数据库: 293 | 294 | ```sh 295 | $ cd activerecord 296 | $ bundle exec rake db:drop 297 | ``` 298 | 299 | NOTE: 使用 rake 任务创建测试数据库能保障数据库使用正确的字符集和排序规则。 300 | 301 | 302 | NOTE: 在 PostgreSQL 9.1.x 及早期版本中激活 HStore 扩展会看到这个提醒(或本地化的提醒):“WARNING: => is deprecated as an operator”。 303 | 304 | 305 | 如果使用其他数据库,默认的连接信息参见 `activerecord/test/config.yml` 或 `activerecord/test/config.example.yml` 文件。如果有必要,可以在你的设备中编辑 `activerecord/test/config.yml` 文件,提供不同的凭据。不过显然,不应该把这种改动推送回 Rails 仓库。 306 | 307 | 308 | 309 | ### 为 Action Cable 做准备 310 | 311 | Action Cable 默认使用 Redis 作为订阅适配器([详情](action_cable_overview.html#broadcasting)),因此为了运行 Action Cable 的测试,要安装并运行 Redis。 312 | 313 | 314 | 315 | #### 从源码安装 Redis 316 | 317 | Redis 的文档不建议通过包管理器安装,因为那里的包往往是过时的。[Redis 的文档](http://redis.io/download#installation)详细说明了如何从源码安装,以及如何运行 Redis 服务器。 318 | 319 | 320 | 321 | #### 使用包管理器安装 322 | 323 | 在 macOS 中可以执行下述命令: 324 | 325 | ```sh 326 | $ brew install redis 327 | ``` 328 | 329 | 然后按照 Homebrew 给出的说明做。 330 | 331 | 在 Ubuntu 中只需运行: 332 | 333 | ```sh 334 | $ sudo apt-get install redis-server 335 | ``` 336 | 337 | 在 Fedora 或 CentOS(要启用 EPEL)中运行: 338 | 339 | ```sh 340 | $ sudo yum install redis 341 | ``` 342 | 343 | 如果使用 Arch Linux,运行: 344 | 345 | ```sh 346 | $ sudo pacman -S redis 347 | $ sudo systemctl start redis 348 | ``` 349 | 350 | FreeBSD 用户要运行下述命令: 351 | 352 | ```sh 353 | # portmaster databases/redis 354 | ``` 355 | -------------------------------------------------------------------------------- /source/zh-CN/documents.yaml: -------------------------------------------------------------------------------- 1 | - 2 | name: 新手入门 3 | documents: 4 | - 5 | name: Rails 入门 6 | url: getting_started.html 7 | description: 从安装到建立第一个应用程序所需知道的一切。 8 | - 9 | name: 模型 10 | documents: 11 | - 12 | name: Active Record 基础 13 | url: active_record_basics.html 14 | description: 本篇介绍 Models、数据库持久性以及 Active Record 模式。 15 | - 16 | name: Active Record 迁移 17 | url: active_record_migrations.html 18 | description: 本篇介绍如何有条有理地使用 Active Record 来修改数据库。 19 | - 20 | name: Active Record 数据验证 21 | url: active_record_validations.html 22 | description: 本篇介绍如何使用 Active Record 验证功能。 23 | - 24 | name: Active Record 回调 25 | url: active_record_callbacks.html 26 | description: 本篇介绍如何使用 Active Record 回调功能。 27 | - 28 | name: Active Record 关联 29 | url: association_basics.html 30 | description: 本篇介绍如何使用 Active Record 的关联功能。 31 | - 32 | name: Active Record 查询接口 33 | url: active_record_querying.html 34 | description: 本篇介绍如何使用 Active Record 的数据库查询功能。 35 | - 36 | name: Active Model 基础 37 | url: active_model_basics.html 38 | description: 本篇介绍如何使用 Active Model。 39 | work_in_progress: true 40 | - 41 | name: 视图 42 | documents: 43 | - 44 | name: Action View 概述 45 | url: action_view_overview.html 46 | description: 本篇介绍 Action View 和常用辅助方法。 47 | work_in_progress: true 48 | - 49 | name: Rails 布局和视图渲染 50 | url: layouts_and_rendering.html 51 | description: 本篇介绍 Action Controller 与 Action View 基本的版型功能,包含了渲染、重定向、使用 content_for 区块、以及局部模版。 52 | - 53 | name: Action View 表单辅助方法 54 | url: form_helpers.html 55 | description: 本篇介绍 Action View 的表单辅助方法。 56 | - 57 | name: 控制器 58 | documents: 59 | - 60 | name: Action Controller 概览 61 | url: action_controller_overview.html 62 | description: 本篇介绍 Controller 的工作原理,Controller 在请求周期所扮演的角色。内容包含 Session、滤动器、Cookies、资料串流以及如何处理由请求所发起的异常。 63 | - 64 | name: Rails 路由全解 65 | url: routing.html 66 | description: 本篇介绍与使用者息息相关的路由功能。想了解如何使用 Rails 的路由,从这里开始。 67 | - 68 | name: 深入探索 69 | documents: 70 | - 71 | name: Active Support 核心扩展 72 | url: active_support_core_extensions.html 73 | description: 本篇介绍由 Active Support 定义的核心扩展功能。 74 | - 75 | name: Rails 国际化 API 76 | url: i18n.html 77 | description: 本篇介绍如何国际化应用程序。将应用程序翻译成多种语言、更改单复数规则、对不同的国家使用正确的日期格式等。 78 | - 79 | name: Action Mailer 基础 80 | url: action_mailer_basics.html 81 | description: 本篇介绍如何使用 Action Mailer 来收发信件。 82 | - 83 | name: Active Job 基础 84 | url: active_job_basics.html 85 | description: 本篇提供创建背景任务、任务排程以及执行任务的所有知识。 86 | - 87 | name: Rails 应用测试指南 88 | url: testing.html 89 | description: 这是 Rails 中测试设施的综合指南。它涵盖了从“什么是测试?”到集成测试的知识。 90 | - 91 | name: Ruby on Rails 安全指南 92 | url: security.html 93 | description: 本篇介绍网路应用程序常见的安全问题,如何在 Rails 里避免这些问题。 94 | - 95 | name: 调试 Rails 应用 96 | url: debugging_rails_applications.html 97 | description: 本篇介绍如何给 Rails 应用程式除错。包含了多种除错技巧、如何理解与了解代码背后究竟发生了什么事。 98 | - 99 | name: 配置 Rails 应用 100 | url: configuring.html 101 | description: 本篇介绍 Rails 应用程序的基本配置选项。 102 | - 103 | name: Rails 命令行 104 | url: command_line.html 105 | description: 本篇介绍 Rails 提供的命令行工具。 106 | - 107 | name: Asset Pipeline 108 | url: asset_pipeline.html 109 | description: 本篇介绍 Asset Pipeline. 110 | - 111 | name: 在 Rails 中使用 JavaScript 112 | url: working_with_javascript_in_rails.html 113 | description: 本篇介绍 Rails 内置的 Ajax 与 JavaScript 功能。 114 | - 115 | name: Rails 初始化过程 116 | work_in_progress: true 117 | url: initialization.html 118 | description: 本篇介绍 Rails 内部初始化过程。 119 | - 120 | name: 自动加载和重新加载常量 121 | url: autoloading_and_reloading_constants.html 122 | description: 本篇介绍自动加载和重新加载常量是如何工作的。 123 | - 124 | name: Rails 缓存概览 125 | url: caching_with_rails.html 126 | description: 本篇介绍如何通过缓存给 Rails 应用提速。 127 | - 128 | name: Active Support 监测程序 129 | work_in_progress: true 130 | url: active_support_instrumentation.html 131 | description: 本篇介绍如何通过 Active Support 监测 API 观察 Rails 和其他 Ruby 代码的事件。 132 | - 133 | name: Rails 应用分析指南 134 | work_in_progress: true 135 | url: profiling.html 136 | description: 本篇介绍如何分析 Rails 应用并提高性能。 137 | - 138 | name: 使用 Rails 开发只提供 API 的应用 139 | url: api_app.html 140 | description: 本篇介绍如何将 Rails 用于只提供 API 的应用。 141 | - 142 | name: Action Cable 概览 143 | url: action_cable_overview.html 144 | description: 本篇介绍 Action Cable 如何工作,以及如何使用 WebSockets 创建实时功能。 145 | 146 | - 147 | name: 扩展 Rails 148 | documents: 149 | - 150 | name: Rails 插件开发简介 151 | work_in_progress: true 152 | url: plugins.html 153 | description: 本篇介绍如何开发插件扩展 Rails 的功能。 154 | - 155 | name: Rails on Rack 156 | url: rails_on_rack.html 157 | description: 本篇介绍 Rails 和 Rack 的集成以及和其他 Rack 组件的交互。 158 | - 159 | name: 创建及定制 Rails 生成器和模板 160 | url: generators.html 161 | description: 本篇介绍如何添加新的生成器,或者为 Rails 内置生成器提供替代选项(例如替换 scaffold 生成器的测试组件)。 162 | - 163 | name: 引擎入门 164 | url: engines.html 165 | description: 本篇介绍如何编写可挂载的引擎。 166 | work_in_progress: true 167 | - 168 | name: 为 Ruby on Rails 做贡献 169 | documents: 170 | - 171 | name: 为 Ruby on Rails 做贡献 172 | url: contributing_to_ruby_on_rails.html 173 | description: Rails 不是“别人的框架”。本篇提供几条贡献 Rails 开发的路线。 174 | - 175 | name: API 文档指导方针 176 | url: api_documentation_guidelines.html 177 | description: 本篇介绍 Ruby on Rails API 文档守则。 178 | - 179 | name: Ruby on Rails 指南指导方针 180 | url: ruby_on_rails_guides_guidelines.html 181 | description: 本篇介绍 Ruby on Rails 指南守则。 182 | - 183 | name: 维护方针 184 | documents: 185 | - 186 | name: Ruby on Rails 的维护方针 187 | url: maintenance_policy.html 188 | description: Ruby on Rails 当前支持版本,和什么时候发布新版本。 189 | - 190 | name: 发布记 191 | documents: 192 | - 193 | name: Ruby on Rails 升级指南 194 | url: upgrading_ruby_on_rails.html 195 | description: 本篇帮助升级到 Ruby on Rails 最新版。 196 | - 197 | name: Ruby on Rails 5.0 发布记 198 | url: 5_0_release_notes.html 199 | description: Rails 5.0 的发布说明。 200 | - 201 | name: Ruby on Rails 4.2 发布记 202 | url: 4_2_release_notes.html 203 | description: Rails 4.2 的发布说明。 204 | - 205 | name: Ruby on Rails 4.1 发布记 206 | url: 4_1_release_notes.html 207 | description: Rails 4.1 的发布说明。 208 | - 209 | name: Ruby on Rails 4.0 发布记 210 | url: 4_0_release_notes.html 211 | description: Rails 4.0 的发布说明 212 | - 213 | name: Ruby on Rails 3.2 发布记 214 | url: 3_2_release_notes.html 215 | description: Rails 3.2 的发布说明 216 | - 217 | name: Ruby on Rails 3.1 发布记 218 | url: 3_1_release_notes.html 219 | description: Rails 3.1 的发布说明 220 | - 221 | name: Ruby on Rails 3.0 发布记 222 | url: 3_0_release_notes.html 223 | description: Rails 3.0 的发布说明 224 | - 225 | name: Ruby on Rails 2.3 发布记 226 | url: 2_3_release_notes.html 227 | description: Rails 2.3 的发布说明 228 | - 229 | name: Ruby on Rails 2.2 发布记 230 | url: 2_2_release_notes.html 231 | description: Rails 2.2 的发布说明 232 | -------------------------------------------------------------------------------- /source/zh-CN/index.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :page_title do %> 2 | Ruby on Rails Guides 3 | <% end %> 4 | 5 | <% content_for :header_section do %> 6 | <%= render 'welcome' %> 7 | <% end %> 8 | 9 | <% content_for :index_section do %> 10 |
11 |
12 |
13 |
Rails 指南同时提供 <%= link_to 'Kindle', 'https://github.com/ruby-china/rails-guides/releases' %> 版。
14 |
如果需要 Epub、PDF 格式,可以购买<%= link_to '安道维护的电子书', 'https://rails.guide/' %>。
15 |
标记了这个图标的指南还在编写中,不会出现在指南索引。这些指南可能包含不完整的信息甚至错误。您可以帮忙检查并且提交评论和修正。
16 |
17 |
18 | <% end %> 19 | 20 | <% documents_by_section.each do |section| %> 21 |

<%= section['name'] %>

22 |
23 | <% section['documents'].each do |document| %> 24 | <%= guide(document['name'], document['url'], work_in_progress: document['work_in_progress']) do %> 25 |

<%= document['description'] %>

26 | <% end %> 27 | <% end %> 28 |
29 | <% end %> 30 | -------------------------------------------------------------------------------- /source/zh-CN/kindle/copyright.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'license' %> -------------------------------------------------------------------------------- /source/zh-CN/kindle/layout.html.erb: -------------------------------------------------------------------------------- 1 | 3 | 4 | 5 | 6 | 7 | 8 | <%= yield(:page_title) || 'Ruby on Rails Guides' %> 9 | 10 | 11 | 12 | 13 | 14 | 15 | <% if content_for? :header_section %> 16 | <%= yield :header_section %> 17 |
18 | <% end %> 19 | 20 | <% if content_for? :index_section %> 21 | <%= yield :index_section %> 22 |
23 | <% end %> 24 | 25 | <%= yield.html_safe %> 26 | 27 | 28 | -------------------------------------------------------------------------------- /source/zh-CN/kindle/rails_guides.opf.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | Ruby on Rails 指南 (<%= @version %>) 9 | 10 | zh-CN 11 | Ruby on Rails 12 | Ruby on Rails 13 | Reference 14 | <%= Time.now.strftime('%Y-%m-%d') %> 15 | 16 | 这份指南旨在使您立即获得 Rails 的生产力,并帮助您了解所有组件如何组合在一起。 17 | 18 | 19 | 20 | 21 | 22 | 23 | 24 | 25 | <% documents_flat.each do |document| %> 26 | 27 | <% end %> 28 | 29 | <% %w{toc.html credits.html welcome.html copyright.html}.each do |url| %> 30 | 31 | <% end %> 32 | 33 | 34 | 35 | 36 | 37 | 38 | 39 | 40 | 41 | 42 | 43 | <% documents_flat.each do |document| %> 44 | 45 | <% end %> 46 | 47 | 48 | 49 | 50 | 51 | 52 | 53 | -------------------------------------------------------------------------------- /source/zh-CN/kindle/toc.html.erb: -------------------------------------------------------------------------------- 1 | <% content_for :page_title do %> 2 | Ruby on Rails 指南 3 | <% end %> 4 | 5 |

Table of Contents

6 |
7 | 8 | <% documents_by_section.each_with_index do |section, i| %> 9 |

<%= "#{i + 1}." %> <%= section['name'] %>

10 |
    11 | <% section['documents'].each do |document| %> 12 |
  • 13 | <%= document['name'] %> 14 | <% if document['work_in_progress']%>(WIP)<% end %> 15 |
  • 16 | <% end %> 17 |
18 | <% end %> 19 |
20 | 24 |
25 | -------------------------------------------------------------------------------- /source/zh-CN/kindle/toc.ncx.erb: -------------------------------------------------------------------------------- 1 | 2 | 4 | 5 | 6 | 7 | 8 | 9 | 10 | 11 | 12 | Ruby on Rails 指南 13 | docrails 14 | 15 | 16 | 17 | Table of Contents 18 | 19 | 20 | 21 | 22 | 23 | Introduction 24 | 25 | 26 | 27 | 28 | 29 | Welcome 30 | 31 | 32 | 33 | 34 | Credits 35 | 36 | 37 | 38 | Copyright & License 39 | 40 | 41 | 42 | 43 | <% play_order = 4 %> 44 | <% documents_by_section.each_with_index do |section, section_no| %> 45 | 46 | 47 | <%= section['name'] %> 48 | 49 | 50 | 51 | <% section['documents'].each_with_index do |document, document_no| %> 52 | 53 | 54 | <%= document['name'] %> 55 | 56 | 57 | 58 | <% end %> 59 | 60 | <% end %> 61 | 62 | 63 | 64 | 65 | -------------------------------------------------------------------------------- /source/zh-CN/kindle/welcome.html.erb: -------------------------------------------------------------------------------- 1 | <%= render 'welcome' %> 2 | 3 |

Kindle Edition

4 | 5 |
6 | The Kindle Edition of the Rails Guides should be considered a work in progress. Feedback is really welcome. Please see the "Feedback" section at the end of each guide for instructions. 7 |
8 | -------------------------------------------------------------------------------- /source/zh-CN/layout.html.erb: -------------------------------------------------------------------------------- 1 | 2 | 3 | 4 | 5 | 6 | 7 | 8 | <%= yield(:page_title) || 'Ruby on Rails 指南' %> 9 | 10 | 11 | 12 | 13 | 14 | 15 | 16 | 17 | 18 | 19 | 20 | <% if @edge %> 21 |
22 | edge-badge 23 |
24 | <% end %> 25 |
26 |
27 | 更多内容 rubyonrails.org: 28 | 29 | 更多内容 30 | 31 | 39 |
40 |
41 | 79 |
80 | 81 |
82 |
83 | <%= yield :header_section %> 84 | 85 | <%= yield :index_section %> 86 |
87 |
88 | 89 |
90 |
91 |
92 | <%= yield.html_safe %> 93 | 94 |

反馈

95 |

96 | 我们鼓励您帮助提高本指南的质量。 97 |

98 |

99 | 如果看到如何错字或错误,请反馈给我们。 100 | 您可以阅读我们的<%= link_to '文档贡献', 'http://edgeguides.rubyonrails.org/contributing_to_ruby_on_rails.html#contributing-to-the-rails-documentation' %>指南。 101 |

102 |

103 | 您还可能会发现内容不完整或不是最新版本。 104 | 请添加缺失文档到 master 分支。请先确认 <%= link_to 'Edge Guides','http://edgeguides.rubyonrails.org' %> 是否已经修复。 105 | 关于用语约定,请查看<%= link_to 'Ruby on Rails 指南指导', 'ruby_on_rails_guides_guidelines.html' %>。 106 |

107 |

108 | 无论什么原因,如果你发现了问题但无法修补它,请<%= link_to '创建 issue', 'https://github.com/rails/rails/issues' %>。 109 |

110 |

111 | 最后,欢迎到 <%= link_to 'rubyonrails-docs 邮件列表', 'http://groups.google.com/group/rubyonrails-docs' %>参与任何有关 Ruby on Rails 文档的讨论。 112 |

113 |

中文翻译反馈

114 |

贡献:https://github.com/ruby-china/guides

115 |
116 |
117 |
118 | 119 |
120 | 125 | 126 | 127 | 128 | 129 | 130 | 136 | 137 | 138 | -------------------------------------------------------------------------------- /source/zh-CN/maintenance_policy.md: -------------------------------------------------------------------------------- 1 | # Ruby on Rails 的维护方针 2 | 3 | 对 Rails 框架的支持分为四种:新功能、缺陷修正、安全问题和严重安全问题。各自的处理方式如下,所有版本号都使用 `X.Y.Z` 格式。 4 | 5 | ----------------------------------------------------------------------------- 6 | 7 | Rails 遵照[语义版本](http://semver.org/)更替版本号: 8 | 9 | **补丁版 Z** 10 | 11 | 只修正缺陷,不改变 API,也不新增功能。安全修正可能例外。 12 | 13 | **小版本 Y** 14 | 15 | 新增功能,可能改变 API(相当于语义版本中的大版本)。重大改变在之前的小版本或大版本中带有弃用提示。 16 | 17 | **大版本 X** 18 | 19 | 新增功能,可能改变 API。Rails 的大版本和小版本之间的区别是对重大改变的处理方式不同,有时也有例外。 20 | 21 | 22 | 23 | ## 新功能 24 | 25 | 新功能只添加到 master 分支,不会包含在补丁版中。 26 | 27 | 28 | 29 | ## 缺陷修正 30 | 31 | 只有最新的发布系列接收缺陷修正。如果修正的缺陷足够多,值得发布新的 gem,从这个分支中获取代码。 32 | 33 | 如果核心团队中有人同意支持更多的发布系列,也会包含在支持的系列中——这是特殊情况。 34 | 35 | 目前支持的系列:`5.1.Z`。 36 | 37 | 38 | 39 | ## 安全问题 40 | 41 | 发现安全问题时,当前发布系列和下一个最新版接收补丁和新版本。 42 | 43 | 新版代码从最近的发布版中获取,应用安全补丁之后发布。然后把安全补丁应用到 x-y-stable 分支。例如,1.2.3 安全发布在 1.2.2 版的基础上得来,然后再把安全补丁应用到 1-2-stable 分支。因此,如果你使用 Rails 的最新版,很容易升级安全修正版。 44 | 45 | 目前支持的系列:`5.1.Z`、`5.0.Z`。 46 | 47 | 48 | 49 | ## 严重安全问题 50 | 51 | 发现严重安全问题时,会发布新版,最近的主发布系列也会接收补丁和新版。安全问题由核心团队甄别分类。 52 | 53 | 目前支持的系列:`5.1.Z`、`5.0.Z`、`4.2.Z`。 54 | 55 | 56 | 57 | ## 不支持的发布系列 58 | 59 | 如果一个发布系列不再得到支持,你要自己负责处理缺陷和安全问题。我们可能会逆向移植,把修正代码发布到 Git 仓库中,但是不会发布新版本。如果你不想自己维护,应该升级到我们支持的版本。 60 | -------------------------------------------------------------------------------- /source/zh-CN/rails_application_templates.md: -------------------------------------------------------------------------------- 1 | # Rails 应用模板 2 | 3 | 应用模板是包含 DSL 的 Ruby 文件,作用是为新建的或现有的 Rails 项目添加 gem 和初始化脚本等。 4 | 5 | 读完本文后,您将学到: 6 | 7 | * 如何使用模板生成和定制 Rails 应用; 8 | * 如何使用 Rails Templates API 编写可复用的应用模板。 9 | 10 | ----------------------------------------------------------------------------- 11 | 12 | 13 | 14 | ## 用法 15 | 16 | 若想使用模板,调用 Rails 生成器时把模板的位置传给 `-m` 选项。模板的位置可以是文件路径,也可以是 URL。 17 | 18 | ```sh 19 | $ rails new blog -m ~/template.rb 20 | $ rails new blog -m http://example.com/template.rb 21 | ``` 22 | 23 | 可以使用 `app:template` 任务在现有的 Rails 应用中使用模板。模板的位置要通过 `LOCATION` 环境变量指定。同样,模板的位置可以是文件路径,也可以是 URL。 24 | 25 | ```sh 26 | $ bin/rails app:template LOCATION=~/template.rb 27 | $ bin/rails app:template LOCATION=http://example.com/template.rb 28 | ``` 29 | 30 | 31 | 32 | ## Templates API 33 | 34 | Rails Templates API 易于理解。下面是一个典型的 Rails 模板: 35 | 36 | ```ruby 37 | # template.rb 38 | generate(:scaffold, "person name:string") 39 | route "root to: 'people#index'" 40 | rails_command("db:migrate") 41 | 42 | after_bundle do 43 | git :init 44 | git add: "." 45 | git commit: %Q{ -m 'Initial commit' } 46 | end 47 | ``` 48 | 49 | 下面各小节简介这个 API 提供的主要方法。 50 | 51 | 52 | 53 | ### `gem(*args)` 54 | 55 | 在生成的应用的 `Gemfile` 中添加指定的 `gem` 条目。 56 | 57 | 例如,如果应用依赖 `bj` 和 `nokogiri`: 58 | 59 | ```ruby 60 | gem "bj" 61 | gem "nokogiri" 62 | ``` 63 | 64 | 请注意,这么做不会为你安装 gem,你要执行 `bundle install` 命令安装。 65 | 66 | ```sh 67 | $ bundle install 68 | ``` 69 | 70 | 71 | 72 | ### `gem_group(*names, &block)` 73 | 74 | 把指定的 gem 条目放在一个分组中。 75 | 76 | 例如,如果只想在 `development` 和 `test` 组中加载 `rspec-rails`: 77 | 78 | ```ruby 79 | gem_group :development, :test do 80 | gem "rspec-rails" 81 | end 82 | ``` 83 | 84 | 85 | 86 | ### `add_source(source, options={}, &block)` 87 | 88 | 在生成的应用的 `Gemfile` 中添加指定的源。 89 | 90 | 例如,如果想安装 `"http://code.whytheluckystiff.net"` 源中的 gem: 91 | 92 | ```ruby 93 | add_source "http://code.whytheluckystiff.net" 94 | ``` 95 | 96 | 如果提供块,块中的 gem 条目放在指定的源分组里: 97 | 98 | ```ruby 99 | add_source "http://gems.github.com/" do 100 | gem "rspec-rails" 101 | end 102 | ``` 103 | 104 | 105 | 106 | ### `environment`/`application(data=nil, options={}, &block)` 107 | 108 | 在 `config/application.rb` 文件中的 `Application` 类里添加一行代码。 109 | 110 | 如果指定了 `options[:env]`,代码添加到 `config/environments` 目录中对应的文件中。 111 | 112 | ```ruby 113 | environment 'config.action_mailer.default_url_options = {host: "http://yourwebsite.example.com"}', env: 'production' 114 | ``` 115 | 116 | `data` 参数的位置可以使用块。 117 | 118 | 119 | 120 | ### `vendor`/`lib`/`file`/`initializer(filename, data = nil, &block)` 121 | 122 | 在生成的应用的 `config/initializers` 目录中添加一个初始化脚本。 123 | 124 | 假设你想使用 `Object#not_nil?` 和 `Object#not_blank?` 方法: 125 | 126 | ```ruby 127 | initializer 'bloatlol.rb', <<-CODE 128 | class Object 129 | def not_nil? 130 | !nil? 131 | end 132 | 133 | def not_blank? 134 | !blank? 135 | end 136 | end 137 | CODE 138 | ``` 139 | 140 | 类似地,`lib()` 方法在 `lib/ directory` 目录中创建一个文件,`vendor()` 方法在 `vendor/` 目录中创建一个文件。 141 | 142 | 此外还有个 `file()` 方法,它的参数是一个相对于 `Rails.root` 的路径,用于创建所需的目录和文件: 143 | 144 | ```ruby 145 | file 'app/components/foo.rb', <<-CODE 146 | class Foo 147 | end 148 | CODE 149 | ``` 150 | 151 | 上述代码会创建 `app/components` 目录,然后在里面创建 `foo.rb` 文件。 152 | 153 | 154 | 155 | ### `rakefile(filename, data = nil, &block)` 156 | 157 | 在 `lib/tasks` 目录中创建一个 Rake 文件,写入指定的任务: 158 | 159 | ```ruby 160 | rakefile("bootstrap.rake") do 161 | <<-TASK 162 | namespace :boot do 163 | task :strap do 164 | puts "i like boots!" 165 | end 166 | end 167 | TASK 168 | end 169 | ``` 170 | 171 | 上述代码会创建 `lib/tasks/bootstrap.rake` 文件,写入 `boot:strap` rake 任务。 172 | 173 | 174 | 175 | ### `generate(what, *args)` 176 | 177 | 运行指定的 Rails 生成器,并传入指定的参数。 178 | 179 | ```ruby 180 | generate(:scaffold, "person", "name:string", "address:text", "age:number") 181 | ``` 182 | 183 | 184 | 185 | ### `run(command)` 186 | 187 | 运行任意命令。作用类似于反引号。假如你想删除 `README.rdoc` 文件: 188 | 189 | ```ruby 190 | run "rm README.rdoc" 191 | ``` 192 | 193 | 194 | 195 | ### `rails_command(command, options = {})` 196 | 197 | 在 Rails 应用中运行指定的任务。假如你想迁移数据库: 198 | 199 | ```ruby 200 | rails_command "db:migrate" 201 | ``` 202 | 203 | 还可以在不同的 Rails 环境中运行任务: 204 | 205 | ```ruby 206 | rails_command "db:migrate", env: 'production' 207 | ``` 208 | 209 | 还能以超级用户的身份运行任务: 210 | 211 | ```ruby 212 | rails_command "log:clear", sudo: true 213 | ``` 214 | 215 | 216 | 217 | ### `route(routing_code)` 218 | 219 | 在 `config/routes.rb` 文件中添加一条路由规则。在前面几节中,我们使用脚手架生成了 Person 资源,还删除了 `README.rdoc` 文件。现在,把 `PeopleController#index` 设为应用的首页: 220 | 221 | ```ruby 222 | route "root to: 'person#index'" 223 | ``` 224 | 225 | 226 | 227 | ### `inside(dir)` 228 | 229 | 在指定的目录中执行命令。假如你有一份最新版 Rails,想通过符号链接指向 `rails` 命令,可以这么做: 230 | 231 | ```ruby 232 | inside('vendor') do 233 | run "ln -s ~/commit-rails/rails rails" 234 | end 235 | ``` 236 | 237 | 238 | 239 | ### `ask(question)` 240 | 241 | `ask()` 方法获取用户的反馈,供模板使用。假如你想让用户为新添加的库起个响亮的名称: 242 | 243 | ```ruby 244 | lib_name = ask("What do you want to call the shiny library ?") 245 | lib_name << ".rb" unless lib_name.index(".rb") 246 | 247 | lib lib_name, <<-CODE 248 | class Shiny 249 | end 250 | CODE 251 | ``` 252 | 253 | 254 | 255 | ### `yes?(question)` 或 `no?(question)` 256 | 257 | 这两个方法用于询问用户问题,然后根据用户的回答决定流程。假如你想在用户同意时才冰封 Rails: 258 | 259 | ```ruby 260 | rails_command("rails:freeze:gems") if yes?("Freeze rails gems?") 261 | # no?(question) 的作用正好相反 262 | ``` 263 | 264 | 265 | 266 | ### `git(:command)` 267 | 268 | 在 Rails 模板中可以运行任意 Git 命令: 269 | 270 | ```ruby 271 | git :init 272 | git add: "." 273 | git commit: "-a -m 'Initial commit'" 274 | ``` 275 | 276 | 277 | 278 | ### `after_bundle(&block)` 279 | 280 | 注册一个回调,在安装好 gem 并生成 binstubs 之后执行。可以用来把生成的文件纳入版本控制: 281 | 282 | ```ruby 283 | after_bundle do 284 | git :init 285 | git add: '.' 286 | git commit: "-a -m 'Initial commit'" 287 | end 288 | ``` 289 | 290 | 即便传入 `--skip-bundle` 和(或) `--skip-spring` 选项,也会执行这个回调。 291 | 292 | 293 | 294 | ## 高级用法 295 | 296 | 应用模板在 `Rails::Generators::AppGenerator` 实例的上下文中运行,用到了 [Thor 提供的 `apply` 方法](https://github.com/erikhuda/thor/blob/master/lib/thor/actions.rb#L207)。因此,你可以扩展或修改这个实例,满足自己的需求。 297 | 298 | 例如,覆盖指定模板位置的 `source_paths` 方法。现在,`copy_file` 等方法能接受相对于模板位置的相对路径。 299 | 300 | ```ruby 301 | def source_paths 302 | [File.expand_path(File.dirname(__FILE__))] 303 | end 304 | ``` 305 | -------------------------------------------------------------------------------- /source/zh-CN/rails_on_rack.md: -------------------------------------------------------------------------------- 1 | # Rails on Rack 2 | 3 | 本文简介 Rails 与 Rack 的集成,以及与其他 Rack 组件的配合。 4 | 5 | 读完本文后,您将学到: 6 | 7 | * 如何在 Rails 应用中使用 Rack 中间件; 8 | * Action Pack 内部的中间件栈; 9 | * 如何自定义中间件栈。 10 | 11 | ----------------------------------------------------------------------------- 12 | 13 | WARNING: 本文假定你对 Rack 协议和相关概念有一定了解,例如中间件、URL 映射和 `Rack::Builder`。 14 | 15 | 16 | 17 | ## Rack 简介 18 | 19 | Rack 为使用 Ruby 开发的 Web 应用提供最简单的模块化接口,而且适应性强。Rack 使用最简单的方式包装 HTTP 请求和响应,从而抽象了 Web 服务器、Web 框架,以及二者之间的软件(称为中间件)的 API,统一成一个方法调用。 20 | 21 | * [Rack API 文档](http://rack.github.io/) 22 | 23 | 本文不详尽说明 Rack。如果你不了解 Rack 的基本概念,请参阅 [资源](#resources)。 24 | 25 | 26 | 27 | ## Rails on Rack 28 | 29 | 30 | 31 | ### Rails 应用的 Rack 对象 32 | 33 | `Rails.application` 是 Rails 应用的主 Rack 应用对象。任何兼容 Rack 的 Web 服务器都应该使用 `Rails.application` 对象伺服 Rails 应用。 34 | 35 | 36 | 37 | ### `rails server` 38 | 39 | `rails server` 负责创建 `Rack::Server` 对象和启动 Web 服务器。 40 | 41 | `rails server` 创建 `Rack::Server` 实例的方式如下: 42 | 43 | ```ruby 44 | Rails::Server.new.tap do |server| 45 | require APP_PATH 46 | Dir.chdir(Rails.application.root) 47 | server.start 48 | end 49 | ``` 50 | 51 | `Rails::Server` 继承自 `Rack::Server`,像下面这样调用 `Rack::Server#start` 方法: 52 | 53 | ```ruby 54 | class Server < ::Rack::Server 55 | def start 56 | ... 57 | super 58 | end 59 | end 60 | ``` 61 | 62 | 63 | 64 | ### `rackup` 65 | 66 | 如果不想使用 Rails 提供的 `rails server` 命令,而是使用 `rackup`,可以把下述代码写入 Rails 应用根目录中的 `config.ru` 文件里: 67 | 68 | ```ruby 69 | # Rails.root/config.ru 70 | require_relative 'config/environment' 71 | run Rails.application 72 | ``` 73 | 74 | 然后使用下述命令启动服务器: 75 | 76 | ```sh 77 | $ rackup config.ru 78 | ``` 79 | 80 | `rackup` 命令的各个选项可以通过下述命令查看: 81 | 82 | ```sh 83 | $ rackup --help 84 | ``` 85 | 86 | 87 | 88 | ### 开发和自动重新加载 89 | 90 | 中间件只加载一次,不会监视变化。若想让改动生效,必须重启服务器。 91 | 92 | 93 | 94 | ## Action Dispatcher 中间件栈 95 | 96 | Action Dispatcher 的内部组件很多都实现为 Rack 中间件。`Rails::Application` 使用 `ActionDispatch::MiddlewareStack` 把不同的内部和外部中间件组合在一起,构成完整的 Rails Rack 中间件。 97 | 98 | NOTE: Rails 中的 `ActionDispatch::MiddlewareStack` 相当于 `Rack::Builder`,但是为了满足 Rails 的需求,前者更灵活,而且功能更多。 99 | 100 | 101 | 102 | ### 审查中间件栈 103 | 104 | Rails 提供了一个方便的任务,用于查看在用的中间件栈: 105 | 106 | ```sh 107 | $ bin/rails middleware 108 | ``` 109 | 110 | 在新生成的 Rails 应用中,上述命令可能会输出下述内容: 111 | 112 | ``` 113 | use Rack::Sendfile 114 | use ActionDispatch::Static 115 | use ActionDispatch::Executor 116 | use ActiveSupport::Cache::Strategy::LocalCache::Middleware 117 | use Rack::Runtime 118 | use Rack::MethodOverride 119 | use ActionDispatch::RequestId 120 | use ActionDispatch::RemoteIp 121 | use Sprockets::Rails::QuietAssets 122 | use Rails::Rack::Logger 123 | use ActionDispatch::ShowExceptions 124 | use WebConsole::Middleware 125 | use ActionDispatch::DebugExceptions 126 | use ActionDispatch::RemoteIp 127 | use ActionDispatch::Reloader 128 | use ActionDispatch::Callbacks 129 | use ActiveRecord::Migration::CheckPending 130 | use ActionDispatch::Cookies 131 | use ActionDispatch::Session::CookieStore 132 | use ActionDispatch::Flash 133 | use Rack::Head 134 | use Rack::ConditionalGet 135 | use Rack::ETag 136 | run MyApp.application.routes 137 | ``` 138 | 139 | 这里列出的默认中间件(以及其他一些)在 [内部中间件栈](#internal-middleware-stack)概述。 140 | 141 | 142 | 143 | ### 配置中间件栈 144 | 145 | Rails 提供了一个简单的配置接口,`config.middleware`,用于在 `application.rb` 或针对环境的配置文件 `environments/.rb` 中添加、删除和修改中间件栈。 146 | 147 | 148 | 149 | #### 添加中间件 150 | 151 | 可以通过下述任意一种方法向中间件栈里添加中间件: 152 | 153 | * `config.middleware.use(new_middleware, args)`:在中间件栈的末尾添加一个中间件。 154 | * `config.middleware.insert_before(existing_middleware, new_middleware, args)`:在中间件栈里指定现有中间件的前面添加一个中间件。 155 | * `config.middleware.insert_after(existing_middleware, new_middleware, args)`:在中间件栈里指定现有中间件的后面添加一个中间件。 156 | 157 | ```ruby 158 | # config/application.rb 159 | 160 | # 把 Rack::BounceFavicon 放在默认 161 | config.middleware.use Rack::BounceFavicon 162 | 163 | # 在 ActionDispatch::Executor 后面添加 Lifo::Cache 164 | # 把 { page_cache: false } 参数传给 Lifo::Cache. 165 | config.middleware.insert_after ActionDispatch::Executor, Lifo::Cache, page_cache: false 166 | ``` 167 | 168 | 169 | 170 | #### 替换中间件 171 | 172 | 可以使用 `config.middleware.swap` 替换中间件栈里的现有中间件: 173 | 174 | ```ruby 175 | # config/application.rb 176 | 177 | # 把 ActionDispatch::ShowExceptions 换成 Lifo::ShowExceptions 178 | config.middleware.swap ActionDispatch::ShowExceptions, Lifo::ShowExceptions 179 | ``` 180 | 181 | 182 | 183 | #### 删除中间件 184 | 185 | 在应用的配置文件中添加下面这行代码: 186 | 187 | ```ruby 188 | # config/application.rb 189 | config.middleware.delete Rack::Runtime 190 | ``` 191 | 192 | 然后审查中间件栈,你会发现没有 `Rack::Runtime` 了: 193 | 194 | ```sh 195 | $ bin/rails middleware 196 | (in /Users/lifo/Rails/blog) 197 | use ActionDispatch::Static 198 | use # 199 | ... 200 | run Rails.application.routes 201 | ``` 202 | 203 | 若想删除会话相关的中间件,这么做: 204 | 205 | ```ruby 206 | # config/application.rb 207 | config.middleware.delete ActionDispatch::Cookies 208 | config.middleware.delete ActionDispatch::Session::CookieStore 209 | config.middleware.delete ActionDispatch::Flash 210 | ``` 211 | 212 | 若想删除浏览器相关的中间件,这么做: 213 | 214 | ```ruby 215 | # config/application.rb 216 | config.middleware.delete Rack::MethodOverride 217 | ``` 218 | 219 | 220 | 221 | ### 内部中间件栈 222 | 223 | Action Controller 的大部分功能都实现成中间件。下面概述它们的作用。 224 | 225 | **`Rack::Sendfile`** 226 | 227 | 在服务器端设定 X-Sendfile 首部。通过 `config.action_dispatch.x_sendfile_header` 选项配置。 228 | 229 | **`ActionDispatch::Static`** 230 | 231 | 用于伺服 public 目录中的静态文件。如果把 `config.public_file_server.enabled` 设为 `false`,禁用这个中间件。 232 | 233 | **`Rack::Lock`** 234 | 235 | 把 `env["rack.multithread"]` 设为 `false`,把应用包装到 Mutex 中。 236 | 237 | **`ActionDispatch::Executor`** 238 | 239 | 用于在开发环境中以线程安全方式重新加载代码。 240 | 241 | **`ActiveSupport::Cache::Strategy::LocalCache::Middleware`** 242 | 243 | 用于缓存内存。这个缓存对线程不安全。 244 | 245 | **`Rack::Runtime`** 246 | 247 | 设定 X-Runtime 首部,包含执行请求的用时(单位为秒)。 248 | 249 | **`Rack::MethodOverride`** 250 | 251 | 如果设定了 `params[:_method]`,允许覆盖请求方法。`PUT` 和 `DELETE` 两个 HTTP 方法就是通过这个中间件提供支持的。 252 | 253 | **`ActionDispatch::RequestId`** 254 | 255 | 在响应中设定唯一的 `X-Request-Id` 首部,并启用 `ActionDispatch::Request#request_id` 方法。 256 | 257 | **`ActionDispatch::RemoteIp`** 258 | 259 | 检查 IP 欺骗攻击。 260 | 261 | `Sprockets::Rails::QuietAssets`:在日志中输出对静态资源的请求。 262 | 263 | **`Rails::Rack::Logger`** 264 | 265 | 通知日志,请求开始了。请求完毕后,清空所有相关日志。 266 | 267 | **`ActionDispatch::ShowExceptions`** 268 | 269 | 拯救应用返回的所有异常,调用处理异常的应用,把异常包装成对终端用户友好的格式。 270 | 271 | **`ActionDispatch::DebugExceptions`** 272 | 273 | 如果是本地请求,负责在日志中记录异常,并显示调试页面。 274 | 275 | **`ActionDispatch::Reloader`** 276 | 277 | 提供准备和清理回调,目的是在开发环境中协助重新加载代码。 278 | 279 | **`ActionDispatch::Callbacks`** 280 | 281 | 提供回调,在分派请求前后执行。 282 | 283 | **`ActiveRecord::Migration::CheckPending`** 284 | 285 | 检查有没有待运行的迁移,如果有,抛出 `ActiveRecord::PendingMigrationError`。 286 | 287 | **`ActionDispatch::Cookies`** 288 | 289 | 为请求设定 cookie。 290 | 291 | **`ActionDispatch::Session::CookieStore`** 292 | 293 | 负责把会话存储在 cookie 中。 294 | 295 | **`ActionDispatch::Flash`** 296 | 297 | 设置闪现消息的键。仅当为 `config.action_controller.session_store` 设定值时才启用。 298 | 299 | **`Rack::Head`** 300 | 301 | 把 HEAD 请求转换成 GET 请求,然后伺服 GET 请求。 302 | 303 | **`Rack::ConditionalGet`** 304 | 305 | 支持“条件 GET 请求”,如果页面没变,服务器不做响应。 306 | 307 | **`Rack::ETag`** 308 | 309 | 为所有字符串主体添加 ETag 首部。ETag 用于验证缓存。 310 | 311 | TIP: 在自定义的 Rack 栈中可以使用上述任何一个中间件。 312 | 313 | 314 | 315 | ## 资源 316 | 317 | 318 | 319 | ### 学习 Rack 320 | 321 | * [Rack 官方网站](http://rack.github.io/) 322 | * [Introducing Rack](http://chneukirchen.org/blog/archive/2007/02/introducing-rack.html) 323 | 324 | 325 | 326 | ### 理解中间件 327 | 328 | * [Railscast 中讲解 Rack 中间件的视频](http://railscasts.com/episodes/151-rack-middleware) 329 | -------------------------------------------------------------------------------- /source/zh-CN/ruby_on_rails_guides_guidelines.md: -------------------------------------------------------------------------------- 1 | # Ruby on Rails 指南指导方针 2 | 3 | 本文说明编写 Ruby on Rails 指南的指导方针。本文也遵守这一方针,本身就是个示例。 4 | 5 | 读完本文后,您将学到: 6 | 7 | * Rails 文档使用的约定; 8 | * 如何在本地生成指南。 9 | 10 | ----------------------------------------------------------------------------- 11 | 12 | 13 | 14 | ## Markdown 15 | 16 | 指南使用 [GitHub Flavored Markdown](https://help.github.com/articles/github-flavored-markdown) 编写。Markdown 有[完整的文档](http://daringfireball.net/projects/markdown/syntax),还有[速查表](http://daringfireball.net/projects/markdown/basics)。 17 | 18 | 19 | 20 | ## 序言 21 | 22 | 每篇文章的开头要有介绍性文字(蓝色区域中的简短介绍)。序言应该告诉读者文章的主旨,以及能让读者学到什么。可以以[Rails 路由全解](routing.html)为例。 23 | 24 | 25 | 26 | ## 标题 27 | 28 | 每篇文章的标题使用 `h1` 标签,文章中的小节使用 `h2` 标签,子节使用 `h3` 标签,以此类推。注意,生成的 HTML 从 `

` 标签开始。 29 | 30 | ```md 31 | Guide Title 32 | =========== 33 | 34 | Section 35 | ------- 36 | 37 | ### Sub Section 38 | ``` 39 | 40 | 标题中除了介词、连词、冠词和“to be”这种形式的动词之外,每个词的首字母大写: 41 | 42 | ```md 43 | #### Middleware Stack is an Array 44 | #### When are Objects Saved? 45 | ``` 46 | 47 | 行内格式与正文一样: 48 | 49 | ```md 50 | ##### The `:content_type` Option 51 | ``` 52 | 53 | 54 | 55 | ## 指向 API 的链接 56 | 57 | 指南生成程序使用下述方式处理指向 API(`api.rubyonrails.org`)的链接。 58 | 59 | 包含版本号的链接原封不动。例如,下述链接不做修改: 60 | 61 | ``` 62 | http://api.rubyonrails.org/v5.0.1/classes/ActiveRecord/Attributes/ClassMethods.html 63 | ``` 64 | 65 | 请在发布记中使用这种链接,因为不管生成哪个版本的指南,发布记中的链接不应该变。 66 | 67 | 如果链接中没有版本号,而且生成的是最新开发版的指南,域名会替换成 `edgeapi.rubyonrails.org`。例如: 68 | 69 | ``` 70 | http://api.rubyonrails.org/classes/ActionDispatch/Response.html 71 | ``` 72 | 73 | 会变成: 74 | 75 | ``` 76 | http://edgeapi.rubyonrails.org/classes/ActionDispatch/Response.html 77 | ``` 78 | 79 | 如果链接中没有版本号,而生成的是某个版本的指南,会在链接中插入版本号。例如,生成 v5.1.0 的指南时,下述链接: 80 | 81 | ``` 82 | http://api.rubyonrails.org/classes/ActionDispatch/Response.html 83 | ``` 84 | 85 | 会变成: 86 | 87 | ``` 88 | http://api.rubyonrails.org/v5.1.0/classes/ActionDispatch/Response.html 89 | ``` 90 | 91 | 请勿直接链接到 `edgeapi.rubyonrails.org`。 92 | 93 | 94 | 95 | ## API 文档指导方针 96 | 97 | 指南和 API 应该连贯一致。尤其是[API 文档指导方针](api_documentation_guidelines.html)中的下述几节,同样适用于指南: 98 | 99 | * [用词](api_documentation_guidelines.html#wording) 100 | * [英语](api_documentation_guidelines.html#english) 101 | * [示例代码](api_documentation_guidelines.html#example-code) 102 | * [文件名](api_documentation_guidelines.html#file-names) 103 | * [字体](api_documentation_guidelines.html#fonts) 104 | 105 | 106 | 107 | ## HTML 版指南 108 | 109 | 在生成指南之前,先确保你的系统中安装了 Bundler 的最新版。写作本文时,要在你的设备中安装 Bundler 1.3.5 或以上版本。 110 | 111 | 安装最新版 Bundler 的方法是,执行 `gem install bundler` 命令。 112 | 113 | 114 | 115 | ### 生成 116 | 117 | 若想生成全部指南,进入 `guides` 目录,执行 `bundle install` 命令之后再执行: 118 | 119 | ```sh 120 | $ bundle exec rake guides:generate 121 | ``` 122 | 123 | 或者 124 | 125 | ```sh 126 | $ bundle exec rake guides:generate:html 127 | ``` 128 | 129 | 得到的 HTML 文件在 `./output` 目录中。 130 | 131 | 如果只想处理 `my_guide.md`,使用 `ONLY` 环境变量: 132 | 133 | ```sh 134 | $ touch my_guide.md 135 | $ bundle exec rake guides:generate ONLY=my_guide 136 | ``` 137 | 138 | 默认情况下,没有改动的文章不会处理,因此实际使用中很少用到 `ONLY`。 139 | 140 | 如果想强制处理所有文章,传入 `ALL=1`。 141 | 142 | 如果想生成英语之外的指南,可以把译文放在 `source` 中的子目录里(如 `source/es`),然后使用 `GUIDES_LANGUAGE` 环境变量: 143 | 144 | ```sh 145 | $ bundle exec rake guides:generate GUIDES_LANGUAGE=es 146 | ``` 147 | 148 | 如果想查看可用于配置生成脚本的全部环境变量,只需执行: 149 | 150 | ```sh 151 | $ rake 152 | ``` 153 | 154 | 155 | 156 | ### 验证 157 | 158 | 请使用下述命令验证生成的 HTML: 159 | 160 | ```sh 161 | $ bundle exec rake guides:validate 162 | ``` 163 | 164 | 尤其要注意,ID 是从标题的内容中生成的,往往会重复。生成指南时请设定 `WARNINGS=1`,监测重复的 ID。提醒消息中有建议的解决方案。 165 | 166 | 167 | 168 | ## Kindle 版指南 169 | 170 | 171 | 172 | ### 生成 173 | 174 | 如果想生成 Kindle 版指南,使用下述 Rake 任务: 175 | 176 | ```sh 177 | $ bundle exec rake guides:generate:kindle 178 | ``` 179 | -------------------------------------------------------------------------------- /w3c_validator.rb: -------------------------------------------------------------------------------- 1 | # --------------------------------------------------------------------------- 2 | # 3 | # This script validates the generated guides against the W3C Validator. 4 | # 5 | # Guides are taken from the output directory, from where all .html files are 6 | # submitted to the validator. 7 | # 8 | # This script is prepared to be launched from the guides directory as a rake task: 9 | # 10 | # rake guides:validate 11 | # 12 | # If nothing is specified, all files will be validated, but you can check just 13 | # some of them using this environment variable: 14 | # 15 | # ONLY 16 | # Use ONLY if you want to validate only one or a set of guides. Prefixes are 17 | # enough: 18 | # 19 | # # validates only association_basics.html 20 | # rake guides:validate ONLY=assoc 21 | # 22 | # Separate many using commas: 23 | # 24 | # # validates only association_basics.html and command_line.html 25 | # rake guides:validate ONLY=assoc,command 26 | # 27 | # --------------------------------------------------------------------------- 28 | 29 | require "w3c_validators" 30 | include W3CValidators 31 | 32 | module RailsGuides 33 | class Validator 34 | def validate 35 | # https://github.com/w3c-validators/w3c_validators/issues/25 36 | validator = NuValidator.new 37 | STDOUT.sync = true 38 | errors_on_guides = {} 39 | 40 | guides_to_validate.each do |f| 41 | begin 42 | results = validator.validate_file(f) 43 | rescue Exception => e 44 | puts "\nCould not validate #{f} because of #{e}" 45 | next 46 | end 47 | 48 | if results.errors.length > 0 49 | print "E" 50 | errors_on_guides[f] = results.errors 51 | else 52 | print "." 53 | end 54 | end 55 | 56 | show_results(errors_on_guides) 57 | end 58 | 59 | private 60 | def guides_to_validate 61 | guides = Dir["./output/*.html"] 62 | guides.delete("./output/layout.html") 63 | guides.delete("./output/_license.html") 64 | guides.delete("./output/_welcome.html") 65 | ENV.key?("ONLY") ? select_only(guides) : guides 66 | end 67 | 68 | def select_only(guides) 69 | prefixes = ENV["ONLY"].split(",").map(&:strip) 70 | guides.select do |guide| 71 | prefixes.any? { |p| guide.start_with?("./output/#{p}") } 72 | end 73 | end 74 | 75 | def show_results(error_list) 76 | if error_list.size == 0 77 | puts "\n\nAll checked guides validate OK!" 78 | else 79 | error_summary = error_detail = "" 80 | 81 | error_list.each_pair do |name, errors| 82 | error_summary += "\n #{name}" 83 | error_detail += "\n\n #{name} has #{errors.size} validation error(s):\n" 84 | errors.each do |error| 85 | error_detail += "\n " + error.to_s.delete("\n") 86 | end 87 | end 88 | 89 | puts "\n\nThere are #{error_list.size} guides with validation errors:\n" + error_summary 90 | puts "\nHere are the detailed errors for each guide:" + error_detail 91 | end 92 | end 93 | end 94 | end 95 | 96 | RailsGuides::Validator.new.validate 97 | --------------------------------------------------------------------------------