rails-tutorial
Table of Contents
Chapter 01: From zero to deploy
- rails 4.0版本和rails 3.2版本的差距不大. 对本书来说,主要的一个不同是一种新的 安全机制:strong parameters
- 本书就是RPG游戏,每一章是一关
Introduction
- rails成功的要素:
- 100% open source
- 在ruby的帮助下,为web开发创建了一种domain-specific language,使得很多操作变 得容易,比如自动生成网页,数据库, url等
- rails core team是一个非常愿意尝试新技术的团队
- 社区的壮大,让"Google the error message"变得非常容易
Comments for various readers
- 先学习ruby再开始rails
- rails是测试驱动开发,所以,要学会先写测试
"Scaling" Rails
- 在你的site大到Hulu或者Yellow Pages之前,不用考虑扩展性
Conventiions in this book
Up and running
Development environments
- 最常见的IDE是rubymine
Ruby, RubyGems, Rails, and Git
- 需要使用"完全一致"的软件版本来达到完全一致的效果
- 安装ruby的时候,本书使用rvm:
- 安装Command Line Tools for Xcode
- 安装RVM
curl -sSL https://get.rvm.io | bash -s stable
- 升级rvm到最稳定版本
rvm get stable
- rvm再第一次安装后,需要重启terminal,或者
source ~/.rvm/scripts/rvm
- rvm在编译并安装ruby之前可以使用命令来查看缺少必要的lib
rvm requirements
- 得到缺少的lib信息以后,就需要brew来安装缺失lib
brew install libtool libxslt libksba openssl
- 我们还需要安装libyaml(新版的 rvm requirements 会提示的)
brew install libyaml
- rvm 搞定以后,下一步就是安装ruby了. 安装的时候,我们要指出OpenSSL的位置
hfengdeMacBook-Air:rails-tutorial hfeng$ rvm install 2.0.0 --with-openssl-dir=/usr/local/opt/openssl Checking requirements for osx. Certificates in '/usr/local/etc/openssl/cert.pem' are already up to date. Requirements installation successful. Installing Ruby from source to: /Users/hfeng/.rvm/rubies/ruby-2.0.0-p481, this may take a while depending on your cpu(s)... ruby-2.0.0-p481 - #downloading ruby-2.0.0-p481, this may take a while depending on your connection... % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 10.2M 100 10.2M 0 0 50421 0 0:03:32 0:03:32 --:--:-- 141k ruby-2.0.0-p481 - #extracting ruby-2.0.0-p481 to /Users/hfeng/.rvm/src/ruby-2.0.0-p481.... ruby-2.0.0-p481 - #configuring................................................. ruby-2.0.0-p481 - #post-configuration. ruby-2.0.0-p481 - #compiling................................................................................ ruby-2.0.0-p481 - #installing.............. ruby-2.0.0-p481 - #making binaries executable.. ruby-2.0.0-p481 - #downloading rubygems-2.2.2 % Total % Received % Xferd Average Speed Time Time Time Current Dload Upload Total Spent Left Speed 100 404k 100 404k 0 0 95267 0 0:00:04 0:00:04 --:--:-- 95285 No checksum for downloaded archive, recording checksum in user configuration. ruby-2.0.0-p481 - #extracting rubygems-2.2.2.... ruby-2.0.0-p481 - #removing old rubygems......... $LANG was empty, setting up LANG=en_US, if it fails again try setting LANG to something sane and try again. ruby-2.0.0-p481 - #installing rubygems-2.2.2............... ruby-2.0.0-p481 - #gemset created /Users/hfeng/.rvm/gems/ruby-2.0.0-p481@global ruby-2.0.0-p481 - #importing gemset /Users/hfeng/.rvm/gemsets/global.gems......................................................... ruby-2.0.0-p481 - #generating global wrappers........ ruby-2.0.0-p481 - #gemset created /Users/hfeng/.rvm/gems/ruby-2.0.0-p481 ruby-2.0.0-p481 - #importing gemsetfile /Users/hfeng/.rvm/gemsets/default.gems evaluated to empty gem list ruby-2.0.0-p481 - #generating default wrappers........ ruby-2.0.0-p481 - #adjusting #shebangs for (gem irb erb ri rdoc testrb rake). Install of ruby-2.0.0-p481 - #complete Ruby was built without documentation, to build it run: rvm docs generate-ri hfengdeMacBook-Air:rails-tutorial hfeng$ which ruby /Users/hfeng/.rvm/rubies/ruby-2.0.0-p481/bin/ruby hfengdeMacBook-Air:rails-tutorial hfeng$ ruby --version ruby 2.0.0p481 (2014-05-08 revision 45883) [x86_64-darwin13.2.0] hfengdeMacBook-Air:rails-tutorial hfeng$ rvm list rvm rubies =* ruby-2.0.0-p481 [ x86_64 ] # => - current # =* - current && default # * - default hfengdeMacBook-Air:rails-tutorial hfeng$
- rvm 的ruby是全局的, 创建的gemset也是要依靠某个ruby版本,比如我们创建一个新
的gemset(名字叫railstutorial_rails_4_0), 在这之前一定要指定ruby版本.
创建gemset之后,还可以指定某个gemset为默认的gemset
hfengdeMacBook-Air:rails-tutorial hfeng$ rvm 2.0.0 hfengdeMacBook-Air:rails-tutorial hfeng$ rvm list rvm rubies =* ruby-2.0.0-p481 [ x86_64 ] # => - current # =* - current && default # * - default hfengdeMacBook-Air:rails-tutorial hfeng$ rvm gemset list gemsets for ruby-2.0.0-p481 (found in /Users/hfeng/.rvm/gems/ruby-2.0.0-p481) => (default) global hfengdeMacBook-Air:rails-tutorial hfeng$ rvm gemset create railstutorial_rails_4_0 ruby-2.0.0-p481 - #gemset created /Users/hfeng/.rvm/gems/ruby-2.0.0-p481@railstutorial_rails_4_0 ruby-2.0.0-p481 - #generating railstutorial_rails_4_0 wrappers........ hfengdeMacBook-Air:rails-tutorial hfeng$ rvm gemset list gemsets for ruby-2.0.0-p481 (found in /Users/hfeng/.rvm/gems/ruby-2.0.0-p481) => (default) global railstutorial_rails_4_0 hfengdeMacBook-Air:rails-tutorial hfeng$ rvm use 2.0.0@railstutorial_rails_4_0 --default Using /Users/hfeng/.rvm/gems/ruby-2.0.0-p481 with gemset railstutorial_rails_4_0 hfengdeMacBook-Air:rails-tutorial hfeng$ rvm gemset list gemsets for ruby-2.0.0-p481 (found in /Users/hfeng/.rvm/gems/ruby-2.0.0-p481) (default) global => railstutorial_rails_4_0
- gem 这个binary是和ruby相关联的,gemset跟着一个特定版本的ruby,也就跟着一个特定版本的gem
hfengdeMacBook-Air:rails-tutorial hfeng$ which gem /Users/hfeng/.rvm/rubies/ruby-2.0.0-p481/bin/gem hfengdeMacBook-Air:rails-tutorial hfeng$ gem --version 2.2.2
- 我们要把默认的跟着ruby-2.0.0的gem更改为2.1.9 (本书所选的gem版本)
hfengdeMacBook-Air:rails-tutorial hfeng$ gem update --system 2.1.9 Updating rubygems-update Fetching: rubygems-update-2.1.9.gem (100%) Successfully installed rubygems-update-2.1.9 Installing RubyGems 2.1.9 RubyGems 2.1.9 installed ------------------------------------------------------------------------------ RubyGems installed the following executables: /Users/hfeng/.rvm/rubies/ruby-2.0.0-p481/bin/gem RubyGems system software updated hfengdeMacBook-Air:rails-tutorial hfeng$ gem --version 2.1.9
- 下面我们要开始gem来安装gems了!为了不安装rdoc和ri,我们可以disable它们,通过
如下设置~/.gemrc
install: --no-rdoc --no-ri update: --no-rdoc --no-ri
- 在安装第一个gem之前,先确认一下环境
hfengdeMacBook-Air:rails-tutorial hfeng$ rvm gemset list gemsets for ruby-2.0.0-p481 (found in /Users/hfeng/.rvm/gems/ruby-2.0.0-p481) (default) global => railstutorial_rails_4_0 hfengdeMacBook-Air:rails-tutorial hfeng$ which gem /Users/hfeng/.rvm/rubies/ruby-2.0.0-p481/bin/gem hfengdeMacBook-Air:rails-tutorial hfeng$ rvm info ruby-2.0.0-p481@railstutorial_rails_4_0: system: uname: "Darwin hfengdeMacBook-Air.local 13.2.0 Darwin Kernel Version 13.2.0: Thu Apr 17 23:03:13 PDT 2014; root:xnu-2422.100.13~1/RELEASE_X86_64 x86_64" system: "osx/10.9/x86_64" bash: "/bin/bash => GNU bash, version 3.2.51(1)-release (x86_64-apple-darwin13)" zsh: "/bin/zsh => zsh 5.0.2 (x86_64-apple-darwin13.0)" rvm: version: "rvm 1.25.28 (stable) by Wayne E. Seguin <wayneeseguin@gmail.com>, Michal Papis <mpapis@gmail.com> [https://rvm.io/]" updated: "7 hours 7 minutes 2 seconds ago" path: "/Users/hfeng/.rvm" ruby: interpreter: "ruby" version: "2.0.0p481" date: "2014-05-08" platform: "x86_64-darwin13.2.0" patchlevel: "2014-05-08 revision 45883" full_version: "ruby 2.0.0p481 (2014-05-08 revision 45883) [x86_64-darwin13.2.0]" homes: gem: "/Users/hfeng/.rvm/gems/ruby-2.0.0-p481@railstutorial_rails_4_0" ruby: "/Users/hfeng/.rvm/rubies/ruby-2.0.0-p481" binaries: ruby: "/Users/hfeng/.rvm/rubies/ruby-2.0.0-p481/bin/ruby" irb: "/Users/hfeng/.rvm/rubies/ruby-2.0.0-p481/bin/irb" gem: "/Users/hfeng/.rvm/rubies/ruby-2.0.0-p481/bin/gem" rake: "/Users/hfeng/.rvm/rubies/ruby-2.0.0-p481/bin/rake" environment: PATH: "/Users/hfeng/.rvm/gems/ruby-2.0.0-p481@railstutorial_rails_4_0/bin:/Users/hfeng/.rvm/gems/ruby-2.0.0-p481@global/bin:/Users/hfeng/.rvm/rubies/ruby-2.0.0-p481/bin:/usr/local/bin:/usr/bin:/bin:/usr/sbin:/sbin:/usr/local/bin:/usr:/Users/hfeng/.rvm/bin" GEM_HOME: "/Users/hfeng/.rvm/gems/ruby-2.0.0-p481@railstutorial_rails_4_0" GEM_PATH: "/Users/hfeng/.rvm/gems/ruby-2.0.0-p481@railstutorial_rails_4_0:/Users/hfeng/.rvm/gems/ruby-2.0.0-p481@global" MY_RUBY_HOME: "/Users/hfeng/.rvm/rubies/ruby-2.0.0-p481" IRBRC: "/Users/hfeng/.rvm/rubies/ruby-2.0.0-p481/.irbrc" RUBYOPT: "" gemset: "railstutorial_rails_4_0"
- 安装第一个gem, rails. 安装完之后确认rails是在gemset ruby-2.0.0-p481@railstutorial_rails_4_0
里面的
hfengdeMacBook-Air:rails-tutorial hfeng$ gem install rails --version 4.0.8 Fetching: i18n-0.6.11.gem (100%) Successfully installed i18n-0.6.11 Fetching: multi_json-1.10.1.gem (100%) Successfully installed multi_json-1.10.1 Fetching: tzinfo-0.3.40.gem (100%) Successfully installed tzinfo-0.3.40 Fetching: thread_safe-0.3.4.gem (100%) Successfully installed thread_safe-0.3.4 Fetching: activesupport-4.0.8.gem (100%) Successfully installed activesupport-4.0.8 Fetching: builder-3.1.4.gem (100%) Successfully installed builder-3.1.4 Fetching: rack-1.5.2.gem (100%) Successfully installed rack-1.5.2 Fetching: rack-test-0.6.2.gem (100%) Successfully installed rack-test-0.6.2 Fetching: erubis-2.7.0.gem (100%) Successfully installed erubis-2.7.0 Fetching: actionpack-4.0.8.gem (100%) Successfully installed actionpack-4.0.8 Fetching: activemodel-4.0.8.gem (100%) Successfully installed activemodel-4.0.8 Fetching: arel-4.0.2.gem (100%) Successfully installed arel-4.0.2 Fetching: activerecord-deprecated_finders-1.0.3.gem (100%) Successfully installed activerecord-deprecated_finders-1.0.3 Fetching: activerecord-4.0.8.gem (100%) Successfully installed activerecord-4.0.8 Fetching: mime-types-1.25.1.gem (100%) Successfully installed mime-types-1.25.1 Fetching: polyglot-0.3.5.gem (100%) Successfully installed polyglot-0.3.5 Fetching: treetop-1.4.15.gem (100%) Successfully installed treetop-1.4.15 Fetching: mail-2.5.4.gem (100%) Successfully installed mail-2.5.4 Fetching: actionmailer-4.0.8.gem (100%) Successfully installed actionmailer-4.0.8 Fetching: thor-0.19.1.gem (100%) Successfully installed thor-0.19.1 Fetching: railties-4.0.8.gem (100%) Successfully installed railties-4.0.8 Fetching: hike-1.2.3.gem (100%) Successfully installed hike-1.2.3 Fetching: tilt-1.4.1.gem (100%) Successfully installed tilt-1.4.1 Fetching: sprockets-2.12.1.gem (100%) Successfully installed sprockets-2.12.1 Fetching: sprockets-rails-2.1.3.gem (100%) Successfully installed sprockets-rails-2.1.3 Fetching: rails-4.0.8.gem (100%) Successfully installed rails-4.0.8 26 gems installed hfengdeMacBook-Air:rails-tutorial hfeng$ which rails /Users/hfeng/.rvm/gems/ruby-2.0.0-p481@railstutorial_rails_4_0/bin/rails hfengdeMacBook-Air:rails-tutorial hfeng$ rails -v Rails 4.0.8
The first application
- 所有的 rails 应用程序都是从 rails new 命令开始的.
hfengdeMacBook-Air:exam hfeng$ mkdir rails_projects hfengdeMacBook-Air:exam hfeng$ cd rails_projects/ hfengdeMacBook-Air:rails_projects hfeng$ which rails /Users/hfeng/.rvm/gems/ruby-2.0.0-p481@railstutorial_rails_4_0/bin/rails hfengdeMacBook-Air:rails_projects hfeng$ rails --version Rails 4.0.8 hfengdeMacBook-Air:rails_projects hfeng$ rails new first_app create create README.rdoc create Rakefile create config.ru create .gitignore create Gemfile create app create app/assets/javascripts/application.js create app/assets/stylesheets/application.css create app/controllers/application_controller.rb create app/helpers/application_helper.rb create app/views/layouts/application.html.erb create app/assets/images/.keep create app/mailers/.keep create app/models/.keep create app/controllers/concerns/.keep create app/models/concerns/.keep create bin create bin/bundle create bin/rails create bin/rake create config create config/routes.rb create config/application.rb create config/environment.rb create config/environments create config/environments/development.rb create config/environments/production.rb create config/environments/test.rb create config/initializers create config/initializers/backtrace_silencers.rb create config/initializers/filter_parameter_logging.rb create config/initializers/inflections.rb create config/initializers/mime_types.rb create config/initializers/secret_token.rb create config/initializers/session_store.rb create config/initializers/wrap_parameters.rb create config/locales create config/locales/en.yml create config/boot.rb create config/database.yml create db create db/seeds.rb create lib create lib/tasks create lib/tasks/.keep create lib/assets create lib/assets/.keep create log create log/.keep create public create public/404.html create public/422.html create public/500.html create public/favicon.ico create public/robots.txt create test/fixtures create test/fixtures/.keep create test/controllers create test/controllers/.keep create test/mailers create test/mailers/.keep create test/models create test/models/.keep create test/helpers create test/helpers/.keep create test/integration create test/integration/.keep create test/test_helper.rb create tmp/cache create tmp/cache/assets create vendor/assets/javascripts create vendor/assets/javascripts/.keep create vendor/assets/stylesheets create vendor/assets/stylesheets/.keep run bundle install Fetching gem metadata from https://rubygems.org/.......... Fetching additional metadata from https://rubygems.org/.. Resolving dependencies... Installing rake 10.3.2 Using i18n 0.6.11 Installing minitest 4.7.5 Using multi_json 1.10.1 Using thread_safe 0.3.4 Using tzinfo 0.3.40 Using activesupport 4.0.8 Using builder 3.1.4 Using erubis 2.7.0 Using rack 1.5.2 Using rack-test 0.6.2 Using actionpack 4.0.8 Using mime-types 1.25.1 Using polyglot 0.3.5 Using treetop 1.4.15 Using mail 2.5.4 Using actionmailer 4.0.8 Using activemodel 4.0.8 Using activerecord-deprecated_finders 1.0.3 Using arel 4.0.2 Using activerecord 4.0.8 Using bundler 1.6.4 Installing coffee-script-source 1.7.1 Installing execjs 2.2.1 Installing coffee-script 2.3.0 Using thor 0.19.1 Using railties 4.0.8 Installing coffee-rails 4.0.1 Using hike 1.2.3 Installing jbuilder 1.5.3 Installing jquery-rails 3.1.1 Installing json 1.8.1 Using tilt 1.4.1 Installing sprockets 2.11.0 Using sprockets-rails 2.1.3 Using rails 4.0.8 Installing rdoc 4.1.1 Installing sass 3.2.19 Installing sass-rails 4.0.3 Installing sdoc 0.4.0 Installing sqlite3 1.3.9 Installing turbolinks 2.2.2 Installing uglifier 2.5.1 Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed. Post-install message from rdoc: Depending on your version of ruby, you may need to install ruby rdoc/ri data: <= 1.8.6 : unsupported = 1.8.7 : gem install rdoc-data; rdoc-data --install = 1.9.1 : gem install rdoc-data; rdoc-data --install >= 1.9.2 : nothing to do! Yay!
- 我们来看看每个 folder 的意义都是什么
File/ Directory Purpose app/ Core application code, including models, views, controllers and helpers app/assets Applications assets such as CSS, JavaScript and images bin/ Binary executable files config/ Application configuration db/ Database files doc/ Documentation for the application lib/ Library modules lib/assets Library assets such as CSS, JavaScript and images log/ application log files public/ Data accessible to the public, such as error pages bin/rails A program for generating code, opening console sessions, or starting a local server test/ Application tests tmp/ Temporary files vendor Third-party code such as plugins and gems vender/assets Third-part assets such as CSS, JS, and images README.rdoc A brief description of the application Rakefile Utility tasks available via the rake command Gemfile Gem requirements for this app Gemfile.lock A list of gems used to ensure that all copies of the app use the same gem versions config.ru A configuration file for Rack middleware .gitignore Patterns for files that shoulde be ignored by Git
Bundler
- 一般创建rails项目之后,下一个步骤就是安装项目Gemfile里面的gem(通过bundle install命令).
- 不过如果你细心的话,会注意到rails new最后会自动调用run bundle install,所以 我们这里要对Gemfile进行一些更改,然后继续调用bundle来使得更改起效.
- 更改大部分是使用版本准确的gem(而不再是一个范围内的gem), 下面是我们做的更改
和原来初始版本的diff
source 'https://rubygems.org' +ruby '2.0.0' +#ruby-gemsets=railstutorial_rails_4_0 # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '4.0.8' # Use sqlite3 as the database for Active Record -gem 'sqlite3' +group :development do + gem 'sqlite3', '1.3.8' +end # Use SCSS for stylesheets -gem 'sass-rails', '~> 4.0.2' +gem 'sass-rails', '4.0.1' # Use Uglifier as compressor for JavaScript assets -gem 'uglifier', '>= 1.3.0' +gem 'uglifier', '2.1.1' # Use CoffeeScript for .js.coffee assets and views -gem 'coffee-rails', '~> 4.0.0' +gem 'coffee-rails', '4.0.1' # See https://github.com/sstephenson/execjs#readme for more supported runtimes # gem 'therubyracer', platforms: :ruby # Use jquery as the JavaScript library -gem 'jquery-rails' +gem 'jquery-rails', '3.0.4' # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks -gem 'turbolinks' +gem 'turbolinks', '1.1.1' # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder -gem 'jbuilder', '~> 1.2' +gem 'jbuilder', '1.0.2' group :doc do # bundle exec rake doc:rails generates the API under doc/api. - gem 'sdoc', require: false + gem 'sdoc', '0.3.20', require: false end # Use ActiveModel has_secure_password
- 开头的两行主要是确定ruby和gemset版本, 如果使用RVM的话,它会来使用的
ruby '2.0.0' #ruby-gemset=railstutorial_rails_4_0
- 我们看看diff文件,就会发现很多种Gemfile定义模糊gem文件版本的方法
- 安装大于某个版本的最新版本:比如下面的例子中,只要uglifier最新版本比1.3.0
大,那么就会安装.比如7.2
gem 'uglifier', '>= 1.3.0'
- 安装某个区间的最新版本:比如下面的例子中, 安装和所列版本的major和minor版
本一样的,最新版本,比如4.0.11
gem 'coffee-rails', '~> 4.0.0'
- 安装大于某个版本的最新版本:比如下面的例子中,只要uglifier最新版本比1.3.0
大,那么就会安装.比如7.2
- 这一切之后,我们就可以bundle install啦!, 在之前可以先bundle update一下, bundle
update其实是进化版本的的bundle install: bundle update比bundle install多做
了两步:
- 如果Gemfile里面的gem没有设置版本,那么就要把它更新到最新
- 如果Gemfile里面的gem设置了模糊版本,比如~>, 那么要更新到其"认可的"最新
- 也就是说bundle update更新Gemfile.lock里面版本的可能性很大,因为很可能有gem
会更新,下面的结果印证了我们的判断.
hfeng@ubuntu1204:~/rails_projects/first_app$ bundle update Fetching source index from https://rubygems.org/ Resolving dependencies........................ Using rake 10.3.2 Using i18n 0.6.11 Using minitest 4.7.5 Using multi_json 1.10.1 Using thread_safe 0.3.4 Using tzinfo 0.3.40 Using activesupport 4.0.8 Using builder 3.1.4 Using erubis 2.7.0 Using rack 1.5.2 Using rack-test 0.6.2 Using actionpack 4.0.8 Using mime-types 1.25.1 Using polyglot 0.3.5 Using treetop 1.4.15 Using mail 2.5.4 Using actionmailer 4.0.8 Using activemodel 4.0.8 Using activerecord-deprecated_finders 1.0.3 Using arel 4.0.2 Using activerecord 4.0.8 Using bundler 1.6.2 Using coffee-script-source 1.7.1 Using execjs 2.2.1 Using coffee-script 2.3.0 Using thor 0.19.1 Using railties 4.0.8 Using coffee-rails 4.0.1 Using hike 1.2.3 Installing jbuilder 1.0.2 (was 1.5.3) Installing jquery-rails 3.0.4 (was 3.1.1) Using json 1.8.1 Using tilt 1.4.1 Using sprockets 2.12.1 (was 2.11.0) Installing sprockets-rails 2.0.1 (was 2.1.3) Using rails 4.0.8 Installing rdoc 3.12.2 (was 4.1.1) Installing sass 3.3.10 (was 3.2.19) Installing sass-rails 4.0.1 (was 4.0.3) Installing sdoc 0.3.20 (was 0.4.0) Installing sqlite3 1.3.8 (was 1.3.9) Installing turbolinks 1.1.1 (was 2.2.2) Installing uglifier 2.1.1 (was 2.5.3) Your bundle is updated!
- 再更新好Gemfile.lock之后,我们就可以开始build install啦. 从log可见,其实在bundle
update的时候,都已经安装好了
hfeng@ubuntu1204:~/rails_projects/first_app$ bundle install Using rake 10.3.2 Using i18n 0.6.11 Using minitest 4.7.5 Using multi_json 1.10.1 Using thread_safe 0.3.4 Using tzinfo 0.3.40 Using activesupport 4.0.8 Using builder 3.1.4 Using erubis 2.7.0 Using rack 1.5.2 Using rack-test 0.6.2 Using actionpack 4.0.8 Using mime-types 1.25.1 Using polyglot 0.3.5 Using treetop 1.4.15 Using mail 2.5.4 Using actionmailer 4.0.8 Using activemodel 4.0.8 Using activerecord-deprecated_finders 1.0.3 Using arel 4.0.2 Using activerecord 4.0.8 Using coffee-script-source 1.7.1 Using execjs 2.2.1 Using coffee-script 2.3.0 Using thor 0.19.1 Using railties 4.0.8 Using coffee-rails 4.0.1 Using hike 1.2.3 Using jbuilder 1.0.2 Using jquery-rails 3.0.4 Using json 1.8.1 Using bundler 1.6.2 Using tilt 1.4.1 Using sprockets 2.12.1 Using sprockets-rails 2.0.1 Using rails 4.0.8 Using rdoc 3.12.2 Using sass 3.3.10 Using sass-rails 4.0.1 Using sdoc 0.3.20 Using sqlite3 1.3.8 Using turbolinks 1.1.1 Using uglifier 2.1.1 Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.
rails server
- 准备都做好之后,就可以开始启动rails server啦! 当然在这之前可能会报一些缺少
javascript runtime的错误apt-get install nodejs就可以了
hfeng@ubuntu1204:~/rails_projects/first_app$ rails server /home/hfeng/.rvm/gems/ruby-2.0.0-p481@railstutorial_rails_4_0/gems/execjs-2.2.1/lib/execjs/runtimes.rb:51:in `autodetect': Could not find a JavaScript runtime. See https://github.com/sstephenson/execjs for a list of available runtimes. (ExecJS::RuntimeUnavailable) from /home/hfeng/.rvm/gems/ruby-2.0.0-p481@railstutorial_rails_4_0/gems/execjs-2.2.1/lib/execjs.rb:5:in `<module:ExecJS>' from /home/hfeng/.rvm/gems/ruby-2.0.0-p481@railstutorial_rails_4_0/gems/execjs-2.2.1/lib/execjs.rb:4:in `<top (required)>' from /home/hfeng/.rvm/gems/ruby-2.0.0-p481@railstutorial_rails_4_0/gems/uglifier-2.1.1/lib/uglifier.rb:3:in `require' from /home/hfeng/.rvm/gems/ruby-2.0.0-p481@railstutorial_rails_4_0/gems/uglifier-2.1.1/lib/uglifier.rb:3:in `<top (required)>' from /home/hfeng/.rvm/gems/ruby-2.0.0-p481@global/gems/bundler-1.6.2/lib/bundler/runtime.rb:76:in `require' from /home/hfeng/.rvm/gems/ruby-2.0.0-p481@global/gems/bundler-1.6.2/lib/bundler/runtime.rb:76:in `block (2 levels) in require' from /home/hfeng/.rvm/gems/ruby-2.0.0-p481@global/gems/bundler-1.6.2/lib/bundler/runtime.rb:72:in `each' from /home/hfeng/.rvm/gems/ruby-2.0.0-p481@global/gems/bundler-1.6.2/lib/bundler/runtime.rb:72:in `block in require' from /home/hfeng/.rvm/gems/ruby-2.0.0-p481@global/gems/bundler-1.6.2/lib/bundler/runtime.rb:61:in `each' from /home/hfeng/.rvm/gems/ruby-2.0.0-p481@global/gems/bundler-1.6.2/lib/bundler/runtime.rb:61:in `require' from /home/hfeng/.rvm/gems/ruby-2.0.0-p481@global/gems/bundler-1.6.2/lib/bundler.rb:132:in `require' from /home/hfeng/rails_projects/first_app/config/application.rb:7:in `<top (required)>' from /home/hfeng/.rvm/gems/ruby-2.0.0-p481@railstutorial_rails_4_0/gems/railties-4.0.8/lib/rails/commands.rb:74:in `require' from /home/hfeng/.rvm/gems/ruby-2.0.0-p481@railstutorial_rails_4_0/gems/railties-4.0.8/lib/rails/commands.rb:74:in `block in <top (required)>' from /home/hfeng/.rvm/gems/ruby-2.0.0-p481@railstutorial_rails_4_0/gems/railties-4.0.8/lib/rails/commands.rb:71:in `tap' from /home/hfeng/.rvm/gems/ruby-2.0.0-p481@railstutorial_rails_4_0/gems/railties-4.0.8/lib/rails/commands.rb:71:in `<top (required)>' from bin/rails:4:in `require' from bin/rails:4:in `<main>' hfeng@ubuntu1204:~/rails_projects/first_app$ sudo apt-get install nodejs [sudo] password for hfeng: Reading package lists... Done Building dependency tree Reading state information... Done The following extra packages will be installed: libc-ares2 libev4 libicu48 libv8-3.7.12.22 The following NEW packages will be installed: libc-ares2 libev4 libicu48 libv8-3.7.12.22 nodejs 0 upgraded, 5 newly installed, 0 to remove and 56 not upgraded. Need to get 10.2 MB of archives. After this operation, 28.6 MB of additional disk space will be used. Do you want to continue [Y/n]? y Get:1 http://us.archive.ubuntu.com/ubuntu/ precise/main libc-ares2 amd64 1.7.5-1 [36.6 kB] Get:2 http://us.archive.ubuntu.com/ubuntu/ precise-updates/main libicu48 amd64 4.8.1.1-3ubuntu0.1 [8105 kB] Get:3 http://us.archive.ubuntu.com/ubuntu/ precise/universe libv8-3.7.12.22 amd64 3.7.12.22-3 [1369 kB] Get:4 http://us.archive.ubuntu.com/ubuntu/ precise/universe libev4 amd64 1:4.11-1 [29.0 kB] Get:5 http://us.archive.ubuntu.com/ubuntu/ precise/universe nodejs amd64 0.6.12~dfsg1-1ubuntu1 [664 kB] Fetched 10.2 MB in 1min 26s (119 kB/s) debconf: unable to initialize frontend: Dialog debconf: (Dialog frontend will not work on a dumb terminal, an emacs shell buffer, or without a controlling terminal.) debconf: falling back to frontend: Readline Selecting previously unselected package libc-ares2. (Reading database ... 57743 files and directories currently installed.) Unpacking libc-ares2 (from .../libc-ares2_1.7.5-1_amd64.deb) ... Selecting previously unselected package libicu48. Unpacking libicu48 (from .../libicu48_4.8.1.1-3ubuntu0.1_amd64.deb) ... Selecting previously unselected package libv8-3.7.12.22. Unpacking libv8-3.7.12.22 (from .../libv8-3.7.12.22_3.7.12.22-3_amd64.deb) ... Selecting previously unselected package libev4. Unpacking libev4 (from .../libev4_1%3a4.11-1_amd64.deb) ... Selecting previously unselected package nodejs. Unpacking nodejs (from .../nodejs_0.6.12~dfsg1-1ubuntu1_amd64.deb) ... Processing triggers for man-db ... debconf: unable to initialize frontend: Dialog debconf: (Dialog frontend will not work on a dumb terminal, an emacs shell buffer, or without a controlling terminal.) debconf: falling back to frontend: Readline Setting up libc-ares2 (1.7.5-1) ... Setting up libicu48 (4.8.1.1-3ubuntu0.1) ... Setting up libv8-3.7.12.22 (3.7.12.22-3) ... Setting up libev4 (1:4.11-1) ... Setting up nodejs (0.6.12~dfsg1-1ubuntu1) ... update-alternatives: using /usr/bin/node to provide /usr/bin/js (js) in auto mode. Processing triggers for libc-bin ... ldconfig deferred processing now taking place hfeng@ubuntu1204:~/rails_projects/first_app$ rails server => Booting WEBrick => Rails 4.0.8 application starting in development on http://0.0.0.0:3000 => Run `rails server -h` for more startup options => Ctrl-C to shutdown server [2014-07-06 02:32:27] INFO WEBrick 1.3.1 [2014-07-06 02:32:27] INFO ruby 2.0.0 (2014-05-08) [x86_64-linux] [2014-07-06 02:32:27] INFO WEBrick::HTTPServer#start: pid=19734 port=3000
Model-view-controller (MVC)
- rails是一个完全的mvc框架:
- 浏览器发送一个requst给web server(不一定是rails server, rails server只是 测试和开发时候用到的, 真正部署的时候可能会使用其他的server)
- web server收到request以后,转给Rails controller
- 少部分情况下Rails controller会直接返回一个HTML(view) 页面给浏览器
- 多数情况下,Rails controller会和model交流,互通有无. 在rails里面, model就 是Ruby对象,负责存储数据(会利用数据库进行存储). 在和model交互完数据后,把 得到的数据转换成html返回给浏览器.
Version control with Git
- 从略.
Deploying
- 使用heroku来进行部署, heroku使用的是postgre数据库,所以要对Gem进行如下修改:
增加pg相关的gem
# Use debugger # gem 'debugger', group: [:development, :test] + +group :production do + gem 'pg', '0.15.1' + gem 'rails_12factor', '0.0.2' +end
- 然后我们要调用bundle install (不使用bundle update的原因是,我们没有更新版本,
只是更新了pg, 贸然的使用bundle update,很可能会引入一些新的版本,导致应用崩溃)
这次还使用了–with production, 其作用"不要"安装任何production的gem,在这个
例子里面就是pg和rails_12factor
hfeng@ubuntu1204:~/rails_projects/first_app$ bundle install --without production Fetching source index from https://rubygems.org/ Resolving dependencies......................... Using rake 10.3.2 Using i18n 0.6.11 Using minitest 4.7.5 Using multi_json 1.10.1 Using thread_safe 0.3.4 Using tzinfo 0.3.40 Using activesupport 4.0.8 Using builder 3.1.4 Using erubis 2.7.0 Using rack 1.5.2 Using rack-test 0.6.2 Using actionpack 4.0.8 Using mime-types 1.25.1 Using polyglot 0.3.5 Using treetop 1.4.15 Using mail 2.5.4 Using actionmailer 4.0.8 Using activemodel 4.0.8 Using activerecord-deprecated_finders 1.0.3 Using arel 4.0.2 Using activerecord 4.0.8 Using bundler 1.6.2 Using coffee-script-source 1.7.1 Using execjs 2.2.1 Using coffee-script 2.3.0 Using thor 0.19.1 Using railties 4.0.8 Using coffee-rails 4.0.1 Using hike 1.2.3 Using jbuilder 1.0.2 Using jquery-rails 3.0.4 Using json 1.8.1 Using tilt 1.4.1 Using sprockets 2.12.1 Using sprockets-rails 2.0.1 Using rails 4.0.8 Using rdoc 3.12.2 Using sass 3.3.10 Using sass-rails 4.0.1 Using sdoc 0.3.20 Using sqlite3 1.3.8 Using turbolinks 1.1.1 Using uglifier 2.1.1 Your bundle is complete! Gems in the group production were not installed. Use `bundle show [gemname]` to see where a bundled gem is installed.
- 虽然我们没有更改gem版本,但是我们增加了production的内容,所以Gemfile.lock还是
会改变,我们要把这个改变加入到版本管理系统里面.
--- a/Gemfile.lock +++ b/Gemfile.lock @@ -50,6 +50,7 @@ GEM mime-types (1.25.1) minitest (4.7.5) multi_json (1.10.1) + pg (0.15.1) polyglot (0.3.5) rack (1.5.2) rack-test (0.6.2) @@ -62,6 +63,11 @@ GEM bundler (>= 1.3.0, < 2.0) railties (= 4.0.8) sprockets-rails (~> 2.0) + rails_12factor (0.0.2) + rails_serve_static_assets + rails_stdout_logging + rails_serve_static_assets (0.0.2) + rails_stdout_logging (0.0.3) railties (4.0.8) actionpack (= 4.0.8) activesupport (= 4.0.8) @@ -108,7 +114,9 @@ DEPENDENCIES coffee-rails (= 4.0.1) jbuilder (= 1.0.2) jquery-rails (= 3.0.4) + pg (= 0.15.1) rails (= 4.0.8) + rails_12factor (= 0.0.2) sass-rails (= 4.0.1) sdoc (= 0.3.20) sqlite3 (= 1.3.8)
- 然后是要安装heroku公司的cli工具, ubuntu上的安装过程如下
hfeng@ubuntu1204:~/rails_projects/first_app$ wget -qO- https://toolbelt.heroku.com/install-ubuntu.sh | sh This script requires superuser access to install apt packages. You will be prompted for your password by sudo. [sudo] password for hfeng: --2014-07-06 03:26:49-- https://toolbelt.heroku.com/apt/release.key Resolving toolbelt.heroku.com (toolbelt.heroku.com)... 23.23.227.87, 107.22.186.187, 50.19.233.212 Connecting to toolbelt.heroku.com (toolbelt.heroku.com)|23.23.227.87|:443... connected. HTTP request sent, awaiting response... 200 OK Length: 1737 (1.7K) [application/octet-stream] Saving to: `STDOUT' 100%[======================================>] 1,737 --.-K/s in 0.002s 2014-07-06 03:26:51 (858 KB/s) - written to stdout [1737/1737] OK Hit http://us.archive.ubuntu.com precise Release.gpg Get:1 http://us.archive.ubuntu.com precise-updates Release.gpg [198 B] Hit http://us.archive.ubuntu.com precise-backports Release.gpg Get:2 http://security.ubuntu.com precise-security Release.gpg [198 B] Hit http://us.archive.ubuntu.com precise Release Get:3 http://us.archive.ubuntu.com precise-updates Release [49.6 kB] Get:4 http://security.ubuntu.com precise-security Release [49.6 kB] Hit http://us.archive.ubuntu.com precise-backports Release Get:5 http://toolbelt.heroku.com ./ Release.gpg [490 B] Hit http://us.archive.ubuntu.com precise/main Sources Hit http://us.archive.ubuntu.com precise/restricted Sources Hit http://us.archive.ubuntu.com precise/universe Sources Hit http://us.archive.ubuntu.com precise/multiverse Sources Hit http://us.archive.ubuntu.com precise/main amd64 Packages Hit http://us.archive.ubuntu.com precise/restricted amd64 Packages Hit http://us.archive.ubuntu.com precise/universe amd64 Packages Hit http://us.archive.ubuntu.com precise/multiverse amd64 Packages Hit http://us.archive.ubuntu.com precise/main i386 Packages Hit http://us.archive.ubuntu.com precise/restricted i386 Packages Get:6 http://security.ubuntu.com precise-security/main Sources [106 kB] Hit http://us.archive.ubuntu.com precise/universe i386 Packages Hit http://us.archive.ubuntu.com precise/multiverse i386 Packages Hit http://us.archive.ubuntu.com precise/main TranslationIndex Hit http://us.archive.ubuntu.com precise/multiverse TranslationIndex Hit http://us.archive.ubuntu.com precise/restricted TranslationIndex Hit http://us.archive.ubuntu.com precise/universe TranslationIndex Get:7 http://us.archive.ubuntu.com precise-updates/main Sources [475 kB] Get:8 http://security.ubuntu.com precise-security/restricted Sources [2494 B] Get:9 http://security.ubuntu.com precise-security/universe Sources [30.7 kB] Get:10 http://toolbelt.heroku.com ./ Release [1609 B] Get:11 http://security.ubuntu.com precise-security/multiverse Sources [1795 B] Get:12 http://security.ubuntu.com precise-security/main amd64 Packages [412 kB] Get:13 http://us.archive.ubuntu.com precise-updates/restricted Sources [8056 B] Get:14 http://us.archive.ubuntu.com precise-updates/universe Sources [108 kB] Get:15 http://toolbelt.heroku.com ./ Packages [1064 B] Get:16 http://us.archive.ubuntu.com precise-updates/multiverse Sources [8905 B] Get:17 http://security.ubuntu.com precise-security/restricted amd64 Packages [4627 B] Get:18 http://us.archive.ubuntu.com precise-updates/main amd64 Packages [821 kB] Get:19 http://security.ubuntu.com precise-security/universe amd64 Packages [94.1 kB] Get:20 http://security.ubuntu.com precise-security/multiverse amd64 Packages [2442 B] Get:21 http://security.ubuntu.com precise-security/main i386 Packages [440 kB] Get:22 http://security.ubuntu.com precise-security/restricted i386 Packages [4620 B] Get:23 http://security.ubuntu.com precise-security/universe i386 Packages [99.7 kB] Get:24 http://security.ubuntu.com precise-security/multiverse i386 Packages [2650 B] Hit http://security.ubuntu.com precise-security/main TranslationIndex Hit http://security.ubuntu.com precise-security/multiverse TranslationIndex Hit http://security.ubuntu.com precise-security/restricted TranslationIndex Hit http://security.ubuntu.com precise-security/universe TranslationIndex Get:25 http://us.archive.ubuntu.com precise-updates/restricted amd64 Packages [13.7 kB] Get:26 http://us.archive.ubuntu.com precise-updates/universe amd64 Packages [244 kB] Hit http://security.ubuntu.com precise-security/main Translation-en Hit http://security.ubuntu.com precise-security/multiverse Translation-en Hit http://security.ubuntu.com precise-security/restricted Translation-en Get:27 http://us.archive.ubuntu.com precise-updates/multiverse amd64 Packages [15.3 kB] Get:28 http://us.archive.ubuntu.com precise-updates/main i386 Packages [853 kB] Hit http://security.ubuntu.com precise-security/universe Translation-en Get:29 http://us.archive.ubuntu.com precise-updates/restricted i386 Packages [13.7 kB] Get:30 http://us.archive.ubuntu.com precise-updates/universe i386 Packages [251 kB] Get:31 http://us.archive.ubuntu.com precise-updates/multiverse i386 Packages [15.5 kB] Hit http://us.archive.ubuntu.com precise-updates/main TranslationIndex Hit http://us.archive.ubuntu.com precise-updates/multiverse TranslationIndex Hit http://us.archive.ubuntu.com precise-updates/restricted TranslationIndex Hit http://us.archive.ubuntu.com precise-updates/universe TranslationIndex Hit http://us.archive.ubuntu.com precise-backports/main Sources Hit http://us.archive.ubuntu.com precise-backports/restricted Sources Hit http://us.archive.ubuntu.com precise-backports/universe Sources Hit http://us.archive.ubuntu.com precise-backports/multiverse Sources Hit http://us.archive.ubuntu.com precise-backports/main amd64 Packages Hit http://us.archive.ubuntu.com precise-backports/restricted amd64 Packages Hit http://us.archive.ubuntu.com precise-backports/universe amd64 Packages Hit http://us.archive.ubuntu.com precise-backports/multiverse amd64 Packages Hit http://us.archive.ubuntu.com precise-backports/main i386 Packages Hit http://us.archive.ubuntu.com precise-backports/restricted i386 Packages Hit http://us.archive.ubuntu.com precise-backports/universe i386 Packages Hit http://us.archive.ubuntu.com precise-backports/multiverse i386 Packages Hit http://us.archive.ubuntu.com precise-backports/main TranslationIndex Hit http://us.archive.ubuntu.com precise-backports/multiverse TranslationIndex Hit http://us.archive.ubuntu.com precise-backports/restricted TranslationIndex Hit http://us.archive.ubuntu.com precise-backports/universe TranslationIndex Hit http://us.archive.ubuntu.com precise/main Translation-en Hit http://us.archive.ubuntu.com precise/multiverse Translation-en Hit http://us.archive.ubuntu.com precise/restricted Translation-en Hit http://us.archive.ubuntu.com precise/universe Translation-en Hit http://us.archive.ubuntu.com precise-updates/main Translation-en Hit http://us.archive.ubuntu.com precise-updates/multiverse Translation-en Hit http://us.archive.ubuntu.com precise-updates/restricted Translation-en Hit http://us.archive.ubuntu.com precise-updates/universe Translation-en Hit http://us.archive.ubuntu.com precise-backports/main Translation-en Hit http://us.archive.ubuntu.com precise-backports/multiverse Translation-en Hit http://us.archive.ubuntu.com precise-backports/restricted Translation-en Hit http://us.archive.ubuntu.com precise-backports/universe Translation-en Ign http://toolbelt.heroku.com ./ Translation-en Fetched 4131 kB in 17s (238 kB/s) Reading package lists... Done Reading package lists... Done Building dependency tree Reading state information... Done The following extra packages will be installed: foreman heroku libruby1.9.1 ruby1.9.1 Suggested packages: ruby1.9.1-examples ri1.9.1 graphviz ruby1.9.1-dev The following NEW packages will be installed: foreman heroku heroku-toolbelt libruby1.9.1 ruby1.9.1 0 upgraded, 5 newly installed, 0 to remove and 56 not upgraded. Need to get 4971 kB of archives. After this operation, 12.6 MB of additional disk space will be used. Get:1 http://us.archive.ubuntu.com/ubuntu/ precise-updates/main libruby1.9.1 amd64 1.9.3.0-1ubuntu2.8 [4104 kB] Get:2 http://toolbelt.heroku.com/ubuntu/ ./ foreman 0.74.0 [113 kB] Get:3 http://toolbelt.heroku.com/ubuntu/ ./ heroku 3.9.1 [716 kB] Get:4 http://toolbelt.heroku.com/ubuntu/ ./ heroku-toolbelt 3.9.1 [614 B] Get:5 http://us.archive.ubuntu.com/ubuntu/ precise-updates/main ruby1.9.1 amd64 1.9.3.0-1ubuntu2.8 [37.1 kB] Fetched 4971 kB in 28s (174 kB/s) debconf: unable to initialize frontend: Dialog debconf: (Dialog frontend will not work on a dumb terminal, an emacs shell buffer, or without a controlling terminal.) debconf: falling back to frontend: Readline Selecting previously unselected package libruby1.9.1. (Reading database ... 57830 files and directories currently installed.) Unpacking libruby1.9.1 (from .../libruby1.9.1_1.9.3.0-1ubuntu2.8_amd64.deb) ... Selecting previously unselected package ruby1.9.1. Unpacking ruby1.9.1 (from .../ruby1.9.1_1.9.3.0-1ubuntu2.8_amd64.deb) ... Selecting previously unselected package foreman. Unpacking foreman (from .../foreman_0.74.0_all.deb) ... Selecting previously unselected package heroku. Unpacking heroku (from .../archives/heroku_3.9.1_all.deb) ... Selecting previously unselected package heroku-toolbelt. Unpacking heroku-toolbelt (from .../heroku-toolbelt_3.9.1_all.deb) ... Processing triggers for man-db ... debconf: unable to initialize frontend: Dialog debconf: (Dialog frontend will not work on a dumb terminal, an emacs shell buffer, or without a controlling terminal.) debconf: falling back to frontend: Readline Setting up libruby1.9.1 (1.9.3.0-1ubuntu2.8) ... Setting up ruby1.9.1 (1.9.3.0-1ubuntu2.8) ... update-alternatives: using /usr/bin/gem1.9.1 to provide /usr/bin/gem (gem) in auto mode. update-alternatives: using /usr/bin/ruby1.9.1 to provide /usr/bin/ruby (ruby) in auto mode. Setting up foreman (0.74.0) ... Setting up heroku (3.9.1) ... Setting up heroku-toolbelt (3.9.1) ... Processing triggers for libc-bin ... ldconfig deferred processing now taking place hfeng@ubuntu1204:~/rails_projects/first_app$ which heroku /usr/bin/heroku hfeng@ubuntu1204:~/rails_projects/first_app$ heroku --version heroku-toolbelt/3.9.1 (x86_64-linux) ruby/1.9.3
- 然后就可以部署啦!
- 首先,要登录
hfeng@ubuntu1204:~/rails_projects/first_app$ heroku login Enter your Heroku credentials. Email: harrifeng@gmail.com Password (typing will be hidden): Authentication successful.
- 其次,是部署
hfeng@ubuntu1204:~/rails_projects/first_app$ pwd pwd /home/hfeng/rails_projects/first_app hfeng@ubuntu1204:~/rails_projects/first_app$ git branch git branch heroku * master hfeng@ubuntu1204:~/rails_projects/first_app$ heroku create heroku create Creating peaceful-falls-3143... done, stack is cedar http://peaceful-falls-3143.herokuapp.com/ | git@heroku.com:peaceful-falls-3143.git Git remote heroku added hfeng@ubuntu1204:~/rails_projects/first_app$ git remote -v git remote -v heroku git@heroku.com:peaceful-falls-3143.git (fetch) heroku git@heroku.com:peaceful-falls-3143.git (push) hfeng@ubuntu1204:~/rails_projects/first_app$ git push heroku master git push heroku master Warning: Permanently added the RSA host key for IP address '50.19.85.132' to the list of known hosts. Initializing repository, done. Counting objects: 72, done. Delta compression using up to 4 threads. Compressing objects: 100% (61/61), done. Writing objects: 100% (72/72), 15.69 KiB, done. Total 72 (delta 10), reused 0 (delta 0) -----> Ruby app detected -----> Compiling Ruby/Rails -----> Using Ruby version: ruby-2.0.0 -----> Installing dependencies using 1.6.3 Running: bundle install --without development:test --path vendor/bundle --binstubs vendor/bundle/bin -j4 --deployment Fetching gem metadata from https://rubygems.org/.......... Fetching additional metadata from https://rubygems.org/.. Installing minitest 4.7.5 Installing i18n 0.6.11 Installing multi_json 1.10.1 Installing rake 10.3.2 Installing thread_safe 0.3.4 Installing builder 3.1.4 Installing erubis 2.7.0 Installing rack 1.5.2 Installing tzinfo 0.3.40 Installing mime-types 1.25.1 Installing activerecord-deprecated_finders 1.0.3 Installing polyglot 0.3.5 Installing execjs 2.2.1 Installing arel 4.0.2 Installing coffee-script-source 1.7.1 Installing thor 0.19.1 Installing hike 1.2.3 Using bundler 1.6.3 Installing tilt 1.4.1 Installing rails_serve_static_assets 0.0.2 Installing rails_stdout_logging 0.0.3 Installing json 1.8.1 Installing rack-test 0.6.2 Installing activesupport 4.0.8 Installing sass 3.3.10 Installing uglifier 2.1.1 Installing treetop 1.4.15 Installing coffee-script 2.3.0 Installing rails_12factor 0.0.2 Installing sprockets 2.12.1 Installing rdoc 3.12.2 Installing actionpack 4.0.8 Installing jbuilder 1.0.2 Installing activemodel 4.0.8 Installing sdoc 0.3.20 Installing mail 2.5.4 Installing railties 4.0.8 Installing sprockets-rails 2.0.1 Installing actionmailer 4.0.8 Installing coffee-rails 4.0.1 Installing activerecord 4.0.8 Installing jquery-rails 3.0.4 Installing sass-rails 4.0.1 Installing turbolinks 1.1.1 Installing pg 0.15.1 Installing rails 4.0.8 Your bundle is complete! Gems in the groups development and test were not installed. It was installed into ./vendor/bundle Post-install message from rdoc: Depending on your version of ruby, you may need to install ruby rdoc/ri data: <= 1.8.6 : unsupported = 1.8.7 : gem install rdoc-data; rdoc-data --install = 1.9.1 : gem install rdoc-data; rdoc-data --install >= 1.9.2 : nothing to do! Yay! Bundle completed (23.17s) Cleaning up the bundler cache. -----> Writing config/database.yml to read from DATABASE_URL -----> Preparing app for Rails asset pipeline Running: rake assets:precompile I, [2014-07-19T10:17:28.533402 #1061] INFO -- : Writing /tmp/build_d9f77b59-def0-4971-a5fc-146f03a6350b/public/assets/application-ca8bf38828b1ee4126682823eb3fa49c.js I, [2014-07-19T10:17:28.556921 #1061] INFO -- : Writing /tmp/build_d9f77b59-def0-4971-a5fc-146f03a6350b/public/assets/application-98f28c7358913ad23ff595d3f8287e32.css Asset precompilation completed (6.56s) Cleaning assets Running: rake assets:clean -----> WARNINGS: No Procfile detected, using the default web server (webrick) https://devcenter.heroku.com/articles/ruby-default-web-server -----> Discovering process types Procfile declares types -> (none) Default types for Ruby -> console, rake, web, worker -----> Compressing... done, 21.1MB -----> Launching... done, v6 http://peaceful-falls-3143.herokuapp.com/ deployed to Heroku To git@heroku.com:peaceful-falls-3143.git * [new branch] master -> master
- 再次,打开浏览器,输入http://peaceful-falls-3143.herokuapp.com 会发现….还 是不对!但是这个状况是必定发生的,在我们config好root route之后,就不会再发生了.
- 首先,要登录
Heroku commands
- 使用heroku cli改下名字,容易记忆一点
hfeng@ubuntu1204:~/rails_projects/first_app$ heroku rename hfeng-railstutorial heroku rename hfeng-railstutorial Renaming peaceful-falls-3143 to hfeng-railstutorial... done http://hfeng-railstutorial.herokuapp.com/ | git@heroku.com:hfeng-railstutorial.git Git remote heroku updated hfeng@ubuntu1204:~/rails_projects/first_app$ git remote -v git remote -v heroku git@heroku.com:hfeng-railstutorial.git (fetch) heroku git@heroku.com:hfeng-railstutorial.git (push)
Chapter 02: A demo app
Planning the application
- 和上一次的Gemfile一样准备一个Gemfile,然后bundle update之后bundle install
–without production
hfeng@ubuntu1204:~/rails_projects/demo_app$ bundle update Fetching gem metadata from https://rubygems.org/.......... Fetching additional metadata from https://rubygems.org/.. Resolving dependencies... Using rake 10.3.2 Using i18n 0.6.11 Using minitest 4.7.5 Using multi_json 1.10.1 Using thread_safe 0.3.4 Using tzinfo 0.3.40 Using activesupport 4.0.8 Using builder 3.1.4 Using erubis 2.7.0 Using rack 1.5.2 Using rack-test 0.6.2 Using actionpack 4.0.8 Using mime-types 1.25.1 Using polyglot 0.3.5 Using treetop 1.4.15 Using mail 2.5.4 Using actionmailer 4.0.8 Using activemodel 4.0.8 Using activerecord-deprecated_finders 1.0.3 Using arel 4.0.2 Using activerecord 4.0.8 Using bundler 1.6.2 Using coffee-script-source 1.7.1 Using execjs 2.2.1 Using coffee-script 2.3.0 Using thor 0.19.1 Using railties 4.0.8 Using coffee-rails 4.0.1 Using hike 1.2.3 Using jbuilder 1.0.2 (was 1.5.3) Using jquery-rails 3.0.4 (was 3.1.1) Using json 1.8.1 Using tilt 1.4.1 Using sprockets 2.12.1 (was 2.11.0) Using sprockets-rails 2.0.1 (was 2.1.3) Using rails 4.0.8 Using rdoc 3.12.2 (was 4.1.1) Using sass 3.3.10 (was 3.2.19) Using sass-rails 4.0.1 (was 4.0.3) Using sdoc 0.3.20 (was 0.4.0) Using sqlite3 1.3.8 (was 1.3.9) Using turbolinks 1.1.1 (was 2.2.2) Using uglifier 2.1.1 (was 2.5.3) Your bundle is updated! Gems in the group production were not installed. hfeng@ubuntu1204:~/rails_projects/demo_app$ bundle install --without production Using rake 10.3.2 Using i18n 0.6.11 Using minitest 4.7.5 Using multi_json 1.10.1 Using thread_safe 0.3.4 Using tzinfo 0.3.40 Using activesupport 4.0.8 Using builder 3.1.4 Using erubis 2.7.0 Using rack 1.5.2 Using rack-test 0.6.2 Using actionpack 4.0.8 Using mime-types 1.25.1 Using polyglot 0.3.5 Using treetop 1.4.15 Using mail 2.5.4 Using actionmailer 4.0.8 Using activemodel 4.0.8 Using activerecord-deprecated_finders 1.0.3 Using arel 4.0.2 Using activerecord 4.0.8 Using coffee-script-source 1.7.1 Using execjs 2.2.1 Using coffee-script 2.3.0 Using thor 0.19.1 Using railties 4.0.8 Using coffee-rails 4.0.1 Using hike 1.2.3 Using jbuilder 1.0.2 Using jquery-rails 3.0.4 Using json 1.8.1 Using bundler 1.6.2 Using tilt 1.4.1 Using sprockets 2.12.1 Using sprockets-rails 2.0.1 Using rails 4.0.8 Using rdoc 3.12.2 Using sass 3.3.10 Using sass-rails 4.0.1 Using sdoc 0.3.20 Using sqlite3 1.3.8 Using turbolinks 1.1.1 Using uglifier 2.1.1 Your bundle is complete! Gems in the group production were not installed. Use `bundle show [gemname]` to see where a bundled gem is installed.
Modeling demo users
- 我们先来创建一个用户的model,其只有三个域名:
- integer类型的id
- string类型的name
- string类型的email
- 后面我们会看到,我们创建了一个user的ruby类,同时会有同名的数据库user创建,里面 有email, name, id三个column
Modeling demo microposts
- micropost就类型twitter的一个状态,也有三个域:
- integer类型的id
- string类型的content(因为不能超过140个字么)
- integer类型的user_id,其实就是外键
The Users resource
- rails可以为我们一次性创建一个resource(在这里就是User resource),为这个resource 提供了HTTP协议的"增删改查"的请求支持
- 提供的方法是rails generate scaffold 命令. 后面跟的都是"单数"
hfeng@ubuntu1204:~/rails_projects/demo_app$ rails generate scaffold User name:string email:string invoke active_record create db/migrate/20140705200918_create_users.rb create app/models/user.rb invoke test_unit create test/models/user_test.rb create test/fixtures/users.yml invoke resource_route route resources :users invoke jbuilder_scaffold_controller create app/controllers/users_controller.rb invoke erb create app/views/users create app/views/users/index.html.erb create app/views/users/edit.html.erb create app/views/users/show.html.erb create app/views/users/new.html.erb create app/views/users/_form.html.erb invoke test_unit create test/controllers/users_controller_test.rb invoke helper create app/helpers/users_helper.rb invoke test_unit create test/helpers/users_helper_test.rb invoke jbuilder exist app/views/users create app/views/users/index.json.jbuilder create app/views/users/show.json.jbuilder invoke assets invoke coffee create app/assets/javascripts/users.js.coffee invoke scss create app/assets/stylesheets/users.css.scss invoke scss create app/assets/stylesheets/scaffolds.css.scss
- 为了能让我们的改变体现在网站上,需要migrate数据库,使用的手段是rake
hfeng@ubuntu1204:~/rails_projects/demo_app$ rake db:migrate == 20140705200918 CreateUsers: migrating ====================================== -- create_table(:users) -> 0.0009s == 20140705200918 CreateUsers: migrated (0.0010s) =============================
- 这是我们第一次使用rake,其实rake的使用在rails里面非常广泛,我们可以使用rake -T
来查看可用命令(加db就是可用rake 数据库命令)
hfeng@ubuntu1204:~/rails_projects/demo_app$ rake -T rake about # List versions of all Rails framew... rake assets:clean[keep] # Remove old compiled assets rake assets:clobber # Remove compiled assets rake assets:environment # Load asset compile environment rake assets:precompile # Compile all the assets named in c... rake cache_digests:dependencies # Lookup first-level dependencies f... rake cache_digests:nested_dependencies # Lookup nested dependencies for TE... rake db:create # Create the database from DATABASE... rake db:drop # Drops the database using DATABASE... rake db:fixtures:load # Load fixtures into the current en... rake db:migrate # Migrate the database (options: VE... rake db:migrate:status # Display status of migrations rake db:rollback # Rolls the schema back to the prev... rake db:schema:cache:clear # Clear a db/schema_cache.dump file rake db:schema:cache:dump # Create a db/schema_cache.dump file rake db:schema:dump # Create a db/schema.rb file that c... rake db:schema:load # Load a schema.rb file into the da... rake db:seed # Load the seed data from db/seeds.rb rake db:setup # Create the database, load the sch... rake db:structure:dump # Dump the database structure to db... rake db:version # Retrieves the current schema vers... rake doc:app # Generate docs for the app -- also... rake log:clear # Truncates all *.log files in log/... rake middleware # Prints out your Rack middleware s... rake notes # Enumerate all annotations (use no... rake notes:custom # Enumerate a custom annotation, sp... rake rails:template # Applies the template supplied by ... rake rails:update # Update configs and some other ini... rake routes # Print out all defined routes in m... rake secret # Generate a cryptographically secu... rake stats # Report code statistics (KLOCs, et... rake test # Runs test:units, test:functionals... rake test:all # Run tests quickly by merging all ... rake test:all:db # Run tests quickly, but also reset db rake test:recent # Run tests for {:recent=>["test:de... rake test:uncommitted # Run tests for {:uncommitted=>["te... rake time:zones:all # Displays all time zones, also ava... rake tmp:clear # Clear session, cache, and socket ... rake tmp:create # Creates tmp directories for sessi... hfeng@ubuntu1204:~/rails_projects/demo_app$ rake -T db rake db:create # Create the database from DATABASE_URL or conf... rake db:drop # Drops the database using DATABASE_URL or the ... rake db:fixtures:load # Load fixtures into the current environment's ... rake db:migrate # Migrate the database (options: VERSION=x, VER... rake db:migrate:status # Display status of migrations rake db:rollback # Rolls the schema back to the previous version... rake db:schema:cache:clear # Clear a db/schema_cache.dump file rake db:schema:cache:dump # Create a db/schema_cache.dump file rake db:schema:dump # Create a db/schema.rb file that can be portab... rake db:schema:load # Load a schema.rb file into the database rake db:seed # Load the seed data from db/seeds.rb rake db:setup # Create the database, load the schema, and ini... rake db:structure:dump # Dump the database structure to db/structure.sql rake db:version # Retrieves the current schema version number rake test:all:db # Run tests quickly, but also reset db
A user tour
- 我们下面来看看新建的数据模型都有神马内容
URL Action Purpose /users index page to list all users /users/1 show page to show user with id 1 /users/new new page to make a new user /users/1/edit edit page to edit user with id 1 - index是列出所有用户
- 添加一个用户后会显示第一个用户的信息, 就是show
- 增加一个用户是new
- 编辑一个用户是edit
- 然后再删除一个, url和show的时候一样,但是如果你知道HTTP协议的话,你会知道其实是method不同
MVC in action
- 下图就是一个URL(/users)如何从浏览器发送,最后又回到浏览器:
Figure 1: mvc-rails.png
- 上面的Rails router负责将/users的解析,在rails里面的对应配置文件是config/routes.rb
# config/routes.rb DemoApp::Applicatin.routes.draw do resources :users # ... end
- 为user resource服务的controller在这个例子里面是users_controllers.rb, 其是
一个继承于ApplicationController的ruby class
# app/controllers/users_controller.rb class UsersController < ApplicationController # ... def index # ... end def show # ... end def new # ... end def create # ... end def edit # ... end def update # ... end def destroy # ... end end
- 其实上面的函数,其实都是HTTP里面的request method, 我们来看看对应表
HTTP request URL Action Purpose GET /users index page to list all users GET /users/1 show page to show user with id 1 GET /users/new new page to make a new user POST /users create create a new user GET /uesers/1/edi edit page to edit suer with id 1 PATCH /users/1 update update user with id 1 DELETE /users/1 destroy delete user with id 1 - 下面我们以index函数来介绍一下是如何将MVC三部分串联起来的:
- 首先看C: index定义在users_controller.rb里面. 其主要内容就是定义了一个instance
variable @users, 然后把User(是M的一个部分)的所有的内容都传递给@users
# app/controllers/users_controller.rb class UsersController < ApplicationController # ... def index @users = User.all end end
- 其次看M,也就是上面的大写的User, 其定义的位置是在app/models/user.rb里面,
虽然看起来没什么内容,但是它主要的函数都是从ActiveRecord::Base里面继承了,
比如函数all
# app/models/user.rb class User < ActiveRecord::Base end
- 最后我们来看看V, 上面C里面成功的初始化的函数@users会被传递到app/views/users/index.html.erb
# app/view/users/index.html.erb <h1>Listing users</h1> <table> <tr> <th>Name</th> <th>Email</th> <th></th> <th></th> <th></th> </tr> <% @users.each do |user| %> <tr> <td><%= user.name %></td> <td><%= user.email %></td> <td><%= link_to 'Show', user %></td> <td><%= link_to 'Edit', edit_user_path(user) %></td> <td><%= link_to 'Destroy', user, method: :delete, data: { confirm: 'Are you sure?' } %></td> </tr> <% end %> </table> <br /> <%= link_to 'New User', new_user_path %>
- 首先看C: index定义在users_controller.rb里面. 其主要内容就是定义了一个instance
variable @users, 然后把User(是M的一个部分)的所有的内容都传递给@users
Weaknesses of this Users resource
- 虽然看起来scaffold运行的情况不错,但是这种方式还是有很多的缺点:
- No data validations
- No authentication
- No tests
- No layout
The Microposts resource
A micropost microtour
- 下面我们来创建另外一个resource, micropost. 注意在命令行里面Micropost要单数,大写
hfengdeMacBook-Air:demo_app hfeng$ rails generate scaffold Micropost content:string user_id:integer invoke active_record create db/migrate/20140721073734_create_microposts.rb create app/models/micropost.rb invoke test_unit create test/models/micropost_test.rb create test/fixtures/microposts.yml invoke resource_route route resources :microposts invoke jbuilder_scaffold_controller create app/controllers/microposts_controller.rb invoke erb create app/views/microposts create app/views/microposts/index.html.erb create app/views/microposts/edit.html.erb create app/views/microposts/show.html.erb create app/views/microposts/new.html.erb create app/views/microposts/_form.html.erb invoke test_unit create test/controllers/microposts_controller_test.rb invoke helper create app/helpers/microposts_helper.rb invoke test_unit create test/helpers/microposts_helper_test.rb invoke jbuilder exist app/views/microposts create app/views/microposts/index.json.jbuilder create app/views/microposts/show.json.jbuilder invoke assets invoke coffee create app/assets/javascripts/microposts.js.coffee invoke scss create app/assets/stylesheets/microposts.css.scss invoke scss identical app/assets/stylesheets/scaffolds.css.scss
- 创建完之后,还是要导入数据库
hfengdeMacBook-Air:demo_app hfeng$ rake db:migrate == 20140721073734 CreateMicroposts: migrating ================================= -- create_table(:microposts) -> 0.0078s == 20140721073734 CreateMicroposts: migrated (0.0079s) ========================
- 我们和User对比,会发现routes.rb还是被更改了
diff --git a/config/routes.rb b/config/routes.rb index 56c203d..d487887 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,6 @@ DemoApp::Application.routes.draw do + resources :microposts + resources :users # The priority is based upon order of creation: first created -> highest priority.
- 其他如controller和URL等等,全部都和Users一样.
Putting the micro in microposts
- 我们的micropost是stirng类型,最多不能超过140个字符.所以要加入一些"长度检查"
diff --git a/app/models/micropost.rb b/app/models/micropost.rb index f333428..91a6135 100644 --- a/app/models/micropost.rb +++ b/app/models/micropost.rb @@ -1,2 +1,3 @@ class Micropost < ActiveRecord::Base + validates :content, length: { maximum: 140 } end
- 长度检查用到了一个函数validates, 其函数代码如下
# File activemodel/lib/active_model/validations/validates.rb, line 100 def validates(*attributes) defaults = attributes.extract_options!.dup validations = defaults.slice!(*_validates_default_keys) raise ArgumentError, "You need to supply at least one attribute" if attributes.empty? raise ArgumentError, "You need to supply at least one validation" if validations.empty? defaults[:attributes] = attributes validations.each do |key, options| next unless options key = "#{key.to_s.camelize}Validator" begin validator = key.include?('::') ? key.constantize : const_get(key) rescue NameError raise ArgumentError, "Unknown validator: '#{key}'" end validates_with(validator, defaults.merge(_parse_validates_options(options))) end end
- 我们可以通过下面的例子来理解下ruby"极简"的函数传递方法
def val(*attributes) attributes.each_with_index do |a, i| puts "NO.#{i} element is #{a}, its type is #{a.class}" end end val :content, length: { maximum: 140 } ################################################################ # <===================OUTPUT===================> # # NO.0 element is content, its type is Symbol # # NO.1 element is {:length=>{:maximum=>140}}, its type is Hash # ################################################################
- 从上面的例子我们可以看到:
- attributes 其实是一个不定参数, 输入不同数目的参数最终会在函数实现里面成为一个类型为array的变量attributes
- :content是第attribute数组里面的第一个参数,类型为Symbol
- {:length=>{:maximum=>140}} 为attributest里面的第二个参数, 类型为Hash. 而且其value是另外一个hash. 这里
又有两个地方的简写:
- {:key => "value"} 简写成 {key: "value}
- 由于Hash是validate的第二个,也是最后一个参数,所以不需要加{}, 也就是说
# Completed version is this {:length=>{:maximum=>140}} # want to save "=>", convert to this { length: { maximum: 140 } } # Last parameter, so sonvert this length: { maximum: 140 }
A user has_many microposts
- 从数据库的角度看, user和micropost是"一对多"的关系:就是一个用户可以有多个 micropost, 反之则不成立: 一个micropost只属于一个user.
- 这种"一对多"的关系是体现在"数据"部分的,所以,我们在M部分进行修改.相关的两个
model都要修改. 相比于前面的validates函数. belongs_to和has_many显得那么的简
单
diff --git a/app/models/micropost.rb b/app/models/micropost.rb index 91a6135..ea092e5 100644 --- a/app/models/micropost.rb +++ b/app/models/micropost.rb @@ -1,3 +1,4 @@ class Micropost < ActiveRecord::Base + belongs_to :user validates :content, length: { maximum: 140 } end Modified app/models/user.rb diff --git a/app/models/user.rb b/app/models/user.rb index 4a57cf0..d41de93 100644 --- a/app/models/user.rb +++ b/app/models/user.rb @@ -1,2 +1,3 @@ class User < ActiveRecord::Base + has_many :microposts end
- 做了这些修改以后,我们可以使用rails console来查看这些更改. rails console非
常强大,涉及M的部分都可以试试
2.0.0-p481 :001 > first_user = User.first User Load (0.2ms) SELECT "users".* FROM "users" ORDER BY "users"."id" ASC LIMIT 1 => #<User id: 1, name: "haoran feng", email: "haoran.feng@sap.com", created_at: "2014-07-21 08:44:55", updated_at: "2014-07-21 08:44:55"> 2.0.0-p481 :002 > first_user.methods => [:autosave_associated_records_for_microposts, ..... :microposts, ...] 2.0.0-p481 :003 > first_user.microposts Micropost Load (1.8ms) SELECT "microposts".* FROM "microposts" WHERE "microposts"."user_id" = ? [["user_id", 1]] => #<ActiveRecord::Associations::CollectionProxy [#<Micropost id: 1, content: "hello world", user_id: 1, created_at: "2014-07-21 08:45:28", updated_at: "2014-07-21 08:45:28">]> 2.0.0-p481 :004 > exit
Inheritance hierarchies
- model和controller都是采取了继承的方法来获取大部分的功能:
- model的继承图如下
Figure 2: model-inheritance.png
- controller的继承图如下
Figure 3: controller-inheritance.png
- model的继承图如下
Deploying the demo app
- 还是老样子,把例子部署到heroku上去
hfengdeMacBook-Air:demo_app hfeng$ heroku login Enter your Heroku credentials. Email: harrifeng@gmail.com Password (typing will be hidden): Authentication successful. hfengdeMacBook-Air:demo_app hfeng$ heroku create heroku create Creating fast-eyrie-3133... done, stack is cedar http://fast-eyrie-3133.herokuapp.com/ | git@heroku.com:fast-eyrie-3133.git Git remote heroku added hfengdeMacBook-Air:demo_app hfeng$ heroku rename hfeng-demo_app heroku rename hfeng-demo_app Renaming fast-eyrie-3133 to hfeng-demo_app... failed ! Name must start with a letter and can only contain lowercase letters, numbers, and dashes. hfengdeMacBook-Air:demo_app hfeng$ heroku rename hfeng-demo-app heroku rename hfeng-demo-app Renaming fast-eyrie-3133 to hfeng-demo-app... done http://hfeng-demo-app.herokuapp.com/ | git@heroku.com:hfeng-demo-app.git Git remote heroku updated hfengdeMacBook-Air:demo_app hfeng$ git remote -v git remote -v heroku git@heroku.com:hfeng-demo-app.git (fetch) heroku git@heroku.com:hfeng-demo-app.git (push) origin git@github.com:harrifeng/demo_app.git (fetch) origin git@github.com:harrifeng/demo_app.git (push) hfengdeMacBook-Air:demo_app hfeng$ git push heroku master git push heroku master Initializing repository, done. Counting objects: 147, done. Delta compression using up to 4 threads. Compressing objects: 100% (132/132), done. Writing objects: 100% (147/147), 24.69 KiB | 0 bytes/s, done. Total 147 (delta 31), reused 0 (delta 0) -----> Ruby app detected -----> Compiling Ruby/Rails -----> Using Ruby version: ruby-2.0.0 -----> Installing dependencies using 1.6.3 Running: bundle install --without development:test --path vendor/bundle --binstubs vendor/bundle/bin -j4 --deployment Fetching gem metadata from https://rubygems.org/.......... Fetching additional metadata from https://rubygems.org/.. Installing minitest 4.7.5 Installing i18n 0.6.11 Installing rake 10.3.2 Installing thread_safe 0.3.4 Installing multi_json 1.10.1 Installing builder 3.1.4 Installing tzinfo 0.3.40 Installing erubis 2.7.0 Installing mime-types 1.25.1 Installing polyglot 0.3.5 Installing activerecord-deprecated_finders 1.0.3 Installing rack 1.5.2 Installing coffee-script-source 1.7.1 Installing arel 4.0.2 Installing execjs 2.2.1 Installing thor 0.19.1 Installing hike 1.2.3 Using bundler 1.6.3 Installing tilt 1.4.1 Installing rails_serve_static_assets 0.0.2 Installing rails_stdout_logging 0.0.3 Installing json 1.8.1 Installing sass 3.3.10 Installing activesupport 4.0.8 Installing rack-test 0.6.2 Installing treetop 1.4.15 Installing coffee-script 2.3.0 Installing uglifier 2.1.1 Installing rails_12factor 0.0.2 Installing sprockets 2.12.1 Installing activemodel 4.0.8 Installing rdoc 3.12.2 Installing jbuilder 1.0.2 Installing mail 2.5.4 Installing actionpack 4.0.8 Installing sdoc 0.3.20 Installing activerecord 4.0.8 Installing actionmailer 4.0.8 Installing sprockets-rails 2.0.1 Installing pg 0.15.1 Installing railties 4.0.8 Installing coffee-rails 4.0.1 Installing jquery-rails 3.0.4 Installing turbolinks 1.1.1 Installing sass-rails 4.0.1 Installing rails 4.0.8 Your bundle is complete! Gems in the groups development and test were not installed. It was installed into ./vendor/bundle Post-install message from rdoc: Depending on your version of ruby, you may need to install ruby rdoc/ri data: <= 1.8.6 : unsupported = 1.8.7 : gem install rdoc-data; rdoc-data --install = 1.9.1 : gem install rdoc-data; rdoc-data --install >= 1.9.2 : nothing to do! Yay! Bundle completed (20.98s) Cleaning up the bundler cache. -----> Writing config/database.yml to read from DATABASE_URL -----> Preparing app for Rails asset pipeline Running: rake assets:precompile I, [2014-07-21T09:36:55.949609 #1017] INFO -- : Writing /tmp/build_b8be5592-5246-464e-ba6a-1c052a86b661/public/assets/application-0101f279a9c8c24ec8783f21287568ab.js I, [2014-07-21T09:36:56.005265 #1017] INFO -- : Writing /tmp/build_b8be5592-5246-464e-ba6a-1c052a86b661/public/assets/application-d0e6f02b399570c37fedbe45a0393e35.css Asset precompilation completed (7.07s) Cleaning assets Running: rake assets:clean -----> WARNINGS: No Procfile detected, using the default web server (webrick) https://devcenter.heroku.com/articles/ruby-default-web-server -----> Discovering process types Procfile declares types -> (none) Default types for Ruby -> console, rake, web, worker -----> Compressing... done, 21.1MB -----> Launching... done, v6 http://hfeng-demo-app.herokuapp.com/ deployed to Heroku To git@heroku.com:hfeng-demo-app.git * [new branch] master -> master hfengdeMacBook-Air:demo_app hfeng$ heroku run rake db:migrate heroku run rake db:migrate Running `rake db:migrate` attached to terminal... up, run.5711 Migrating to CreateUsers (20140721023511) == 20140721023511 CreateUsers: migrating ====================================== -- create_table(:users) -> 0.0438s == 20140721023511 CreateUsers: migrated (0.0441s) ============================= Migrating to CreateMicroposts (20140721073734) == 20140721073734 CreateMicroposts: migrating ================================= -- create_table(:microposts) -> 0.0322s == 20140721073734 CreateMicroposts: migrated (0.0324s) ========================
Chapter 03: Mostly static pages
- 虽然rails是为冬天网页设计的,但是使用rail来设计静态的网页也有其优点
- 首先,我们来创建一个rails项目,但是去掉rails默认的unit test部分. 并非是我们不想
做测试,而是我们要使用Rspec来做rails的测试部分
hfengdeMacBook-Air:rails_projects hfeng$ rails new sample_app --skip-test-unit rails new sample_app --skip-test-unit create create README.rdoc create Rakefile create config.ru create .gitignore create Gemfile create app create app/assets/javascripts/application.js create app/assets/stylesheets/application.css create app/controllers/application_controller.rb create app/helpers/application_helper.rb create app/views/layouts/application.html.erb create app/assets/images/.keep create app/mailers/.keep create app/models/.keep create app/controllers/concerns/.keep create app/models/concerns/.keep create bin create bin/bundle create bin/rails create bin/rake create config create config/routes.rb create config/application.rb create config/environment.rb create config/environments create config/environments/development.rb create config/environments/production.rb create config/environments/test.rb create config/initializers create config/initializers/backtrace_silencers.rb create config/initializers/filter_parameter_logging.rb create config/initializers/inflections.rb create config/initializers/mime_types.rb create config/initializers/secret_token.rb create config/initializers/session_store.rb create config/initializers/wrap_parameters.rb create config/locales create config/locales/en.yml create config/boot.rb create config/database.yml create db create db/seeds.rb create lib create lib/tasks create lib/tasks/.keep create lib/assets create lib/assets/.keep create log create log/.keep create public create public/404.html create public/422.html create public/500.html create public/favicon.ico create public/robots.txt create tmp/cache create tmp/cache/assets create vendor/assets/javascripts create vendor/assets/javascripts/.keep create vendor/assets/stylesheets create vendor/assets/stylesheets/.keep run bundle install Fetching gem metadata from https://rubygems.org/.......... Fetching additional metadata from https://rubygems.org/.. Resolving dependencies... Using rake 10.3.2 Using i18n 0.6.11 Using minitest 4.7.5 Using multi_json 1.10.1 Using thread_safe 0.3.4 Using tzinfo 0.3.40 Using activesupport 4.0.8 Using builder 3.1.4 Using erubis 2.7.0 Using rack 1.5.2 Using rack-test 0.6.2 Using actionpack 4.0.8 Using mime-types 1.25.1 Using polyglot 0.3.5 Using treetop 1.4.15 Using mail 2.5.4 Using actionmailer 4.0.8 Using activemodel 4.0.8 Using activerecord-deprecated_finders 1.0.3 Using arel 4.0.2 Using activerecord 4.0.8 Using bundler 1.6.4 Using coffee-script-source 1.7.1 Using execjs 2.2.1 Using coffee-script 2.3.0 Using thor 0.19.1 Using railties 4.0.8 Using coffee-rails 4.0.1 Using hike 1.2.3 Using jbuilder 1.5.3 Using jquery-rails 3.1.1 Using json 1.8.1 Using tilt 1.4.1 Using sprockets 2.11.0 Using sprockets-rails 2.1.3 Using rails 4.0.8 Using rdoc 4.1.1 Using sass 3.2.19 Using sass-rails 4.0.3 Using sdoc 0.4.0 Using sqlite3 1.3.9 Using turbolinks 2.2.2 Using uglifier 2.5.3 Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.
- 然后,很自然的,我们又要hack一下Gemfile
diff --git a/Gemfile b/Gemfile index bb6d51a..02f186f 100644 --- a/Gemfile +++ b/Gemfile @@ -1,35 +1,48 @@ source 'https://rubygems.org' +ruby '2.0.0' +#ruby-gemset=railstutorial_rails_4_0 # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '4.0.8' -# Use sqlite3 as the database for Active Record -gem 'sqlite3' +group :development, :test do + gem 'sqlite3' + gem 'rspec-rails', '2.13.1' +end + +group :test do + gem 'selenium-webdriver', '2.35.1' + gem 'capybara', '2.1.0' +end # Use SCSS for stylesheets -gem 'sass-rails', '~> 4.0.2' +gem 'sass-rails', '4.0.1' # Use Uglifier as compressor for JavaScript assets -gem 'uglifier', '>= 1.3.0' +gem 'uglifier', '2.1.1' # Use CoffeeScript for .js.coffee assets and views -gem 'coffee-rails', '~> 4.0.0' +gem 'coffee-rails', '4.0.1' # See https://github.com/sstephenson/execjs#readme for more supported runtimes # gem 'therubyracer', platforms: :ruby # Use jquery as the JavaScript library -gem 'jquery-rails' +gem 'jquery-rails', '3.0.4' # Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks -gem 'turbolinks' +gem 'turbolinks', '1.1.1' # Build JSON APIs with ease. Read more: https://github.com/rails/jbuilder -gem 'jbuilder', '~> 1.2' +gem 'jbuilder', '1.0.2' group :doc do - # bundle exec rake doc:rails generates the API under doc/api. - gem 'sdoc', require: false + gem 'sdoc', '0.3.20', require: false +end + +group :production do + gem 'pg', '0.15.1' + gem 'rails_12factor', '0.0.2' end # Use ActiveModel has_secure_password
- 修改Gemfile之后是bundle install自然也要without production
hfengdeMacBook-Air:sample_app hfeng$ bundle install --without production bundle install --without production Fetching gem metadata from https://rubygems.org/......... Fetching additional metadata from https://rubygems.org/.. Resolving dependencies... Bundler could not find compatible versions for gem "sprockets-rails": In snapshot (Gemfile.lock): sprockets-rails (2.1.3) In Gemfile: sass-rails (= 4.0.1) ruby depends on sprockets-rails (~> 2.0.0) ruby Running `bundle update` will rebuild your snapshot from scratch, using only the gems in your Gemfile, which may resolve the conflict.
- 从上面看,without production的结果并不成功. 需要bundle update. 然后再bundle
install(注意,没有 –without production了)
hfengdeMacBook-Air:sample_app hfeng$ bundle update --without production bundle update --without production Unknown switches '--without' hfengdeMacBook-Air:sample_app hfeng$ bundle update bundle update Fetching gem metadata from https://rubygems.org/......... Fetching additional metadata from https://rubygems.org/.. Resolving dependencies... Using rake 10.3.2 Using i18n 0.6.11 Using minitest 4.7.5 Using multi_json 1.10.1 Using thread_safe 0.3.4 Using tzinfo 0.3.40 Using activesupport 4.0.8 Using builder 3.1.4 Using erubis 2.7.0 Using rack 1.5.2 Using rack-test 0.6.2 Using actionpack 4.0.8 Using mime-types 1.25.1 Using polyglot 0.3.5 Using treetop 1.4.15 Using mail 2.5.4 Using actionmailer 4.0.8 Using activemodel 4.0.8 Using activerecord-deprecated_finders 1.0.3 Using arel 4.0.2 Using activerecord 4.0.8 Using bundler 1.6.4 Installing mini_portile 0.6.0 Building nokogiri using packaged libraries. Building libxml2-2.8.0 for nokogiri with the following patches applied: - 0001-Fix-parser-local-buffers-size-problems.patch - 0002-Fix-entities-local-buffers-size-problems.patch - 0003-Fix-an-error-in-previous-commit.patch - 0004-Fix-potential-out-of-bound-access.patch - 0005-Detect-excessive-entities-expansion-upon-replacement.patch - 0006-Do-not-fetch-external-parsed-entities.patch - 0007-Enforce-XML_PARSER_EOF-state-handling-through-the-pa.patch - 0008-Improve-handling-of-xmlStopParser.patch - 0009-Fix-a-couple-of-return-without-value.patch - 0010-Keep-non-significant-blanks-node-in-HTML-parser.patch - 0011-Do-not-fetch-external-parameter-entities.patch ************************************************************************ IMPORTANT! Nokogiri builds and uses a packaged version of libxml2. If this is a concern for you and you want to use the system library instead, abort this installation process and reinstall nokogiri as follows: gem install nokogiri -- --use-system-libraries If you are using Bundler, tell it to use the option: bundle config build.nokogiri --use-system-libraries bundle install However, note that nokogiri does not necessarily support all versions of libxml2. For example, libxml2-2.9.0 and higher are currently known to be broken and thus unsupported by nokogiri, due to compatibility problems and XPath optimization bugs. ************************************************************************ Building libxslt-1.1.28 for nokogiri with the following patches applied: - 0001-Adding-doc-update-related-to-1.1.28.patch - 0002-Fix-a-couple-of-places-where-f-printf-parameters-wer.patch - 0003-Initialize-pseudo-random-number-generator-with-curre.patch - 0004-EXSLT-function-str-replace-is-broken-as-is.patch - 0006-Fix-str-padding-to-work-with-UTF-8-strings.patch - 0007-Separate-function-for-predicate-matching-in-patterns.patch - 0008-Fix-direct-pattern-matching.patch - 0009-Fix-certain-patterns-with-predicates.patch - 0010-Fix-handling-of-UTF-8-strings-in-EXSLT-crypto-module.patch - 0013-Memory-leak-in-xsltCompileIdKeyPattern-error-path.patch - 0014-Fix-for-bug-436589.patch - 0015-Fix-mkdir-for-mingw.patch ************************************************************************ IMPORTANT! Nokogiri builds and uses a packaged version of libxslt. If this is a concern for you and you want to use the system library instead, abort this installation process and reinstall nokogiri as follows: gem install nokogiri -- --use-system-libraries If you are using Bundler, tell it to use the option: bundle config build.nokogiri --use-system-libraries bundle install ************************************************************************ Installing nokogiri 1.6.3.1 Installing xpath 2.0.0 Installing capybara 2.1.0 Installing ffi 1.9.3 Installing childprocess 0.5.3 Using coffee-script-source 1.7.1 Using execjs 2.2.1 Using coffee-script 2.3.0 Using thor 0.19.1 Using railties 4.0.8 Using coffee-rails 4.0.1 Installing diff-lcs 1.2.5 Using hike 1.2.3 Using jbuilder 1.0.2 (was 1.5.3) Using jquery-rails 3.0.4 (was 3.1.1) Using json 1.8.1 Using tilt 1.4.1 Using sprockets 2.12.1 (was 2.11.0) Using sprockets-rails 2.0.1 (was 2.1.3) Using rails 4.0.8 Using rdoc 3.12.2 (was 4.1.1) Installing rspec-core 2.13.1 Installing rspec-expectations 2.13.0 Installing rspec-mocks 2.13.1 Installing rspec-rails 2.13.1 Installing rubyzip 0.9.9 Using sass 3.3.10 (was 3.2.19) Using sass-rails 4.0.1 (was 4.0.3) Using sdoc 0.3.20 (was 0.4.0) Installing websocket 1.0.7 Installing selenium-webdriver 2.35.1 Using sqlite3 1.3.9 Using turbolinks 1.1.1 (was 2.2.2) Using uglifier 2.1.1 (was 2.5.3) Your bundle is updated! Gems in the group production were not installed. hfengdeMacBook-Air:sample_app hfeng$ bundle install bundle install --without production Using rake 10.3.2 Using i18n 0.6.11 Using minitest 4.7.5 Using multi_json 1.10.1 Using thread_safe 0.3.4 Using tzinfo 0.3.40 Using activesupport 4.0.8 Using builder 3.1.4 Using erubis 2.7.0 Using rack 1.5.2 Using rack-test 0.6.2 Using actionpack 4.0.8 Using mime-types 1.25.1 Using polyglot 0.3.5 Using treetop 1.4.15 Using mail 2.5.4 Using actionmailer 4.0.8 Using activemodel 4.0.8 Using activerecord-deprecated_finders 1.0.3 Using arel 4.0.2 Using activerecord 4.0.8 Using mini_portile 0.6.0 Using nokogiri 1.6.3.1 Using xpath 2.0.0 Using capybara 2.1.0 Using ffi 1.9.3 Using childprocess 0.5.3 Using coffee-script-source 1.7.1 Using execjs 2.2.1 Using coffee-script 2.3.0 Using thor 0.19.1 Using railties 4.0.8 Using coffee-rails 4.0.1 Using diff-lcs 1.2.5 Using hike 1.2.3 Using jbuilder 1.0.2 Using jquery-rails 3.0.4 Using json 1.8.1 Using bundler 1.6.4 Using tilt 1.4.1 Using sprockets 2.12.1 Using sprockets-rails 2.0.1 Using rails 4.0.8 Using rdoc 3.12.2 Using rspec-core 2.13.1 Using rspec-expectations 2.13.0 Using rspec-mocks 2.13.1 Using rspec-rails 2.13.1 Using rubyzip 0.9.9 Using sass 3.3.10 Using sass-rails 4.0.1 Using sdoc 0.3.20 Using websocket 1.0.7 Using selenium-webdriver 2.35.1 Using sqlite3 1.3.9 Using turbolinks 1.1.1 Using uglifier 2.1.1 Your bundle is complete! Gems in the group production were not installed. Use `bundle show [gemname]` to see where a bundled gem is installed.
- 这里为什么bundle install不再加 –withou production了?
因为--without production是一个"remembered option", 一旦你加过一次,以后就不用再加了
- 这也同时解释了,我们为什么"不用bundle update"来代替"bundle install", 直接输入
一次bundle update不是比1. bundle install –without production 2. bundle
update 3. bundle install强么?
第一个bundle install的作用,其实就是加入一个--without production的参数, bundle update 在最后会调用bundle install, 但是如果没有第一个bundle install 那么直接bundle update的话,就会"必然"引入production, 这不是我们想要的. 所以要"三步走"
- 下面我们的工作,是创建一个动态的.secret:
- 首先,在root文件夹下面创建一个叫做.secret的文件(不加也可以,一会自动创建)
- 其次,在.gitignore里面加入这个文件,防止其被版本管理
Modified .gitignore diff --git a/.gitignore b/.gitignore index 6a502e9..b603f56 100644 --- a/.gitignore +++ b/.gitignore @@ -14,3 +14,4 @@ # Ignore all logfiles and tempfiles. /log/*.log /tmp +.secret
- 更改config/initializers/secret_token.rb文件如下
# Be sure to restart your server when you modify this file. # Your secret key is used for verifying the integrity of signed cookies. # If you change this key, all old signed cookies will become invalid! # Make sure the secret is at least 30 characters and all random, # no regular words or you'll be exposed to dictionary attacks. # You can use `rake secret` to generate a secure secret key. # Make sure your secret_key_base is kept private # if you're sharing your code publicly. require 'securerandom' def secure_token token_file = Rails.root.join('.secret') if File.exist?(token_file) # Use the existing token. File.read(token_file).chomp else # Generate a new token and store it in token_file token = SecureRandom.hex(64) File.write(token_file, token) token end end SampleApp::Application.config.secret_key_base = secure_token
- 好的,万事俱备,下面我们来看看如何使用rspec来替代Test::Unit
hfeng@ubuntu1204:~/rails_projects/sample_app$ rails generate rspec:install create .rspec create spec create spec/spec_helper.rb
- 然后我们像往常一样
- 创建heroku(重命名)
heroku login heroku create heroku rename
- 加入heroku的版本管理
git push heroku master
- 运行数据库更改
heroku run rakd db:migrate
- 还可以看log
heroku log
- 创建heroku(重命名)
Static pages
- 墨迹这么久之后,终于开始要探究static网页的内容了,为此我们专门创建一个branch
hfeng@ubuntu1204:~/rails_projects/sample_app$ git checkout -b static-pages git checkout -b static-pages Switched to a new branch 'static-pages' hfeng@ubuntu1204:~/rails_projects/sample_app$ git branch -a git branch -a master * static-pages remotes/heroku/master remotes/origin/master
- 好,我们的任务开始了,我们要创建三个静态网页:
- Home page
- Help page
- About page
- 我们希望这三个静态网页都由一个controller来管理,这个controller叫做StaticPage
controller从这个名字你就能猜出来,以后有了新的其他的静态网页,我也想用这个controller,
这从一个侧面揭示了controller的一个事实:
controller从某种意义上来说,是某些相关页面的"容器".
- 下面就是创建controller的过程,创建的时候可以同时初始化一些页面,我们这里把home
和help都写上了,about留着以后写
hfeng@ubuntu1204:~/rails_projects/sample_app$ rails generate controller StaticPages home help --no-test-framework rails generate controller StaticPages home help --no-test-framework create app/controllers/static_pages_controller.rb route get "static_pages/help" route get "static_pages/home" invoke erb create app/views/static_pages create app/views/static_pages/home.html.erb create app/views/static_pages/help.html.erb invoke helper create app/helpers/static_pages_helper.rb invoke assets invoke coffee create app/assets/javascripts/static_pages.js.coffee invoke scss create app/assets/stylesheets/static_pages.css.scss
- 尽管你很小心,有时候,你还是会出错误,而自动生成代码一般是好几个文件,你也不知道
到底改了什么,这个时候具备undo是非常重要的. 而rails正好有这个功能
hfeng@ubuntu1204:~/rails_projects/sample_app$ git checkout -b undo-test git checkout -b undo-test A app/assets/javascripts/static_pages.js.coffee A app/assets/stylesheets/static_pages.css.scss A app/controllers/static_pages_controller.rb A app/helpers/static_pages_helper.rb A app/views/static_pages/help.html.erb A app/views/static_pages/home.html.erb M config/routes.rb Switched to a new branch 'undo-test' hfeng@ubuntu1204:~/rails_projects/sample_app$ git branch git branch master static-pages * undo-test hfeng@ubuntu1204:~/rails_projects/sample_app$ git add . git add . hfeng@ubuntu1204:~/rails_projects/sample_app$ git commit -m "StaticPages controller" git commit -m "StaticPages controller" [undo-test 0293b1f] StaticPages controller 7 files changed, 21 insertions(+) create mode 100644 app/assets/javascripts/static_pages.js.coffee create mode 100644 app/assets/stylesheets/static_pages.css.scss create mode 100644 app/controllers/static_pages_controller.rb create mode 100644 app/helpers/static_pages_helper.rb create mode 100644 app/views/static_pages/help.html.erb create mode 100644 app/views/static_pages/home.html.erb hfeng@ubuntu1204:~/rails_projects/sample_app$ git log -2 git log -2 commit 0293b1f819b2cdac66dda1e94c5ad560ca11fed2 Author: harri feng <harrifeng@gmail.com> Date: Sat Jul 26 21:08:41 2014 +0800 StaticPages controller commit 932b6873d0031f65aa88723735e44da7984a3303 Author: harri feng <harrifeng@gmail.com> Date: Sat Jul 26 20:35:06 2014 +0800 improve the readme hfeng@ubuntu1204:~/rails_projects/sample_app$ git checkout static-pages git checkout static-pages Switched to branch 'static-pages' hfeng@ubuntu1204:~/rails_projects/sample_app$ git log -2 git log -2 commit 932b6873d0031f65aa88723735e44da7984a3303 Author: harri feng <harrifeng@gmail.com> Date: Sat Jul 26 20:35:06 2014 +0800 improve the readme commit 23fc2ef76a4c7dcc78b0ac0184876e5dae5032d0 Author: harri feng <harrifeng@gmail.com> Date: Sat Jul 26 20:34:41 2014 +0800 use RSpec instead of Test::Unit hfeng@ubuntu1204:~/rails_projects/sample_app$ git diff static-pages --stat git diff static-pages --stat app/assets/javascripts/static_pages.js.coffee | 3 +++ app/assets/stylesheets/static_pages.css.scss | 3 +++ app/controllers/static_pages_controller.rb | 7 +++++++ app/helpers/static_pages_helper.rb | 2 ++ app/views/static_pages/help.html.erb | 2 ++ app/views/static_pages/home.html.erb | 2 ++ config/routes.rb | 2 ++ 7 files changed, 21 insertions(+) hfeng@ubuntu1204:~/rails_projects/sample_app$ rails destroy controller StaticPages home help --no-test-framework rails destroy controller StaticPages home help --no-test-framework remove app/controllers/static_pages_controller.rb route get "static_pages/help" route get "static_pages/home" invoke erb remove app/views/static_pages remove app/views/static_pages/home.html.erb remove app/views/static_pages/help.html.erb invoke helper remove app/helpers/static_pages_helper.rb invoke assets invoke coffee remove app/assets/javascripts/static_pages.js.coffee invoke scss remove app/assets/stylesheets/static_pages.css.scss hfeng@ubuntu1204:~/rails_projects/sample_app$ git branch git branch master static-pages * undo-test hfeng@ubuntu1204:~/rails_projects/sample_app$ git diff static-pages --stat git diff static-pages --stat hfeng@ubuntu1204:~/rails_projects/sample_app$ git log -2 git log -2 commit 0293b1f819b2cdac66dda1e94c5ad560ca11fed2 Author: harri feng <harrifeng@gmail.com> Date: Sat Jul 26 21:08:41 2014 +0800 StaticPages controller commit 932b6873d0031f65aa88723735e44da7984a3303 Author: harri feng <harrifeng@gmail.com> Date: Sat Jul 26 20:35:06 2014 +0800 improve the readme hfeng@ubuntu1204:~/rails_projects/sample_app$ git status git status # On branch undo-test # Changes not staged for commit: # (use "git add/rm <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # deleted: app/assets/javascripts/static_pages.js.coffee # deleted: app/assets/stylesheets/static_pages.css.scss # deleted: app/controllers/static_pages_controller.rb # deleted: app/helpers/static_pages_helper.rb # deleted: app/views/static_pages/help.html.erb # deleted: app/views/static_pages/home.html.erb # modified: config/routes.rb # no changes added to commit (use "git add" and/or "git commit -a")
- 数据库也有创建,和"破坏(回滚)"的方法
# generate rake db:migrate # destroy rake db:rollback
- 你可以看到,我们这里使用了"驼峰"命名法写StaticPages,这个会产生一个名字为static_pages_controller.rb
的文件. 当然了,其实你就算写static_pages代替StaticPages,也是可以的(rails内部
使用undersocre命令来转换把所有"驼峰"命名转换成"蛇形"命名). 借助上面的branch
undo-test我们来看看使用"蛇形"命名的结果: 我们可以看到,只有一行注释不同.
hfeng@ubuntu1204:~/rails_projects/sample_app$ git checkout static-pages git checkout static-pages Switched to branch 'static-pages' hfeng@ubuntu1204:~/rails_projects/sample_app$ git diff undo-test --stat git diff undo-test --stat app/assets/javascripts/static_pages.js.coffee | 3 --- app/assets/stylesheets/static_pages.css.scss | 3 --- app/controllers/static_pages_controller.rb | 7 ------- app/helpers/static_pages_helper.rb | 2 -- app/views/static_pages/help.html.erb | 2 -- app/views/static_pages/home.html.erb | 2 -- config/routes.rb | 2 -- 7 files changed, 21 deletions(-) hfeng@ubuntu1204:~/rails_projects/sample_app$ rails generate controller static_pages home help --no-test-framework rails generate controller static_pages home help --no-test-framework create app/controllers/static_pages_controller.rb route get "static_pages/help" route get "static_pages/home" invoke erb create app/views/static_pages create app/views/static_pages/home.html.erb create app/views/static_pages/help.html.erb invoke helper create app/helpers/static_pages_helper.rb invoke assets invoke coffee create app/assets/javascripts/static_pages.js.coffee invoke scss create app/assets/stylesheets/static_pages.css.scss hfeng@ubuntu1204:~/rails_projects/sample_app$ git diff undo-test --cached --stat git diff undo-test --cached --stat app/assets/stylesheets/static_pages.css.scss | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) hfeng@ubuntu1204:~/rails_projects/sample_app$ git diff undo-test --cached git diff undo-test --cached diff --git a/app/assets/stylesheets/static_pages.css.scss b/app/assets/stylesheets/static_pages.css.scss index d55836c..40608b2 100644 --- a/app/assets/stylesheets/static_pages.css.scss +++ b/app/assets/stylesheets/static_pages.css.scss @@ -1,3 +1,3 @@ -// Place all the styles related to the StaticPages controller here. +// Place all the styles related to the static_pages controller here. // They will automatically be included in application.css. // You can use Sass (SCSS) here: http://sass-lang.com/
- 我们的routes.rb里面多了这么几行
Modified config/routes.rb diff --git a/config/routes.rb b/config/routes.rb index d2facc2..f3d176d 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,4 +1,6 @@ SampleApp::Application.routes.draw do + get "static_pages/home" + get "static_pages/help" # The priority is based upon order of creation: first created -> highest priority. # See how all your routes lay out with "rake routes".
- 我们就会从get static_pages/home这一句里面得到如下信息:我们使用GET method的
时候,会触发static_pages_controller里面的home页面
Figure 4: static-page-home.png
- 如果我们看看StaticPages Controller的代码的话,我们就会发现. 其并不是RESTful的
API, 这是因为这个世界上也没有绝对的事情, 静态的网页很难抽象出resource的概念,
也就不需要RESTful API啦.
# app/controllers/static_pages_controller.rb class StaticPagesController < ApplicationController def home end def help end end
- 我们可以看到home和help两个函数是空的!但是由于StaticPagesController继承了
ApplicationController,所以所有的子函数都"默认自带"转发请求到"View"的功能.到
哪个view呢?还是通过"名字"来的!,刚才创建了两个ViewPage,一个是为home 函数准备
的,一个是为help函数准备的.
# app/view/static_pages/home.html.erb <h1>StaticPages#home</h1> <p>Find me in app/views/static_pages/home.html.erb</p> # app/view/static_pages/help.html.erb <h1>StaticPages#help</h1> <p>Find me in app/views/static_pages/help.html.erb</p>
Our first tests
- Rails是一种强调behavior的框架(对于内部如何实现,不是特别关心), 所以它使用了 一种叫做BDD(Behavior-driven development)的开发模式
- rails里面主要有两种类型的测试:
- integration test: 模仿用户使用浏览器来和我们的server进行交互
- unit test
- BDD是TDD(Test-driven development)的一种变形, 强调在写代码之前,先写测试, 当然 了凡事没有绝对,并不是一定要每次都先写测试
Test-driven development
- 在test-driven的开发流程中,我们要遵循一个"Red, Green, Refactor"的过程:
- red: 首先写一个会fail的测试
- green: 写一个成功的fail的测试
- refactor: 不改变函数,重构函数内容,再运行测试,保证测试能够成功
- 下面开始写第一个integration测试用例:
hfeng@ubuntu1204:~/rails_projects/sample_app$ rails generate integration_test static_pages invoke rspec create spec/requests/static_pages_spec.rb
- 首先写一个red的测试:
diff --git a/spec/requests/static_pages_spec.rb b/spec/requests/static_pages_spec.rb index 7c64515..f3993c0 100644 --- a/spec/requests/static_pages_spec.rb +++ b/spec/requests/static_pages_spec.rb @@ -1,11 +1,10 @@ require 'spec_helper' describe "Static Pages" do - describe "GET /static_pages" do - it "works! (now write some real specs)" do - # Run the generator again with the --webrat flag if you want to use webrat methods/matchers - get static_pages_index_path - response.status.should be(200) + describe "Home page" do + it "should have the content 'Sample App'" do + visit '/static_pages/home' + expect(page).to have_content('Sample App') end end end
- 测试要能够使用visit函数,必须在helper函数里面加上如下配置
diff --git a/spec/spec_helper.rb b/spec/spec_helper.rb index 943bc19..031f92c 100644 --- a/spec/spec_helper.rb +++ b/spec/spec_helper.rb @@ -39,4 +39,5 @@ RSpec.configure do |config| # the seed, which is printed after each run. # --seed 1234 config.order = "random" + config.include Capybara::DSL end
- 测试的结果必然是寒心的.
hfeng@ubuntu1204:~/rails_projects/sample_app$ rspec spec/requests/static_pages_spec.rb F Failures: 1) Static Pages Home page should have the content 'Sample App' Failure/Error: expect(page).to have_content('Sample App') expected #has_content?("Sample App") to return true, got false # ./spec/requests/static_pages_spec.rb:7:in `block (3 levels) in <top (required)>' Finished in 0.33291 seconds 1 example, 1 failure Failed examples: rspec ./spec/requests/static_pages_spec.rb:5 # Static Pages Home page should have the content 'Sample App' Randomized with seed 44199
- 在页面添加必要的字符,就通过了测试,来到了green stage
diff --git a/app/views/static_pages/home.html.erb b/app/views/static_pages/home.html.erb index af94c7f..d4f4fe7 100644 --- a/app/views/static_pages/home.html.erb +++ b/app/views/static_pages/home.html.erb @@ -1,2 +1,6 @@ -<h1>StaticPages#home</h1> -<p>Find me in app/views/static_pages/home.html.erb</p> +<h1>Sample App</h1> +<p> + This is the ome page for the + <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a> + sample application. +</p>
- 如法炮制,就得到了help的green stage
diff --git a/app/views/static_pages/help.html.erb b/app/views/static_pages/help.html.erb index 61896f5..bb57b44 100644 --- a/app/views/static_pages/help.html.erb +++ b/app/views/static_pages/help.html.erb @@ -1,2 +1,7 @@ -<h1>StaticPages#help</h1> -<p>Find me in app/views/static_pages/help.html.erb</p> +<h1>Help</h1> +<p> + Get help on the Ruby on Rails Tutorial at the + <a href="http://railstutorial.org/help">Rails Tutorial help page</a> + To get help on this sample app, see the + <a href="http://railstutorial.org/book">Rails Tutorial book</a> +</p> diff --git a/spec/requests/static_pages_spec.rb b/spec/requests/static_pages_spec.rb index 432450c..2f65d48 100644 --- a/spec/requests/static_pages_spec.rb +++ b/spec/requests/static_pages_spec.rb @@ -2,9 +2,17 @@ require 'spec_helper' describe "Static Pages" do describe "Home page" do + it "should have the content 'Sample App'" do visit '/static_pages/home' expect(page).to have_content('Sample App') end end + + describe "Help page" do + it "should have the content 'Help'" do + visit '/static_pages/help' + expect(page).to have_content('Help') + end + end end
Adding a page
- 我们前面故意遗漏了一个About的页面,这里就是通过"Red, Green, Refator"的三步来添加.
Red:
- 先添加测试如下
diff --git a/spec/requests/static_pages_spec.rb b/spec/requests/static_pages_spec.rb index 2f65d48..23cddaf 100644 --- a/spec/requests/static_pages_spec.rb +++ b/spec/requests/static_pages_spec.rb @@ -15,4 +15,12 @@ describe "Static Pages" do expect(page).to have_content('Help') end end + + describe "About page" do + it "should have the content 'About Us'" do + visit '/static_pages/about' + expect(page).to have_content('About Us') + end + end + end
- 然后,测试很"如所料"的挂掉了
hfeng@ubuntu1204:~/rails_projects/sample_app$ rspec spec/requests/static_pages_spec.rb ..F Failures: 1) Static Pages About page should have the content 'About Us' Failure/Error: visit '/static_pages/about' ActionController::RoutingError: No route matches [GET] "/static_pages/about" # ./spec/requests/static_pages_spec.rb:21:in `block (3 levels) in <top (required)>' Finished in 0.0552 seconds 3 examples, 1 failure Failed examples: rspec ./spec/requests/static_pages_spec.rb:20 # Static Pages About page should have the content 'About Us' Randomized with seed 43839
Green
- 上面的抱怨是我们根本没有/static_pages/about.这是routes没设置对, 改之
diff --git a/config/routes.rb b/config/routes.rb index f3d176d..d13c93c 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,6 +1,7 @@ SampleApp::Application.routes.draw do get "static_pages/home" get "static_pages/help" + get "static_pages/about" # The priority is based upon order of creation: first created -> highest priority. # See how all your routes lay out with "rake routes".
- 依然不对,抱怨controller没有相应的函数
hfeng@ubuntu1204:~/rails_projects/sample_app$ rspec spec/requests/static_pages_spec.rb F.. Failures: 1) Static Pages About page should have the content 'About Us' Failure/Error: visit '/static_pages/about' AbstractController::ActionNotFound: The action 'about' could not be found for StaticPagesController # ./spec/requests/static_pages_spec.rb:21:in `block (3 levels) in <top (required)>'
- 那么我们就加上响应的函数
diff --git a/app/controllers/static_pages_controller.rb b/app/controllers/static_pages_controller.rb index c76b925..19f79a9 100644 --- a/app/controllers/static_pages_controller.rb +++ b/app/controllers/static_pages_controller.rb @@ -4,4 +4,7 @@ class StaticPagesController < ApplicationController def help end + + def about + end end
- 这次的抱怨是没有template
hfeng@ubuntu1204:~/rails_projects/sample_app$ rspec spec/requests/static_pages_spec.rb F.. Failures: 1) Static Pages About page should have the content 'About Us' Failure/Error: visit '/static_pages/about' ActionView::MissingTemplate: Missing template static_pages/about, application/about with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder, :raw, :ruby, :jbuilder, :coffee]}. Searched in: * "/home/hfeng/rails_projects/sample_app/app/views" # ./spec/requests/static_pages_spec.rb:21:in `block (3 levels) in <top (required)>' Finished in 0.05173 seconds 3 examples, 1 failure Failed examples: rspec ./spec/requests/static_pages_spec.rb:20 # Static Pages About page should have the content 'About Us' Randomized with seed 63833
- 没有template其实就是没有View,然后我们添加一下
<!-- sample_app/app/views/static_pages/about.html.erb --> <h1>About Us</h1> <p> The <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a> is a project to make a book and screencasts to teach web development with <a href="http://rubyonrails.org/">Ruby on Rails</a>. This is the sample application for the tutorial. </p>
- 这下终于green了
hfeng@ubuntu1204:~/rails_projects/sample_app$ rspec spec/requests/static_pages_spec.rb ... Finished in 0.05589 seconds 3 examples, 0 failures Randomized with seed 39259
Refactor
- 现阶段我们还没有重构的必要,但是拥有健壮的测试是重构的基础.
Slightly dynamic pages
Testing a title change
- 这次我们是要满足title包含(注意是包含,不是完全匹配)某些字符
- 首先我们还是要做red part,所以得把rails提供的默认的layout给先屏蔽掉
mv app/views/layouts/application.html.erb foobar
- 然后是更改测试代码
@@ -2,11 +2,14 @@ require 'spec_helper' describe "Static Pages" do describe "Home page" do - it "should have the content 'Sample App'" do visit '/static_pages/home' expect(page).to have_content('Sample App') end + it "should have the right title 'Home'" do + visit '/static_pages/home' + expect(page).to have_title("Ruby on Rails Tutorial Sample App | Home") + end end describe "Help page" do @@ -14,6 +17,10 @@ describe "Static Pages" do visit '/static_pages/help' expect(page).to have_content('Help') end + it "should have the right title 'Help'" do + visit '/static_pages/help' + expect(page).to have_title("Ruby on Rails Tutorial Sample App | Help") + end end describe "About page" do @@ -21,6 +28,9 @@ describe "Static Pages" do visit '/static_pages/about' expect(page).to have_content('About Us') end + it "should have the right title 'About Us'" do + visit '/static_pages/about' + expect(page).to have_title("Ruby on Rails Tutorial Sample App | About Us") + end end - end
- 注定凄凉的结果
hfeng@ubuntu1204:~/rails_projects/sample_app$ rspec spec/requests/static_pages_spec.rb F.F.F. Failures: 1) Static Pages Home page should have the right title 'Home' Failure/Error: expect(page).to have_title("Ruby on Rails Tutorial Sample App | Home") expected #has_title?("Ruby on Rails Tutorial Sample App | Home") to return true, got false # ./spec/requests/static_pages_spec.rb:11:in `block (3 levels) in <top (required)>' 2) Static Pages Help page should have the right title 'Help' Failure/Error: expect(page).to have_title("Ruby on Rails Tutorial Sample App | Help") expected #has_title?("Ruby on Rails Tutorial Sample App | Help") to return true, got false # ./spec/requests/static_pages_spec.rb:22:in `block (3 levels) in <top (required)>' 3) Static Pages About page should have the right title 'About Us' Failure/Error: expect(page).to have_title("Ruby on Rails Tutorial Sample App | About Us") expected #has_title?("Ruby on Rails Tutorial Sample App | About Us") to return true, got false # ./spec/requests/static_pages_spec.rb:33:in `block (3 levels) in <top (required)>' Finished in 0.05635 seconds 6 examples, 3 failures Failed examples: rspec ./spec/requests/static_pages_spec.rb:9 # Static Pages Home page should have the right title 'Home' rspec ./spec/requests/static_pages_spec.rb:20 # Static Pages Help page should have the right title 'Help' rspec ./spec/requests/static_pages_spec.rb:31 # Static Pages About page should have the right title 'About Us' Randomized with seed 2602
Passing title tests
- 下面就要来让这些测试通过啦,想通过也就很简单,让他们的title含有响应字符就可以了
- 对于home来说就是
<!-- app/views/static_pages/home.html.erb --> <!DOCTYPE html> <html> <head> <title>Ruby on Rails Tutorial Sample App | Home</title> </head> <body> <h1>Sample App</h1> <p> This is the home page for the <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a> sample application. </p> </body> </html>
- 对于help来说就是
<!-- app/views/static_pages/help.html.erb --> <!DOCTYPE html> <html> <head> <title>Ruby on Rails Tutorial Sample App | Help</title> </head> <body> <h1>Sample App</h1> <p> This is the help page for the <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a> sample application. </p> </body> </html>
- 对于about来说就是
<!-- app/views/static_pages/about.html.erb --> <!DOCTYPE html> <html> <head> <title>Ruby on Rails Tutorial Sample App | About Us</title> </head> <body> <h1>Sample App</h1> <p> This is the about page for the <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a> sample application. </p> </body> </html>
- 对于home来说就是
Embedded Ruby
- 上面三个Html页面最大的特点,就是"重复", Rails是为了DRY原则设计的一个框架,如 此频繁的重复肯定无法容忍.需要通过某些方式引入"变量",替代可变部分.
- 以Home为例, 其他类似
<!-- app/views/static_pages/home.html.erb --> <% provide(:title, 'Home') %> <!DOCTYPE html> <html> <head> <title>Ruby on Rails Tutorial Sample App | <%= yield(:title) %></title> </head> <body> <h1>Sample App</h1> <p> This is the home page for the <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a> sample application. </p> </body> </html>
Eliminating duplication with layouts
- 为每个html.erb页面在头上加上如下这么一句, 肯定是非常麻烦的
<% provide(:title, 'Foo') %> <!DOCTYPE html> <html> <head> <title>Ruby on Rails Tutorial Sample App | <%= yield(:title) %></title> </head> <!-- .... --> </html>
- rails的解决办法,是把相同的部分抽出来,设计成layout, 然后每个页面都include layout
- 在默认的模板生成的代码之下,只需要更改一部分就可以得到预想的功能
<html> <head> - <title>SampleApp</title> + <title>Ruby on Rails Tutorial Sample App | <% yield(:title) %></title>
- 我们顺便来研究下app/views/layouts/application.html.erb的全部代码
<!DOCTYPE html> <html> <head> <title>Ruby on Rails Tutorial Sample App | <% yield(:title) %></title> <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %> <%= javascript_include_tag "application", "data-turbolinks-track" => true %> <%= csrf_meta_tags %> </head> <body> <%= yield %> </body> </html>
- 代码有这个几个需要注意的点:
- <%= stylesheet_lik_tag %>是引入style
- <%= javascript_include_tag %>是引入javascript
- <%= csrf_meta_tags %>的引入,是为了防止某些恶意攻击
- <% yield(:title) %> 就是会使用对应页面(比如home.html.erb)的:title的值啦
- <%= yield %> 这个很关键, 对应页面(比如home.html.erb)就会加载到这个地方来. 对应页面就像block一样,被引入,然后在这个地方和layout结合成一个页面.用法和 ruby的block很类似.
- 对html.erb稍加改动
diff --git a/app/views/static_pages/about.html.erb b/app/views/static_pages/about.html.erb index 463bdb3..26012f2 100644 --- a/app/views/static_pages/about.html.erb +++ b/app/views/static_pages/about.html.erb @@ -1,5 +1,4 @@ -<!-- sample_app/app/views/static_pages/about.html.erb --> - +<% provide(:title, 'About Us') %> <h1>About Us</h1> <p> The <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a> Modified app/views/static_pages/help.html.erb diff --git a/app/views/static_pages/help.html.erb b/app/views/static_pages/help.html.erb index bb57b44..50245e4 100644 --- a/app/views/static_pages/help.html.erb +++ b/app/views/static_pages/help.html.erb @@ -1,3 +1,4 @@ +<% provide(:title, 'Help') %> <h1>Help</h1> <p> Get help on the Ruby on Rails Tutorial at the Modified app/views/static_pages/home.html.erb diff --git a/app/views/static_pages/home.html.erb b/app/views/static_pages/home.html.erb index d4f4fe7..f184b43 100644 --- a/app/views/static_pages/home.html.erb +++ b/app/views/static_pages/home.html.erb @@ -1,3 +1,4 @@ +<% provide(:title, 'Home') %> <h1>Sample App</h1> <p> This is the ome page for the Modified spec/requests/static_pages_spec.rb
- 结果就很满意啦(Green stage)
hfeng@ubuntu1204:~/rails_projects/sample_app$ rspec spec/requests/static_pages_spec.rb ...... Finished in 0.06567 seconds 6 examples, 0 failures Randomized with seed 19940
Conclusion
- 操作做完以后,把branch更改后merge到master
hfeng@ubuntu1204:~/rails_projects/sample_app$ git status # On branch static-pages # Changes not staged for commit: # (use "git add <file>..." to update what will be committed) # (use "git checkout -- <file>..." to discard changes in working directory) # # modified: app/views/layouts/application.html.erb # modified: app/views/static_pages/about.html.erb # modified: app/views/static_pages/help.html.erb # modified: app/views/static_pages/home.html.erb # modified: spec/requests/static_pages_spec.rb # no changes added to commit (use "git add" and/or "git commit -a") hfeng@ubuntu1204:~/rails_projects/sample_app$ git add . hfeng@ubuntu1204:~/rails_projects/sample_app$ git branch master * static-pages undo-test hfeng@ubuntu1204:~/rails_projects/sample_app$ git add . hfeng@ubuntu1204:~/rails_projects/sample_app$ git commit -m "Finish static pages" [static-pages 77fb631] Finish static pages 5 files changed, 17 insertions(+), 6 deletions(-) hfeng@ubuntu1204:~/rails_projects/sample_app$ git checkout master Switched to branch 'master' hfeng@ubuntu1204:~/rails_projects/sample_app$ git merge static-pages Updating 932b687..77fb631 Fast-forward app/assets/javascripts/static_pages.js.coffee | 3 +++ app/assets/stylesheets/static_pages.css.scss | 3 +++ app/controllers/static_pages_controller.rb | 10 +++++++ app/helpers/static_pages_helper.rb | 2 ++ app/views/layouts/application.html.erb | 4 +-- app/views/static_pages/about.html.erb | 8 ++++++ app/views/static_pages/help.html.erb | 8 ++++++ app/views/static_pages/home.html.erb | 7 +++++ config/routes.rb | 3 +++ spec/requests/static_pages_spec.rb | 36 +++++++++++++++++++++++++ spec/spec_helper.rb | 1 + 11 files changed, 83 insertions(+), 2 deletions(-) create mode 100644 app/assets/javascripts/static_pages.js.coffee create mode 100644 app/assets/stylesheets/static_pages.css.scss create mode 100644 app/controllers/static_pages_controller.rb create mode 100644 app/helpers/static_pages_helper.rb create mode 100644 app/views/static_pages/about.html.erb create mode 100644 app/views/static_pages/help.html.erb create mode 100644 app/views/static_pages/home.html.erb create mode 100644 spec/requests/static_pages_spec.rb hfeng@ubuntu1204:~/rails_projects/sample_app$ git remote -v heroku git@heroku.com:hfeng-sample-app.git (fetch) heroku git@heroku.com:hfeng-sample-app.git (push) origin git@github.com:harrifeng/sample_app.git (fetch) origin git@github.com:harrifeng/sample_app.git (push) hfeng@ubuntu1204:~/rails_projects/sample_app$ git push origin master Counting objects: 25, done. Delta compression using up to 4 threads. Compressing objects: 100% (11/11), done. Writing objects: 100% (13/13), 1.18 KiB, done. Total 13 (delta 7), reused 0 (delta 0) To git@github.com:harrifeng/sample_app.git 932b687..77fb631 master -> master
Chapter 04: Rails-flavored Ruby
Motivation
- 前面两章为我们展示了,即便是对ruby语言不太熟悉,也是可以开发rails项目的.但是, 这不可能一直继续,我们还是要对ruby有深入的了解,才能更好的使用rails.
- 首先来看看上一章提到的layout
<!DOCTYPE html> <html> <head> <title>Ruby on Rails Tutorial Sample App | <%= yield(:title) %></title> <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %> <%= javascript_include_tag "application", "data-turbolinks-track" => true %> <%= csrf_meta_tags %> </head> <body> <%= yield %> </body> </html>
- 其中<%= %>的部分,都是引入的ruby代码,比如下面的这个
stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true
- 从API上我们可以看到函数原型是
def stylesheet_link_tag(*sources)
- 也就是说stylesheet_link_tag的参数不限制个数,会最后组合成sources这个数组.这也 就意味着,最后一个参数如果是hash的话,可以不加括号.上面其实就是这么做的.
- 上一章解决title的问题是通过provide一个title变量,但是如果忘了引入这个变量的
话,会出现如下不是很好看的title
Ruby on Rails Tutorial Sample App |
- 解决办法是让返回的基础文本"Ruby on Rails Tutorial Sample App"更加的"智能",
有title的时候加"|", 没有title的时候, 不加"|"
# app/helpers/application_helper.rb module ApplicationHelper # Returns the full title on a per-page basis def full_title(page_title) base_title = "Ruby on Rails Turorial Sample Aapp" if page_title.empty? base_title else "{base_title} | #{page_title}" end end end
- 于是在layout里面就是这样进行配置了
diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 91672b9..c57717a 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -1,7 +1,7 @@ <!DOCTYPE html> <html> <head> - <title>Ruby on Rails Tutorial Sample App | <%= yield(:title) %></title> + <title>Ruby on Rails Tutorial Sample App | <%= full_title(yield(:title)) %></title> <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %> <%= javascript_include_tag "application", "data-turbolinks-track" => true %> <%= csrf_meta_tags %>
- 然后我们修改testcase,会fail testcase
diff --git a/spec/requests/static_pages_spec.rb b/spec/requests/static_pages_spec.rb index 65b2995..4686d4a 100644 --- a/spec/requests/static_pages_spec.rb +++ b/spec/requests/static_pages_spec.rb @@ -6,9 +6,13 @@ describe "Static Pages" do visit '/static_pages/home' expect(page).to have_content('Sample App') end - it "should have the right title 'Home'" do + it "should have the base title" do visit '/static_pages/home' - expect(page).to have_title("Ruby on Rails Tutorial Sample App | Home") + expect(page).to have_title("Ruby on Rails Tutorial Sample App") + end + it "shoudl not have a custom page title" do + visit '/static_pages/home' + expect(page).not_to have_title('| Home') end end
- 最后我们把home里面的开头代码去掉,就对了
diff --git a/app/views/static_pages/home.html.erb b/app/views/static_pages/home.html.erb index f184b43..d4f4fe7 100644 --- a/app/views/static_pages/home.html.erb +++ b/app/views/static_pages/home.html.erb @@ -1,4 +1,3 @@ -<% provide(:title, 'Home') %> <h1>Sample App</h1> <p>
Strings and methods
- 在rails里面学习ruby的绝佳武器是Rails console
hfeng@ubuntu1204:~/rails_projects/sample_app$ rails console Loading development environment (Rails 4.0.8) 2.0.0-p481 :001 >
Comments
- ruby中的#是注释符号
2.0.0-p481 :001 > 17 + 42 # Integer addition => 59
String
- string在ruby里面非常灵活
2.0.0-p481 :002 > "" # empty string => "" 2.0.0-p481 :003 > "foo" => "foo" 2.0.0-p481 :004 > "foo" + "bar" => "foobar" 2.0.0-p481 :005 > first_name = "Michael" => "Michael" 2.0.0-p481 :010 > " #{first_name} " # String interpolation => " Michael " 2.0.0-p481 :011 >
Printing
- puts会在最后加上一个"\n", 而且返回值为nil
2.0.0-p481 :013 > print "hello" hello => nil 2.0.0-p481 :014 > puts "hello" hello => nil
Single-quoted strings
- 在ruby中,单引号和双引号之间几乎完全相等,除了一个地方: 单引号不会被interpolation
2.0.0-p481 :017 > foo = 'bar' => "bar" 2.0.0-p481 :018 > foo => "bar" 2.0.0-p481 :019 > '#{foo}' => "\#{foo}"
- Ruby中单引号的意义当然不仅仅局限于这一个地方咯.单引号最重要的意义在于''里面
有几个单词,那么就是有几个ascii码字符. 而""里面却有可能是两个字符代表一个ascii
2.0.0-p481 :020 > "\n" => "\n" 2.0.0-p481 :021 > '\n' => "\\n"
Objects and message passing
- 在ruby中,所有的一切都是对象(包括string和nil)
irb(main):001:0> "foobar".length "foobar".length => 6 irb(main):002:0> "foobar".empty? "foobar".empty? => false irb(main):003:0> s = "foobar" s = "foobar" => "foobar"irb(main):001:0> "foobar".length "foobar".length => 6 irb(main):002:0> "foobar".empty? "foobar".empty? => false irb(main):003:0> s = "foobar" s = "foobar" => "foobar"
- nil是最奇怪的对象, nil并不是empty,是一种特殊的存在,我们可以测试一个对象是
不是nil,通过nil?函数
irb(main):001:0> "foo".nil? "foo".nil? => false irb(main):002:0> "".nil? "".nil? => false irb(main):004:0> nil.nil? => true
- nil是false,其他所有都是true,甚至0都是false!
irb(main):002:0> puts "zero is true" if 0 puts "zero is true" if 0 zero is true
Method definitions
- ruby代码的最后一行是"隐形返回值"
Back to the title helper
- 我们来看看full_title的代码
module ApplicationHelper # Returns the full title on a per-page basis def full_title(page_title) base_title = "Ruby on Rails Tutorial Sample App" if page_title.empty? base_title else "#{base_title} | #{page_title}" end end end
- 这里的full_tilte被写入了一个叫做ApplicationHelper的module,这个module会作为 一个整体被include到其他ruby代码里面,然后发挥作用.
- 在rails里面,我们不需要明确的include,框架本身已经帮我们include了
Other data structures
Arrays and ranges
- split可以把字符串改成数组
irb(main):003:0> "foo bar baz".split "foo bar baz".split => ["foo", "bar", "baz"] irb(main):004:0> "fooxbarxbaz".split "fooxbarxbaz".split => ["fooxbarxbaz"] irb(main):005:0> "fooxbarxbaz".split('x') "fooxbarxbaz".split('x') => ["foo", "bar", "baz"]
- x..x就是一个数组,range可以使用to_a来创建数组
irb(main):006:0> (0..9).to_a (0..9).to_a => [0, 1, 2, 3, 4, 5, 6, 7, 8, 9]
- range还可以作为数组的index
irb(main):007:0> a = %w[foo bar baz quux] a = %w[foo bar baz quux] => ["foo", "bar", "baz", "quux"] irb(main):008:0> a[0..2] a[0..2] => ["foo", "bar", "baz"]
- ruby里面创建random的域名的函数如下(主要就是利用了range)
irb(main):009:0> ('a'..'z').to_a ('a'..'z').to_a => ["a", "b", "c", "d", "e", "f", "g", "h", "i", "j", "k", "l", "m", "n", "o", "p", "q", "r", "s", "t", "u", "v", "w", "x", "y", "z"] irb(main):010:0> ('a'..'z').to_a.shuffle ('a'..'z').to_a.shuffle => ["k", "q", "d", "g", "r", "v", "x", "y", "u", "n", "f", "s", "c", "l", "w", "b", "a", "e", "m", "t", "p", "h", "j", "z", "i", "o"] irb(main):011:0> ('a'..'z').to_a.shuffle[0..7] ('a'..'z').to_a.shuffle[0..7] => ["n", "q", "h", "d", "j", "z", "t", "v"] irb(main):012:0> ('a'..'z').to_a.shuffle[0..7].join # final solution ('a'..'z').to_a.shuffle[0..7].join => "qfdlgxek"
Blocks
- 数组和range都有一系列的函数可以使用block作为自己的参数
- 而block,则是ruby里面最难以理解的概念
Hashes and symbols
- 其实hash就是array的变体,只不过不仅仅能使用integer来作为index罢了. 在Perl里
面Hash又叫做"associative array"
irb(main):013:0> user = {} user = {} => {} irb(main):014:0> user["first_name"] = "Michael" user["first_name"] = "Michael" => "Michael" irb(main):016:0> user["last_name"]="Hartl" user["last_name"]="Hartl" => "Hartl" irb(main):017:0> user["first_name"] user["first_name"] => "Michael" irb(main):018:0> user user => {"first_name"=>"Michael", "last_name"=>"Hartl"}
- 虽然可以使用各种object作为key,但是,在rails甚至是ruby里面,使用symbol作为
index是一种"更为常规"的做法, symbol有"ruby中的enum"的称号.
irb(main):002:0> h1 = {:name=>"Michael Hartl", :email=>"michael@example.com"} h1 = {:name=>"Michael Hartl", :email=>"michael@example.com"} => {:name=>"Michael Hartl", :email=>"michael@example.com"}
- 由于在ruby里面基本作用就是hash的key,所以ruby甚至为它创建了更简单的定义方法
irb(main):001:0> h1 = {name: "Michael Hartl", email: "michael@example.com"} h1 = {name: "Michael Hartl", email: "michael@example.com"} => {:name=>"Michael Hartl", :email=>"michael@example.com"}
- hash和数组,range一样,也有很多使用block作为参数的函数, 只不过返回两个成员
irb(main):004:0> flash.each {|key, value| puts "key is #{key}, value is #{value}"} flash.each {|key, value| puts "key is #{key}, value is #{value}"} key is success, value is It worked! key is error, value is It failed. => {:success=>"It worked!", :error=>"It failed."}
CSS revisited
- 上面我们设置css的时候,使用了如下的代码
<%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %>
- 其实就是一个函数调用, 因为ruby下面函数调用是可以没有括号的,所以下面两个是
相等的
# Parentheses on function calls are optional. stylesheet_link_tag("application", media: "all", "data-turbolinks-track" => true) stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true
- 另外media看起来是个hash啊,怎么变成了参数…? 原来ruby有如下规定:
如果hash是最后一个参数的话,hash的括号是可以省略的.
- 所以,下面两个也是等价的
# Curly braces on final hash arguments are optional. stylesheet_link_tag "application", { media: "all", "data-turbolinks-track" => true } stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true
- 更搞笑的是下面这句, 由于有"-"的变量不能使用symbol: value的形式,所以,他还是得用老的
"data-turbolinks-track" => true
Ruby classes
Constructors
- ruby和java一样也是可以使用赋值来创建新的class, 其原理就是内部调用了一次new
irb(main):005:0> s = "foobar" s = "foobar" => "foobar" irb(main):006:0> s.class s.class => String irb(main):007:0> s1 = String.new("foobar") s1 = String.new("foobar") => "foobar" irb(main):008:0> s1.class s1.class => String irb(main):009:0> s1 == "foobar" s1 == "foobar" => true
Class inheritance
- 我们可以使用superclass来对付处女座
irb(main):010:0> s = String.new("foobar") s = String.new("foobar") => "foobar" irb(main):011:0> s.class s.class => String irb(main):012:0> s.class.superclass s.class.superclass => Object irb(main):013:0> s.class.superclass.superclass s.class.superclass.superclass => BasicObject irb(main):014:0> s.class.superclass.superclass.superclass s.class.superclass.superclass.superclass => nil
- 比如我们想创建一个word class, 其有一个判断是否"对称"的功能
class Word def palindrome?(string) string == string.reverse end end w = Word.new p w.palindrome?("foobar") p w.palindrome?("level") ################################################## # <===================OUTPUT===================> # # false # # true # ##################################################
- 如果用继承的方法来写就是如下
class Word < String def palindrome? self == self.reverse end end s = Word.new("level") p s.palindrome? p s.length ################################################## # <===================OUTPUT===================> # # true # # 5 # ##################################################
Modifying built-in classes
- ruby 灵活的地方,还在于可以更改built-in的函数. 比如,下面这个函数肯定是不对的
irb(main):014:0> "level".palindrome? "level".palindrome? NoMethodError: undefined method `palindrome?' for "level":String
- 我们可以如下更改built-in类
class String def palindrome? self == self.reverse end end "level".palindrome? ################################################## # <===================OUTPUT===================> # # true # ##################################################
- 一般人不要随便更改built-in的class, rails不算一般,所以它可以更改, 最著名的
更改就是在String里面加了一个blank?函数, blank?函数是判断是否为"无意义的空
字符(用户名就不能只是空格)", 比如tab和space等等, (下面的例子要在rails console
里面才能成功)
i309511@[git-svn-ct] > rails c /Users/i309511/Code/git-svn-ct/lib/content_admin_mixin.rb:93: warning: already initialized constant CONTENT_SEARCH_AUTOCOMPLETE_CLASS_NAMES Loading development environment (Rails 3.0.20) irb(main):001:0> "".empty? => true irb(main):002:0> " ".empty? => false irb(main):003:0> "".blank? => true irb(main):004:0> " ".blank? => true irb(main):005:0> nil.blank? => true
A controller class
- 下面是demo_app里面的controller继承序列
i309511@[sample_app] > rails c Loading development environment (Rails 4.0.8) irb(main):001:0> controller = StaticPagesController.new => #<StaticPagesController:0x007ff9dce29bf8 @_routes=nil, @_action_has_layout=true, @_headers={"Content-Type"=>"text/html"}, @_status=200, @_request=nil, @_response=nil> irb(main):002:0> controller.class => StaticPagesController irb(main):003:0> controller.class.superclass => ApplicationController irb(main):004:0> controller.class.superclass.superclass => ActionController::Base irb(main):005:0> controller.class.superclass.superclass.superclass => ActionController::Metal irb(main):006:0> controller.class.superclass.superclass.superclass.superclass => AbstractController::Base irb(main):007:0> controller.class.superclass.superclass.superclass.superclass.superclass => Object
- controller明显是一个ruby类,所以我们试着来调用这个类的函数home
irb(main):008:0> controller.home => nil
- 为什么controller不能调用函数?这是因为
rails虽然是有ruby写成的,但是其运行方法是自成一体的.所以有些rails的类和ruby类用法一样, 而有些rails的类只不过是rails的魔法而已
A user class
- 一个简单的ruby class例子如下
class User attr_accessor :name, :email def initialize(attributes = {}) @name = attributes[:name] @email = attributes[:email] end def formatted_email "#{@name} <#{@email}>" end end
Chapter 05: Filling in the layout
Adding some structure
- 我们开始来让界面好看一点.
- 首先要更改的是application.html.erb, 这个是所有其他页面都会用的模板
Modified app/views/layouts/application.html.erb diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index c57717a..78407fb 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -7,8 +7,21 @@ <%= csrf_meta_tags %> </head> <body> + <header class="navbar navbar-fixed-top navbar-inverse"> + <div class="navbar-inner"> + <%= link_to "sample app", '#', id: "logo"%> + <nav> + <ul class="nav pull-right"> + <li><%= link_to "Home", '#' %></li> + <li><%= link_to "Help", '#' %></li> + <li><%= link_to "Sign in", '#' %></li> + </ul> + </nav> + </div> + </header> -<%= yield %> - + <div class="container"> + <%= yield %> + </div> </body> </html>
- 然后我们来更改其中一个页面home
Modified app/views/static_pages/home.html.erb diff --git a/app/views/static_pages/home.html.erb b/app/views/static_pages/home.html.erb index d4f4fe7..a33631c 100644 --- a/app/views/static_pages/home.html.erb +++ b/app/views/static_pages/home.html.erb @@ -1,6 +1,11 @@ -<h1>Sample App</h1> -<p> - This is the ome page for the - <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a> - sample application. -</p> +<div class="center hero-unit"> + <h1>Sample App</h1> + <h2> + This is the ome page for the + <a href="http://railstutorial.org/">Ruby on Rails Tutorial</a> + sample application. + </h2> + <%= link_to "Sinnup now!", '#', class: "btn btn-large btn-primay" %> +</div> + +<%= link_to image_tag("rails.png", alt: "Rails"), 'http://rubyonrails.org/' %>
- 然后界面就是这个样子了
Figure 5: index_with_layout
- 上面只是大体位置都有了,我们为header和div创建的class都还没安装呢.安装过程如下:
- 首先更改Gemfile, 这里引入的是bootstrap-saas, 之所以gem名字里面还有个sass,
是因为默认bootstrap是使用LESS的,而Rails是使用SASS的,所以这个gem还负责转换
less到sass
Modified Gemfile diff --git a/Gemfile b/Gemfile index 74a49e3..0b1f188 100644 --- a/Gemfile +++ b/Gemfile @@ -4,6 +4,8 @@ ruby '2.0.0' # Bundle edge Rails instead: gem 'rails', github: 'rails/rails' gem 'rails', '4.0.8' +gem 'bootstrap-sass', '2.3.2.0' +gem 'sprockets', '2.11.0' group :development, :test do gem 'sqlite3'
- 然后要加上一行,来让bootstrap-sass和rails的asset pipeline兼容
Modified config/application.rb diff --git a/config/application.rb b/config/application.rb index ffb130e..85b957a 100644 --- a/config/application.rb +++ b/config/application.rb @@ -24,5 +24,6 @@ module SampleApp # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}').to_s] # config.i18n.default_locale = :de + config.assets.precompile += %w(*.png *.jpg *.jpeg *.gif) end end
- 好了,下面是要把css引入到我们的网站,需要重新创建一个文件. 我们发现,我们只
是在assets/stylesheets文件夹下面加个文件,就立马可以加入到网站了? 原因
是assets下面的文件夹叫做asset pipeline,里面所有的文件都会自动导入
New app/assets/stylesheets/custom.css.scss diff --git a/app/assets/stylesheets/custom.css.scss b/app/assets/stylesheets/custom.css.scss new file mode 100644 index 0000000..7697a21 --- /dev/null +++ b/app/assets/stylesheets/custom.css.scss @@ -0,0 +1 @@ +@import "bootstrap"
- 首先更改Gemfile, 这里引入的是bootstrap-saas, 之所以gem名字里面还有个sass,
是因为默认bootstrap是使用LESS的,而Rails是使用SASS的,所以这个gem还负责转换
less到sass
- 结果就是如图了
Figure 6: index_with_css_layout
- 下面我们要添加一些scss代码(这些代码最后会转换成css)
@import "bootstrap"; /* universal */ html { overflow-y: scroll; } body { padding-top: 60px; } section { overflow: auto; } textarea { resize: vertical; } .center { text-align: center; } .center h1 { margin-bottom: 10px; }
- 结果就是下面了
Figure 7: index_with_css_change
- 除了第一行外,都是新加的,而且加的,也基本都是css代码,并不需要scss进行转换.关
于这些css,还真有许多要说的:
- css 在{}前面的这些符号有三种来源:
- HTML tag : 比如下面设置会让所有body最上面有60像素的间隔
body { padding-top: 60px; }
- id: #logo (后面会看到)
- class: .center: 下面的就会让所有设置center class的成员(比如div)都居中
对齐, 我们在home.html.erb里面,我们就全局定义了center class
.center { text-align: center; }
- HTML tag : 比如下面设置会让所有body最上面有60像素的间隔
- css 在{}前面的这些符号有三种来源:
- 然后我们继续改进,增加了文字的大小的style设置
Modified app/assets/stylesheets/custom.css.scss diff --git a/app/assets/stylesheets/custom.css.scss b/app/assets/stylesheets/custom.css.scss index 3963868..7348f92 100644 --- a/app/assets/stylesheets/custom.css.scss +++ b/app/assets/stylesheets/custom.css.scss @@ -1,5 +1,4 @@ - - +@import "bootstrap"; /* universal */ @@ -24,4 +23,31 @@ textarea { } .center h1 { margin-bottom: 10px; +} + +/* typography */ + +h1, h2, h3, h4, h5, h6 { + line-height: 1, +} + +h1 { + font-size: 3em; + letter-spacing: -2px; + margin-bottom: 30px; + text-align: center; +} + +h2 { + font-size: 1.2em; + letter-spacing: -1px; + margin-bottom: 30px; + text-align: center; + font-weight: normal; + color: #999; +} + +p { + font-size: 1.1em; + line-height: 1.7em;
- 然后就如图所示了
Figure 8: index_with_css_oneline
- 上面的class或者html tag都是对"一群"对象的更改, 下面我们来针对某个对象(具有
且只有某个id)来更改它的style
#logo { float: left; margin-right: 10px; font-size: 1.7em; color: #fff; text-transform: uppercase; letter-spacing: -1px; padding-top: 9px; font-weight: bold; line-height: 1; } #logo:hover { color: #fff; text-decoration: none; }
- 效果如下
Figure 9: index_with_css_big_title
Partials
- 某一个页面的独大,都不是程序想要看到的,所以,我们要把页面打开,分成几个部分:
- 首先在application.html.erb里面把老的代码去掉,加上新的"部分"标示
Modified app/views/layouts/application.html.erb diff --git a/app/views/layouts/application.html.erb b/app/views/layouts/application.html.erb index 78407fb..1a9bfef 100644 --- a/app/views/layouts/application.html.erb +++ b/app/views/layouts/application.html.erb @@ -7,21 +7,12 @@ <%= csrf_meta_tags %> </head> <body> - <header class="navbar navbar-fixed-top navbar-inverse"> - <div class="navbar-inner"> - <%= link_to "sample app", '#', id: "logo"%> - <nav> - <ul class="nav pull-right"> - <li><%= link_to "Home", '#' %></li> - <li><%= link_to "Help", '#' %></li> - <li><%= link_to "Sign in", '#' %></li> - </ul> - </nav> - </div> - </header> + <%= render 'layouts/header' %> <div class="container"> <%= yield %> + <%= render 'layouts/footer' %> + </div> </body> </html>
- 然后把header和footer分别创立,注意这里文件名都多了个下划线
New app/views/layouts/_footer.html.erb diff --git a/app/views/layouts/_footer.html.erb b/app/views/layouts/_footer.html.erb new file mode 100644 index 0000000..9cc6a71 --- /dev/null +++ b/app/views/layouts/_footer.html.erb @@ -0,0 +1,13 @@ +<footer class="footer"> + <small> + <a href="http://railstutorial.org/">Rails Tutorial</a> + by Michael Hartl + </small> + <nav> + <ul> + <li><%= link_to "About", '#' %></li> + <li><%= link_to "Contact", '#' %></li> + <li><a href="http://news.railstutorial.org/">News</a></li> + </ul> + </nav> +</footer> New app/views/layouts/_header.html.erb diff --git a/app/views/layouts/_header.html.erb b/app/views/layouts/_header.html.erb new file mode 100644 index 0000000..889ace7 --- /dev/null +++ b/app/views/layouts/_header.html.erb @@ -0,0 +1,14 @@ +<header class="navbar navbar-fixed-top navbar-inverse"> + <div class="navbar-inner"> + <div class="container"> + <%= link_to "sample app", '#', id: "logo" %> + <nav> + <ul class="nav pull-right"> + <li><%= link_to "Home", '#' %></li> + <li><%= link_to "Help", '#' %></li> + <li><%= link_to "Sign in", '#' %></li> + </ul> + </nav> + </div> + </div> +</header>
- 最后更改css,增加footer部分的样式
/* footer */ footer { margin-top: 45px; padding-top: 5px; border-top: 1px solid #eaeaea; color: #999; } footer a { color: #555; } footer a:hover { color: #222; } footer small { float: left; } footer ul { float: right; list-style: none; } footer ul li { float: left; margin-left: 10px; }
- 首先在application.html.erb里面把老的代码去掉,加上新的"部分"标示
- 最后就是这个样子的了
Figure 10: index_final
Sass and the asset pipeline
The asset pipeline
- asset pipeline是rails处理style的方法,其有三个点比较重要:
- asset directories
- manifest files
- preprocessor engine
Asset directories
- 在Rails 3.0以前, static assets 存放在public/下面, 也就是
public/stylesheets public/javascripts public/images
- 从3.1开始,asset directories改成了下面三个folder
Place Description app/assets assets specific to the present application lib/assets assets for libraries written by your dev tea vendor/assets assets from third-party vendors - 当然了,每个folder下面的subfolder里面的内容,也是pipeline的一部分. 比如,我 们的custom.css.scss就是在app/assets/stylesheets
Manifest files
- 所谓的manifest就是告诉rails如何导入css, 是把stylesheets里面的全部导入
application.css作为style呢,还是只使用application.css里面的内容
/* app/assets/stylesheets/application.css */ /* * This is a manifest file that'll be compiled into application.css, which will include all the files * listed below. * * Any CSS and SCSS file within this directory, lib/assets/stylesheets, vendor/assets/stylesheets, * or vendor/assets/stylesheets of plugins, if any, can be referenced here using a relative path. * * You're free to add application-wide styles to this file and they'll appear at the top of the * compiled file, but it's generally better to create a new file per style scope. * *= require_self *= require_tree . */
Preprocessorengines
- 预处理其实是你使用了什么扩展名就用什么方法预处理, 比如:
- erb
- coffee
- scss
Efficiency in production
- 这样分开的好处,是rails可以最后把所有的css, js各自合并成一个文件,在生产模 式中会提高效率