忘備録

日々の調べ物をまとめる。アウトプットする。基本自分用。

Rails技術者認定試験(Silver)対策めも

※ 自分用のメモなのであしからず

覚えることが多い。。。

1. Rails 概要

Railsコンポーネント

  • Active Record
  • ORM(Object Relational Mapper)
  • クラス <-> テーブル, 属性 <-> カラム のマッピングを行う
  • モデル間の関連を定義する
  • マイグレーション機能を提供する

  • Active Model

    • Modelクラスのインターフェースを提供
    • バリデーションやコールバックのサポート
  • Action Pack

    • Action Dispatch
      • リクエストの解析とルーティングを行う
    • Action Controller
      • Controllerのベースクラスを提供
  • Action View

    • ビューテンプレートのルックアップとレンダリングを行う
    • HTMLフォームを組み立てるためのビューヘルパを提供
  • Action Mailer

    • Eメールサービスを提供し、メールの送受信処理を行う
  • Active Support

  • Railties

    • Railsアプリの起動プロセス管理
    • Railsコマンドの提供
    • Railsジェネレータの提供

ツール及びライブラリ

フレームワークディレクトリ構成

root/
├ app/ ...
│ ├ assets/
│ │ ├ images/ ...画像ファイル
│ │ ├ javascripts/ ...jsファイル
│ │ └ sytlesheets/ ...cssファイル
│ ├ controllers/
│ │ └ conserns/ ...共通コードの置き場所。Railsのデフォルトのロー ドパスに含まれる
│ ├ helpers/
│ ├ mailers/
│ ├ models/
│ │ └ conserns/ ...共通コードの置き場所。Railsのデフォルトのロードパスに含まれる
│ └ views/ ...テンプレートファイル置き場
│ │ └ layouts/
│ │   └ application.html.erb ...アプリケーション共通レイアウトテンプレート
├ bin/
├ config/
│ ├ environments/ ...環境毎の設定ファイルを格納
│ ├ initilizers/ ...初期化処理を定義したファイルを格納
│ │ ├ backtrace_silencers.rb ... 例外バックトレースのフィルタ
│ │ ├ filter_parameter_logging.rb ...ロギングから除外するパラメータ情報の条件
│ │ ├ inflections.rb ...単数形/複数形の変換ルール
│ │ ├ mime_types.rb ...アプリケーションで使用できるコンテンツタイプ
│ │ ├ secret_token.rb ...クッキーを署名するためのトークン情報とかAPIのトークンを記載
│ │ │                    * v4.1以降は「config/secrets.yml」に移行
│ │ └ session_store.rb ...セッション保存のためのリソースファイル
│ ├ locales/ ...辞書ファイルを格納
│ ├ secrets.yml ... config/initilizers/secret_token.rb を参考
│ └ routes.rb ...ルーティグ設定ファイル
├ db/
│ ├ migrate/ ...マイグレーションファイルを格納
│ ├ schema.rb ...スキーマ情報を定義したファイル
│ └ seeds.rb 初期データを定義するファイル
├ lib/
├ log/ ...アプリケーションのログファイル
├ public/ ...静的なファイル、コンパイルされたアセットファイル
├ test/
│ ├ fixtures/ ...フィクスチャファイルを格納
│ ├ 
│ └ 
├ temp/
└ vendor/

railsコマンド

rails new

Usage:
  rails new APP_PATH [options]

Options:
  -r, [--ruby=PATH]                                      # Path to the Ruby binary of your choice
                                                         # Default: /Users/makoto/.rbenv/versions/2.2.5/bin/ruby
  -m, [--template=TEMPLATE]                              # Path to some application template (can be a filesystem path or URL)
      [--skip-gemfile], [--no-skip-gemfile]              # Don't create a Gemfile
  -B, [--skip-bundle], [--no-skip-bundle]                # Don't run bundle install
  -G, [--skip-git], [--no-skip-git]                      # Skip .gitignore file
      [--skip-keeps], [--no-skip-keeps]                  # Skip source control .keep files
  -O, [--skip-active-record], [--no-skip-active-record]  # Skip Active Record files
  -S, [--skip-sprockets], [--no-skip-sprockets]          # Skip Sprockets files
      [--skip-spring], [--no-skip-spring]                # Don't install Spring application preloader
  -d, [--database=DATABASE]                              # Preconfigure for selected database (options: mysql/oracle/postgresql/sqlite3/frontbase/ibm_db/sqlserver/jdbcmysql/jdbcsqlite3/jdbcpostgresql/jdbc)
                                                         # Default: sqlite3
  -j, [--javascript=JAVASCRIPT]                          # Preconfigure for selected JavaScript library
                                                         # Default: jquery
  -J, [--skip-javascript], [--no-skip-javascript]        # Skip JavaScript files
      [--dev], [--no-dev]                                # Setup the application with Gemfile pointing to your Rails checkout
      [--edge], [--no-edge]                              # Setup the application with Gemfile pointing to Rails repository
      [--skip-turbolinks], [--no-skip-turbolinks]        # Skip turbolinks gem
  -T, [--skip-test-unit], [--no-skip-test-unit]          # Skip Test::Unit files
      [--rc=RC]                                          # Path to file containing extra configuration options for rails command
      [--no-rc], [--no-no-rc]                            # Skip loading of extra configuration options from .railsrc file

Runtime options:
  -f, [--force]                    # Overwrite files that already exist
  -p, [--pretend], [--no-pretend]  # Run but do not make any changes
  -q, [--quiet], [--no-quiet]      # Suppress status output
  -s, [--skip], [--no-skip]        # Skip files that already exist

Rails options:
  -h, [--help], [--no-help]        # Show this help message and quit
  -v, [--version], [--no-version]  # Show Rails version number and quit

Description:
    The 'rails new' command creates a new Rails application with a default
    directory structure and configuration at the path you specify.

    You can specify extra command-line arguments to be used every time
    'rails new' runs in the .railsrc configuration file in your home directory.

    Note that the arguments specified in the .railsrc file don't affect the
    defaults values shown above in this help message.

Example:
    rails new ~/Code/Ruby/weblog

    This generates a skeletal Rails installation in ~/Code/Ruby/weblog.
    See the README in the newly created application to get going.

よく使うオプションは-d(データベースの指定)?

例) testAppを作成

$ rails new testApp
      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  bin/setup
      create  config
      create  config/routes.rb
      create  config/application.rb
      create  config/environment.rb
      create  config/secrets.yml
      create  config/environments
      create  config/environments/development.rb
      create  config/environments/production.rb
      create  config/environments/test.rb
      create  config/initializers
      create  config/initializers/assets.rb
      create  config/initializers/backtrace_silencers.rb
      create  config/initializers/cookies_serializer.rb
      create  config/initializers/filter_parameter_logging.rb
      create  config/initializers/inflections.rb
      create  config/initializers/mime_types.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 version metadata from https://rubygems.org/
Fetching dependency metadata from https://rubygems.org/
Resolving dependencies.....
Installing rake 11.1.2
Using i18n 0.7.0
Installing json 1.8.3 with native extensions
Installing minitest 5.9.0
Using thread_safe 0.3.5
Using builder 3.2.2
Using erubis 2.7.0
Using mini_portile2 2.1.0
Using pkg-config 1.1.7
Using rack 1.6.4
Using mime-types-data 3.2016.0521
Using arel 6.0.3
Installing debug_inspector 0.0.2 with native extensions
Using bundler 1.12.5
Installing byebug 9.0.5 with native extensions
Installing coffee-script-source 1.10.0
Installing execjs 2.7.0
Using thor 0.19.1
Using concurrent-ruby 1.0.2
Installing multi_json 1.12.1
Installing sass 3.4.22
Installing tilt 2.0.5
Installing spring 1.7.1
Installing sqlite3 1.3.11 with native extensions
Installing rdoc 4.2.2
Using tzinfo 1.2.2
Using nokogiri 1.6.8
Using rack-test 0.6.3
Using mime-types 3.1
Installing binding_of_caller 0.7.2 with native extensions
Installing coffee-script 2.4.1
Installing uglifier 3.0.0
Using sprockets 3.6.0
Installing sdoc 0.4.1
Using activesupport 4.2.6
Using loofah 2.0.3
Using mail 2.6.4
Using rails-deprecated_sanitizer 1.0.3
Using globalid 0.3.6
Using activemodel 4.2.6
Installing jbuilder 2.5.0
Using rails-html-sanitizer 1.0.3
Using rails-dom-testing 1.0.7
Using activejob 4.2.6
Using activerecord 4.2.6
Using actionview 4.2.6
Using actionpack 4.2.6
Using actionmailer 4.2.6
Using railties 4.2.6
Using sprockets-rails 3.0.4
Installing coffee-rails 4.1.1
Installing jquery-rails 4.1.1
Using rails 4.2.6
Installing sass-rails 5.0.4
Installing web-console 2.3.0
Installing turbolinks 2.5.3
Bundle complete! 12 Gemfile dependencies, 56 gems now installed.
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!
         run  bundle exec spring binstub --all
* bin/rake: spring inserted
* bin/rails: spring inserted

rails generate

モデルやコントローラなどのテンプレートを作成する。

$ rails generate -h
Running via Spring preloader in process 77105
Usage: rails generate GENERATOR [args] [options]

General options:
  -h, [--help]     # Print generator's options and usage
  -p, [--pretend]  # Run but do not make any changes
  -f, [--force]    # Overwrite files that already exist
  -s, [--skip]     # Skip files that already exist
  -q, [--quiet]    # Suppress status output

Please choose a generator below.

Rails:
  assets
  controller
  generator
  helper
  integration_test
  jbuilder
  job
  mailer
  migration
  model
  resource
  scaffold
  scaffold_controller
  task

Coffee:
  coffee:assets

Js:
  js:assets

TestUnit:
  test_unit:generator
  test_unit:job
  test_unit:plugin

例) コントローラの作成

$ rails generate controller test
Running via Spring preloader in process 77709
      create  app/controllers/test_controller.rb
      invoke  erb
      create    app/views/test
      invoke  test_unit
      create    test/controllers/test_controller_test.rb
      invoke  helper
      create    app/helpers/test_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/test.coffee
      invoke    scss
      create      app/assets/stylesheets/test.scss

rails destroy

rails generateで作成したテンプレートを削除する。

$ rails destroy -h
Running via Spring preloader in process 77128
Usage: rails destroy GENERATOR [args] [options]

General options:
  -h, [--help]     # Print generator's options and usage
  -p, [--pretend]  # Run but do not make any changes
  -f, [--force]    # Overwrite files that already exist
  -s, [--skip]     # Skip files that already exist
  -q, [--quiet]    # Suppress status output

Please choose a generator below.

Rails:
  assets
  controller
  generator
  helper
  integration_test
  jbuilder
  job
  mailer
  migration
  model
  resource
  scaffold
  scaffold_controller
  task

Coffee:
  coffee:assets

Js:
  js:assets

TestUnit:
  test_unit:generator
  test_unit:job
  test_unit:plugin

例) コントローラの削除

$ rails destroy controller test
Running via Spring preloader in process 77727
      remove  app/controllers/test_controller.rb
      invoke  erb
      remove    app/views/test
      invoke  test_unit
      remove    test/controllers/test_controller_test.rb
      invoke  helper
      remove    app/helpers/test_helper.rb
      invoke    test_unit
      invoke  assets
      invoke    coffee
      remove      app/assets/javascripts/test.coffee
      invoke    scss
      remove      app/assets/stylesheets/test.scss

rails server

Webサーバーを起動し、アプリケーションを実行する。
デフォルトのWebサーバはWEBrickで、そのほかにもmongrelthinが使える。

$ rails server -h
Usage: rails server [mongrel, thin etc] [options]
    -p, --port=port                  Runs Rails on the specified port.
                                     Default: 3000
    -b, --binding=IP                 Binds Rails to the specified IP.
                                     Default: localhost
    -c, --config=file                Uses a custom rackup configuration.
    -d, --daemon                     Runs server as a Daemon.
    -u, --debugger                   Enables the debugger.
    -e, --environment=name           Specifies the environment to run this server under (test/development/production).
                                     Default: development
    -P, --pid=pid                    Specifies the PID file.
                                     Default: tmp/pids/server.pid

    -h, --help                       Shows this help message.

rails console

Railsアプリケーションの環境上で、対話的にRubyコードを実行する(irbが起動する)。

$ rails console -h
Running via Spring preloader in process 77177
Usage: rails console [environment] [options]
    -s, --sandbox                    Rollback database modifications on exit.
    -e, --environment=name           Specifies the environment to run this console under (test/development/production).
                                     Default: development
        --debugger                   Enables the debugger.

rails dbconsole

使用しているデータベースのコマンドラインツールを起動する。

$ rails dbconsole -h
Usage: rails dbconsole [environment] [options]
    -p, --include-password           Automatically provide the password from database.yml
        --mode [MODE]                Automatically put the sqlite3 database in the specified mode (html, list, line, column).
        --header
    -h, --help                       Show this help message.
    -e, --environment=name           Specifies the environment to run this console under (test/development/production).
                                     Default: development

rails runner

Railsアプリケーションの環境上で、非対話的にRubyコードを実行する。

$ rails runner -h
Running via Spring preloader in process 77209
Usage: rails runner [options] [<'Some.ruby(code)'> | <filename.rb>]

    -e, --environment=name           Specifies the environment for the runner to operate under (test/development/production).
                                     Default: development

    -h, --help                       Show this help message.

Examples: 
    rails runner 'puts Rails.env'
        This runs the code `puts Rails.env` after loading the app

    rails runner path/to/filename.rb
        This runs the Ruby file located at `path/to/filename.rb` after loading the app

You can also use runner as a shebang line for your executables:
    -------------------------------------------------------------
    #!/usr/bin/env /Users/makoto/Documents/src/Rails/testApp/rails_runner runner

    Product.all.each { |p| p.price *= 2 ; p.save! }
    -------------------------------------------------------------

例)

# 'hoge'と出力
$ rails runner 'puts "hoge"'
Running via Spring preloader in process 77620
hoge
# 環境設定を出力
$ rails runner 'puts Rails.env'
Running via Spring preloader in process 77634
development

rake

RakeとはUNIXのMakeに相当するRubyのビルドツール。

$ rake -h
rake [-f rakefile] {options} targets...

Options are ...
        --backtrace=[OUT]            Enable full backtrace.  OUT can be stderr (default) or stdout.
        --comments                   Show commented tasks only
        --job-stats [LEVEL]          Display job statistics. LEVEL=history displays a complete job list
        --rules                      Trace the rules resolution.
        --suppress-backtrace PATTERN Suppress backtrace lines matching regexp PATTERN. Ignored if --trace is on.
    -A, --all                        Show all tasks, even uncommented ones (in combination with -T or -D)
    -B, --build-all                  Build all prerequisites, including those which are up-to-date.
    -D, --describe [PATTERN]         Describe the tasks (matching optional PATTERN), then exit.
    -e, --execute CODE               Execute some Ruby code and exit.
    -E, --execute-continue CODE      Execute some Ruby code, then continue with normal task processing.
    -f, --rakefile [FILENAME]        Use FILENAME as the rakefile to search for.
    -G, --no-system, --nosystem      Use standard project Rakefile search paths, ignore system wide rakefiles.
    -g, --system                     Using system wide (global) rakefiles (usually '~/.rake/*.rake').
    -I, --libdir LIBDIR              Include LIBDIR in the search path for required modules.
    -j, --jobs [NUMBER]              Specifies the maximum number of tasks to execute in parallel. (default is number of CPU cores + 4)
    -m, --multitask                  Treat all tasks as multitasks.
    -n, --dry-run                    Do a dry run without executing actions.
    -N, --no-search, --nosearch      Do not search parent directories for the Rakefile.
    -P, --prereqs                    Display the tasks and dependencies, then exit.
    -p, --execute-print CODE         Execute some Ruby code, print the result, then exit.
    -q, --quiet                      Do not log messages to standard output.
    -r, --require MODULE             Require MODULE before executing rakefile.
    -R, --rakelibdir RAKELIBDIR,     Auto-import any .rake files in RAKELIBDIR. (default is 'rakelib')
        --rakelib
    -s, --silent                     Like --quiet, but also suppresses the 'in directory' announcement.
    -t, --trace=[OUT]                Turn on invoke/execute tracing, enable full backtrace. OUT can be stderr (default) or stdout.
    -T, --tasks [PATTERN]            Display the tasks (matching optional PATTERN) with descriptions, then exit.
    -v, --verbose                    Log message to standard output.
    -V, --version                    Display the program version.
    -W, --where [PATTERN]            Describe the tasks (matching optional PATTERN), then exit.
    -X, --no-deprecation-warnings    Disable the deprecation warnings.
    -h, -H, --help                   Display this help message.

タスクの一覧表示

$ rake -T
rake about                              # List versions of all Rails frameworks and the environment
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 config.assets.precompile
rake cache_digests:dependencies         # Lookup first-level dependencies for TEMPLATE (like messages/show or comments/_comment.html)
rake cache_digests:nested_dependencies  # Lookup nested dependencies for TEMPLATE (like messages/show or comments/_comment.html)
rake db:create                          # Creates the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:crea...
rake db:drop                            # Drops the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:drop:a...
rake db:fixtures:load                   # Load fixtures into the current environment's database
rake db:migrate                         # Migrate the database (options: VERSION=x, VERBOSE=false, SCOPE=blog)
rake db:migrate:status                  # Display status of migrations
rake db:rollback                        # Rolls the schema back to the previous version (specify steps w/ STEP=n)
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 is portable against any DB supported by AR
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 initialize with the seed data (use db:reset to also drop t...
rake db:structure:dump                  # Dump the database structure to db/structure.sql
rake db:structure:load                  # Recreate the databases from the structure.sql file
rake db:version                         # Retrieves the current schema version number
rake doc:app                            # Generate docs for the app -- also available doc:rails, doc:guides (options: TEMPLATE=/rdoc-template....
rake log:clear                          # Truncates all *.log files in log/ to zero bytes (specify which logs with LOGS=test,development)
rake middleware                         # Prints out your Rack middleware stack
rake notes                              # Enumerate all annotations (use notes:optimize, :fixme, :todo for focus)
rake notes:custom                       # Enumerate a custom annotation, specify with ANNOTATION=CUSTOM
rake rails:template                     # Applies the template supplied by LOCATION=(/path/to/template) or URL
rake rails:update                       # Update configs and some other initially generated files (or use just update:configs or update:bin)
rake routes                             # Print out all defined routes in match order, with names
rake secret                             # Generate a cryptographically secure secret key (this is typically used to generate a secret for cook...
rake stats                              # Report code statistics (KLOCs, etc) from the application or engine
rake test                               # Runs all tests in test folder
rake test:all                           # Run tests quickly by merging all types and not resetting db
rake test:all:db                        # Run tests quickly, but also reset db
rake test:db                            # Run tests quickly, but also reset db
rake time:zones:all                     # Displays all time zones, also available: time:zones:us, time:zones:local -- filter with OFFSET param...
rake tmp:clear                          # Clear session, cache, and socket files from tmp/ (narrow w/ tmp:sessions:clear, tmp:cache:clear, tmp...
rake tmp:create                         # Creates tmp directories for sessions, cache, sockets, and pids

タスクの説明を表示

$ rake -D db:create
rake db:create
    Creates the database from DATABASE_URL or config/database.yml for the current RAILS_ENV (use db:create:all to create all databases in the config). Without RAILS_ENV it defaults to creating the development and test databases.

rake routes

routes.rbに定義されたルーティング設定に基づき、URIパターン、対応するコントローラとアクションの一覧を表示する。

$ rake routes
   Prefix Verb   URI Pattern               Controller#Action
    blogs GET    /blogs(.:format)          blogs#index
          POST   /blogs(.:format)          blogs#create
 new_blog GET    /blogs/new(.:format)      blogs#new
edit_blog GET    /blogs/:id/edit(.:format) blogs#edit
     blog GET    /blogs/:id(.:format)      blogs#show
          PATCH  /blogs/:id(.:format)      blogs#update
          PUT    /blogs/:id(.:format)      blogs#update
          DELETE /blogs/:id(.:format)      blogs#destroy

rake db:migrate

データベースのマイグレーション(移行)を実行する。

rake test

テストの実行。

引数なしでrakeを実行してもテストが実行される。

rake stats

アプリケーションのコードの統計を表示する。

$ rake stats
+----------------------+-------+-------+---------+---------+-----+-------+
| Name                 | Lines |   LOC | Classes | Methods | M/C | LOC/M |
+----------------------+-------+-------+---------+---------+-----+-------+
| Controllers          |     5 |     3 |       1 |       0 |   0 |     0 |
| Helpers              |     2 |     2 |       0 |       0 |   0 |     0 |
| Models               |     0 |     0 |       0 |       0 |   0 |     0 |
| Mailers              |     0 |     0 |       0 |       0 |   0 |     0 |
| Javascripts          |    18 |     0 |       0 |       0 |   0 |     0 |
| Libraries            |     0 |     0 |       0 |       0 |   0 |     0 |
| Controller tests     |     0 |     0 |       0 |       0 |   0 |     0 |
| Helper tests         |     0 |     0 |       0 |       0 |   0 |     0 |
| Model tests          |     0 |     0 |       0 |       0 |   0 |     0 |
| Mailer tests         |     0 |     0 |       0 |       0 |   0 |     0 |
| Integration tests    |     0 |     0 |       0 |       0 |   0 |     0 |
+----------------------+-------+-------+---------+---------+-----+-------+
| Total                |    25 |     5 |       1 |       0 |   0 |     0 |
+----------------------+-------+-------+---------+---------+-----+-------+
  Code LOC: 5     Test LOC: 0     Code to Test Ratio: 1:0.0

※ LOC: Line Of Code の略。コメントをのぞいた行数。

rake notes

ソースコード内のTODO、FIXME、OPTIMIZEコメントを抽出し、その行を表示する。

$ rake notes
app/controllers/application_controller.rb:
  * [5] [TODO] rake notesのテストのため1
  * [6] [FIXME] rake notesのテストのため2
  * [7] [OPTIMIZE] rake notesのテストのため3

Active Support

参考

Active Support Core Extensions — Ruby on Rails Guides

Active Support コア拡張機能 | Rails ガイド

html_safe

html_safe

html_escape (h)
harmful_string = '< > \' " & '
puts ERB::Util.html_escape(harmful_string) # &lt; &gt; &#39; &quot; &amp;
# hがhtml_escapeのエイリアスとして割り当てられているので、下記でもOK
# puts ERB::Util.h(harmful_string)
html_safe?

実際にエスケープされているかどうかは分からない

html_escapeによってエスケープされた文字はhtml_safe?でtrueが返る

blank?

レシーバがブランクの場合、trueを返す。

なお、レシーバが以下の場合、ブランクとみなされる。

  • nilとfalse
  • 空白文字 (whitespace) だけで構成された文字列 (※1, ※2)
  • 空欄の配列とハッシュ
  • その他、empty?メソッドに応答するオブジェクトはすべて空白として扱われる

※1 文字列を判定する述語として、Unicode対応した文字クラスである[:space:]が使用されています。そのため、たとえばU+2029 (段落区切り文字)は空白文字と判断される。

※2 数字については空白であるかどうかは判断されません。特に0および0.0は空白ではないことに注意。

{}.blank? # => true
[].blank? # => true
nil.blank? # => true
false.blank? # => true
# 数値はblank?でtrueを返すことはない
0.blank? # => false
present?

blank?の逆。

presence

presence? がtrueの場合、レシーバを返す。

falseの場合、nilを返す。

''.presence # => nil
'hoge'.presence # => hoge
duplicable?

レシーバがシングルトンインスタンスの場合、falseを返す。

"foo".duplicable? # => true
"".duplicable?    # => true
0.0.duplicable?  # => false
false.duplicable? # => false
try

レシーバがnilでない時だけ、引数に渡したメソッドを呼び出す

num = nil
num.try :to_s # => to_s は呼ばれない
num = 1
num.try :to_s # => "1"
in?

レシーバに引数が含まれていればtrueを返す。

引数に渡したオブジェクトがinclue?メソッドを持たない場合、ArgumentErrorが発生する

1.in?([1,2])        # true
"lo".in?("hello")   # true
25.in?(30..50)      # false
1.in?(1)            # ArgumentError
# 引数がハッシュの場合、キーが検索対象
'key'.in?({ 'key' => 'val' }) # true
'val'.in?({ 'key' => 'val' }) # false
delegate

他のオブジェクトのメソッドを移譲する。

class Greeter < ActiveRecord::Base
  def hello
    'hello'
  end

  def goodbye
    'goodbye'
  end
end

class Foo < ActiveRecord::Base
  belongs_to :greeter
  delegate :hello, to: :greeter
end

Foo.new.hello   # => "hello"
Foo.new.goodbye # => NoMethodError: undefined method `goodbye' for #<Foo:0x1af30c>
cattr_accessor

クラス変数のアクセサを定義する。

実体はmattr_accessorのエイリアス

stringの拡張

remove
str = "Hello World"
str.remove(/Hello /) # => "World"

# str自体は操作されない
remove!

removeの破壊的メソッド

squish

冒頭と末尾のホワイトスペースを除去し、連続したホワイトスペースを1つに減らす

" \n  foo\n\r \t bar \n".squish # => "foo bar"
squish!

squishの破壊的メソッド

truncate(truncate_at, options = {})
# デフォルトのomissionは''(空文字)
'123456789 123456789 123456789'.truncate(10)
# => "1234567..."
'1234 6789 123456789 123456789'.truncate(10, separator: ' ')
# => "1234..."
# omissionの指定例
'1234 6789 123456789 123456789'.truncate(10, omission: '')
# => "1234 6789 "
truncate_words(words_count, options = {})
'Once upon a time in a world far far away'.truncate_words(4)
# => "Once upon a time..."
options
  • omission
  • separator
starts_with?とends_with?

String#start_with?とString#end_with?のエイリアス(英語的に自然な三人称にしている)

at(position)
"hello".at(0)  # => "h"
"hello".at(4)  # => "o"
"hello".at(-1) # => "o"
"hello".at(10) # => nil
from(position)
"hello".from(0)  # => "hello"
"hello".from(2)  # => "llo"
"hello".from(-2) # => "lo"
"hello".from(10) # => nil
to(position)
"hello".to(0)  # => "h"
"hello".to(2)  # => "hel"
"hello".to(-2) # => "hell"
"hello".to(10) # => "hello"
first(limit = 1)
"hello".first(2) # => "he"
# to(-2)と等価
last(limit = 1)
"hello".last(2) # => "lo"
# from(-2) と等価
pluralize(count = nil, locale = :en)

複数形に変換にして返す。

"cap".pluralize
# => "caps"
"knife".pluralize
# => "knives"

定義は config/initializers/inflections.rb で可能

singularize(locale = :en)

pluralizeの逆

camelize(first_letter = :upper)

スネークケースをキャメルケースにして返す。

なお、 /::に置き換えられる。

'active_record'.camelize                # => "ActiveRecord"
'active_record'.camelize(:lower)        # => "activeRecord"
'active_record/errors'.camelize         # => "ActiveRecord::Errors"
'active_record/errors'.camelize(:lower) # => "activeRecord::Errors"

camelcaseエイリアス

underscore()

キャメルケースをスネークケースにして返す。

なお、::/に置き換えられる。

'ActiveModel'.underscore         # => "active_model"
'ActiveModel::Errors'.underscore # => "active_model/errors"
titleize()
dasherize(underscored_word)
demodulize()
'ActiveRecord::CoreExtensions::String::Inflections'.demodulize # => "Inflections"
'Inflections'.demodulize                                       # => "Inflections"
'::Inflections'.demodulize                                     # => "Inflections"
''.demodulize                                                  # => ''
deconstantize
parameterize
tableize

underscoreの次にpluralizeを実行したもの

classify

tableizeと逆の動作

humanize(options = {})
  • 引数に (英語の) 活用ルールを適用します(inflection)。
  • 冒頭にアンダースコアがある場合は削除します。
  • 末尾に"_id"がある場合は削除します。
  • アンダースコアが他にもある場合はスペースに置き換えます。
  • 略語を除いてすべての単語を小文字にします(downcase)。
  • 最初の単語だけ冒頭の文字を大文字にします(capitalize)。
'employee_salary'.humanize              # => "Employee salary"
'author_id'.humanize                    # => "Author"
'author_id'.humanize(capitalize: false) # => "author"
'_id'.humanize                          # => "Id"
to_date()
"1-1-2012".to_date   # => Sun, 01 Jan 2012
"01/01/2012".to_date # => Sun, 01 Jan 2012
"2012-12-13".to_date # => Thu, 13 Dec 2012
"12/13/2012".to_date # => ArgumentError: invalid date
to_datetime()
"1-1-2012".to_datetime            # => Sun, 01 Jan 2012 00:00:00 +0000
"01/01/2012 23:59:59".to_datetime # => Sun, 01 Jan 2012 23:59:59 +0000
"2012-12-13 12:50".to_datetime    # => Thu, 13 Dec 2012 12:50:00 +0000
"12/13/2012".to_datetime          # => ArgumentError: invalid date
to_time(form = :local)
"13-12-2012".to_time               # => 2012-12-13 00:00:00 +0100
"06:12".to_time                    # => 2012-12-13 06:12:00 +0100
"2012-12-13 06:12".to_time         # => 2012-12-13 06:12:00 +0100
"2012-12-13T06:12".to_time         # => 2012-12-13 06:12:00 +0100
"2012-12-13T06:12".to_time(:utc)   # => 2012-12-13 06:12:00 UTC
"2012-12-13T06:12".to_time(:jst)   # => 2012-12-13 06:12:00 +0900
"12/13/2012".to_time               # => ArgumentError: argument out of range

Numericの拡張

バイ
2.kilobytes   # => 2048
3.megabytes   # => 3145728
3.5.gigabytes # => 3758096384
-4.exabytes   # => -4611686018427387904

それぞれ単数系のエイリアスがある

1.megabyte # => 1048576
# レシーバが1より多くても問題無い
2.megabyte # => 2097152
Time
# Time.current.advance(months: 1) と等価
1.month.from_now
 
# Time.current.advance(years: 2) と等価
2.years.from_now
 
# Time.current.advance(months: 4, years: 5) と等価
(4.months + 5.years).from_now
to_s(*args)
Phone Numbers:
5551234.to_s(:phone)                                     # => "555-1234"
1235551234.to_s(:phone)                                  # => "123-555-1234"
1235551234.to_s(:phone, area_code: true)                 # => "(123) 555-1234"
1235551234.to_s(:phone, delimiter: ' ')                  # => "123 555 1234"
1235551234.to_s(:phone, area_code: true, extension: 555) # => "(123) 555-1234 x 555"
1235551234.to_s(:phone, country_code: 1)                 # => "+1-123-555-1234"
1235551234.to_s(:phone, country_code: 1, extension: 1343, delimiter: '.')
# => "+1.123.555.1234 x 1343"

Currency:
1234567890.50.to_s(:currency)                 # => "$1,234,567,890.50"
1234567890.506.to_s(:currency)                # => "$1,234,567,890.51"
1234567890.506.to_s(:currency, precision: 3)  # => "$1,234,567,890.506"
1234567890.506.to_s(:currency, locale: :fr)   # => "1 234 567 890,51 €"
-1234567890.50.to_s(:currency, negative_format: '(%u%n)')
# => "($1,234,567,890.50)"
1234567890.50.to_s(:currency, unit: '&pound;', separator: ',', delimiter: '')
# => "&pound;1234567890,50"
1234567890.50.to_s(:currency, unit: '&pound;', separator: ',', delimiter: '', format: '%n %u')
# => "1234567890,50 &pound;"

Percentage:
100.to_s(:percentage)                                  # => "100.000%"
100.to_s(:percentage, precision: 0)                    # => "100%"
1000.to_s(:percentage, delimiter: '.', separator: ',') # => "1.000,000%"
302.24398923423.to_s(:percentage, precision: 5)        # => "302.24399%"
1000.to_s(:percentage, locale: :fr)                    # => "1 000,000%"
100.to_s(:percentage, format: '%n  %')                 # => "100.000  %"

Delimited:
12345678.to_s(:delimited)                     # => "12,345,678"
12345678.05.to_s(:delimited)                  # => "12,345,678.05"
12345678.to_s(:delimited, delimiter: '.')     # => "12.345.678"
12345678.to_s(:delimited, delimiter: ',')     # => "12,345,678"
12345678.05.to_s(:delimited, separator: ' ')  # => "12,345,678 05"
12345678.05.to_s(:delimited, locale: :fr)     # => "12 345 678,05"
98765432.98.to_s(:delimited, delimiter: ' ', separator: ',')
# => "98 765 432,98"

Rounded:
111.2345.to_s(:rounded)                                      # => "111.235"
111.2345.to_s(:rounded, precision: 2)                        # => "111.23"
13.to_s(:rounded, precision: 5)                              # => "13.00000"
389.32314.to_s(:rounded, precision: 0)                       # => "389"
111.2345.to_s(:rounded, significant: true)                   # => "111"
111.2345.to_s(:rounded, precision: 1, significant: true)     # => "100"
13.to_s(:rounded, precision: 5, significant: true)           # => "13.000"
111.234.to_s(:rounded, locale: :fr)                          # => "111,234"
13.to_s(:rounded, precision: 5, significant: true, strip_insignificant_zeros: true)
# => "13"
389.32314.to_s(:rounded, precision: 4, significant: true)    # => "389.3"
1111.2345.to_s(:rounded, precision: 2, separator: ',', delimiter: '.')
# => "1.111,23"

# Human-friendly size in Bytes:
123.to_s(:human_size)                                   # => "123 Bytes"
1234.to_s(:human_size)                                  # => "1.21 KB"
12345.to_s(:human_size)                                 # => "12.1 KB"
1234567.to_s(:human_size)                               # => "1.18 MB"
1234567.to_s(:human_size, precision: 2)                 # => "1.2 MB"
1234567890.to_s(:human_size)                            # => "1.15 GB"
1234567890123.to_s(:human_size)                         # => "1.12 TB"
1234567890123.to_s(:human_size, precision: 5)           # => "1.1228 TB"
1234567890123456.to_s(:human_size)                      # => "1.1 PB"
1234567890123456789.to_s(:human_size)                   # => "1.07 483989.to_s(:human_size)                                # => "473 KB"
483989.to_s(:human_size, precision: 2)                  # => "470 KB"
1234567.to_s(:human_size, precision: 2, separator: ',') # => "1,2 MB"
524288000.to_s(:human_size)                             # => "500 MB"
524288000.to_s(:human_size, precision: 5)               # => "500 MB"

# Human-friendly format:
123.to_s(:human)                                       # => "123"
1234.to_s(:human)                                      # => "1.23 Thousand"
12345.to_s(:human)                                     # => "12.3 Thousand"
1234567.to_s(:human)                                   # => "1.23 Million"
1234567890.to_s(:human)                                # => "1.23 Billion"
1234567890123.to_s(:human)                             # => "1.23 Trillion"
1234567890123456.to_s(:human)                          # => "1.23 Quadrillion"
1234567890123456789.to_s(:human)                       # => "1230 Quadrillion"
489939.to_s(:human, precision: 2)                      # => "490 Thousand"
489939.to_s(:human, precision: 4)                      # => "489.9 Thousand"
1234567.to_s(:human, precision: 4,
                 significant: false)                   # => "1.2346 Million"
1234567.to_s(:human, precision: 1,
                 separator: ',',
                 significant: false)                   # => "1,2 Million"

Integerの拡張

ordinalize()
1.ordinalize    # => "1st"
2.ordinalize    # => "2nd"
53.ordinalize   # => "53rd"
2009.ordinalize # => "2009th"
-21.ordinalize  # => "-21st"
-134.ordinalize # => "-134th"

Enumerableの拡張

sum
[1, 2, 3].sum # => 6
(1..100).sum  # => 5050

[[1, 2], [2, 3], [3, 4]].sum    # => [1, 2, 2, 3, 3, 4]
%w(foo bar baz).sum             # => "foobarbaz"
{a: 1, b: 2, c: 3}.sum # => [:b, 2, :c, 3, :a, 1]

[].sum    # => 0

(1..5).sum {|n| n * 2 } # => 30
# [2, 4, 6, 8, 10].sum  # => 30
many?
# collection.size > 1
[1].many? # => false
[1, 2].many? => true
[1, 2, 3].many? {|n| n > 2} # => false
[1, 2, 3].many? {|n| n > 1} # => true
exclude?

include?の逆

without
["David", "Rafael", "Aaron", "Todd"].without("Aaron", "Todd") # => ["David", "Rafael"]

Arrayの拡張

Accessing
# to
%w(a b c d).to(2) # => %w(a b c)
[].to(7)          # => []

# from
%w(a b c d).from(2)  # => %w(c d)
%w(a b c d).from(10) # => []
[].from(0)           # => []

%w(a b c d).third # => c
%w(a b c d).fifth # => nil
append

Array#<<エイリアス

%w(a b c d).append('e')  # => %w(a b c d e)
[].append([1,2])         # => [[1,2]]
prepend

Array#unshiftエイリアス

%w(a b c d).prepend('e')  # => %w(e a b c d)
[].prepend(10)            # => [10]

その他

to_sentence
to_formatted_s
to_xml

設定

設定ファイルのパラメータ

設定ファイル | Railsドキュメント

分類 パラメータ名 概要 デフォルト
基本 cache_classes アプリケーションクラスのリロードをするか develpment = false
test = false
production = true
cache_store キャッシュの保存先
colorize_logging ログ情報をカラーリングするか true
autoload_paths オートロード対象となるパス
asset_host Assetヘルパーで付与するホスト名
log_level ログレベル develpment = :debug
test = :debug
production = :info
logger 使用するロガー
time_zone アプリケーションやActiveRecordで利用するタイムゾーン :local
i18n.default_locale 国際化対応で利用するデフォルトのロケール :en
Active Record active_record.logger 利用するロガー
active_record.schema_format スキーマダンプ形式 :ruby
active_record.timestamped_migrations マイグレーションファイルをタイムスタンプで管理するか true
Action Controller action_controller.default_charset デフォルトの文字コード utf-8
action_controller.logger 利用するロガー
action_controller.perform_caching キャッシュ機能を有効にするか develpment = false
test = false
production = true
session_store セッション格納するストア名
Action View action_view.default_form_builder デフォルトで利用されるフォームビルダ
action_view.logger 利用するロガー
action_view.field_error_proc エラー時に入力要素を括るタグ Proc.new do | html_tag, instance |
%Q(
#{html_tag}
).html_safe
end

2. Active Record

Modelの生成

rails generate model

$ rails generate model -h
Running via Spring preloader in process 80982
Usage:
  rails generate model NAME [field[:type][:index] field[:type][:index]] [options]

Options:
      [--skip-namespace], [--no-skip-namespace]  # Skip namespace (affects only isolated applications)
      [--force-plural], [--no-force-plural]      # Forces the use of the given model name
  -o, --orm=NAME                                 # Orm to be invoked
                                                 # Default: active_record

ActiveRecord options:
      [--migration], [--no-migration]    # Indicates when to generate migration
                                         # Default: true
      [--timestamps], [--no-timestamps]  # Indicates when to generate timestamps
                                         # Default: true
      [--parent=PARENT]                  # The parent class for the generated model
      [--indexes], [--no-indexes]        # Add indexes for references and belongs_to columns
                                         # Default: true
  -t, [--test-framework=NAME]            # Test framework to be invoked
                                         # Default: test_unit

TestUnit options:
      [--fixture], [--no-fixture]   # Indicates when to generate fixture
                                    # Default: true
  -r, [--fixture-replacement=NAME]  # Fixture replacement to be invoked

Runtime options:
  -f, [--force]                    # Overwrite files that already exist
  -p, [--pretend], [--no-pretend]  # Run but do not make any changes
  -q, [--quiet], [--no-quiet]      # Suppress status output
  -s, [--skip], [--no-skip]        # Skip files that already exist

Description:
    Stubs out a new model. Pass the model name, either CamelCased or
    under_scored, and an optional list of attribute pairs as arguments.

    Attribute pairs are field:type arguments specifying the
    model's attributes. Timestamps are added by default, so you don't have to
    specify them by hand as 'created_at:datetime updated_at:datetime'.

    As a special case, specifying 'password:digest' will generate a
    password_digest field of string type, and configure your generated model and
    tests for use with ActiveModel has_secure_password (assuming the default ORM
    and test framework are being used).

    You don't have to think up every attribute up front, but it helps to
    sketch out a few so you can start working with the model immediately.

    This generator invokes your configured ORM and test framework, which
    defaults to ActiveRecord and TestUnit.

    Finally, if --parent option is given, it's used as superclass of the
    created model. This allows you create Single Table Inheritance models.

    If you pass a namespaced model name (e.g. admin/account or Admin::Account)
    then the generator will create a module with a table_name_prefix method
    to prefix the model's table name with the module name (e.g. admin_accounts)

Available field types:

    Just after the field name you can specify a type like text or boolean.
    It will generate the column with the associated SQL type. For instance:

        `rails generate model post title:string body:text`

    will generate a title column with a varchar type and a body column with a text
    type. If no type is specified the string type will be used by default.
    You can use the following types:

        integer
        primary_key
        decimal
        float
        boolean
        binary
        string
        text
        date
        time
        datetime

    You can also consider `references` as a kind of type. For instance, if you run:

        `rails generate model photo title:string album:references`

    It will generate an `album_id` column. You should generate these kinds of fields when
    you will use a `belongs_to` association, for instance. `references` also supports
    polymorphism, you can enable polymorphism like this:

        `rails generate model product supplier:references{polymorphic}`

    For integer, string, text and binary fields, an integer in curly braces will
    be set as the limit:

        `rails generate model user pseudo:string{30}`

    For decimal, two integers separated by a comma in curly braces will be used
    for precision and scale:

        `rails generate model product 'price:decimal{10,2}'`

    You can add a `:uniq` or `:index` suffix for unique or standard indexes
    respectively:

        `rails generate model user pseudo:string:uniq`
        `rails generate model user pseudo:string:index`

    You can combine any single curly brace option with the index options:

        `rails generate model user username:string{30}:uniq`
        `rails generate model product supplier:references{polymorphic}:index`

    If you require a `password_digest` string column for use with
    has_secure_password, you should specify `password:digest`:

        `rails generate model user password:digest`

Examples:
    `rails generate model account`

        For ActiveRecord and TestUnit it creates:

            Model:      app/models/account.rb
            Test:       test/models/account_test.rb
            Fixtures:   test/fixtures/accounts.yml
            Migration:  db/migrate/XXX_create_accounts.rb

    `rails generate model post title:string body:text published:boolean`

        Creates a Post model with a string title, text body, and published flag.

    `rails generate model admin/account`

        For ActiveRecord and TestUnit it creates:

            Module:     app/models/admin.rb
            Model:      app/models/admin/account.rb
            Test:       test/models/admin/account_test.rb
            Fixtures:   test/fixtures/admin/accounts.yml
            Migration:  db/migrate/XXX_create_admin_accounts.rb

例)Itemモデルの作成

$ rails generate model Item name:string:uniq price:integer
Running via Spring preloader in process 80709
      invoke  active_record
      create    db/migrate/20160609184609_create_items.rb # マイグレーション
      create    app/models/item.rb # モデル
      invoke    test_unit
      create      test/models/item_test.rb #
      create      test/fixtures/items.yml # フィクスチャ

生成されるマイグレーションファイル

class CreateItems < ActiveRecord::Migration
  def change
    create_table :items do |t|
      t.string :name
      t.integer :price

      t.timestamps null: false
    end
    add_index :items, :name, unique: true
  end
end

フィールドのデータ型

データ型
integer{limit}
decimal{precision, scale}
float
test{limit}
string{limit}
date
time
datetime
boolean
binary{limit}
primary_key
references{polymorphic}

インデックス

インデックス 概要
uniq 一意性制約を追加
index インデックスを追加

Modelクラスについて

例)app/models/item.rb

class Item < ActiveRecord::Base
end

値の設定と保存

# モデルクラスのインスタンス化
Item item = Item.new
# 値の設定
item.name = 'apple'
item.price = 100
# 値をDBへ保存
item.save

CoC

種類 概要
モデルクラス キャメル(先頭大文字) Book
モデルクラス(ファイル名) スネークケース(小文字) book.rb
テーブル名* スネークケース(小文字) books
テストスクリプト クラス名(先頭小文字)に接尾辞「_test.rb」 book_test.rb

* 複数形になっていることに注意

Finder Methods

find(*args)

  • レコードが見つからないとActiveRecord::RecordNotFoundが発生
  • 引数が単一の場合戻り値はモデル、配列の場合はArrayオブジェクトが返る
Person.find(1)          # returns the object for ID = 1
Person.find("1")        # returns the object for ID = 1
Person.find("31-sarah") # returns the object for ID = 31
Person.find(1, 2, 6)    # returns an array for objects with IDs in (1, 2, 6)
Person.find([7, 17])    # returns an array for objects with IDs in (7, 17)
Person.find([1])        # returns an array for the object with ID = 1
Person.where("administrator = 1").order("created_on DESC").find(1)

find_by(arg, *args)

  • 単一のレコードを返す(複数ある場合、最初のレコードが返る)
  • レコードが見つからない場合、nilが返る
  • 戻り値はモデルのオブジェクト

find_by!(arg, *args)

  • レコードが見つからない場合、ActiveRecord::RecordNotFoundが発生する
  • 上記以外はfind_by(arg, *args)と同じ

find_by_sql(sql, binds = [], preparable: nil)

  • 戻り値はArrayのオブジェクト
  • レコードが見つからない場合、空のArray

first(limit = nil)

  • 引数がないまたはnilの場合、モデルのオブジェクトが返る
  • 引数があり、nilでない場合、Arrayのオブジェクトが返る
Book.select(:id, :title).first 
# Book Load (0.1ms)  SELECT  "books"."id", "books"."title" FROM "books"  ORDER BY "books"."id" ASC LIMIT 1
# #<Book:0x007ff04b22c250 id: 1, title: "AndroidエンジニアのためのモダンJava">
Book.select(:id, :title).first 3
# Book Load (0.1ms)  SELECT  "books"."id", "books"."title" FROM "books"  ORDER BY "books"."id" ASC LIMIT 3
# [#<Book:0x007ff04ca5a818 id: 1, title: "AndroidエンジニアのためのモダンJava">,
# <Book:0x007ff04ca5a688 id: 2, title: "JavaScriptライブラリ実践活用">,
# <Book:0x007ff04ca5a4f8 id: 3, title: "Ruby on Rails 4ポケットリファレンス">]

last(limit = nil)

exists?(conditions = :none)

  • Integer - Finds the record with this primary key.
  • String - Finds the record with a primary key corresponding to this string (such as '5').
  • Array - Finds the record that matches these find-style conditions (such as ['name LIKE ?', "%#{query}%"]).
  • Hash - Finds the record that matches these find-style conditions (such as {name: 'David'}).
  • false - Returns always false.
  • No args - Returns false if the table is empty, true otherwise.
Person.exists?(5)
Person.exists?('5')
Person.exists?(['name LIKE ?', "%#{query}%"])
Person.exists?(id: [1, 4, 8])
Person.exists?(name: 'David')
Person.exists?(false)
Person.exists?

Query Methods

Query Methodの戻り値はActiveRecord_Relationのオブジェクトとなる

where(opts = :chain, *rest)

### String
Client.where("orders_count = '2'")
# SELECT * from clients where orders_count = '2';

### Array
User.where(["name = ? and email = ?", "Joe", "joe@example.com"])
# SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';
User.where(["name = :name and email = :email", { name: "Joe", email: "joe@example.com" }])
# SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com';

### Hash
User.where({ name: "Joe", email: "joe@example.com" })
# SELECT * FROM users WHERE name = 'Joe' AND email = 'joe@example.com'
User.where({ name: ["Alice", "Bob"]})
# SELECT * FROM users WHERE name IN ('Alice', 'Bob')
User.where({ created_at: (Time.now.midnight - 1.day)..Time.now.midnight })
# SELECT * FROM users WHERE (created_at BETWEEN '2012-06-09 07:00:00.000000' AND '2012-06-10 07:00:00.000000')
Book.where id: 1...4
# SELECT "books".* FROM "books" WHERE ("books"."id" >= 1 AND "books"."id" < 4)
Book.where id: 1..4
# SELECT "books".* FROM "books" WHERE ("books"."id" BETWEEN 1 AND 4)

not(opts, *rest)

  • ActiveRecord::QueryMethods::WhereChainが呼び出すことができる
  • whereに引数を与えるとActiveRecord_Relationが返るので、NoMethodErrorが発生する
User.where.not("name = 'Jon'")
# SELECT * FROM users WHERE NOT (name = 'Jon')

User.where.not(["name = ?", "Jon"])
# SELECT * FROM users WHERE NOT (name = 'Jon')

User.where.not(name: "Jon")
# SELECT * FROM users WHERE name != 'Jon'

User.where.not(name: nil)
# SELECT * FROM users WHERE name IS NOT NULL

User.where.not(name: %w(Ko1 Nobu))
# SELECT * FROM users WHERE name NOT IN ('Ko1', 'Nobu')

User.where.not(name: "Jon", role: "admin")
# SELECT * FROM users WHERE name != 'Jon' AND role != 'admin'

エラー例

User.where(name: 'Jon').not
# => NoMethodErrorが発生
User.where.not(name: 'Jon')
# => OK

order(*args)

### Symbol
User.order(:name)
# SELECT "users".* FROM "users" ORDER BY "users"."name" ASC

### Hash
User.order(email: :desc)
# SELECT "users".* FROM "users" ORDER BY "users"."email" DESC

### Array
User.order(:name, email: :desc)
# SELECT "users".* FROM "users" ORDER BY "users"."name" ASC, "users"."email" DESC

### String
User.order('name')
# SELECT "users".* FROM "users" ORDER BY name
User.order('name DESC')
# SELECT "users".* FROM "users" ORDER BY name DESC
User.order('name DESC, email')
# SELECT "users".* FROM "users" ORDER BY name DESC, email

reorder(*args)

  • reorderを呼び出す前のorderが破棄される点に注意
User.order('email DESC').reorder('id ASC') # order('email DESC')は破棄される
# generated SQL has 'ORDER BY id ASC'
User.order('email DESC').order('id ASC') # order('email DESC')は有効
# generated SQL has 'ORDER BY email DESC, id ASC'

select(*fields)

指定した列だけ取得する

Model.select(:field)
# => [#<Model id: nil, field: "value">]

distinct

publishes = Book.select(:publish)
 #[#<Book:0x007ff04c7bbb48 id: nil, publish: "技術評論社">,
 #<Book:0x007ff04c7bb9e0 id: nil, publish: "技術評論社">,
 #<Book:0x007ff04c7bb878 id: nil, publish: "技術評論社">,
 #<Book:0x007ff04c7bb710 id: nil, publish: "日経BP社">,
 #<Book:0x007ff04c7bb5a8 id: nil, publish: "秀和システム">,
 #<Book:0x007ff04c7bb440 id: nil, publish: "翔泳社">,
 #<Book:0x007ff04c7bb288 id: nil, publish: "日経BP社">,
 #<Book:0x007ff04c7bb0f8 id: nil, publish: "翔泳社">,
 #<Book:0x007ff04c7baf90 id: nil, publish: "翔泳社">,
 #<Book:0x007ff04c7bae00 id: nil, publish: "ソシム">]
publishes.distinct
#[#<Book:0x007ff04c6aad08 id: nil, publish: "技術評論社">,
  #<Book:0x007ff04c6aab28 id: nil, publish: "日経BP社">,
  #<Book:0x007ff04c6aa920 id: nil, publish: "秀和システム">,
  #<Book:0x007ff04c6aa718 id: nil, publish: "翔泳社">,
  #<Book:0x007ff04c6aa560 id: nil, publish: "ソシム">]
publishes.distinct false
#[#<Book:0x007ff04bb25280 id: nil, publish: "技術評論社">,
  #<Book:0x007ff04bb24b50 id: nil, publish: "技術評論社">,
  #<Book:0x007ff04bb24538 id: nil, publish: "技術評論社">,
  #<Book:0x007ff04bb1fdf8 id: nil, publish: "日経BP社">,
  #<Book:0x007ff04bb1fa60 id: nil, publish: "秀和システム">,
  #<Book:0x007ff04bb1f498 id: nil, publish: "翔泳社">,
  #<Book:0x007ff04bb1f1f0 id: nil, publish: "日経BP社">,
  #<Book:0x007ff04bb1eed0 id: nil, publish: "翔泳社">,
  #<Book:0x007ff04bb1eb60 id: nil, publish: "翔泳社">,
  #<Book:0x007ff04bb1e638 id: nil, publish: "ソシム">]
Book.distinct.pluck :publish
# ["技術評論社", "日経BP社", "秀和システム", "翔泳社", "ソシム"]

limit(value)

offset(value)

Book.select(:id).limit(2).offset(5)
#  Book Load (0.1ms)  SELECT  "books"."id" FROM "books" LIMIT 2 OFFSET 5
# => [#<Book:0x007ff04b231020 id: 6>, #<Book:0x007ff04b230ee0 id: 7>]

group(*args)

books = Book.select('publish, AVG(price) AS avg_price').group(:publish)
# SELECT publish, AVG(price) AS avg_price FROM "books" GROUP BY "books"."publish"
books.each do |book| puts book.avg_price.to_s + "\t" + book.publish end
# 2625.0   ソシム
# 2996.0   技術評論社
# 2152.5   日経BP社
# 3150.0   秀和システム
# 3283.0   翔泳社

having(opts, *rest)

books = Book.select('publish, SUM(price) AS sum_price').group(:publish).having('sum_price < 8000')
books.each do |book|
  puts book.sum_price.to_s + '|' + book.publish
end
# 2625|ソシム
# 4305|日経BP社
# 3150|秀和システム

unscope(*args)

User.order('email DESC').unscope(:order)
# = User.all
User.where(name: "John", active: true).unscope(where: :name)
# = User.where(active: true)

none()

user = User.none
user.class
=> User::ActiveRecord_Relation
user.each
=> #<Enumerator: ...>

user = nil
user.each
# NoMethodError: undefined method `each' for nil:NilClass

その他

find_each(start: nil, finish: nil, batch_size: 1000, error_on_ignore: nil)

options
  • batch_size
  • start
  • finish
  • error_on_ignore

finisherror_on_ignoreはバージョン5.0から

# 2000件目から2000件ずつ処理
User.find_each(start: 2000, batch_size: 2000) do |user|
  # something to process
end

pluck(*column_names)

指定した列の配列を取得する

  • Person.pluck(:name)Person.all.map(&:name)に置き換えられる
  • 上記のmapを使う場合よりpluckを使う方が4倍ほど早いらしい (ソース)
Person.pluck(:name)
# SELECT people.name FROM people
# => ['David', 'Jeremy', 'Jose']

Person.pluck(:id, :name)
# SELECT people.id, people.name FROM people
# => [[1, 'David'], [2, 'Jeremy'], [3, 'Jose']]

Person.distinct.pluck(:role)
# SELECT DISTINCT role FROM people
# => ['admin', 'member', 'guest']

Person.where(age: 21).limit(5).pluck(:id)
# SELECT people.id FROM people WHERE people.age = 21 LIMIT 5
# => [2, 3]

Person.pluck('DATEDIFF(updated_at, created_at)')
# SELECT DATEDIFF(updated_at, created_at) FROM people
# => ['0', '27761', '173']

scope(name, body, &block)

class Book < ActiveRecord::Base
  scope :gihyo, -> { where(publish: '技術評論社') }
  scope :newer, -> { order(published: :desc) }
  scope :top10, -> { newer.limit(10) } # 既存のスコープ(ここではnewer)の利用可能
  scope :whats_new, ->(pub) {
    where(publish: pub).order(published: :desc).limit(5)
  }

default_scope(scope = nil)

unscopeメソッドでクリアできる。

  # TODO

count(column_name = nil)

レコードが存在しない場合、0を返す。

引数にカラム名が指定された場合、そのカラムがNULLでないレコードの件数を返す。

average(column_name)

レコードが存在しない場合、nilを返す。

avgs = Book.group(:publish).average(:price)
# SELECT AVG("books"."price") AS average_price, publish AS publish FROM "books" GROUP BY "books"."publish"
# Book.select('publish, AVG(price) AS avg_price').group(:publish)と同等

avgs.each do |publish, avg| 
  puts avg.to_s + '|' + publish
end

# 2625.0|ソシム
# 2996.0|技術評論社
# 2152.5|日経BP社
# 3150.0|秀和システム
# 3283.0|翔泳社

minimum(column_name)

レコードが存在しない場合、nilを返す。

maximum(column_name)

レコードが存在しない場合、nilを返す。

sum(column_name = nil, &block)

レコードが存在しない場合、0を返す

ids()

Person.ids # SELECT people.id FROM people
Person.joins(:companies).ids # SELECT people.id FROM people INNER JOIN companies ON companies.person_id = people.id

ActiveRecord::Base.connection.select_rows(sql, name = nil, binds = [])

引数に渡したクエリを実行して結果を配列で返す。

まず業務では使わないと思うが試験対策として。

ActiveRecord::Base.connection.select_rows('select id, title from books')
# select id, title from books
# => [[1, "AndroidエンジニアのためのモダンJava"],
#     [2, "JavaScriptライブラリ実践活用"],
#     [3, "Ruby on Rails 4ポケットリファレンス"],
#     [4, "書き込み式SQLのドリル"],
#     [5, "はじめてのAndroidアプリ開発"],
#     [6, "10日でおぼえるPHP入門教室"],
#     [7, "アプリを作ろう!HTML5入門"],
#     [8, "HTML5 開発ポケットリファレンス"],
#     [9, "独習Java サーバサイド編"],
#     [10, "Flash CSで学ぶ iPhone実践プログラミング"]]

select_values(arel, name = nil, binds = [])

引数に渡したクエリを実行して、最初のカラムの値を配列で返す。

まず業務では使わないと思うがs(ry

ActiveRecord::Base.connection.select_values('select id, title from books')
# select id, title from books
# => [1, 2, 3, 4, 5, 6, 7, 8, 9, 10]

ActiveRecord::Relation

update_all(updates)

destroy(id)

Book.destroy 2
# Book Load (0.2ms)  SELECT  "books".* FROM "books" WHERE "books"."id" = ? LIMIT 1  [["id", 2]]
#  (0.1ms)  SAVEPOINT active_record_1
# SQL (0.9ms)  DELETE FROM "authors_books" WHERE "authors_books"."book_id" = ?  [["book_id", 2]]
# SQL (0.6ms)  DELETE FROM "books" WHERE "books"."id" = ?  [["id", 2]]
#  (0.1ms)  RELEASE SAVEPOINT active_record_1

destroy()

book = Book.find 2
book.destroy

delete(id_or_array)

DELETE文の発行。

Modelで定義したassociationは考慮されない。

Book.delete 1
# DELETE FROM "books" WHERE "books"."id" = ?  [["id", 1]]

delete()

book = Book.find 2
book.delete

destroy_all(conditions = nil)

Person.where(age: 0..18).destroy_all

transaction

transaction(requires_new: nil, isolation: nil, joinable: true)

isolation levels
  • :read_uncommitted
  • :read_committed
  • :repeatable_read
  • :serializable

Optimistic Locking (楽観的ロック)

Optimistic Lockingを実装することで、競合を回避することができる。

以下の手順で実装すると競合を起こした際にActiveRecord::StaleObjectErrorが発生するようになる

  1. 同時実行制御したいテーブルにlock_versionカラムを追加する。なお、型はintegerで、デフォルト値は0
  2. ActiveRecord::Base.lock_optimisticallytrueにする (デフォルトはtrueとなっているが)
  3. HTMLのフォームにlock_versionパラメータを仕込む
  4. strong parameterにlock_versionを追加

例)

class Item < ActiveRecord::Migration
  def change
    create_table :itmes do |t|
      t.string :name
      t.integer :lock_version, default: 0
      t.time_stamps
    end
  end
end

なお、楽観的ロック用のカラム名locking_column=メソッドで変更できる

例)ロック用カラム名lock_itmeに変更

class Item < ActiveRecord::Migration
  locking_column = :lock_item

Validation

common options
  • on
  • if
  • unless
  • allow_nil
  • allow_blank
  • strict
  • message

presence

値が存在しているかどうか。

判断基準はObject#blank?による。

  validates :name, presence: true

absence

上記precenceの逆

length

options
  • minimum
  • maximum
  • is
  • within
  • in
  • too_long
  • too_short
  • allow_nil
  • allow_blank
  • wrong_length
  validates :first_name, length: { maximum: 30 }
  validates :last_name, length: { maximum: 30, message: "less than 30 if you don't mind" }
  validates :fax, length: { in: 7..32, allow_nil: true }
  validates :phone, length: { in: 7..32, allow_blank: true }
  validates :user_name, length: { within: 6..20, too_long: 'pick a shorter name', too_short: 'pick a longer name' }
  validates :zip_code, length: { minimum: 5, too_short: 'please enter at least 5 characters' }
  validates :smurf_leader, length: { is: 4, message: "papa is spelled with 4 characters... don't play me." }

numericality

options
  • only_integer
  • allow_nil
  • greater_than
  • greater_than_or_equal_to
  • equal_to
  • less_than
  • less_than_or_equal_to
  • other_than
  • odd
  • even

format

options
  • with
  • without
  • multiline
  validates :phone_number, format: { with: /\A\d{2,3}-\d{4}-\d{4}\z/ }

uniqueness

options
  • scope
  • conditions
  • case_sensitive
  • allow_nil
  • allow_blank
  • if
  • unless

acceptance

options
  • accept

confirmation

options
  • case_sensitive

inclusion

options
  • in

exclusion

options
  • in

Association

DB

設定

config/database.yml

# SQLite version 3.x
#   gem install sqlite3
#
#   Ensure the SQLite 3 gem is defined in your Gemfile
#   gem 'sqlite3'
#
default: &default
  adapter: sqlite3
  pool: 5
  timeout: 5000

development:
  <<: *default
  database: db/development.sqlite3

# Warning: The database defined as "test" will be erased and
# re-generated from your development database when you run "rake".
# Do not set this db to the same as development or production.
test:
  <<: *default
  database: db/test.sqlite3

production:
  <<: *default
  database: db/production.sqlite3

database.ymlのパラメータ

パラメータ名 概要
adapter 接続するデータベースの種類(sqlite3, mysql2, postgresqlなど)
database データベース名(SQLiteの場合データベースファイルのパス)
host ホスト名 / IPアドレス
port ポート番号
pool プール数
timeout タイムアウト時間
encoding 文字コード
username ユーザ名
password パスワード
socket ソケット(/tmp/mysql.sockなど)

マイグレーションによるテーブル作成

  • マイグレーションファイルはrails g migrationもしくはrails g modelコマンドで生成される
  • ファイル名の規約は{UTCタイムスタンプ}_{マイグレーション名}.rb
  • マイグレーション名とファイル名が一致していないとエラーが発生する
  • マイグレーションクラスはActiveRecord::Migrationを継承させる

例)20130914060842_create_books.rb

$ rails g model book isbn:string title:string price:integer publish:string published:date cd:boolean 
# ファイル名が20130914060842_create_books.rbの場合、
# クラス名はCreateBooksでなければならない。
class CreateBooks < ActiveRecord::Migration
  def change
    create_table :books do |t|
      t.string :isbn
      t.string :title
      t.integer :price
      t.string :publish
      t.date :published
      t.boolean :cd

      t.timestamps
    end
  end
end
rails g migration のマイグレーション
マイグレーション 概要
Create{TableName} テーブルの作成 rails g migration CreateUsers name:string
Add{ColumnName}To{TableName} カラムの追加
Remove{ColumnName}From{TableName} カラムの削除
CreateJoinTable{Table1Table2} habtm用テーブルの作成

Railsが自動生成するカラム

カラム名 概要
id 主キー(連番)
created_at レコードの新規作成日時(Active Recordが自動セット)
updated_at レコードの更新日時(Active Recordが自動セット)

フィクスチャによるテストデータの作成

$ rake db:fixtures:load FIXTURES=items

ActiveRecordのコールバック

作成時 更新時 削除時
before_validation before_validation before_destroy
after_validation after_validation around_destroy
before_save before_save after_destroy
around_save around_save
before_create before_update
around_create around_update
before_create after_update
after_save after_save

* 上から順に呼ばれる

app/models/item.rb

class Item < ActiveRecord::Base
  before_create { logger.debug 'called before_create' }
  after_create { logger.debug 'called after_create' }
end

データ作成処理

require 'item'

item = Item.new
item.name = 'fuga'
item.price = 200
item.save

log

   (0.1ms)  SAVEPOINT active_record_1
called before_create
  SQL (0.9ms)  INSERT INTO "items" ("name", "price", "created_at", "updated_at") VALUES (?, ?, ?, ?)  [["name", "fuga"], ["price", 200], ["created_at", "2016-06-09 19:59:15.390360"], ["updated_at", "2016-06-09 19:59:15.390360"]]
called after_create
   (0.1ms)  RELEASE SAVEPOINT active_record_1

初期データの投入

db/seeds.rb

# This file should contain all the record creation needed to seed the database with its default values.
# The data can then be loaded with the rake db:seed (or created alongside the db with db:setup).
#
# Examples:
#
#   cities = City.create([{ name: 'Chicago' }, { name: 'Copenhagen' }])
#   Mayor.create(name: 'Emanuel', city: cities.first)

Item.create(name: 'hoge', price: 100)
$ rake db:seed
sqlite> select * from items;
1|hoge|100|2016-06-09 19:57:38.782345|2016-06-09 19:57:38.782345

データベースを設定する

rake db:setupタスクは、データベースの作成、スキーマの読み込み、シードデータを使用したデータベースの初期化を実行します。

db:seedはテーブルのクリアは行われないため、既存のデータと一意性制約で引っかかることがある。

$ rake db:seed
rake aborted!
ActiveRecord::RecordNotUnique: SQLite3::ConstraintException: UNIQUE constraint failed: items.name: INSERT INTO "items" ("name", "price", "created_at", "updated_at") VALUES (?, ?, ?, ?)
/Users/makoto/Documents/src/Rails/TestApp/db/seeds.rb:9:in `<top (required)>'
SQLite3::ConstraintException: UNIQUE constraint failed: items.name
/Users/makoto/Documents/src/Rails/TestApp/db/seeds.rb:9:in `<top (required)>'
Tasks: TOP => db:seed
(See full trace by running task with --trace)

その際は、db:setupを行う。

$ rake db:setup
db/development.sqlite3 already exists
db/test.sqlite3 already exists
-- create_table("items", {:force=>:cascade})
   -> 0.0067s
-- add_index("items", ["name"], {:name=>"index_items_on_name", :unique=>true})
   -> 0.0241s
-- initialize_schema_migrations_table()
   -> 0.0083s
-- create_table("items", {:force=>:cascade})
   -> 0.0115s
-- add_index("items", ["name"], {:name=>"index_items_on_name", :unique=>true})
   -> 0.0022s
-- initialize_schema_migrations_table()
   -> 0.0003s

データベースをリセットする

rake db:resetタスクは、データベースをdropして再度設定します。このタスクは、rake db:drop db:setupと同等です。

バリューオブジェクト

3. RoutingとController

リソースフルのルーティング

resourcesメソッドによるルーティング設定

resources :users
$ rake routes
    Prefix Verb   URI Pattern               Controller#Action
     users GET    /users(.:format)          users#index
           POST   /users(.:format)          users#create
  new_user GET    /users/new(.:format)      users#new
 edit_user GET    /users/:id/edit(.:format) users#edit
      user GET    /users/:id(.:format)      users#show
           PATCH  /users/:id(.:format)      users#update
           PUT    /users/:id(.:format)      users#update
           DELETE /users/:id(.:format)      users#destroy

resourceメソッドによるルーティング設定

resource :config
$ rake routes
     Prefix Verb   URI Pattern            Controller#Action
     config POST   /config(.:format)      configs#create
 new_config GET    /config/new(.:format)  configs#new
edit_config GET    /config/edit(.:format) configs#edit
            GET    /config(.:format)      configs#show
            PATCH  /config(.:format)      configs#update
            PUT    /config(.:format)      configs#update
            DELETE /config(.:format)      configs#destroy

only / except

デフォルトで生成されるアクションの一部を無効化する。

必要のないルーティングを残すとパフォーマンスが低下するため、必要ないものが分かっていれば設定すること。

# show, destroyアクション以外を生成する
resources :users, except: [ :show, :destroy ]
# index, new, createアクションだけを生成する
resources :users, only: [ :index, :new, :create]

constraints

ルートパラメータに制限を設ける。

# idに指定できるパラメータを2桁の数字に制限する。
resources :users, constraints: { id: /[0-9]{1,2}/ }

format

  • trueにするとフォーマットの指定が必須となる
  • 指定しないとActionController::RoutingErrorが発生する
resources :users, format: true
   Prefix Verb   URI Pattern             Controller#Action
    users GET    /users.:format          users#index {:format=>/.+/}
          POST   /users.:format          users#create {:format=>/.+/}
 new_user GET    /users/new.:format      users#new {:format=>/.+/}
edit_user GET    /users/:id/edit.:format users#edit {:format=>/.+/}
     user GET    /users/:id.:format      users#show {:format=>/.+/}
          PATCH  /users/:id.:format      users#update {:format=>/.+/}
          PUT    /users/:id.:format      users#update {:format=>/.+/}
          DELETE /users/:id.:format      users#destroy {:format=>/.+/}
  • falseにするとフォーマットができなくなる
  • 指定するとActionController::RoutingErrorが発生する
resources :users, format: false
   Prefix Verb   URI Pattern     Controller#Action
    users GET    /users          users#index
          POST   /users          users#create
 new_user GET    /users/new      users#new
edit_user GET    /users/:id/edit users#edit
     user GET    /users/:id      users#show
          PATCH  /users/:id      users#update
          PUT    /users/:id      users#update
          DELETE /users/:id      users#destroy

controllers

デフォルトと異なるコントローラを指定する。

# デフォルトの場合、UsersControllerとマッピングされるが、
# 下記の場合、MembersControllerとマッピングされる。
resources :users, controllers: :members
$ rake routes
   Prefix Verb   URI Pattern               Controller#Action
    users GET    /users(.:format)          users#index {:controllers=>:members}
          POST   /users(.:format)          users#create {:controllers=>:members}
 new_user GET    /users/new(.:format)      users#new {:controllers=>:members}
edit_user GET    /users/:id/edit(.:format) users#edit {:controllers=>:members}
     user GET    /users/:id(.:format)      users#show {:controllers=>:members}
          PATCH  /users/:id(.:format)      users#update {:controllers=>:members}
          PUT    /users/:id(.:format)      users#update {:controllers=>:members}
          DELETE /users/:id(.:format)      users#destroy {:controllers=>:members}

as

デフォルトと異なるUrlヘルパーを生成する。

# デフォルトの場合、users_pathやuser_pathが生成されるが、
# 下記の場合、members_pathやmember_pathが生成される。
resources :users, as: :members
$ rake routes
     Prefix Verb   URI Pattern               Controller#Action
    members GET    /users(.:format)          users#index
            POST   /users(.:format)          users#create
 new_member GET    /users/new(.:format)      users#new
edit_member GET    /users/:id/edit(.:format) users#edit
     member GET    /users/:id(.:format)      users#show
            PATCH  /users/:id(.:format)      users#update
            PUT    /users/:id(.:format)      users#update
            DELETE /users/:id(.:format)      users#destroy

collection / member ブロック

任意のアクションを追加する。

  resources :reviews do
    collection do
      get :unapproval
    end
    member do
      get :draft
    end
  end
$ rake routes
            Prefix Verb   URI Pattern                   Controller#Action
unapproval_reviews GET    /reviews/unapproval(.:format) reviews#unapproval
      draft_review GET    /reviews/:id/draft(.:format)  reviews#draft
           reviews GET    /reviews(.:format)            reviews#index
                   POST   /reviews(.:format)            reviews#create
        new_review GET    /reviews/new(.:format)        reviews#new
       edit_review GET    /reviews/:id/edit(.:format)   reviews#edit
            review GET    /reviews/:id(.:format)        reviews#show
                   PATCH  /reviews/:id(.:format)        reviews#update
                   PUT    /reviews/:id(.:format)        reviews#update
                   DELETE /reviews/:id(.:format)        reviews#destroy

なお、ブロック配下のアクションが1つの場合、onパラメータを使って簡易に記述できる

  resources :reviews do
    get :unapproval, on: :collection
    get :draft, on: :member
  end

shallow

namespace

コントローラをモジュール分割する

  namespace :admin do
    resources :book
  end
admin_book_index GET    /admin/book(.:format)          admin/book#index
                 POST   /admin/book(.:format)          admin/book#create
  new_admin_book GET    /admin/book/new(.:format)      admin/book#new
 edit_admin_book GET    /admin/book/:id/edit(.:format) admin/book#edit
      admin_book GET    /admin/book/:id(.:format)      admin/book#show
                 PATCH  /admin/book/:id(.:format)      admin/book#update
                 PUT    /admin/book/:id(.:format)      admin/book#update
                 DELETE /admin/book/:id(.:format)      admin/book#destroy

scope

コントローラをモジュール分割する(URLに影響しない)

  scope module: :admin do
    resources :book
  end
    Prefix Verb   URI Pattern              Controller#Action
book_index GET    /book(.:format)          admin/book#index
           POST   /book(.:format)          admin/book#create
  new_book GET    /book/new(.:format)      admin/book#new
 edit_book GET    /book/:id/edit(.:format) admin/book#edit
      book GET    /book/:id(.:format)      admin/book#show
           PATCH  /book/:id(.:format)      admin/book#update
           PUT    /book/:id(.:format)      admin/book#update
           DELETE /book/:id(.:format)      admin/book#destroy

URLにプレフィクスをつける

  scope :admin do
    resources :book
  end
    Prefix Verb   URI Pattern                    Controller#Action
book_index GET    /admin/book(.:format)          book#index
           POST   /admin/book(.:format)          book#create
  new_book GET    /admin/book/new(.:format)      book#new
 edit_book GET    /admin/book/:id/edit(.:format) book#edit
      book GET    /admin/book/:id(.:format)      book#show
           PATCH  /admin/book/:id(.:format)      book#update
           PUT    /admin/book/:id(.:format)      book#update
           DELETE /admin/book/:id(.:format)      book#destroy

concern

ルート定義の再利用。

  concern :addtional do
    get :unapproval, on: :collection
    get :darft, on: :member
  end

  resources :reviews, concerns: :addtional
  resources :users, concerns: :addtional
$ rake routes
            Prefix Verb   URI Pattern                   Controller#Action
unapproval_reviews GET    /reviews/unapproval(.:format) reviews#unapproval
      darft_review GET    /reviews/:id/darft(.:format)  reviews#darft
           reviews GET    /reviews(.:format)            reviews#index
                   POST   /reviews(.:format)            reviews#create
        new_review GET    /reviews/new(.:format)        reviews#new
       edit_review GET    /reviews/:id/edit(.:format)   reviews#edit
            review GET    /reviews/:id(.:format)        reviews#show
                   PATCH  /reviews/:id(.:format)        reviews#update
                   PUT    /reviews/:id(.:format)        reviews#update
                   DELETE /reviews/:id(.:format)        reviews#destroy
  unapproval_users GET    /users/unapproval(.:format)   users#unapproval
        darft_user GET    /users/:id/darft(.:format)    users#darft
             users GET    /users(.:format)              users#index
                   POST   /users(.:format)              users#create
          new_user GET    /users/new(.:format)          users#new
         edit_user GET    /users/:id/edit(.:format)     users#edit
              user GET    /users/:id(.:format)          users#show
                   PATCH  /users/:id(.:format)          users#update
                   PUT    /users/:id(.:format)          users#update
                   DELETE /users/:id(.:format)          users#destroy

ノンリソースフル

matchメソッドの引数のパターン

# 以下3つは同じルーティングになる
match 'photos/:id' => 'photos#show', via: :get
match 'photos/:id', to: 'photos#show', via: :get
match 'photos/:id', controller: 'photos', action: 'show', via: :get
# また、viaオプションの代わりにget(post, put, patch, delete)メソッドに置き換えることができる
get 'photos/:id' => 'photos#show'
get 'photos/:id', to: 'photos#show'
get 'photos/:id', controller: 'photos', action: 'show'
$ rake routes
Prefix Verb URI Pattern           Controller#Action
       GET  /photos/:id(.:format) photos#show

URLパラメータ

# :controllerと:actionはそれぞれコントローラ名、アクション名とマッピングされる
match ':controller/:action/:id', via: [:get, :post]

アクション、コントローラの指定の省略

# URIパターンが「/action/method」の形であれば「=> 'action#method'」を省略できる
get '/home/about'
$ rake routes
    Prefix Verb URI Pattern           Controller#Action
home_about GET  /home/about(.:format) home#about

ワイルドカード

# urlのパターンにはワイルドカード指定ができる
get 'songs/*category/:title', to: 'songs#show'
# 'songs/rock/classic/stairway-to-heaven' sets
#  params[:category] = 'rock/classic'
#  params[:title] = 'stairway-to-heaven'
$ rake routes
Prefix Verb URI Pattern                       Controller#Action
       GET  /songs/*category/:title(.:format) songs#show

rootメソッド

root to: 'pages#main'
# toは省略可能
# root 'pages#main'

redirectメソッド

redirctメソッドを利用すると指定したURLへリダイレクトさせることができる

get 'home/about' => redirect('/home2/about/')
    Prefix Verb URI Pattern           Controller#Action
home_about GET  /home/about(.:format) redirect(301, /home2/about/)
# rootメソッドと組み合わせる場合、toの省略はできない
root to: redirect('/admin/uers')
Prefix Verb URI Pattern Controller#Action
  root GET  /           redirect(301, /admin/uers)

その他

URLに日本語を含めることができる

get 'ほげ/index' => 'hoge#index'
Prefix Verb URI Pattern                         Controller#Action
       GET  /%E3%81%BB%E3%81%92/index(.:format) hoge#index

ルーティングの優先順位

  • 上から順に評価される
    • よって、スコープの広いもの(rootとか)は後のほうに定義すべき
# リソースフル
resources :users
# 「/about」でアクセスするとHomeController#aboutが呼ばれる
get '/about' => 'home#about'
# URIパターンが「/action/method」の形であれば「=> 'action#method'」を省略できる
get '/home/about'
# 「/hoge」でアクセスするとPagesController#hogeが呼ばれる
get '/:action', controller: :pages
# matchメソッド。「/hoge」でアクセスすると、HogeController#indexが呼ばれる
match '/hoge', to: 'hoge#index' , via: :get 
# rootメソッドによるルーティング
root 'home#index'

URLやパスに関するメソッド

resources :users
root 'users#index'

xxx_url

root_url
# => "http://localhost:3000/"

users_url
# => "http://localhost:3000/users"
users_url :json
# => "http://localhost:3000/users.json"

user_url 1
# => "http://localhost:3000/users/1"
user_url 1, :json
# => "http://localhost:3000/users/1.json"

# パラメータが指定されているルーティングに対し、パラメータを渡さないとActionController::UrlGenerationErrorが発生する
user_url
# ActionController::UrlGenerationError: No route matches {:action=>"show", :controller=>"users"} missing required keys: [:id]

xxx_path

root_path
# => "/"

users_path
# => "/users"
edit_user_path 1
#=> "/users/1/edit"

edit_user_path
# ActionController::UrlGenerationError: No route matches {:action=>"edit", :controller=>"users"} missing required keys: [:id]

url_for(options = nil)

options
  • only_path - If true, the relative url is returned. Defaults to false.
  • protocol - The protocol to connect to. Defaults to 'http'.
  • host - Specifies the host the link should be targeted at. If :only_path is false, this option must be provided either explicitly, or via default_url_options.
  • subdomain - Specifies the subdomain of the link, using the tld_length to split the subdomain from the host. If false, removes all subdomains from the host part of the link.
  • domain - Specifies the domain of the link, using the tld_length to split the domain from the host.
  • tld_length - Number of labels the TLD id composed of, only used if :subdomain or :domain are supplied. Defaults to ActionDispatch::Http::URL.tld_length, which in turn defaults to 1.
  • port - Optionally specify the port to connect to.
  • anchor - An anchor name to be appended to the path.
  • trailing_slash - If true, adds a trailing slash, as in “/archive/2009/”
  • script_name - Specifies application path relative to domain root. If provided, prepends application path.
url_for controller: 'tasks', action: 'testing', host: 'somehost.org', port: '8080'
# => 'http://somehost.org:8080/tasks/testing'
url_for controller: 'tasks', action: 'testing', host: 'somehost.org', anchor: 'ok', only_path: true
# => '/tasks/testing#ok'
url_for controller: 'tasks', action: 'testing', trailing_slash: true
# => 'http://somehost.org/tasks/testing/'
url_for controller: 'tasks', action: 'testing', host: 'somehost.org', number: '33'
# => 'http://somehost.org/tasks/testing?number=33'
url_for controller: 'tasks', action: 'testing', host: 'somehost.org', script_name: "/myapp"
# => 'http://somehost.org/myapp/tasks/testing'
url_for controller: 'tasks', action: 'testing', host: 'somehost.org', script_name: "/myapp", only_path: true
# => '/myapp/tasks/testing'
url_for(only_path: true)                        # => '/users/1'
url_for(only_path: true, action: 'edit')        # => '/users/1/edit'
url_for(only_path: true, action: 'edit', id: 2) # => '/users/2/edit'

scaffold

$ rails g scaffold Note title:string content:text
Running via Spring preloader in process 84352
      invoke  active_record
      create    db/migrate/20160610073826_create_notes.rb
      create    app/models/note.rb
      invoke    test_unit
      create      test/models/note_test.rb
      create      test/fixtures/notes.yml
      invoke  resource_route
       route    resources :notes
      invoke  scaffold_controller
      create    app/controllers/notes_controller.rb
      invoke    erb
      create      app/views/notes
      create      app/views/notes/index.html.erb
      create      app/views/notes/edit.html.erb
      create      app/views/notes/show.html.erb
      create      app/views/notes/new.html.erb
      create      app/views/notes/_form.html.erb
      invoke    test_unit
      create      test/controllers/notes_controller_test.rb
      invoke    helper
      create      app/helpers/notes_helper.rb
      invoke      test_unit
      invoke    jbuilder
      create      app/views/notes/index.json.jbuilder
      create      app/views/notes/show.json.jbuilder
      invoke  assets
      invoke    coffee
      create      app/assets/javascripts/notes.coffee
      invoke    scss
      create      app/assets/stylesheets/notes.scss
      invoke  scss
      create    app/assets/stylesheets/scaffolds.scss
$ 
$ 
$ 
$ git status
On branch master
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:   config/routes.rb

Untracked files:
  (use "git add <file>..." to include in what will be committed)

    app/assets/javascripts/notes.coffee
    app/assets/stylesheets/notes.scss
    app/assets/stylesheets/scaffolds.scss
    app/controllers/notes_controller.rb
    app/helpers/notes_helper.rb
    app/models/note.rb
    app/views/notes/
    db/migrate/
    test/controllers/notes_controller_test.rb
    test/fixtures/notes.yml
    test/models/note_test.rb

コントローラ

CoC (Convention over Configuration)

種類 概要
コントローラクラス キャメル(先頭大文字) HelloController
コントローラクラス(ファイル) スネークケース(小文字) hello_controller.rb
ヘルパーファイル名 コントローラ名に接尾辞「_helper.rb」 hello_helper.rb
テストスクリプト コントローラ名に接尾辞「_controller_test.rb」 hello_controller_test.rb

rails generate controller

$ rails g controller -h
Running via Spring preloader in process 14930
Usage:
  rails generate controller NAME [action action] [options]

Options:
      [--skip-namespace], [--no-skip-namespace]  # Skip namespace (affects only isolated applications)
      [--skip-routes], [--no-skip-routes]        # Don't add routes to config/routes.rb.
  -e, [--template-engine=NAME]                   # Template engine to be invoked
                                                 # Default: erb
  -t, [--test-framework=NAME]                    # Test framework to be invoked
                                                 # Default: test_unit
      [--helper]                                 # Indicates when to generate helper
                                                 # Default: true
      [--assets]                                 # Indicates when to generate assets
                                                 # Default: true

Runtime options:
  -f, [--force]                    # Overwrite files that already exist
  -p, [--pretend], [--no-pretend]  # Run but do not make any changes
  -q, [--quiet], [--no-quiet]      # Suppress status output
  -s, [--skip], [--no-skip]        # Skip files that already exist

Description:
    Stubs out a new controller and its views. Pass the controller name, either
    CamelCased or under_scored, and a list of views as arguments.

    To create a controller within a module, specify the controller name as a
    path like 'parent_module/controller_name'.

    This generates a controller class in app/controllers and invokes helper,
    template engine, assets, and test framework generators.

Example:
    `rails generate controller CreditCards open debit credit close`

    CreditCards controller with URLs like /credit_cards/debit.
        Controller: app/controllers/credit_cards_controller.rb
        Test:       test/controllers/credit_cards_controller_test.rb
        Views:      app/views/credit_cards/debit.html.erb [...]
        Helper:     app/helpers/credit_cards_helper.rb

redirect_to(options = {}, response_status = {})

redirect_toメソッドに渡せる引数やオプションはurl_forメソッドと同じ

# URL
redirect_to 'http://example.com'
# 同一コントローラ内のアクション
redirect_to action: :index
# 他コントローラのアクション
redirect_to controller: :user, action: :index
# viewヘルパー
users_path
# 前の画面
redirect_to :back

render

options
  • action: 違うアクションに対応するテンプレートファイルを指定する
  • template: 異なるコントローラのテンプレートそ指定する
  • partial: 部分テンプレートを出力する
  • file: 絶対パスでテンプレートファイルを指定する
  • text: 値をそのまま出力する
  • plain: 値をそのまま出力する。textとの違いはcontent-typeがtext/plainとなること
  • html: htmlエスケープされて出力される
  • inline: erbと解釈して
  • body: TODO
class BooksController < ApplicationController
 
 def create
   render action: :index
   # render :index # actionは省略可能
   render template: 'others/index'
   # render 'others/index' # 指定した値の途中に「/」がある場合、templateは省略できる
   render file: '/hoge/index.html.erb'
   # render '/hoge/index.html.erb' # 指定した値が「/」で始まっている場合、fileは省略できる

respond_to

  respond_to do |format|
    format.xxx { 
      #something to process
    }
  end

指定されたフォーマットの処理がない場合、ActionController::UnKnownFormatが発生する

head

  def ok
    head :ok
  end

  def created
    head :created
  end

  def moved
    head :moved_permanently
  end

  def found
    head :found
  end

  def see_other
    head :see_other
  end

  def unauthorized
    head :unauthorized
  end

  def forbidden
    head :forbidden
  end

  def not_found
    head :not_found
  end

  def method_not_allowed
    head :method_not_allowed
  end

  def internal_server_error
    head :internal_server_error
  end

send_data

optinos
  • filename
  • type
  • disposition
  • status

send_file

options
  • filename
  • type
  • disposition
  • status
  • url_based_filename

認証

authenticate_or_request_with_http_basic(realm = "Application", message = nil, &login_procedure)

class ApplicationController < ActionController::Base
  before_action :auth

  def auth
    authenticate_or_request_with_http_basic do |user, pass|
      user == 'name' && pass == 'pass'
    end 
  end

http_basic_authenticate_with(options = {})

class ApplicationController < ActionController::Base
  http_basic_authenticate_with name: 'name', password: 'pass'

logger

log level

config.log_level

logging フィルタ

config/initializers/filter_parameter_logging.rb

Rails.application.config.filter_parameters += [:password, :credit]

正規表現も使える。

Rails.application.config.filter_parameters += [/credit/]

ステート管理

Cookie

options
  • value
  • path
  • domain
  • tld_length
  • expires
  • secure
  • httponly
# 設定
cookies[:email] = { 
                    value: 'makoto@example.com',
                    expires: 1.days.from_now, # 有効期限
                    domain: 'example.com', # Cookieが有効なドメイン
                    path: '/~www', # Cookieが有効なパス
                    httponly: true, # trueにするとjavascriptから操作できなくなる (default: false)
                    secure: true # trueにするとssl通信時のみCookieを利用する (default: false)
                  }
# 取得
cookies[:email] # => makoto@example.com
# 削除
cookies.delete :email
# domain、pathオプションを設定している場合は、明示的に指定しないと削除できない
cookies.delete :email, path: '/~www'
signed

ユーザによるCookieの書き換えを無効にする。

secrets.secret_key_baseを設定しておく必要がある。

encrypted

暗号化する。

secrets.secret_key_baseを設定しておく必要がある。

cookies.encrypted[:discount] = 45
permanent

有効期限が20年のCookieを生成する。

また、expiresオプションを設定してもpermanentが優先される。

cookies.permanent[:login] = "XJ-122"

なお、上記メソッドは複数合わせて利用可能

cookies.permanent.signed[:login] = "XJ-122"

Session

# 設定
session[:email] = test@example.com
# 取得
@email = session[:email]
# 特定の値の削除
session[:email] = nil
# 全て削除
reset_session
設定

セッションに関する設定を変更するにはconfig/initializers/session_store.rb中のconfig.session_storeの値を変更する。

第一引数にはデータストアの種類を、第二引数にはハッシュで動作パラメータを渡す。

config/initializers/session_store.rb

Rails.application.config.session_store :cookie_store, key: '_tmp_session'
データストアの種類

セッションには以下の通り、保存先が3つ設定可能

  • クッキーストア
  • キャッシュストア
  • アクティブレコードストア(DB)
保存場所 設定値
クッキーストア :cookie_store
キャッシュストア :cache_store
アクティブレコードストア :active_record_store
セッションの無効化 :disabled
動作パラメータ

データストアの種類によって動作パラメータは変わる。

以下は共通のパラメータ。

パラメータ 概要 default
key セッションキーを格納するクッキー名 _session_id
domain セッションクッキーの有効なドメイン nil
path セッションクッキーが有効なパス /
expire_after セッションの有効期間 nil (ブラウザを閉じるまで)
secure :SSL通信の場合のみクッキーを送信するか false
httponly HTTPクッキーを有効にするか true

flash

notice
alert
flash

コントローラでの設定

flash[:msg] = 'hogehoge'

erbでの参照

<%= flash[:msg] %>
now

現在のアクションのみで有効。

flash.now[:msg] = 'hogeeeeee'
keep

次のアクションまで有効。

discard

flashの破棄。

フィルタ

共通オプション

  • only
  • except

before_action

after_action

around_action

around_action :around_logger, only: [:index, :new]
# around_action :around_logger, only: :index
# around_action :around_logger, except: :index

private
def around_logger
  logger.debug 'start aciton'
  yield # アクションの実行
  logger.debug 'end action'
end

skip_xxxx_action

継承したフィルタを除外する。

例)TopControllerのindexアクションだけ、before_action :authenticate_user!をスキップする

application_controller.rb

class ApplicationController < ActionController::Base
  before_action :authenticate_user!

top_controller.rb

class TopController < ApplicationController
  skip_before_action :authenticate_user!, only: :index

ApplicationControllerについて

ApplicationControllerは全てのControllerの基底クラスになるため、Controllerで共通する処理を定義するのに適している。

以下よくある共通処理

  • ログイン認証などのフィルタ
  • 共通的な例外処理
  • protect_from_forgeryによるCSRF対策
  • add_flash_typesによる独自のフラッシュメッセージの定義

例外処理

rescue_fromメソッドを利用することで、例外発生時の処理を定義できる。

ちなみに例外が補足されなかった場合は、発生した例外に応じてHTTPステータスが割り当てられ、それに応じたエラーページが表示される。

例えば、RoutingErrorが発生すると404 Not Foundがステータスとして割り振られ、404.htmlが表示される。

なお、このエラーページが表示される挙動はconfig.consider_all_requests_localfalseの場合のみで、development環境ではデフォルトでtrueとなっている。

rescue_from(*klasses, with: nil, &block)
  # ブロックで処理を記述
  rescue_from MyAppError::Base do |exception|
    render xml: exception, status: 500
  end
  
  # メソッド呼び出し
  rescure_from MyAppError::Base, with: :id_invalid
  
  private
  
  def id_invalid
    # someting to process
  end

4. View

コメント

<%# コメント %>

<%# 複数行
    コメントもOK! %>
<%
=begin
%>
この間は全てコメントとなる
*! "=begin"や"=end"は行頭にないと無効
<%
=end
%>

<% if false %>
この間は全てコメントとなる(処理されない)
<% end %>

FormHelper

form_tag(url_for_options = {}, options = {}, &block)

例)

form_tag('/posts/1', method: :put)
# =>
# <form action="/posts/1" accept-charset="UTF-8" method="post">
#   <input name="utf8" type="hidden" value="✓">
#   <input type="hidden" name="_method" value="put">
#   <input type="hidden" name="authenticity_token" value="jZygzwz35Uvy6+BcusAn1VTokEZGfsZMa4KEJfi3fYIKhtgh/eztG9Yk+/+oPUOuhsJJNsHBbgPd3lMnyLcAFw==">
# </form>

* 第一引数のURLにハッシュを渡す場合、url_forに準じた書式で値を渡すことでURLが生成される

例)

form_tag({controller: "people", action: "search"}, method: "get", class: "nifty_form")
# =>
# <form class="nifty_form" action="/people/search" accept-charset="UTF-8" method="get">
#   <input name="utf8" type="hidden" value="✓">
# </form>

* 第一引数のハッシュと第二引数のハッシュを区別するため、第一引数の{}は省略できないことに注意

text_field_tag(name, value = nil, options = {})
text_field_tag 'name'
# => <input id="name" name="name" type="text" />

text_field_tag 'query', 'Enter your search query here'
# => <input id="query" name="query" type="text" value="Enter your search query here" />

text_field_tag 'search', nil, placeholder: 'Enter search term...'
# => <input id="search" name="search" placeholder="Enter search term..." type="text" />

text_field_tag 'request', nil, class: 'special_input'
# => <input class="special_input" id="request" name="request" type="text" />

text_field_tag 'address', '', size: 75
# => <input id="address" name="address" size="75" type="text" value="" />

text_field_tag 'zip', nil, maxlength: 5
# => <input id="zip" maxlength="5" name="zip" type="text" />

text_field_tag 'payment_amount', '$0.00', disabled: true
# => <input disabled="disabled" id="payment_amount" name="payment_amount" type="text" value="$0.00" />

text_field_tag 'ip', '0.0.0.0', maxlength: 15, size: 20, class: "ip-input"
# => <input class="ip-input" id="ip" maxlength="15" name="ip" size="20" type="text" value="0.0.0.0" />
select_tag(name, option_tags = nil, options = {})
options
  • multiple
  • disabled
  • include_blank
  • prompt
  • その他HTML属性
select_tag "people", options_from_collection_for_select(@people, "id", "name")
# <select id="people" name="people"><option value="1">David</option></select>

select_tag "people", options_from_collection_for_select(@people, "id", "name", "1")
# <select id="people" name="people"><option value="1" selected="selected">David</option></select>

select_tag "people", raw("<option>David</option>")
# => <select id="people" name="people"><option>David</option></select>

select_tag "count", raw("<option>1</option><option>2</option><option>3</option><option>4</option>")
# => <select id="count" name="count"><option>1</option><option>2</option>
#    <option>3</option><option>4</option></select>

select_tag "colors", raw("<option>Red</option><option>Green</option><option>Blue</option>"), multiple: true
# => <select id="colors" multiple="multiple" name="colors[]"><option>Red</option>
#    <option>Green</option><option>Blue</option></select>

select_tag "locations", raw("<option>Home</option><option selected='selected'>Work</option><option>Out</option>")
# => <select id="locations" name="locations"><option>Home</option><option selected='selected'>Work</option>
#    <option>Out</option></select>

select_tag "access", raw("<option>Read</option><option>Write</option>"), multiple: true, class: 'form_input', id: 'unique_id'
# => <select class="form_input" id="unique_id" multiple="multiple" name="access[]"><option>Read</option>
#    <option>Write</option></select>

select_tag "people", options_from_collection_for_select(@people, "id", "name"), include_blank: true
# => <select id="people" name="people"><option value=""></option><option value="1">David</option></select>

select_tag "people", options_from_collection_for_select(@people, "id", "name"), include_blank: "All"
# => <select id="people" name="people"><option value="">All</option><option value="1">David</option></select>

select_tag "people", options_from_collection_for_select(@people, "id", "name"), prompt: "Select something"
# => <select id="people" name="people"><option value="">Select something</option><option value="1">David</option></select>

select_tag "destination", raw("<option>NYC</option><option>Paris</option><option>Rome</option>"), disabled: true
# => <select disabled="disabled" id="destination" name="destination"><option>NYC</option>
#    <option>Paris</option><option>Rome</option></select>

select_tag "credit_card", options_for_select([ "VISA", "MasterCard" ], "MasterCard")
# => <select id="credit_card" name="credit_card"><option>VISA</option>
#    <option selected="selected">MasterCard</option></select>
<%= select_tag 'publish', raw('<option value="1">技術評論社</option><option value="2">翔泳社</option><option value="3">マイナビ</option>') %>
<%= select_tag 'publish', options_for_select(
    { '技術評論社' => 1, '翔泳社' => 2, 'マイナビ' => 3 }, 2) %>
options_from_collection_for_select(collection, value_method, text_method, selected = nil)
options_from_collection_for_select(@people, 'id', 'name')
# => <option value="#{person.id}">#{person.name}</option>

select_tagとの併用例

select_tag 'person', options_from_collection_for_select(@people, 'id', 'name')
options_for_select(container, selected = nil)
options_for_select([["Dollar", "$"], ["Kroner", "DKK"]])
# => <option value="$">Dollar</option>
# => <option value="DKK">Kroner</option>

options_for_select([ "VISA", "MasterCard" ], "MasterCard")
# => <option value="VISA">VISA</option>
# => <option selected="selected" value="MasterCard">MasterCard</option>

options_for_select({ "Basic" => "$20", "Plus" => "$40" }, "$40")
# => <option value="$20">Basic</option>
# => <option value="$40" selected="selected">Plus</option>

options_for_select([ "VISA", "MasterCard", "Discover" ], ["VISA", "Discover"])
# => <option selected="selected" value="VISA">VISA</option>
# => <option value="MasterCard">MasterCard</option>
# => <option selected="selected" value="Discover">Discover</option>

HTMLの属性を付与する際は要素の最後にハッシュで値を渡す

options_for_select([ "Denmark", ["USA", {class: 'bold'}], "Sweden" ], ["USA", "Sweden"])
# => <option value="Denmark">Denmark</option>
# => <option value="USA" class="bold" selected="selected">USA</option>
# => <option value="Sweden" selected="selected">Sweden</option>

options_for_select([["Dollar", "$", {class: "bold"}], ["Kroner", "DKK", {onclick: "alert('HI');"}]])
# => <option value="$" class="bold">Dollar</option>
# => <option value="DKK" onclick="alert('HI');">Kroner</option>
check_box_tag(name, value = "1", checked = false, options = {})
radio_button_tag(name, value, checked = false, options = {})
<%= form_for(@book) do |f| %>
  ラジオボタン:
  <label><%= f.radio_button :publish, '技術評論社', class: :rd %> 技術評論社</label>
  <label><%= f.radio_button :publish, '翔泳社', class: :rd %> 翔泳社</label>
  <label><%= f.radio_button :publish, 'マイナビ', class: :rd %> マイナビ</label><br />
  チェックボックス:
  <label><%= f.check_box :cd, { class: 'chk'}, 'yes', 'no' %> CD付属?</label><br />
<% end %>
submit_tag(value = "Save changes", options = {})
options
  • data:
  • disabled
  • その他のHTML属性
data attributes
  • confirm
  • disable_with
submit_tag
# => <input name="commit" data-disable-with="Save changes" type="submit" value="Save changes" />

submit_tag "Edit this article"
# => <input name="commit" data-disable-with="Edit this article" type="submit" value="Edit this article" />

submit_tag "Save edits", disabled: true
# => <input disabled="disabled" name="commit" data-disable-with="Save edits" type="submit" value="Save edits" />

submit_tag "Complete sale", data: { disable_with: "Submitting..." }
# => <input name="commit" data-disable-with="Submitting..." type="submit" value="Complete sale" />

submit_tag nil, class: "form_submit"
# => <input class="form_submit" name="commit" type="submit" />

submit_tag "Edit", class: "edit_button"
# => <input class="edit_button" data-disable-with="Edit" name="commit" type="submit" value="Edit" />

submit_tag "Save", data: { confirm: "Are you sure?" }
# => <input name='commit' type='submit' value='Save' data-disable-with="Save" data-confirm="Are you sure?" />
form_for(record, options = {}, &block)

注意点

  • 対応したルーティングがないとテンプレート読み込み時にエラーが発生する
  • Modelの属性に対応したメソッド名(属性名)を指定しないとテンプレート読み込み時にエラーが発生する
text_field(object_name, method, options = {})

例)

text_field(:post, :title, size: 20)
# => <input type="text" id="post_title" name="post[title]" size="20" value="#{@post.title}" />

text_field(:post, :title, class: "create_input")
# => <input type="text" id="post_title" name="post[title]" value="#{@post.title}" class="create_input" />

text_field(:session, :user, onchange: "if ($('#session_user').val() === 'admin') { alert('Your login cannot be admin!'); }")
# => <input type="text" id="session_user" name="session[user]" value="#{@session.user}" onchange="if ($('#session_user').val() === 'admin') { alert('Your login cannot be admin!'); }"/>

text_field(:snippet, :code, size: 20, class: 'code_input')
# => <input type="text" id="snippet_code" name="snippet[code]" size="20" value="#{@snippet.code}" class="code_input" />

* ActionView::Helpers::FormBuilderから呼ばれている場合、第一引数が省略できる

<%= form_for @user do |f| %>
  <%= f.text_field :name, size: 20, calss: 'name_input' %>
select(object, method, choices = nil, options = {}, html_options = {}, &block)
options
  • include_blank
  • disabled
  • selected
<%= form_for(@book) do |f| %>
  <%= f.select :publish, ['技術評論社', '翔泳社', 'マイナビ'], { include_blank: '選択してください' },  class: 'pub' %>
  <br />
  <%= f.select :publish, { '技術評論社' => 1, '翔泳社' => 2, 'マイナビ' => 3 } %>
  <br />
  <%= f.select(:publish, [['技術評論社', 1], ['翔泳社', 2], ['マイナビ', 3]]) %>
  <br />
  <%= f.select :publish, ['技術評論社', '翔泳社', 'マイナビ'], { include_blank: '選択してください' }, multiple: true %>
<% end %>
check_box(object_name, method, options = {}, checked_value = "1", unchecked_value = "0")
radio_button(object_name, method, tag_value, options = {})
collection_select(method, collection, value_method, text_method, options = {}, html_options = {})
options
  • include_blank
  • disabled
  • selected
<%= form_for(@book) do |f| %>
  <%= f.collection_select :publish, @books, :id, :publish, { include_blank: '選択してください' }, { class: 'select' } %>
<% end %>
collection_check_boxes(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
collection_radio_buttons(object, method, collection, value_method, text_method, options = {}, html_options = {}, &block)
<%= form_for(@book) do |f| %>
  <%= f.collection_check_boxes(:publish, @books, :id, :publish) %><br />
  <%= f.collection_radio_buttons(:publish, @books, :id, :publish) %>
<% end %>
submit(value=nil, options={})
<%= form_for @post do |f| %>
  <%= f.submit %>
<% end %>

ActionView::Helpers

simple_format(text, html_options = {}, options = {})
article = 
'TITLE

CONTENT1
CONTENT2
CONTENT3'

simple_format(article, class: "article article-primary")
# <p class="article article-primary">TITLE</p>

# <p class="article article-primary">CONTENT1
# <br>CONTENT2
# <br>CONTENT3</p>

simple_format("<font color='red'>sanitize</font> is false.", {}, sanitize: false)
# <p><font color="red">sanitize</font> is false.</p>
simple_format("<font color='red'>sanitize</font> is true.", {}, sanitize: true)
# <p>sanitize is true.</p>

simple_format('wrappered by div', {}, wrapper_tag: 'div')
# <div>wrappered by div</div>
truncate(text, options = {}, &block)
truncate("Once upon a time in a world far far away")
# Once upon a time in a world...
truncate("Once upon a time in a world far far away", length: 17)
# Once upon a time 
truncate("Once upon a time in a world far far away", length: 17, separator: ' ')
# Once upon a...
truncate("And they found that many people were sleeping better.", length: 25, omission: '(continued)')
# And they found(continued)
truncate("<p>Once upon a time in a world far far away</p>")
# &lt;p&gt;Once upon a time in a wo...
truncate("<p>Once upon a time in a world far far away</p>", escape: false)
# <p>Once upon a time in a wo...

* lengthで指定する文字数がomissionの文字数を含む点に注意

excerpt(text, phrase, options = {})
excerpt('hoge0987654321fuga1234567890piyo', 'fuga', radius: 3)
# ...321fuga123...
cycle(first_value, *values)
current_cycle(name = "default")
reset_cycle(name = "default")
highlight(text, phrases, options = {})
concat(string)
raw(stringish)

* <%== %> を利用することで同様の結果が得られる

html_escape(s)

* Rails 3 以降<%= %>内では暗黙的にエスケープ処理されているため、基本的に不要

* hはこのメソッドのエイリアス

sanitize(html, options = {})
options
  • tag
  • attributes
  • scrubber

ActionView::Helpers::NumberHelper

number_to_currency(number, options = {})
options
  • locale - Sets the locale to be used for formatting (defaults to current locale).
  • precision - Sets the level of precision (defaults to 2).
  • unit - Sets the denomination of the currency (defaults to “$”).
  • separator - Sets the separator between the units (defaults to “.”).
  • delimiter - Sets the thousands delimiter (defaults to “,”).
  • format - Sets the format for non-negative numbers (defaults to “%u%n”). Fields are %u for the currency, and %n for the number.
  • negative_format - Sets the format for negative numbers (defaults to prepending an hyphen to the formatted number given by :format). Accepts the same fields than :format, except %n is here the absolute value of the number.
  • raise - If true, raises InvalidNumberError when the argument is invalid.
number_to_currency(1234567890.50)                    # => $1,234,567,890.50
number_to_currency(1234567890.506)                   # => $1,234,567,890.51
number_to_currency(1234567890.506, precision: 3)     # => $1,234,567,890.506
number_to_currency(1234567890.506, locale: :fr)      # => 1 234 567 890,51 €
number_to_currency("123a456")                        # => $123a456

number_to_currency("123a456", raise: true)           # => InvalidNumberError

number_to_currency(-1234567890.50, negative_format: "(%u%n)")
# => ($1,234,567,890.50)
number_to_currency(1234567890.50, unit: "R$", separator: ",", delimiter: "")
# => R$1234567890,50
number_to_currency(1234567890.50, unit: "R$", separator: ",", delimiter: "", format: "%n %u")
# => 1234567890,50 R$
number_to_human(number, options = {})
options
  • locale - Sets the locale to be used for formatting (defaults to current locale).
  • precision - Sets the precision of the number (defaults to 3).
  • significant - If true, precision will be the # of significant_digits. If false, the # of fractional digits (defaults to true)
  • separator - Sets the separator between the fractional and integer digits (defaults to “.”).
  • delimiter - Sets the thousands delimiter (defaults to “”).
  • strip_insignificant_zeros - If true removes insignificant zeros after the decimal separator (defaults to true)
  • units - A Hash of unit quantifier names. Or a string containing an i18n scope where to find this hash. It might have the following keys:
    • integers: :unit, :ten, :hundred, :thousand, :million, :billion, :trillion, :quadrillion
    • fractionals: :deci, :centi, :mili, :micro, :nano, :pico, :femto
  • format - Sets the format of the output string (defaults to “%n %u”). The field types are:
    • %u - The quantifier (ex.: 'thousand')
    • %n - The number
  • raise - If true, raises InvalidNumberError when the argument is invalid.
number_to_human(123)                                          # => "123"
number_to_human(1234)                                         # => "1.23 Thousand"
number_to_human(12345)                                        # => "12.3 Thousand"
number_to_human(1234567)                                      # => "1.23 Million"
number_to_human(1234567890)                                   # => "1.23 Billion"
number_to_human(1234567890123)                                # => "1.23 Trillion"
number_to_human(1234567890123456)                             # => "1.23 Quadrillion"
number_to_human(1234567890123456789)                          # => "1230 Quadrillion"
number_to_human(489939, precision: 2)                         # => "490 Thousand"
number_to_human(489939, precision: 4)                         # => "489.9 Thousand"
number_to_human(1234567, precision: 4,
                        significant: false)                   # => "1.2346 Million"
number_to_human(1234567, precision: 1,
                        separator: ',',
                        significant: false)                   # => "1,2 Million"

number_to_human(500000000, precision: 5)                      # => "500 Million"
number_to_human(12345012345, significant: false)              # => "12.345 Billion"
number_to_human_size(number, options = {})
options
  • locale - Sets the locale to be used for formatting (defaults to current locale).
  • precision - Sets the precision of the number (defaults to 3).
  • significant - If true, precision will be the # of significant_digits. If false, the # of fractional digits (defaults to true)
  • separator - Sets the separator between the fractional and integer digits (defaults to “.”).
  • delimiter - Sets the thousands delimiter (defaults to “”).
  • strip_insignificant_zeros - If true removes insignificant zeros after the decimal separator (defaults to true)
  • prefix - If :si formats the number using the SI prefix (defaults to :binary)
  • raise - If true, raises InvalidNumberError when the argument is invalid.
number_to_human_size(123)                                          # => 123 Bytes
number_to_human_size(1234)                                         # => 1.21 KB
number_to_human_size(12345)                                        # => 12.1 KB
number_to_human_size(1234567)                                      # => 1.18 MB
number_to_human_size(1234567890)                                   # => 1.15 GB
number_to_human_size(1234567890123)                                # => 1.12 TB
number_to_human_size(1234567890123456)                             # => 1.1 PB
number_to_human_size(1234567890123456789)                          # => 1.07 EB
number_to_human_size(1234567, precision: 2)                        # => 1.2 MB
number_to_human_size(483989, precision: 2)                         # => 470 KB
number_to_human_size(1234567, precision: 2, separator: ',')        # => 1,2 MB
number_to_human_size(1234567890123, precision: 5)                  # => "1.1228 TB"
number_to_human_size(524288000, precision: 5)                      # => "500 MB"
number_to_percentage(number, options = {})
options
  • locale - Sets the locale to be used for formatting (defaults to current locale).
  • precision - Sets the precision of the number (defaults to 3).
  • significant - If true, precision will be the # of significant_digits. If false, the # of fractional digits (defaults to false).
  • separator - Sets the separator between the fractional and integer digits (defaults to “.”).
  • delimiter - Sets the thousands delimiter (defaults to “”).
  • strip_insignificant_zeros - If true removes insignificant zeros after the decimal separator (defaults to false).
  • format - Specifies the format of the percentage string The number field is %n (defaults to “%n%”).
  • raise - If true, raises InvalidNumberError when the argument is invalid.
number_to_percentage(100)                                        # => 100.000%
number_to_percentage("98")                                       # => 98.000%
number_to_percentage(100, precision: 0)                          # => 100%
number_to_percentage(1000, delimiter: '.', separator: ',')       # => 1.000,000%
number_to_percentage(302.24398923423, precision: 5)              # => 302.24399%
number_to_percentage(1000, locale: :fr)                          # => 1 000,000%
number_to_percentage("98a")                                      # => 98a%
number_to_percentage(100, format: "%n  %")                       # => 100.000  %

number_to_percentage("98a", raise: true)                         # => InvalidNumberError
number_to_phone(number, options = {})
options
  • area_code - Adds parentheses around the area code.
  • delimiter - Specifies the delimiter to use (defaults to “-”).
  • extension - Specifies an extension to add to the end of the generated number.
  • country_code - Sets the country code for the phone number.
  • raise - If true, raises InvalidNumberError when the argument is invalid.
number_to_phone(5551234)                                           # => 555-1234
number_to_phone("5551234")                                         # => 555-1234
number_to_phone(1235551234)                                        # => 123-555-1234
number_to_phone(1235551234, area_code: true)                       # => (123) 555-1234
number_to_phone(1235551234, delimiter: " ")                        # => 123 555 1234
number_to_phone(1235551234, area_code: true, extension: 555)       # => (123) 555-1234 x 555
number_to_phone(1235551234, country_code: 1)                       # => +1-123-555-1234
number_to_phone("123a456")                                         # => 123a456
number_to_phone("1234a567", raise: true)                           # => InvalidNumberError

number_to_phone(1235551234, country_code: 1, extension: 1343, delimiter: ".")
# => +1.123.555.1234 x 1343

number_to_phone(75561234567, pattern: /(\d{1,4})(\d{4})(\d{4})$/, area_code: true)
# => "(755) 6123-4567"
number_to_phone(13312345678, pattern: /(\d{3})(\d{4})(\d{4})$/))
# => "133-1234-5678"
number_with_delimiter(number, options = {})
options
  • locale - Sets the locale to be used for formatting (defaults to current locale).
  • delimiter - Sets the thousands delimiter (defaults to “,”).
  • separator - Sets the separator between the fractional and integer digits (defaults to “.”).
  • raise - If true, raises InvalidNumberError when the argument is invalid.
 number_with_delimiter(12345678)                        # => 12,345,678
 number_with_delimiter("123456")                        # => 123,456
 number_with_delimiter(12345678.05)                     # => 12,345,678.05
 number_with_delimiter(12345678, delimiter: ".")        # => 12.345.678
 number_with_delimiter(12345678, delimiter: ",")        # => 12,345,678
 number_with_delimiter(12345678.05, separator: " ")     # => 12,345,678 05
 number_with_delimiter(12345678.05, locale: :fr)        # => 12 345 678,05
 number_with_delimiter("112a")                          # => 112a
 number_with_delimiter(98765432.98, delimiter: " ", separator: ",")
 # => 98 765 432,98

number_with_delimiter("112a", raise: true)              # => raise InvalidNumberError
number_with_precision(number, options = {})
options
  • locale - Sets the locale to be used for formatting (defaults to current locale).
  • precision - Sets the precision of the number (defaults to 3).
  • significant - If true, precision will be the # of significant_digits. If false, the # of fractional digits (defaults to false).
  • separator - Sets the separator between the fractional and integer digits (defaults to “.”).
  • delimiter - Sets the thousands delimiter (defaults to “”).
  • strip_insignificant_zeros - If true removes insignificant zeros after the decimal separator (defaults to false).
  • raise - If true, raises InvalidNumberError when the argument is invalid.
number_with_precision(111.2345)                                         # => 111.235
number_with_precision(111.2345, precision: 2)                           # => 111.23
number_with_precision(13, precision: 5)                                 # => 13.00000
number_with_precision(389.32314, precision: 0)                          # => 389
number_with_precision(111.2345, significant: true)                      # => 111
number_with_precision(111.2345, precision: 1, significant: true)        # => 100
number_with_precision(13, precision: 5, significant: true)              # => 13.000
number_with_precision(111.234, locale: :fr)                             # => 111,234

number_with_precision(13, precision: 5, significant: true, strip_insignificant_zeros: true)
# => 13

number_with_precision(389.32314, precision: 4, significant: true)       # => 389.3
number_with_precision(1111.2345, precision: 2, separator: ',', delimiter: '.')
# => 1.111,23
strftime(format)

ActionView::Helpers::UrlHelper

link_to(name = nil, options = nil, html_options = nil, &block)
options
  • data - This option can be used to add custom data attributes.

    • confirm: 'question?' - This will allow the unobtrusive JavaScript driver to prompt with the question specified (in this case, the resulting text would be question?. If the user accepts, the link is processed normally, otherwise no action is taken.

    • disable_with - Value of this parameter will be used as the value for a disabled version of the submit button when the form is submitted. This feature is provided by the unobtrusive JavaScript driver.

  • method: symbol of HTTP verb - This modifier will dynamically create an HTML form and immediately submit the form for processing using the HTTP verb specified. Useful for having links perform a POST operation in dangerous actions like deleting a record (which search bots can follow while spidering your site). Supported verbs are :post, :delete, :patch, and :put. Note that if the user has JavaScript disabled, the request will fall back to using GET. If href: '#' is used and the user has JavaScript disabled clicking the link will have no effect. If you are relying on the POST behavior, you should check for it in your controller's action by using the request object's methods for post?, delete?, patch?, or put?.

  • remote: true - This will allow the unobtrusive JavaScript driver to make an Ajax request to the URL in question instead of following the link. The drivers each provide mechanisms for listening for the completion of the Ajax request and performing JavaScript operations once they're complete

link_to_if(condition, name, options = {}, html_options = {}, &block)
link_to_unless(condition, name, options = {}, html_options = {}, &block)
link_to_unless_current(name, options = {}, html_options = {}, &block)
mail_to(email_address, name = nil, html_options = {}, &block)
options
  • subject - Preset the subject line of the email.
  • body - Preset the body of the email.
  • cc - Carbon Copy additional recipients on the email.
  • bcc - Blind Carbon Copy additional recipients on the email.

ActionView::Helpers::AssetTagHelper

audio_tag(*sources)
options
video_tag(*sources)
options
  • poster - Set an image (like a screenshot) to be shown before the video loads. The path is calculated like the src of image_tag.
  • size - Supplied as “{Width}x{Height}” or “{Number}”, so “30x45” becomes width=“30” and height=“45”, and “50” becomes width=“50” and height=“50”. :size will be ignored if the value is not in the correct format.

ActionView::Helpers::AssetUrlHelper

auto_discovery_link_tag(type = :rss, url_options = {}, tag_options = {})
options
  • rel - Specify the relation of this link, defaults to “alternate”
  • type - Override the auto-generated mime type
  • title - Specify the title of the link, defaults to the type
favicon_link_tag(source='favicon.ico', options={})
options
path_to_asset(source, options = {})

asset_pathメソッドのエイリアス

options
  • type
  • host
asset_url "application.js"
# => http://example.com/assets/application.js
asset_url "application.js", host: "http://cdn.example.com"
# => http://cdn.example.com/assets/application.js

※ 他のpath_to〜メソッドは、このメソッドを呼び出している

例)path_to_audio

# path_to_audioはaudio_pathのエイリアス
def audio_path(source, options = {})
  path_to_asset(source, {type: :audio}.merge!(options))
end
path_to_audio(source, options = {})
path_to_font(source, options = {})
path_to_image(source, options = {})
path_to_javascript(source, options = {})
path_to_stylesheet(source, options = {})
path_to_video(source, options = {})

その他のActionView::Helpers

debug(object)
options
capture(*args)
options
tag(name, options = nil, open = false, escape = true)
options
content_tag(name, content_or_options_with_block = nil, options = nil, escape = true, &block)
options

ViewHelperの実装

  • app/helpersディレクトリ配下に「xxxxx_helper.rb」というファイル名で作成する
  • 設定ファイル(application.rb)のconfig.action_controller.include_all_helpers = false
module ViewHelper

  def format_datetime(datetime, type = :datetime)

    return '' unless datetime

    case type
      when :datetime
        format = '%Y年%m月%d日 %H:%M:%S'
      when :date
        format = '%Y年%m月%d日'
      when :time
        format = '%H:%M:%S'
    end
    datetime.strftime(format)
  end

  def list_tag(collection, prop)
    content_tag(:ul) do
      collection.each do |element|
        concat content_tag(:li, element.attributes[prop])
      end
    end
  end

  def list_tag2(collection, prop)
    list = '<ul>'
    collection.each do |element|
      list.concat('<li>')
      list.concat(h element.attributes[prop])
      list.concat('</li>')
    end
    raw list.concat('</ul>')
  end

  def blockquote_tag(cite, citetext, options = {}, &block)
    options.merge! cite: cite
    quote_tag = content_tag(:blockquote, capture(&block), options)
    p_tag = content_tag(:p) do
      concat '出典:'
      concat content_tag(:cite, citetext)
    end
    quote_tag.concat(p_tag)
  end

  def blockquote_tag2(cite, citetext, body = '', options = {}, &block)
    options.merge! cite: cite
    quote_tag = content_tag(:blockquote,
      block_given? ? capture(&block) : body,
      options)
  end
end

JBuiler

JSONの生成

json.key(value)

json.extract!(obj, prop[, ...])

index.json.jbuilder

json.array!(@books) do |book|
  json.extract! book, :isbn, :title, :price, :publish, :published, :cd
  json.url book_url(book, format: :json)
end
[
  {
     "isbn":"978-4-7741-5878-5",
     "title":"AndroidエンジニアのためのモダンJava",
     "price":3360,"publish":"技術評社",
     "published":"2013-08-20",
     "cd":false,
     "url":"http://localhost:3000/books/1.json"
  },
  .
  .
  .

Builder

XMLの生成

xml.element([content] [, attr: value, ...]) do &block

index.xml.builder

xml.books do
  @books.each do |book|
    xml.book(isbn: book.isbn) do
      xml.title(book.title)
      xml.price(book.price)
      xml.publish(book.publish)
      xml.published(book.published)
      xml.cd(book.cd)
    end
  end
end
<?xml version="1.0" ?>
<books>
    <book isbn="978-4-7741-5878-5">
      <title>AndroidエンジニアのためのモダンJava</title>
      <price>3360</price>
      <publish>技術評論社</publish>
      <published>2013-08-20</published>
      <cd>false</cd>
    </book>
    .
    .
    .

Sprockets

マニフェスト

app/assets/javascripts/application.js

//= require jquery
//= require jquery_ujs
//= require turbolinks
//= require_tree .

app/assets/stylesheets/application.css

/*
 *= require_self
 *= require_tree .
 */
ディレクティブ
ディレクティブ 概要
require path 指定したファイルを一度だけ読み込み
include path 指定したファイルを読み込み
require_directory path 指定したフォルダ内のファイルをアルファベット順に読み込み
require_tree path 指定されたフォルダ配下を再帰的に読み込み
require_self 現在のファイルの内容をrequire/includeの前に挿入

マニフェスト経由でアセットを読み込む

javascript_include_tag
stylesheet_link_tag

layouts/application.html.erb

  <%= stylesheet_link_tag "application", media: "all", "data-turbolinks-track" => true %>
  <%= javascript_include_tag "application", "data-turbolinks-track" => true %>

data-turbolinks-trackturbolinksを有効にするか。

mediaスタイルシートを出力する対象を指定。

キャッシュ

設定

  # キャッシュの有効化
  config.action_controller.perform_caching = true
  # キャッシュの格納先
  config.cash_store = :memory_store

cache(name = {}, options = {}, &block)

ページ内で複数キャッシュがある場合、suffixを設定し、キャッシュキーの重複を避ける

現在時刻(キャッシュなし):<%= Time.now %><br />
<% cache do %>
<%# cache(suffix: 'footer') do %>
現在時刻(キャッシュあり):<%= Time.now %><br />
<% end %>

複数ページでの共有

キャッシュする内容をパーシャルにし、それぞれのページで読み込む

_share.html.erb

<% cache('GlobalTime') do %>
現在時刻(キャッシュあり):<%= Time.now %><br />
<% end %>

share1.html.erb

現在時刻(キャッシュなし):<%= Time.now %><br />
<%= render 'share' %>

share2.html.erb

現在時刻(キャッシュなし):<%= Time.now %><br />
<%= render 'share' %>

モデルによるキャッシュキーの生成

cacheメソッドの引数にモデルのインスタンスを渡す。

キャッシュキーが「モデル/id-update_at/ハッシュ値」のようになる

<% cache(book) do %>
  <img src="http://www.wings.msn.to/books/<%= book.isbn%>/<%= book.isbn%>_logo.jpg" width="80" height="30" /><br />
  <%= book.title %><br />
  <%= book.publish %>/発行<br />
  定価 <%= book.price %>円(税込)<br />
  ISBN <%= book.isbn %><br />
  発刊日: <%= book.published %>
  レビュー:<ul><%= render book.reviews %></ul>
<% end %>

cache_if(condition, name = {}, options = {}, &block)

条件に応じてキャッシュする。

<% cache_if(book.published <= Date.today ,book) do %>
  <img src="http://www.wings.msn.to/books/<%= book.isbn%>/<%= book.isbn%>_logo.jpg" width="80" height="30" />
  <br />
  <%= book.title %>
  <br />
  <%= book.publish %>/発行
  <br />
  定価 <%= book.price %>円(税込)
  <br />
  ISBN <%= book.isbn %>
  <br />
  発刊日: <%= book.published %>
  レビュー:<ul><%= render book.reviews %></ul>
<% end %>

fields_for(record_name, record_object = nil, options = {}, &block)

<%= form_for(@user, url: { action: :create }) do |f| %>
  <div class="field">
    <%= f.label :username, 'ユーザ名' %><br />
    <%= f.text_field :username %>
  </div>
  <div class="field">
    <%= f.label :email, 'メールアドレス:' %><br />
    <%= f.text_field :email %>
  </div>
  <%= field_set_tag '著者情報' do %>
    <%= fields_for @user.author do |af| %>
      <div class="field">
        <%= af.label :name, '著者名:' %><br />
        <%= af.text_field :name %>
      </div>
      <div class="field">
        <%= af.label :birth, '誕生日:' %><br />
        <%= af.text_field :birth %>
      </div>
    <% end %>
  <% end %>
  <%= f.submit '登録' %>
<% end %>

5.1. テスト

  # 各テストの実行前に呼び出される
  def setup
    
  end
  
  # 各テストの実行後に呼び出される
  def taardown
    
  end

 # テスト
  test 'test_name' do
    # テストコード
  end

model

メソッド

assert

controller

メソッド

assert_difference(expression, difference = 1, message = nil, &block)
assert_no_difference(expression, message = nil, &block)

integration

rails g integration_test [name]

メソッド

follow_redirect!

5.2. メーラー

ActionMailerの作成

rails g mailer [name] [method] [options]

設定

Rails.application.configure do

  # デフォルトのメールヘッダ
  config.action_mailer.default_options = {}
  # メールを実際に送信するか (デフォルト: true)
  config.action_mailer.perform_deliveries = true
  # メール配信失敗時にエラーを発生させるか (デフォルト: false)
  config.action_mailer.raise_delivery_errors = true
  # メールテンプレート(url_for)に利用されるホスト名
  config.action_mailer.default_url_options = { host: 'sample.com' }
  # メールの送信情報 (デフォルト: smtp)
  #ActionMailer::Base.delivery_method = :smtp
  config.action_mailer.delivery_method = :smtp
  # smtpの設定
  #ActionMailer::Base.smtp_settings =
  config.action_mailer.smtp_settings =
  {
   user_name: ENV['SENDGRID_USERNAME'],
   password: ENV['SENDGRID_PASSWORD'],
   domain: "heroku.com",
   address: "smtp.sendgrid.net",
   port: 587,
   authentication: :plain,
   enable_starttls_auto: true
  }
end

メソッド

default(value = nil)
mail(headers = {}, &block)
attachments()
deliver()

mailers/hoge_mailer.rb

class HogeMailer < ActionMialer::Base
  # デフォルトのメールヘッダ指定
  default from: 'hoge@example.com',
          cc: 'fuga@example.com'

  def sendmail_confirm
  
    # ファイルの添付
    attachments['hoge.png'] = File.read Rails.root.join('/hoge.png')
    # ファイルのインライン添付
    attachments.inline['fuga.png'] = File.read Rails.root.join('/fuga.png')
  
   # Mail::Message オブジェクトの作成
    mail from: 'from@example.com',
         to: 'to@example.com'
  end

controllers/hoge_controller.rb

class HogeController < ApplicationController
  def create
    user = User.find 1
    # メールの送信実行
    HogeMailer.sendmail_confirm(user).deliver

views/hoge_mailer/sendmail_confirm.html.erb

<h1>title</h1>

<%# インライン添付 %>
<%= image_tag attachments['fuga.png'].url %>

インターセプタ

config/initializers/test_mail_config.rb

mailers/hoge_interceptor

rbenvでRubyのバージョンを管理する

rbenvを利用して、Rubyのバージョンを切り替える方法のメモ。

当方の環境

Homebrewでrbenvをインストール

$ ruby -v
ruby 2.0.0p481 (2014-05-08 revision 45883) [universal.x86_64-darwin14] # プリセットのrubyバージョン
# Homebrewの更新
$ brew update
Updated Homebrew from 9185c50 to dde20cd.
==> Migrating Homebrew to v0.9.9
remote: Counting objects: 271, done.
remote: Compressing objects: 100% (219/219), done.
remote: Total 271 (delta 142), reused 124 (delta 38), pack-reused 0
Receiving objects: 100% (271/271), 445.51 KiB | 413.00 KiB/s, done.
Resolving deltas: 100% (142/142), completed with 140 local objects.
From https://github.com/Homebrew/brew
 + dde20cd...2ca6dbb master     -> origin/master  (forced update)
HEAD is now at 2ca6dbb brew.rb: Don’t ask `xcrun` for output if no CLT is installed (#334)
==> Homebrew has enabled anonymous aggregate user behaviour analytics
Read the analytics documentation (and how to opt-out) here:
  https://git.io/brew-analytics
==> Tapping homebrew/core
Cloning into '/usr/local/Library/Taps/homebrew/homebrew-core'...
remote: Counting objects: 3710, done.
remote: Compressing objects: 100% (3593/3593), done.
remote: Total 3710 (delta 13), reused 2375 (delta 8), pack-reused 0
Receiving objects: 100% (3710/3710), 2.88 MiB | 224.00 KiB/s, done.
Resolving deltas: 100% (13/13), done.
Checking connectivity... done.
Tapped 3588 formulae (3,736 files, 9.0M)
==> Cleaning up /Library/Caches/Homebrew...
Removing: /Library/Caches/Homebrew/binutils-2.25.1.yosemite.bottle.tar.gz... (44.2M)
Removing: /Library/Caches/Homebrew/coreutils-8.24.yosemite.bottle.1.tar.gz... (3.3M)
Removing: /Library/Caches/Homebrew/nmap-7.00.yosemite.bottle.tar.gz... (6M)
Removing: /Library/Caches/Homebrew/openssl-1.0.2d_1.yosemite.bottle.tar.gz... (3.6M)
==> Migrating /Library/Caches/Homebrew to /Users/makoto/Library/Caches/Homebrew.
==> Deleting /Library/Caches/Homebrew...
Already up-to-date.
# rbenvのインストール
$ brew install rbenv
==> Installing dependencies for rbenv: autoconf, pkg-config, openssl, rub
==> Installing rbenv dependency: autoconf
==> Downloading https://homebrew.bintray.com/bottles/autoconf-2.69.yosemite.bott
######################################################################## 100.0%
==> Pouring autoconf-2.69.yosemite.bottle.4.tar.gz
==> Caveats
Emacs Lisp files have been installed to:
  /usr/local/share/emacs/site-lisp/autoconf
==> Summary
🍺  /usr/local/Cellar/autoconf/2.69: 70 files, 3.0M
==> Installing rbenv dependency: pkg-config
==> Downloading https://homebrew.bintray.com/bottles/pkg-config-0.29.1.yosemite.
######################################################################## 100.0%
==> Pouring pkg-config-0.29.1.yosemite.bottle.tar.gz
🍺  /usr/local/Cellar/pkg-config/0.29.1: 10 files, 627.2K
==> Installing rbenv dependency: openssl
==> Downloading https://homebrew.bintray.com/bottles/openssl-1.0.2h_1.yosemite.b
######################################################################## 100.0%
==> Pouring openssl-1.0.2h_1.yosemite.bottle.tar.gz
==> Caveats
A CA file has been bootstrapped using certificates from the system
keychain. To add additional certificates, place .pem files in
  /usr/local/etc/openssl/certs

and run
  /usr/local/opt/openssl/bin/c_rehash

This formula is keg-only, which means it was not symlinked into /usr/local.

Apple has deprecated use of OpenSSL in favor of its own TLS and crypto libraries

Generally there are no consequences of this for you. If you build your
own software and it requires this formula, you’ll need to add to your
build variables:

    LDFLAGS:  -L/usr/local/opt/openssl/lib
    CPPFLAGS: -I/usr/local/opt/openssl/include

==> Summary
🍺  /usr/local/Cellar/openssl/1.0.2h_1: 1,691 files, 12.0M
==> Installing rbenv dependency: ruby-build
==> Downloading https://github.com/rbenv/ruby-build/archive/v20160602.tar.gz
==> Downloading from https://codeload.github.com/rbenv/ruby-build/tar.gz/v201606
######################################################################## 100.0%
==> ./install.sh
🍺  /usr/local/Cellar/ruby-build/20160602: 282 files, 155.5K, built in 9 seconds
==> Installing rbenv
==> Downloading https://homebrew.bintray.com/bottles/rbenv-1.0.0.yosemite.bottle
######################################################################## 100.0%
==> Pouring rbenv-1.0.0.yosemite.bottle.tar.gz
==> Caveats
Rbenv stores data under ~/.rbenv by default. If you absolutely need to
store everything under Homebrew’s prefix, include this in your profile:
  export RBENV_ROOT=/usr/local/var/rbenv

To enable shims and autocompletion, run this and follow the instructions:
  rbenv init
==> Summary
🍺  /usr/local/Cellar/rbenv/1.0.0: 36 files, 61.9K

rbenvの使い方

# インストールできるバージョンの確認
$ rbenv install -l
Available versions:
  1.8.5-p113
  1.8.5-p114
  1.8.5-p115
  1.8.5-p231
  1.8.5-p52
  1.8.6-p110
  1.8.6-p111
  1.8.6-p114
  1.8.6-p230
  1.8.6-p286
  1.8.6-p287
  1.8.6-p36
  1.8.6-p368
  1.8.6-p369
  1.8.6-p383
  1.8.6-p388
  1.8.6-p398
  1.8.6-p399
  1.8.6-p420
  1.8.6
  1.8.7-preview1
  1.8.7-preview2
  1.8.7-preview3
  1.8.7-preview4
  1.8.7-p160
  1.8.7-p17
  1.8.7-p173
  1.8.7-p174
  1.8.7-p22
  1.8.7-p248
  1.8.7-p249
  1.8.7-p299
  1.8.7-p301
  1.8.7-p302
  1.8.7-p330
  1.8.7-p334
  1.8.7-p352
  1.8.7-p357
  1.8.7-p358
  1.8.7-p370
  1.8.7-p371
  1.8.7-p373
  1.8.7-p374
  1.8.7-p375
  1.8.7-p71
  1.8.7-p72
  1.8.7
  1.9.0-0
  1.9.0-1
  1.9.0-2
  1.9.0-3
  1.9.0-4
  1.9.0-5
  1.9.1-preview1
  1.9.1-preview2
  1.9.1-rc1
  1.9.1-rc2
  1.9.1-p0
  1.9.1-p129
  1.9.1-p243
  1.9.1-p376
  1.9.1-p378
  1.9.1-p429
  1.9.1-p430
  1.9.1-p431
  1.9.2-preview1
  1.9.2-preview3
  1.9.2-rc1
  1.9.2-rc2
  1.9.2-p0
  1.9.2-p136
  1.9.2-p180
  1.9.2-p290
  1.9.2-p318
  1.9.2-p320
  1.9.2-p326
  1.9.2-p330
  1.9.3-dev
  1.9.3-preview1
  1.9.3-rc1
  1.9.3-p0
  1.9.3-p105
  1.9.3-p125
  1.9.3-p194
  1.9.3-p286
  1.9.3-p327
  1.9.3-p362
  1.9.3-p374
  1.9.3-p385
  1.9.3-p392
  1.9.3-p426
  1.9.3-p429
  1.9.3-p448
  1.9.3-p484
  1.9.3-p545
  1.9.3-p547
  1.9.3-p550
  1.9.3-p551
  2.0.0-dev
  2.0.0-preview1
  2.0.0-preview2
  2.0.0-rc1
  2.0.0-rc2
  2.0.0-p0
  2.0.0-p195
  2.0.0-p247
  2.0.0-p353
  2.0.0-p451
  2.0.0-p481
  2.0.0-p576
  2.0.0-p594
  2.0.0-p598
  2.0.0-p643
  2.0.0-p645
  2.0.0-p647
  2.0.0-p648
  2.1.0-dev
  2.1.0-preview1
  2.1.0-preview2
  2.1.0-rc1
  2.1.0
  2.1.1
  2.1.2
  2.1.3
  2.1.4
  2.1.5
  2.1.6
  2.1.7
  2.1.8
  2.1.9
  2.1.10
  2.2.0-dev
  2.2.0-preview1
  2.2.0-preview2
  2.2.0-rc1
  2.2.0
  2.2.1
  2.2.2
  2.2.3
  2.2.4
  2.2.5
  2.3.0-dev
  2.3.0-preview1
  2.3.0-preview2
  2.3.0
  2.3.1
  2.4.0-dev
  jruby-1.5.6
  jruby-1.6.3
  jruby-1.6.4
  jruby-1.6.5
  jruby-1.6.5.1
  jruby-1.6.6
  jruby-1.6.7
  jruby-1.6.7.2
  jruby-1.6.8
  jruby-1.7.0-preview1
  jruby-1.7.0-preview2
  jruby-1.7.0-rc1
  jruby-1.7.0-rc2
  jruby-1.7.0
  jruby-1.7.1
  jruby-1.7.2
  jruby-1.7.3
  jruby-1.7.4
  jruby-1.7.5
  jruby-1.7.6
  jruby-1.7.7
  jruby-1.7.8
  jruby-1.7.9
  jruby-1.7.10
  jruby-1.7.11
  jruby-1.7.12
  jruby-1.7.13
  jruby-1.7.14
  jruby-1.7.15
  jruby-1.7.16
  jruby-1.7.16.1
  jruby-1.7.16.2
  jruby-1.7.17
  jruby-1.7.18
  jruby-1.7.19
  jruby-1.7.20
  jruby-1.7.20.1
  jruby-1.7.21
  jruby-1.7.22
  jruby-1.7.23
  jruby-1.7.24
  jruby-1.7.25
  jruby-9.0.0.0.pre1
  jruby-9.0.0.0.pre2
  jruby-9.0.0.0.rc1
  jruby-9.0.0.0.rc2
  jruby-9.0.0.0
  jruby-9.0.1.0
  jruby-9.0.3.0
  jruby-9.0.4.0
  jruby-9.0.5.0
  jruby-9.1.0.0-dev
  jruby-9.1.0.0
  jruby-9.1.1.0
  jruby-9.1.2.0
  maglev-1.0.0
  maglev-1.1.0-dev
  maglev-2.0.0-dev
  mruby-dev
  mruby-1.0.0
  mruby-1.1.0
  mruby-1.2.0
  rbx-2.2.2
  rbx-2.2.3
  rbx-2.2.4
  rbx-2.2.5
  rbx-2.2.6
  rbx-2.2.7
  rbx-2.2.8
  rbx-2.2.9
  rbx-2.2.10
  rbx-2.3.0
  rbx-2.4.0
  rbx-2.4.1
  rbx-2.5.0
  rbx-2.5.1
  rbx-2.5.2
  rbx-2.5.3
  rbx-2.5.4
  rbx-2.5.5
  rbx-2.5.6
  rbx-2.5.7
  rbx-2.5.8
  rbx-2.6
  rbx-2.7
  rbx-2.8
  rbx-2.9
  rbx-2.10
  rbx-2.11
  rbx-2.71828182
  rbx-3.0
  rbx-3.1
  rbx-3.2
  rbx-3.3
  rbx-3.4
  rbx-3.5
  rbx-3.6
  rbx-3.7
  rbx-3.8
  rbx-3.9
  rbx-3.10
  rbx-3.11
  rbx-3.12
  rbx-3.13
  rbx-3.14
  rbx-3.15
  rbx-3.16
  rbx-3.17
  rbx-3.18
  rbx-3.19
  rbx-3.20
  rbx-3.21
  rbx-3.22
  rbx-3.23
  rbx-3.24
  rbx-3.25
  rbx-3.26
  rbx-3.27
  rbx-3.28
  rbx-3.29
  rbx-3.30
  rbx-3.31
  rbx-3.32
  rbx-3.33
  ree-1.8.7-2011.03
  ree-1.8.7-2011.12
  ree-1.8.7-2012.01
  ree-1.8.7-2012.02
  topaz-dev
# 任意のバージョンをインストール
$ rbenv install 2.2.5
Downloading ruby-2.2.5.tar.bz2...
-> https://cache.ruby-lang.org/pub/ruby/2.2/ruby-2.2.5.tar.bz2
Installing ruby-2.2.5...
Installed ruby-2.2.5 to /Users/makoto/.rbenv/versions/2.2.5
# 利用できるバージョンの確認
$ rbenv versions
* system (set by /Users/makoto/.rbenv/version)
  2.2.5
# 有効になっているバージョンの確認
$ rbenv version
system (set by /Users/makoto/.rbenv/version)
# 環境全体のバージョンを変更
$ rbenv global 2.2.5
# ディレクトリ固有のバージョン変更
$ rbenv local 2.2.5
# バージョン確認
$ rbenv version
2.2.5 (set by /Users/makoto/.rbenv/version)
# rubyコマンドでもバージョン確認
$ ruby -v
ruby 2.0.0p481 (2014-05-08 revision 45883) [universal.x86_64-darwin14] # 。。。っ!!?

バージョンの切り替えが上手くいかなかった原因

~/.bash_profileに以下を追記する必要があった。

eval "$(rbenv init -)"

これをしないと、rbenvでインストールしたrubyのPATHが反映されない。

$ vi ~/.bash_profile # eval "$(rbenv init -)"を追記
$ source ~/.bash_profile # .bash_profileを読み込み、追記した設定を反映
# 改めてバージョンを確認
$ ruby -v
ruby 2.2.5p319 (2016-04-26 revision 54774) [x86_64-darwin14]

参考サイト

rbenvでRubyのバージョンアップをする(for Mac) - Qiita

rbenv を利用した Ruby 環境の構築 | Developers.IO

rbenvでバージョンがうまく切り替わらなかった時にやったこと - Qiita

『初めてのRuby』

0 3 * * 1 /root/bin/clamdscan.sh > /dev/null 2>&1

http://mktktmr.hatenablog.jp/entry/

初めてのRuby

を読み始めたので、ちょっとずつ、要点をメモってく。 自分がJava屋出身なので、Javaとの違いとか書いてくかも(書くとは言っていない)

読み始めて気付いたこと

この記事を書いてる時点(2016/06/08)でこの書籍はまだ初版なのですが、これからRubyを始める人にとっては情報が古くてしんどいかも。

というのも、この書籍がカバーしているRubyのバージョンは1.9までで、現時点での最新の安定版は2.2.5。

著者はバージョン2.0で大きな変化があるはずと予言(実際にそうなったかは、今後調べる)していて、もし本当なら、この書籍を通して勉強して大丈夫なのかと不安に駆られる。

まあ、Rubyの歴史を勉強するというつもりで読めば良いかも 笑

1章

バージョン体系

バージョンの書式

(Major).(Minor).(Teeny)(Patch Level)
  • Minor
    • 偶数のバージョンが安定版
      ※ 1.9は例外で1.9.0が開発版で1.9.1以降は安定版
  • Path Level
    • 1.9以降に導入された形式でバグ修正のカウント数
    • 非互換性は含まれない
  • Teeny
    • ライブラリへの新機能の追加やAPIのdeprecateが含まれる場合がある
    • 互換性に対する注意が必要

例)バージョンの確認

ruby -v
ruby 2.0.0p481

構成

言語本体

変数やクラス定義、メソッド定義、制御構造などを含む言語本体の仕様。
この仕様に基づいて、プログラムやライブラリを解釈、実行することを処理系と呼ぶ。

組み込みライブラリ

入出力や文字列、配列など、処理系に組み込まれている基本的なライブラリで、明示的にライブラリをロードしなくても利用できる。
また、以下のように細分化できる。

  • 組み込み定数
  • 組み込み変数
  • 組み込み関数
  • 組み込みクラス

標準添付ライブラリ

処理系に付属して配布される外部ライブラリで、明示的にライブラリをロードしないと利用できない。

RubyGems

Ruby1.9から標準添付ライブラリに入り、デファクトスタンダードとなっているRubyのパッケージ管理システム。

Rubyの処理系実装

MRI (Matz's Ruby Implementation)

オリジナルのRuby実装。C言語で書かれているためCRubyとも呼ばれる。

JRuby

MRIJavaで書き直して実装されていて、JVM上で動作する。

IronRuby

.NET Dynamic Language Runtime上で動作する実装。

実行モデル

MRIの場合、インタプリタ
1.9以降は、

解析 => バイトコード変換 => 実行  

という流れ。
解析フェーズでは構文上の妥当性のみ検証されるので、存在しないクラスの継承などは実行時にエラーとなる。

ガベージコレクション

が、実装されているので、プログラマがメモリ管理をする必要はない。

実行時ロード

外部ライブラリをロードする際は実行時にロードされる

main.rb

require 'exlib.rb'

# メイン処理

上記ソースの場合、

  1. main.rbの解析(この時点でexlib.rbの解析は行われない)
  2. main.rbの実行
    2.1. main.rbの実行開始
    2.2. exlib.rbの解析(この時点でまだメイン処理は実行されていない)
    2.3. exlib.rbの実行
    2.4. main.rbの実行に戻る(メイン処理の実行)

という順番で処理が行われる。

文法と機能

式の区切り

入出力

制御式

ブロック付きメソッド

イテレータ
リソース管理
コールバック

OOP

クラス
特異メソッド
演算子ポリモフィズム

2

章 配列とハッシュ

配列

参照

  • まず知っておくべきことは、添字参照はArray#[]メソッドのシンタックスシュガーであること
    • array[0] => array.
  • 範囲外の要素を参照するとnilを返す
array = [0, 1, 2, 3]
puts array[4] # nil. JavaのListクラスの場合、範囲外の要素を参照しようとすると例外(IndexOutOfBoundsException)が発生する
  • 負の添字
    末尾から逆順に要素を取得する
array = [0, 1, 2, 3]
puts array[-1] # 3. array[array.length-1]と等価
puts array[-2] # 2
  • 位置と長さを指定して取得
array = [0, 1, 2, 3]
puts array[2, 2] # [2, 3]. 2番目の要素から2個の要素を取得.
puts array[2, 100] # [2, 100]. 範囲内の要素が含まれていればそれだけを返す
puts array[100, 1] # nil. 範囲内の要素が含まれていなければ、nilを返す
puts array[-3, 3] # [1, 2, 3]. 位置の指定には負の値が利用できる(長さには使えない)
  • 範囲の指定
array = [0, 1, 2, 3]
puts array[1..3] # [1, 2, 3]
puts array[1...3] # [1, 2]. 末端を含まない範囲指定には「...」を利用する
puts array[-4..-3] # [0, 1]. 負の値を範囲の指定に利用できる
puts array[-4..1] # [0, 1]

代入

  • 添字代入はArray#[]=メソッドのシンタックスシュガー
  • 添字代入のいろいろ
array = [1,2]
array[0] = 3 # [3, 2]
array[4] = 4 # [3, 2, nil, nil, 4]. 範囲外を指定して代入した場合、配列が自動的に拡張され、空白の部分にはnilが挿入される
array[0, 3] = 'a', 'b', 'c' # ["a", "b", "c", nil, 4]. 位置と長さによる指定で代入もできる
array[0, 3] = 'a', 'b', 'c', 'd' # ["a", "b", "c", "d", nil, 4]. 左辺で指定した要素数よりも右辺の式が多い場合、配列が拡張されて代入される
array[1..2] = 1, 2 # ["a", 1, 2, "d", nil, 4]
array[0, 2] = '?' # ["?", 2, "d", nil, 4]. #左辺で指定した要素数よりも、右辺の式が少ない場合、配列が詰められて代入される
array[0..2] = 'A' # ["A", nil, 4]
array[-1] = 'Z' # ["A", nil, "Z"]

【Ubuntu】Hubotの開発環境構築をVagrantで自動化

Hubotを開発するための環境構築用のVagrantfile作りました。

github.com

ホストOSで起動しているクライアントからのリクエストがゲストOSで起動したHubotに飛ぶようになっています。

Slackがクライアントの場合でしか試せてませんが、他のクライアントでもいけると思います。

また、node-inspectorとChromeがインストールされるようプロビジョニングしているので、GUIデバッグすることもできます。

なお、前提としてゲストOSはUbuntuデスクトップです。

以前上げた記事のBoxファイルをベースにしてます。

mktktmr.hatenablog.jp

はまったこと

nodeのバージョンが古い

最初、下記の記事とか類似の記事がたくさんあったので参考にnodeを入れたのですが、最新が入りませんでした。

Ubuntu 14.04 に Node.jsをインストールする - Qiita

結局、公式に行ったら答えがありました。

Installing Node.js via package manager | Node.js

curl -sL https://deb.nodesource.com/setup_4.x | sudo -E bash - sudo apt-get install -y nodejs

npmのバージョンが古い

Node.jsとnpmをアップデートする方法 – Rriver

npm update -g npm

で解決。

再起動するとiptablesの設定が吹っ飛ぶ

Ubuntuは再起動するたびにiptablesの設定がリセットされてしまうみたい。

iptables-persistentというツールを使うと永続化できます。

$ sudo apt-get install -y iptables-persistent

これで解決だと思いきや、プロビジョニングで、エラーが発生。。。

$ sudo apt-get install -y iptables-persistent
・
・
・
==> default: dpkg-preconfigure: 標準入力を再オープンできません: そのようなファイルやディレクトリはありません

環境変数DEBIAN_FRONTENDnoninteractiveにしてインストールすることで解決できました。

sudo DEBIAN_FRONTEND=noninteractive apt-get install -y iptables-persistent

DEBIAN_FRONTEND=noninteractive ってなんだ - Qiita

これは、インストーラで使うユーザインタフェースを制御するものらしい。 DEBIAN_FRONTEND=noninteractive のときは、インタラクティブな設定をしなくなる(=入力待ちでブロックしなくなる)ので、自動インストールの際には便利だとか。

Chromeのインストールでエラーが発生する

sudo wget -q -O - https://dl-ssl.google.com/linux/linux_signing_key.pub | sudo apt-key add -
sudo sh -c 'echo "deb http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'
sudo apt-get update
sudo apt-get install -y google-chrome-stable
・
・
・
==> default: W
==> default: : 
==> default: http://dl.google.com/linux/chrome/deb/dists/stable/Release の取得に失敗しました  期待されるエントリ 'main/binary-i386/Packages' が Release ファイル内に見つかりません (誤った sources.list エントリか、壊れたファイル)
==> default: E
==> default: : 
==> default: いくつかのインデックスファイルのダウンロードに失敗しました。これらは無視されるか、古いものが代わりに使われます。

Google Chromeのリポジトリ取得に失敗しましたエラーを修正する | Ubuntuアプリのいいところ

これは、2016年3月に、32bit-LinuxへのGoogle Chromeのサポートが終了した後に発生しているエラーで、64bit版のUbuntuにおいても同じ現象が発生します。

というわけで、

sudo sh -c 'echo "deb [arch=amd64] http://dl.google.com/linux/chrome/deb/ stable main" >> /etc/apt/sources.list.d/google.list'

というふうに、アーキテクチャを64bitに指定することでエラーを回避できます。

参考

Hubotインストール

slackと連携するhubotを3分でインストールする(動画付き)

nodeインストール

Installing Node.js via package manager | Node.js

redisインストール

Ubuntu Linux 14.04 LTSにRedis 3をインストールする - CLOVER

iptables

mysql - Vagrant and MariaDB (provision) - Server Fault

Ubuntu 14.04 で OpenVPN - BLOG EX MACHINA

ubuntu - dpkg-reconfigure: unable to re-open stdin: No file or directory - Server Fault

Chromeのインストール

Ubuntu 14.04:Google Chrome インストール | よしまさのブログ

【Vagrant】日本語環境に設定したUbutuデスクトップをBoxファイルにする

環境

  • ホストOS:OSX
  • ゲストOS:Ubuntu14.02
  • Vagrant :1.8.1
  • プロバイダ:VirtualBox 5.0.16
  • Packer:0.10.0

※最終的に行うBoxファイルの作成はVirtualBoxのみ対応しているので、ご注意を。

手順

PackerによるBoxファイルの作成

VagrantとPackerについては過去記事でも上げてます。

mktktmr.hatenablog.jp

mktktmr.hatenablog.jp

まずはベースとなるBoxファイルをPackerを利用して作成します。

Packerのテンプレートを一から作るのは大変なため、boxcutterをフォークして作成しています。

ubuntu/ubuntu1404-desktop.json at master · mktktmr/ubuntu · GitHub

ubuntu1404-desktop.json

{
  "_comment": "Build with `packer build -var-file=ubuntu1404-desktop.json ubuntu.json`",
  "vm_name": "ubuntu1404-desktop",
  "desktop": "true",
  "cpus": "1",
  "disk_size": "130048",
  "iso_checksum": "3ffb7a3690ce9a07ac4a4d1b829f990681f7e47d",
  "iso_checksum_type": "sha1",
  "iso_name": "ubuntu-14.04.4-server-amd64.iso",
  "iso_path": "iso",
  "iso_url": "http://ftp.riken.jp/Linux/ubuntu-releases/14.04.2/ubuntu-14.04.4-server-amd64.iso",
  "memory": "2048",
  "preseed": "preseed.cfg",
  "vagrantfile_template": "tpl/vagrantfile-ubuntu1404-desktop.tpl"
}

boxcutterのテンプレートからの主な変更点は、ISOイメージの取得先をオリジナルから、理研のミラーサーバに変更しています。

多分日本でダウンロードするならそのほうが早いかなと。

また、試行錯誤するために何度かpacker buildを繰り返したのですが、そのたびにISOイメージをダウンロードしてると時間かかるし、通信料がばかになりません。

PackerのビルドはローカルにISOイメージがあればダウンロードせずにそちらを参照する仕組みになっています。

その際に参照するディレクトリのPATHの設定が「iso_path」で、そこの値も変えています。

というわけであらかじめ理研ミラーサイトからISOファイルを入手して、gitのクローン先にisoディレクトリ掘って、突っ込んでおきます。

テンプレートの作成が終わったら、packer buildでテンプレートファイルを元にBoxファイルを作成します。

$ packer build -only=virtualbox-iso -var-file=ubuntu1404-desktop.json ubuntu.json
virtualbox-iso output will be in this color.

==> virtualbox-iso: Downloading or copying Guest additions
    virtualbox-iso: Downloading or copying: file:///Applications/VirtualBox.app/Contents/MacOS/VBoxGuestAdditions.iso
==> virtualbox-iso: Downloading or copying ISO
    virtualbox-iso: Downloading or copying: file:///Users/hoge/Documents/%E3%82%A4%E3%83%B3%E3%82%B9%E3%83%88%E3%83%BC%E3%83%A9/iso/ubuntu-14.04.4-server-amd64.iso
==> virtualbox-iso: Creating floppy disk...
    virtualbox-iso: Copying: http/preseed.cfg
==> virtualbox-iso: Creating virtual machine...
==> virtualbox-iso: Creating hard drive...
==> virtualbox-iso: Attaching floppy disk...
==> virtualbox-iso: Creating forwarded port mapping for communicator (SSH, WinRM, etc) (host port 3667)
==> virtualbox-iso: Executing custom VBoxManage commands...
    virtualbox-iso: Executing: modifyvm ubuntu1404-desktop --memory 2048
    virtualbox-iso: Executing: modifyvm ubuntu1404-desktop --cpus 1
==> virtualbox-iso: Starting the virtual machine...
==> virtualbox-iso: Waiting 10s for boot...
==> virtualbox-iso: Typing the boot command...
==> virtualbox-iso: Waiting for SSH to become available...
==> virtualbox-iso: Connected to SSH!
==> virtualbox-iso: Uploading VirtualBox version info (5.0.16)
==> virtualbox-iso: Uploading VirtualBox guest additions ISO...
==> virtualbox-iso: Provisioning with shell script: script/update.sh
    virtualbox-iso: ==> Disabling the release upgrader
    virtualbox-iso: ==> Updating list of repositories
    virtualbox-iso: Ign http://us.archive.ubuntu.com trusty InRelease
    virtualbox-iso: Get:1 http://us.archive.ubuntu.com trusty-updates InRelease [65.9 kB]
    virtualbox-iso: Get:2 http://security.ubuntu.com trusty-security InRelease [65.9 kB]
    virtualbox-iso: Get:3 http://us.archive.ubuntu.com trusty-backports InRelease [65.9 kB]
    virtualbox-iso: Hit http://us.archive.ubuntu.com trusty Release.gpg
    ・
    ・
    中略
    ・
    ・
    virtualbox-iso: zenity-common                   install
    virtualbox-iso: zip                     install
    virtualbox-iso: zlib1g:amd64                    install
    virtualbox-iso: ==> Clearing last login information
    virtualbox-iso: 118528571+0 records in
    virtualbox-iso: 118528571+0 records out
    virtualbox-iso: 121373256704 bytes (121 GB) copied, 425.23 s, 285 MB/s
    virtualbox-iso: 189336+0 records in
    virtualbox-iso: 189336+0 records out
    virtualbox-iso: 193880064 bytes (194 MB) copied, 0.478528 s, 405 MB/s
    virtualbox-iso: ==> Clear out swap and disable until reboot
    virtualbox-iso: dd: error writing ‘/dev/dm-1’: No space left on device
    virtualbox-iso: 2049+0 records in
    virtualbox-iso: 2048+0 records out
    virtualbox-iso: 2147483648 bytes (2.1 GB) copied, 3.58034 s, 600 MB/s
    virtualbox-iso: dd exit code 1 is suppressed
    virtualbox-iso: mkswap: /dev/dm-1: warning: don't erase bootbits sectors
    virtualbox-iso: on whole disk. Use -f to force.
    virtualbox-iso: Setting up swapspace version 1, size = 2097148 KiB
    virtualbox-iso: no label, UUID=b604802c-0ad0-41a1-ade1-aa8d8d81dce5
    virtualbox-iso: dd: error writing ‘/EMPTY’: No space left on device
    virtualbox-iso: 122139+0 records in
    virtualbox-iso: 122138+0 records out
    virtualbox-iso: 128071090176 bytes (128 GB) copied, 287.836 s, 445 MB/s
    virtualbox-iso: dd exit code 1 is suppressed
    virtualbox-iso: ==> Disk usage before cleanup
    virtualbox-iso: Filesystem Size Used Avail Use% Mounted on udev 990M 4.0K 990M 1% /dev tmpfs 201M 476K 200M 1% /run /dev/mapper/vagrant--vg-root 123G 3.4G 114G 3% / none 4.0K 0 4.0K 0% /sys/fs/cgroup none 5.0M 0 5.0M 0% /run/lock none 1001M 0 1001M 0% /run/shm none 100M 0 100M 0% /run/user /dev/sda1 236M 39M 185M 18% /boot
    virtualbox-iso: ==> Disk usage after cleanup
    virtualbox-iso: Filesystem                    Size  Used Avail Use% Mounted on
    virtualbox-iso: udev                          990M  8.0K  990M   1% /dev
    virtualbox-iso: tmpfs                         201M  476K  200M   1% /run
    virtualbox-iso: /dev/mapper/vagrant--vg-root  123G  3.4G  114G   3% /
    virtualbox-iso: none                          4.0K     0  4.0K   0% /sys/fs/cgroup
    virtualbox-iso: none                          5.0M     0  5.0M   0% /run/lock
    virtualbox-iso: none                         1001M     0 1001M   0% /run/shm
    virtualbox-iso: none                          100M     0  100M   0% /run/user
    virtualbox-iso: /dev/sda1                     236M   39M  185M  18% /boot
==> virtualbox-iso: Gracefully halting virtual machine...
    virtualbox-iso: Removing floppy drive...
==> virtualbox-iso: Preparing to export machine...
    virtualbox-iso: Deleting forwarded port mapping for the communicator (SSH, WinRM, etc) (host port 3667)
==> virtualbox-iso: Exporting virtual machine...
    virtualbox-iso: Executing: export ubuntu1404-desktop --output output-ubuntu1404-desktop-virtualbox-iso/ubuntu1404-desktop.ovf
==> virtualbox-iso: Unregistering and deleting virtual machine...
==> virtualbox-iso: Running post-processor: vagrant
==> virtualbox-iso (vagrant): Creating Vagrant box for 'virtualbox' provider
    virtualbox-iso (vagrant): Copying from artifact: output-ubuntu1404-desktop-virtualbox-iso/ubuntu1404-desktop-disk1.vmdk
    virtualbox-iso (vagrant): Copying from artifact: output-ubuntu1404-desktop-virtualbox-iso/ubuntu1404-desktop.ovf
    virtualbox-iso (vagrant): Renaming the OVF to box.ovf...
    virtualbox-iso (vagrant): Using custom Vagrantfile: tpl/vagrantfile-ubuntu1404-desktop.tpl
    virtualbox-iso (vagrant): Compressing: Vagrantfile
    virtualbox-iso (vagrant): Compressing: box.ovf
    virtualbox-iso (vagrant): Compressing: metadata.json
    virtualbox-iso (vagrant): Compressing: ubuntu1404-desktop-disk1.vmdk
Build 'virtualbox-iso' finished.

==> Builds finished. The artifacts of successful builds are:
--> virtualbox-iso: 'virtualbox' provider box: box/virtualbox/ubuntu1404-desktop-nocm-0.1.0.box

Boxファイルの登録

作成したBoxファイルをvagrantに登録します。

登録のためのコマンドはvagrant box add {登録名} {Boxファイルのパス | URL}です。

登録名は任意ですが、今回は「ubuntu1404-desktop」にしておきます。

$ vagrant box add ubuntu1404-desktop box/virtualbox/ubuntu1404-desktop-nocm-0.1.0.box
==> box: Box file was not detected as metadata. Adding it directly...
==> box: Adding box '1404-desktop' (v0) for provider: 
    box: Unpacking necessary files from: file:///Users/hoge/Documents/git/boxcutter/ubuntu/box/virtualbox/ubuntu1404-desktop-nocm-0.1.0.box
==> box: Successfully added box '1404-desktop' (v0) for 'virtualbox'!

仮想マシンの起動

vagrant initをすることでVagrantfileのテンプレートファイルが作成されます。

引数には先ほど登録したBoxファイルの名前を指定します。

$ vagrant init ubuntu1404-desktop
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.
$ ls
Vagrantfile

Vagrantファイルの中身を確認。

Vagrantfile

# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure(2) do |config|
  # The most common configuration options are documented and commented below.
  # For a complete reference, please see the online documentation at
  # https://docs.vagrantup.com.

  # Every Vagrant development environment requires a box. You can search for
  # boxes at https://atlas.hashicorp.com/search.
  config.vm.box = "ubuntu1404-desktop"

  # Disable automatic box update checking. If you disable this, then
  # boxes will only be checked for updates when the user runs
  # `vagrant box outdated`. This is not recommended.
  # config.vm.box_check_update = false

  # Create a forwarded port mapping which allows access to a specific port
  # within the machine from a port on the host machine. In the example below,
  # accessing "localhost:8080" will access port 80 on the guest machine.
  # config.vm.network "forwarded_port", guest: 80, host: 8080

  # Create a private network, which allows host-only access to the machine
  # using a specific IP.
  # config.vm.network "private_network", ip: "192.168.33.10"

  # Create a public network, which generally matched to bridged network.
  # Bridged networks make the machine appear as another physical device on
  # your network.
  # config.vm.network "public_network"

  # Share an additional folder to the guest VM. The first argument is
  # the path on the host to the actual folder. The second argument is
  # the path on the guest to mount the folder. And the optional third
  # argument is a set of non-required options.
  # config.vm.synced_folder "../data", "/vagrant_data"

  # Provider-specific configuration so you can fine-tune various
  # backing providers for Vagrant. These expose provider-specific options.
  # Example for VirtualBox:
  #
  # config.vm.provider "virtualbox" do |vb|
  #   # Display the VirtualBox GUI when booting the machine
  #   vb.gui = true
  #
  #   # Customize the amount of memory on the VM:
  #   vb.memory = "1024"
  # end
  #
  # View the documentation for the provider you are using for more
  # information on available options.

  # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies
  # such as FTP and Heroku are also available. See the documentation at
  # https://docs.vagrantup.com/v2/push/atlas.html for more information.
  # config.push.define "atlas" do |push|
  #   push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME"
  # end

  # Enable provisioning with a shell script. Additional provisioners such as
  # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
  # documentation for more information about their specific syntax and use.
  # config.vm.provision "shell", inline: <<-SHELL
  #   sudo apt-get update
  #   sudo apt-get install -y apache2
  # SHELL
end

今回立ち上げるゲストOSは日本語化のベースにするだけで、特にプロビジョニングなどはしないため、修正は行わずそのまま使います。

config.vm.boxの値のが正しく設定されていることだけ確認できればOKです。

というわけで、vagrant initに続けてvagrant up仮想マシンを立ち上げます。

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'ubuntu1404-desktop'...
==> default: Matching MAC address for NAT networking...
==> default: Setting the name of the VM: ubuntu1404-desktop_default_1459864237341_70016
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Running 'pre-boot' VM customizations...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Remote connection disconnect. Retrying...
    default: Warning: Remote connection disconnect. Retrying...
    default: 
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default: 
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
==> default: Mounting shared folders...
    default: /vagrant => /Users/hoge/Documents/vagrant/ubuntu1404-desktop

f:id:mktktmr:20160405225501p:plain

日本語環境の設定

以下、ゲストOSのUbuntuでの作業になります。

ソフトウェアアップデート

初めてUbuntuを立ち上げるとソフトウェアアップデートの通知が来るので、アップデートしておきます。

f:id:mktktmr:20160328123142p:plain

f:id:mktktmr:20160328123153p:plain

f:id:mktktmr:20160328123203p:plain

再起動が必要な設定がこの後もあるので、ここでは再起動しないことにします。

f:id:mktktmr:20160328123213p:plain

日付のローカライズ

タイムゾーンをTokyoに設定します。

System Settings > Time&Date を選択します。

f:id:mktktmr:20160406194942p:plain

f:id:mktktmr:20160328123223p:plain

地図をクリックしたり、フォームに直接入力するなどしてLocationをTokyoに設定。

f:id:mktktmr:20160328123232p:plain

f:id:mktktmr:20160328123242p:plain

ついでにClockの設定も好みに合わせて設定しておきます。

f:id:mktktmr:20160328123251p:plain

言語のローカライズ

System Settings > Language Support を選択します。

f:id:mktktmr:20160328123258p:plain

Language Supportを開くと、「言語サポートに必要なソフトウェアのインストールが完了していないので、インストールしてください」的なことを言われます。

英語のサポートなので必要ないかもしれませんが、一応インストールします。

f:id:mktktmr:20160328123306p:plain

f:id:mktktmr:20160328123312p:plain

f:id:mktktmr:20160328123319p:plain

本題の日本語化の作業に入ります。

Install / Remove Languages を選択します。

f:id:mktktmr:20160328123326p:plain

Japaneseにチェックを入れて、Apply Changesをクリック。

f:id:mktktmr:20160328123333p:plain

日本語の言語サポートに必要なソフトウェアのインストールが始まります。

f:id:mktktmr:20160328123340p:plain

インストールが終了したら、日本語の優先度を上げるため言語のリストの一番上に「日本語」が来るようにドラッグします。

日本語を一番上にしたら、Apply System-Wideをクリックし、システム全体に適用します。

f:id:mktktmr:20160328123347p:plain

次に通貨や日付のフォーマットを日本に合わせる設定を行います。

Regional Format タブを選択します。

f:id:mktktmr:20160328123356p:plain

日本語を選択し、Apply System-Wideをクリックし、システム全体に適用します。

設定直後はExampleが正しく反映されません。

Language Supportを開き直すと正しく反映されるので、開き直して確認します。

f:id:mktktmr:20160328123405p:plain

f:id:mktktmr:20160328123412p:plain

ここまで設定したら、仮想マシンを再起動します。

再起動はホストOSのコンソールからvagrant reloadを叩いて行います。

再起動が済むとフォルダ名を日本語にリネームするか聞かれますので、好みでリネームするかどうか選択してください。

f:id:mktktmr:20160328123421p:plain

ちなみに私はしませんでした。

キーマップのローカライズ

キーボードのレイアウトがUS配列になっているので、日本語配列に設定します。

システム設定 > テキスト入力を選択。

f:id:mktktmr:20160328123430p:plain

日本語(Anthy)を選択して右下に現れるツールアイコンをクリックします。

f:id:mktktmr:20160406230541p:plain

IBus-Anthyの設定ダイアログが開くので、入力タイプを選択します。

f:id:mktktmr:20160328123437p:plain

キーボードレイアウトをデフォルトからjpに変更します。

f:id:mktktmr:20160407193512p:plain

f:id:mktktmr:20160407193521p:plain

ibusを再起動してくださいと表示されますが、一度ログアウトしてログインしなおすだけで大丈夫です。

f:id:mktktmr:20160328123500p:plain

ここまでで日本語化の作業は終わりになります。

ホストOSに戻ってvagrant halt仮想マシンを止めてください。

日本語環境にした仮想環境のBoxファイルの出力

日本語設定を行った仮想マシンのVagrantfileがあるディレクトリでvagrant packageを実行することで、日本語設定がされた状態のBoxファイルを作成することができます。

下記例では--output オプションで出力されるBoxファイルのファイル名を指定しています。

指定しないとデフォルトの package.boxというファイル名で出力されます。

$ vagrant package --output ubuntu1404-ja-desktop-nocm-0.1.0.box
==> default: Clearing any previously set forwarded ports...
==> default: Exporting VM...
==> default: Compressing package to: /Users/hoge/Documents/vagrant/ubuntu1404-desktop/ubuntu1404-ja-desktop-nocm-0.1.0.box

ここまでの作業で日本語環境になったUbuntuが使えるようになりました。

利用のする際はBoxファイルの登録で行ったようにvagrant box addをしてvagrant initvagrant upをするだけです。

今後はこのBoxファイルをベースに、Vagrantfileでプロビジョニングして色々と開発環境を作ってみようと思います。

参考

Vagrantの既存Boxに日本語環境 + GuestAdditions + Chefを入れ、新しいBoxとして追加する - メモ的な思考的な

Ubuntu 13.10 その7 - 日本語環境の構築・Ubuntu Japanese Teamのリポジトリーを追加する - kledgeb

【Vagrant】Packerでboxファイルの作成

以前、vagrantの初歩的な使い方について記事を挙げましたが、その際に利用したBoxファイルはvagrantboxからダウンロードしていました。

mktktmr.hatenablog.jp

今回は他人が作ったBoxファイルを拝借していたのを、自作にしようというのが趣旨です。

環境

Packerの入手

Boxファイルを作成するツールは幾つかあるみたいですが、一番メジャーそうなPackerを使います。

Packer by HashiCorp

公式サイトからツールをダウンロードしてきます。

ダウンロードしたファイル(packer_0.10.0_darwin_amd64.zip)を解凍すると"packer"になります。

自分は上記pakerを"/usr/local/bin/"ディレクトリに移動しました。

テンプレートの入手

一からBoxファイルのテンプレート(設定ファイル)を作成するのは大変なためboxcutterからテンプレートを入手します。

今回はUbuntuのBoxファイルを作るので、Ubuntuリポジトリをgitクローンしておきます。

テンプレートの設定値を変えることで、作成する仮想マシンのディスク容量やメモリ量、ISOファイルのダウンロード先などを変更できますが、今回はいじらず、デフォルトで作成します。

$ git clone https://github.com/boxcutter/ubuntu.git
Cloning into 'ubuntu'...
remote: Counting objects: 1790, done.
remote: Compressing objects: 100% (13/13), done.
remote: Total 1790 (delta 3), reused 0 (delta 0), pack-reused 1776
Receiving objects: 100% (1790/1790), 323.25 KiB | 21.00 KiB/s, done.
Resolving deltas: 100% (1178/1178), done.
Checking connectivity... done.
$ cd ubuntu
$ ls -la
-rw-r--r--   1 hoge  staff   9630  3 21 03:04 ubuntu1510.json
-rw-r--r--   1 hoge  staff    421  3 21 03:04 ubuntu1404.json
-rw-r--r--   1 hoge  staff    544  3 21 03:04 ubuntu1404-i386.json
-rw-r--r--   1 hoge  staff    455  3 21 03:04 ubuntu1404-docker.json
-rw-r--r--   1 hoge  staff    421  3 21 03:04 ubuntu1204.json
-rw-r--r--   1 hoge  staff    544  3 21 03:04 ubuntu1204-i386.json
-rw-r--r--   1 hoge  staff    455  3 21 03:04 ubuntu1204-docker.json
-rw-r--r--   1 hoge  staff    547  3 21 03:04 ubuntu1204-desktop.json
drwxr-xr-x   6 hoge  staff    204  3 21 03:04 tpl
drwxr-xr-x   5 hoge  staff    170  3 21 03:04 test
drwxr-xr-x  14 hoge  staff    476  3 21 03:04 script
drwxr-xr-x   5 hoge  staff    170  3 21 03:04 floppy
-rw-r--r--   1 hoge  staff    218  3 21 03:04 custom-script.sh
drwxr-xr-x  10 hoge  staff    340  3 21 03:04 bin
-rw-r--r--   1 hoge  staff      7  3 21 03:04 VERSION
-rw-r--r--   1 hoge  staff   8381  3 21 03:04 README.md
-rw-r--r--   1 hoge  staff   4170  3 21 03:04 Makefile
-rw-r--r--   1 hoge  staff  11335  3 21 03:04 LICENSE
-rw-r--r--   1 hoge  staff   4528  3 21 03:04 CHANGELOG.md
-rw-r--r--   1 hoge  staff    887  3 21 03:04 AUTHORS
drwxr-xr-x   2 hoge  staff     68  3 23 07:29 tmp
-rw-r--r--   1 hoge  staff   8580  3 23 19:08 ubuntu.json
drwxr-xr-x   6 hoge  staff    204  3 25 21:41 iso
drwxr-xr-x   3 hoge  staff    102  3 25 21:46 packer_cache
drwxr-xr-x   6 hoge  staff    204  3 25 21:50 http
-rw-r--r--   1 hoge  staff    565  3 27 10:48 ubuntu1404-desktop.json
drwxr-xr-x   5 hoge  staff    170  3 27 15:26 box

boxファイルの作成

packer buildでbuildを実行。

boxcutterのテンプレートはpacker buildの-var-fileオプションで設定するテンプレートを組み合わせることで、 様々なOSバージョンやアーキテクチャに対応できるように構成されています。

今回はUbuntu14.04のデスクトップ版のBoxファイルを作成するため、ubuntu1404-desktop.jsonubuntu.jsonを組み合わせてbuildします。

ちなみに、packer buildはISOファイルのダウンロードなどあるためかなり時間がかかります(1時間くらい)。

気長に待ちましょう。

# 今回作成するBoxファイルはVirtualBox向けのものだけなので、オプションに-only=virtualbox-isoを設定しています
$ packer build -only=virtualbox-iso -var-file=ubuntu1404-desktop.json ubuntu.json
virtualbox-iso output will be in this color.

==> virtualbox-iso: Downloading or copying Guest additions
    virtualbox-iso: Downloading or copying: file:///Applications/VirtualBox.app/Contents/MacOS/VBoxGuestAdditions.iso
==> virtualbox-iso: Downloading or copying ISO
    virtualbox-iso: Downloading or copying: file:///Users/hoge/Documents/ubuntu-14.04.4-server-amd64.iso
==> virtualbox-iso: Creating floppy disk...
    virtualbox-iso: Copying: http/preseed.cfg
==> virtualbox-iso: Creating virtual machine...
==> virtualbox-iso: Creating hard drive...
==> virtualbox-iso: Attaching floppy disk...
==> virtualbox-iso: Creating forwarded port mapping for communicator (SSH, WinRM, etc) (host port 2590)
==> virtualbox-iso: Executing custom VBoxManage commands...
    virtualbox-iso: Executing: modifyvm ubuntu1404-desktop --memory 2048
    virtualbox-iso: Executing: modifyvm ubuntu1404-desktop --cpus 1
==> virtualbox-iso: Starting the virtual machine...
==> virtualbox-iso: Waiting 10s for boot...
==> virtualbox-iso: Typing the boot command...
==> virtualbox-iso: Waiting for SSH to become available...
==> virtualbox-iso: Connected to SSH!
==> virtualbox-iso: Uploading VirtualBox version info (5.0.16)
==> virtualbox-iso: Uploading VirtualBox guest additions ISO...
==> virtualbox-iso: Provisioning with shell script: script/update.sh
    virtualbox-iso: ==> Disabling the release upgrader
    virtualbox-iso: ==> Updating list of repositories
    virtualbox-iso: Ign http://us.archive.ubuntu.com trusty InRelease
    virtualbox-iso: Hit http://security.ubuntu.com trusty-security InRelease
    virtualbox-iso: Hit http://us.archive.ubuntu.com trusty-updates InRelease
#〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜(中略)〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
    virtualbox-iso: 0 upgraded, 1296 newly installed, 0 to remove and 26 not upgraded.
    virtualbox-iso: Need to get 549 MB of archives.
    virtualbox-iso: After this operation, 2,093 MB of additional disk space will be used.
    virtualbox-iso: Get:1 http://us.archive.ubuntu.com/ubuntu/ trusty/main libavahi-common-data amd64 0.6.31-4ubuntu1 [21.2 kB]
    virtualbox-iso: Get:2 http://us.archive.ubuntu.com/ubuntu/ trusty/main libavahi-common3 amd64 0.6.31-4ubuntu1 [21.7 kB]
    virtualbox-iso: Get:3 http://us.archive.ubuntu.com/ubuntu/ trusty/main libavahi-client3 amd64 0.6.31-4ubuntu1 [25.1 kB]
    virtualbox-iso: Get:4 http://us.archive.ubuntu.com/ubuntu/ trusty-updates/main libcups2 amd64 1.7.2-0ubuntu1.7 [179 kB]
    virtualbox-iso: Get:5 http://us.archive.ubuntu.com/ubuntu/ trusty-updates/main libcupsmime1 amd64 1.7.2-0ubuntu1.7 [12.1 kB]
    virtualbox-iso: Get:6 http://us.archive.ubuntu.com/ubuntu/ trusty/main libpaper1 amd64 1.1.24+nmu2ubuntu3 [13.4 kB]
#〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜(中略)〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
    virtualbox-iso: Get:1292 http://us.archive.ubuntu.com/ubuntu/ trusty/main unity-lens-friends amd64 0.1.3+14.04.20140317-0ubuntu1 [22.9 kB]
    virtualbox-iso: Get:1293 http://us.archive.ubuntu.com/ubuntu/ trusty/main unity-scope-gdrive all 0.9+13.10.20130723-0ubuntu1 [11.6 kB]
    virtualbox-iso: Get:1294 http://us.archive.ubuntu.com/ubuntu/ trusty/main usb-modeswitch-data all 20140327-1 [27.0 kB]
    virtualbox-iso: Get:1295 http://us.archive.ubuntu.com/ubuntu/ trusty/main usb-modeswitch amd64 2.1.1+repack0-1ubuntu1 [50.0 kB]
    virtualbox-iso: Get:1296 http://us.archive.ubuntu.com/ubuntu/ trusty/main xfonts-mathml all 6ubuntu1 [42.5 kB]
    virtualbox-iso: [sudo] password for vagrant: debconf: unable to initialize frontend: Dialog
    virtualbox-iso: debconf: (Dialog frontend will not work on a dumb terminal, an emacs shell buffer, or without a controlling terminal.)
    virtualbox-iso: debconf: falling back to frontend: Readline
    virtualbox-iso: debconf: unable to initialize frontend: Readline
    virtualbox-iso: debconf: (This frontend requires a controlling tty.)
    virtualbox-iso: debconf: falling back to frontend: Teletype
    virtualbox-iso: dpkg-preconfigure: unable to re-open stdin:
    virtualbox-iso: Fetched 549 MB in 34min 7s (268 kB/s)
    virtualbox-iso: Selecting previously unselected package libavahi-common-data:amd64.
    virtualbox-iso: (Reading database ... 61160 files and directories currently installed.)
    virtualbox-iso: Preparing to unpack .../libavahi-common-data_0.6.31-4ubuntu1_amd64.deb ...
    virtualbox-iso: Unpacking libavahi-common-data:amd64 (0.6.31-4ubuntu1) ...
#〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜(中略)〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜〜
    virtualbox-iso: zenity-common                   install
    virtualbox-iso: zip                     install
    virtualbox-iso: zlib1g:amd64                    install
    virtualbox-iso: ==> Clearing last login information

    virtualbox-iso: 118528559+0 records in
    virtualbox-iso: 118528559+0 records out
    virtualbox-iso: 121373244416 bytes (121 GB) copied, 438.839 s, 277 MB/s
    virtualbox-iso: 189341+0 records in
    virtualbox-iso: 189341+0 records out
    virtualbox-iso: 193885184 bytes (194 MB) copied, 0.553525 s, 350 MB/s
    virtualbox-iso: ==> Clear out swap and disable until reboot
    virtualbox-iso: dd: error writing ‘/dev/dm-1’: No space left on device
    virtualbox-iso: 2049+0 records in
    virtualbox-iso: 2048+0 records out
    virtualbox-iso: 2147483648 bytes (2.1 GB) copied, 3.58726 s, 599 MB/s
    virtualbox-iso: dd exit code 1 is suppressed
    virtualbox-iso: mkswap: /dev/dm-1: warning: don't erase bootbits sectors
    virtualbox-iso: on whole disk. Use -f to force.
    virtualbox-iso: Setting up swapspace version 1, size = 2097148 KiB
    virtualbox-iso: no label, UUID=a02f609d-1d2e-4086-8ed6-b3c1340a89cc
    virtualbox-iso: dd: error writing ‘/EMPTY’: No space left on device
    virtualbox-iso: 122139+0 records in
    virtualbox-iso: dd exit code 1 is suppressed
    virtualbox-iso: 122138+0 records out
    virtualbox-iso: 128071041024 bytes (128 GB) copied, 292.316 s, 438 MB/s
    virtualbox-iso: ==> Disk usage before cleanup
    virtualbox-iso: Filesystem Size Used Avail Use% Mounted on udev 990M 4.0K 990M 1% /dev tmpfs 201M 476K 200M 1% /run /dev/mapper/vagrant--vg-root 123G 3.4G 114G 3% / none 4.0K 0 4.0K 0% /sys/fs/cgroup none 5.0M 0 5.0M 0% /run/lock none 1001M 0 1001M 0% /run/shm none 100M 0 100M 0% /run/user /dev/sda1 236M 39M 185M 18% /boot
    virtualbox-iso: ==> Disk usage after cleanup
    virtualbox-iso: Filesystem                    Size  Used Avail Use% Mounted on
    virtualbox-iso: udev                          990M  8.0K  990M   1% /dev
    virtualbox-iso: tmpfs                         201M  476K  200M   1% /run
    virtualbox-iso: /dev/mapper/vagrant--vg-root  123G  3.4G  114G   3% /
    virtualbox-iso: none                          4.0K     0  4.0K   0% /sys/fs/cgroup
    virtualbox-iso: none                          5.0M     0  5.0M   0% /run/lock
    virtualbox-iso: none                         1001M     0 1001M   0% /run/shm
    virtualbox-iso: none                          100M     0  100M   0% /run/user
    virtualbox-iso: /dev/sda1                     236M   39M  185M  18% /boot
==> virtualbox-iso: Gracefully halting virtual machine...
    virtualbox-iso: Removing floppy drive...
==> virtualbox-iso: Preparing to export machine...
    virtualbox-iso: Deleting forwarded port mapping for the communicator (SSH, WinRM, etc) (host port 2590)
==> virtualbox-iso: Exporting virtual machine...
    virtualbox-iso: Executing: export ubuntu1404-desktop --output output-ubuntu1404-desktop-virtualbox-iso/ubuntu1404-desktop.ovf
==> virtualbox-iso: Unregistering and deleting virtual machine...
==> virtualbox-iso: Running post-processor: vagrant
==> virtualbox-iso (vagrant): Creating Vagrant box for 'virtualbox' provider
    virtualbox-iso (vagrant): Copying from artifact: output-ubuntu1404-desktop-virtualbox-iso/ubuntu1404-desktop-disk1.vmdk
    virtualbox-iso (vagrant): Copying from artifact: output-ubuntu1404-desktop-virtualbox-iso/ubuntu1404-desktop.ovf
    virtualbox-iso (vagrant): Renaming the OVF to box.ovf...
    virtualbox-iso (vagrant): Using custom Vagrantfile: tpl/vagrantfile-ubuntu1404-desktop.tpl
    virtualbox-iso (vagrant): Compressing: Vagrantfile
    virtualbox-iso (vagrant): Compressing: box.ovf
    virtualbox-iso (vagrant): Compressing: metadata.json
    virtualbox-iso (vagrant): Compressing: ubuntu1404-desktop-disk1.vmdk
Build 'virtualbox-iso' finished.

==> Builds finished. The artifacts of successful builds are:
--> virtualbox-iso: 'virtualbox' provider box: box/virtualbox/ubuntu1404-desktop-nocm-0.1.0.box

ここまででBoxファイルの作成は完了です。

box/virtualboxディレクトリ下に

ubuntu1404-desktop-nocm-0.1.0.boxというBoxファイルが出来上がっているはずなので、確認してみてください。

vagrantへのbox追加

これ以降は以前挙げたVagrantの使い方と同じなのですが、一応手順載せておきます。

作業は適当なディレクトリを掘って行ってください。

vagrant box addでboxファイルを登録

$ vagrant box add test box/virtualbox/ubuntu1404-desktop-nocm-0.1.0.box
==> box: Box file was not detected as metadata. Adding it directly...
==> box: Adding box 'test' (v0) for provider: 
    box: Unpacking necessary files from: file:///Users/hoge/Documents/ubuntu/box/virtualbox/ubuntu1404-desktop-nocm-0.1.0.box
==> box: Successfully added box 'test' (v0) for 'virtualbox'!

Vagrantfileの作成

vagrant initでVagrantfaileファイルを生成

$ vagrant init test
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.

vagrantの実行

vagrant up仮想マシン起動

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'test2'...
==> default: Matching MAC address for NAT networking...
==> default: Setting the name of the VM: ubuntu1404-desktop_default_1458702162155_73549
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Running 'pre-boot' VM customizations...
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: 
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default: 
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
==> default: Mounting shared folders...
    default: /vagrant => /Users/hoge/Documents/vagrant/ubuntu1404-desktop

うまく起動したでしょうか?

f:id:mktktmr:20160406192511p:plain

Boxファイルを自作しておけば、中身が分かるし、Boxファイルを余所からダウンロードする時間も省けるため、Boxファイルの登録が気軽にできて良いかと思います。

参考

Appendix B. Automating the installation using preseeding

Packerをつかって3ステップでVagrantのBoxを作る - Qiita

PreseedによるUbuntuの自動インストール入門 - Qiita

日本人向け Packer スクリプト差分 for Ubuntu Trusty - 来年読む。

「Packer」でDocker用のイメージファイルを作ってみよう - さくらのナレッジ

Packerを使ったISOイメージからの仮想マシン自動デプロイ - さくらのナレッジ

【Vagrant】インストールと基本コマンド

環境

用語

プロバイダ

Virtual Boxとか、VM Wareとか仮想マシン本体のこと。 EC2なんかもプロバイダとして利用出来るらしい。

プロビジョニング

Chefとか。 ミドルウェアのインストールや設定を行うツールシェルスクリプトでも出来るみたいで、Chefを使う気満々だったのが少し削がれた。

Boxファイル

仮想マシンを起動する際のベースとなるイメージファイル。 通常、OSイメージをベースにvagrantユーザの作成・sshd起動・プロビジョニングツールのインストールなど最小限の設定を行う。

Vagrantfile

仮想マシンのスペックやプロビジョニングツールの指定などの構成を記述するファイル。 Rubyで記述する。 こいつさえあれば、どんな環境でも同じ仮想マシンが構築できる。

Vagrantインストール

公式サイトからインストーラを入手、ポチポチしてインストールします。

Vagrant by HashiCorp

バージョン確認

$ vagrant -v
Vagrant 1.8.1

ヘルプ

$ vagrant -h
Usage: vagrant [options] <command> [<args>]

    -v, --version                    Print the version and exit.
    -h, --help                       Print this help.

Common commands:
     box             manages boxes: installation, removal, etc.
     connect         connect to a remotely shared Vagrant environment
     destroy         stops and deletes all traces of the vagrant machine
     global-status   outputs status Vagrant environments for this user
     halt            stops the vagrant machine
     help            shows the help for a subcommand
     init            initializes a new Vagrant environment by creating a Vagrantfile
     login           log in to HashiCorp's Atlas
     package         packages a running vagrant environment into a box
     plugin          manages plugins: install, uninstall, update, etc.
     port            displays information about guest port mappings
     powershell      connects to machine via powershell remoting
     provision       provisions the vagrant machine
     push            deploys code in this environment to a configured destination
     rdp             connects to machine via RDP
     reload          restarts vagrant machine, loads new Vagrantfile configuration
     resume          resume a suspended vagrant machine
     share           share your Vagrant environment with anyone in the world
     snapshot        manages snapshots: saving, restoring, etc.
     ssh             connects to machine via SSH
     ssh-config      outputs OpenSSH valid configuration to connect to the machine
     status          outputs status of the vagrant machine
     suspend         suspends the machine
     up              starts and provisions the vagrant environment
     version         prints current and latest Vagrant version

For help on any individual command run `vagrant COMMAND -h`

Additional subcommands are available, but are either more advanced
or not commonly used. To see all subcommands, run the command
`vagrant list-commands`.

# vagrant [subcmd] -h でサブコマンドのヘルプも参照できる
$ vagrant box -h
Usage: vagrant box <subcommand> [<args>]

Available subcommands:
     add
     list
     outdated
     remove
     repackage
     update

For help on any individual subcommand run `vagrant box <subcommand> -h`

Boxファイルのインストール

Boxファイルは自分で作成可能ですが、vagrantbox.esで配布もされています。

自作は必要ができたら取り組むとして今回はvagrantboxから入手します。

Boxファイルの追加

# vagrant box add [box名(任意)] [URL or PATH]
$ vagrant box add Ubuntu14.04_daily_Cloud_Image_amd64 https://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box
==> box: Box file was not detected as metadata. Adding it directly...
==> box: Adding box 'Ubuntu14.04_daily_Cloud_Image_amd64' (v0) for provider: 
    box: Downloading: https://cloud-images.ubuntu.com/vagrant/trusty/current/trusty-server-cloudimg-amd64-vagrant-disk1.box
==> box: Successfully added box 'Ubuntu14.04_daily_Cloud_Image_amd64' (v0) for 'virtualbox'!

# 確認
$ vagrant box list
Ubuntu14.04_daily_Cloud_Image_amd64 (virtualbox, 0)

Boxファイルの削除

# vagrant box remove [box名]
$ vagrant box remove Ubuntu14.04_daily_Cloud_Image_amd64

Vagrantfileの作成

Vagrantfileは"vagrant init [box名]"を叩くと作成されます。

# vagrant init [box名]
$ vagrant init Ubuntu14.04_daily_Cloud_Image_amd64
A `Vagrantfile` has been placed in this directory. You are now
ready to `vagrant up` your first virtual environment! Please read
the comments in the Vagrantfile as well as documentation on
`vagrantup.com` for more information on using Vagrant.

# 確認
$ ls
Vagrantfile

vagrant init で作成されたvagrantファイルにはデフォルトで色々書かれているので、それを参考に設定を記述してみた。

ちなみに今回設定していることは以下

  1. 仮想マシンのウィンドウを開く
  2. プロビジョニングの設定
    • パッケージの更新
    • ubuntu-desktopのインストール

Vagrantfile

# -*- mode: ruby -*-
# vi: set ft=ruby :

# All Vagrant configuration is done below. The "2" in Vagrant.configure
# configures the configuration version (we support older styles for
# backwards compatibility). Please don't change it unless you know what
# you're doing.
Vagrant.configure(2) do |config|
  # The most common configuration options are documented and commented below.
  # For a complete reference, please see the online documentation at
  # https://docs.vagrantup.com.

  # Every Vagrant development environment requires a box. You can search for
  # boxes at https://atlas.hashicorp.com/search.
  config.vm.box = "Ubuntu14.04_daily_Cloud_Image_amd64"

  # Disable automatic box update checking. If you disable this, then
  # boxes will only be checked for updates when the user runs
  # `vagrant box outdated`. This is not recommended.
  # config.vm.box_check_update = false

  # Create a forwarded port mapping which allows access to a specific port
  # within the machine from a port on the host machine. In the example below,
  # accessing "localhost:8080" will access port 80 on the guest machine.
  # config.vm.network "forwarded_port", guest: 80, host: 8080

  # Create a private network, which allows host-only access to the machine
  # using a specific IP.
  # config.vm.network "private_network", ip: "192.168.33.10"

  # Create a public network, which generally matched to bridged network.
  # Bridged networks make the machine appear as another physical device on
  # your network.
  # config.vm.network "public_network"

  # Share an additional folder to the guest VM. The first argument is
  # the path on the host to the actual folder. The second argument is
  # the path on the guest to mount the folder. And the optional third
  # argument is a set of non-required options.
  # config.vm.synced_folder "../data", "/vagrant_data"

  # Provider-specific configuration so you can fine-tune various
  # backing providers for Vagrant. These expose provider-specific options.
  # Example for VirtualBox:
  #
  config.vm.provider "virtualbox" do |vb|
    # Display the VirtualBox GUI when booting the machine
    ### (1)仮想マシンをGUIで起動する(ウィンドウを立ち上げる)
    vb.gui = true
  
  #   # Customize the amount of memory on the VM:
  #   vb.memory = "1024"
  end
  #
  # View the documentation for the provider you are using for more
  # information on available options.

  # Define a Vagrant Push strategy for pushing to Atlas. Other push strategies
  # such as FTP and Heroku are also available. See the documentation at
  # https://docs.vagrantup.com/v2/push/atlas.html for more information.
  # config.push.define "atlas" do |push|
  #   push.app = "YOUR_ATLAS_USERNAME/YOUR_APPLICATION_NAME"
  # end

  # Enable provisioning with a shell script. Additional provisioners such as
  # Puppet, Chef, Ansible, Salt, and Docker are also available. Please see the
  # documentation for more information about their specific syntax and use.
  ### (2)起動時にパッケージの更新とデスクトップのインストールをする
  ### プロビジョニングは初回のvagrant upでしか走らないので注意
  config.vm.provision "shell", inline: <<-SHELL
    sudo apt-get update
    sudo apt-get install -y ubuntu-desktop
  SHELL
end

仮想マシンの起動と停止

起動

vagrant仮想マシンを起動するには、Vagrantfileがあるディレクトリでvagrant upを叩きます。

$ vagrant up
Bringing machine 'default' up with 'virtualbox' provider...
==> default: Importing base box 'Ubuntu14.04_daily_Cloud_Image_amd64'...
==> default: Matching MAC address for NAT networking...
==> default: Setting the name of the VM: Ubuntu1404_daily_Cloud_Image_amd64_default_1458397444446_89786
==> default: Clearing any previously set forwarded ports...
==> default: Clearing any previously set network interfaces...
==> default: Preparing network interfaces based on configuration...
    default: Adapter 1: nat
==> default: Forwarding ports...
    default: 22 (guest) => 2222 (host) (adapter 1)
==> default: Booting VM...
==> default: Waiting for machine to boot. This may take a few minutes...
    default: SSH address: 127.0.0.1:2222
    default: SSH username: vagrant
    default: SSH auth method: private key
    default: Warning: Remote connection disconnect. Retrying...
    default: 
    default: Vagrant insecure key detected. Vagrant will automatically replace
    default: this with a newly generated keypair for better security.
    default: 
    default: Inserting generated public key within guest...
    default: Removing insecure key from the guest if it's present...
    default: Key inserted! Disconnecting and reconnecting using new SSH key...
==> default: Machine booted and ready!
==> default: Checking for guest additions in VM...
    default: The guest additions on this VM do not match the installed version of
    default: VirtualBox! In most cases this is fine, but in rare cases it can
    default: prevent things such as shared folders from working properly. If you see
    default: shared folder errors, please make sure the guest additions within the
    default: virtual machine match the version of VirtualBox you have installed on
    default: your host and reload your VM.
    default: 
    default: Guest Additions Version: 4.3.36
    default: VirtualBox Version: 5.0
==> default: Mounting shared folders...
    default: /vagrant => /Users/hoge/Documents/vagrant/Ubuntu14.04_daily_Cloud_Image_amd64

Vagrantファイルにて"vb.gui = true"と設定しているので、VirtualBoxのウィンドウで立ち上がる。

VirtualBox

f:id:mktktmr:20160319233308p:plain

初期ユーザは

id: vagrant
pass: vagrant

となってます。

停止

$ vagrant halt
==> default: Attempting graceful shutdown of VM...

廃棄

"vagrant destroy"を叩くと、仮想マシンを廃棄できます。

プロビジョニングの設定などは初期起動時のみしか読み込まれないので、Vagrantfileを書き直した時などは一度仮想マシンを廃棄したほうが良いです。

$ vagrant destroy
    default: Are you sure you want to destroy the 'default' VM? [y/N] y
==> default: Destroying VM and associated drives...

参考

vagrant

ubuntu