Web Marina

日々の業務や勉強などで得た知識をアウトプットしていきます。

Rails プラグインを学んでみる。(後編)

f:id:song-of-life1352607:20170411225447p:plain

こんにちは、マリンです。


前回はRailsガイドの「プラグイン作成入門」の3まで書きました。今回は続きの4から書きます。


前回の記事はこちら。

song-of-life.hatenablog.com

4.“acts_as"メソッドをActive Recordに追加する。

acts_as~

  • プラグインのモデルによく追加されるメソッドの名前。


ここでは後に作成する"squawk"メソッドをActive Recordに追加するためのメソッド、"acts_as_yaffle"を追加します。


<必要ファイルの準備>

   # test/acts_as_yaffle_test.rb
        require 'test_helper'
         
        class ActsAsYaffleTest < ActiveSupport::TestCase
        end
        
    # lib/yaffle.rb
        require 'yaffle/core_ext'
        require 'yaffle/acts_as_yaffle'
         
        module Yaffle
        end
        
         # lib/yaffle/acts_as_yaffle.rb
        module Yaffle
          module ActsAsYaffle
            # ここにコードを書く
          end
        end
    # test/acts_as_yaffle_test.rb
        require 'test_helper'
         
        class ActsAsYaffleTest < ActiveSupport::TestCase
        end
        
    # lib/yaffle.rb
        require 'yaffle/core_ext'
        require 'yaffle/acts_as_yaffle'
         
        module Yaffle
        end
        
    # lib/yaffle/acts_as_yaffle.rb
        module Yaffle
          module ActsAsYaffle
            # ここにコードを書く
          end
        end

クラスメソッドを追加する

プラグインはホストアプリにインストールして使うものです。 そのため、もしかするとホストアプリとプラグインでメソッド名が被ってしまうことが起こるかもしれません。その問題を解決するためのクラスメソッドを追加します。

  • “yaffle_text_field” : 名前を変更できるようにするメソッド。


<test作成>

# yaffle/test/acts_as_yaffle_test.rb
 
require 'test_helper'
 
class ActsAsYaffleTest < ActiveSupport::TestCase
 
  def test_a_hickwalls_yaffle_text_field_should_be_last_squawk
    assert_equal "last_squawk", Hickwall.yaffle_text_field
  end
 
  def test_a_wickwalls_yaffle_text_field_should_be_last_tweet
    assert_equal "last_tweet", Wickwall.yaffle_text_field
  end
 
end

このテストはHickwallとWickwallモデルがないのでRedです。


<モデル作成>

テストを通すために必要なモデルをダミーアプリに作成します。

$ cd test/dummy
$ bin/rails generate model Hickwall last_squawk:string
$ bin/rails generate model Wickwall last_squawk:string last_tweet:string

マイグレーションを実行して、テストデータベースにテーブルを作成します。

$ cd test/dummy
$ bin/rails db:migrate

作成したモデルがyafflesとして振る舞うよう指示します。

# test/dummy/app/models/hickwall.rb
 
class Hickwall < ActiveRecord::Base
  acts_as_yaffle
end
 
# test/dummy/app/models/wickwall.rb
 
class Wickwall < ActiveRecord::Base
  acts_as_yaffle yaffle_text_field: :last_tweet
end




<acts_as_yaffleメソッドを定義>

前述のモデルに使用したacts_as_yaffleを定義します。

# yaffle/lib/yaffle/acts_as_yaffle.rb
module Yaffle
  module ActsAsYaffle
    extend ActiveSupport::Concern
 
    included do
    end
 
    module ClassMethods
      def acts_as_yaffle(options = {})
        # ここにコードを書く
      end
    end
  end
end
 
ActiveRecord::Base.send :include, Yaffle::ActsAsYaffle




ここでテストを行うと、今度はyaffle_text_fieldが定義されていないということでRedになると思います。前述のモデルに使用したacts_as_yaffleメソッドを実装し、この中で定義してテストが通るようにします。

# yaffle/lib/yaffle/acts_as_yaffle.rb
 
module Yaffle
  module ActsAsYaffle
   extend ActiveSupport::Concern
 
    included do
    end
 
    module ClassMethods
      def acts_as_yaffle(options = {})
        cattr_accessor :yaffle_text_field
        self.yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s
      end
    end
  end
end
 
ActiveRecord::Base.send :include, Yaffle::ActsAsYaffle

cattr_accessor

  • クラス外から対象のクラス変数へアクセスするためのメソッドです。
  • 似たものにattr_accessorがありますが、これはインスタンス変数にアクセスするためのメソッドです。

これでテストはGreenになります。

インスタンスメソッドを追加する

先に述べた'squawk'メソッドを追加します。このメソッドはデータベースの値のいずれかひとつを設定するメソッドです。

<テスト作成>

# yaffle/test/acts_as_yaffle_test.rb
require 'test_helper'
 
class ActsAsYaffleTest < ActiveSupport::TestCase
 
  def test_a_hickwalls_yaffle_text_field_should_be_last_squawk
    assert_equal "last_squawk", Hickwall.yaffle_text_field
  end
 
  def test_a_wickwalls_yaffle_text_field_should_be_last_tweet
    assert_equal "last_tweet", Wickwall.yaffle_text_field
  end
 
  def test_hickwalls_squawk_should_populate_last_squawk
    hickwall = Hickwall.new
    hickwall.squawk("Hello World")
    assert_equal "squawk! Hello World", hickwall.last_squawk
  end
 
  def test_wickwalls_squawk_should_populate_last_tweet
    wickwall = Wickwall.new
    wickwall.squawk("Hello World")
    assert_equal "squawk! Hello World", wickwall.last_tweet
  end
end

squawkが定義されていないのでこれはRedになります。


<'squawk'を定義>

# yaffle/lib/yaffle/acts_as_yaffle.rb
 
module Yaffle
  module ActsAsYaffle
    extend ActiveSupport::Concern
 
    included do
    end
 
    module ClassMethods
      def acts_as_yaffle(options = {})
        cattr_accessor :yaffle_text_field
        self.yaffle_text_field = (options[:yaffle_text_field] || :last_squawk).to_s
 
        include Yaffle::ActsAsYaffle::LocalInstanceMethods
      end
    end
 
    module LocalInstanceMethods
      def squawk(string)
        write_attribute(self.class.yaffle_text_field, string.to_squawk)
      end
    end
  end
end
 
ActiveRecord::Base.send :include, Yaffle::ActsAsYaffle

これでテストはGreenになります。


ここまででプラグインの開発は終了です。

5. ジェネレータ

ジェネレータ

  • 与えられた条件などに基づいてデータやプログラムコードを自動的に生成するプログラムなど。


    もしgemにジェネレータを含めたい場合には、作成したジェネレータをプラグインlib/generatorsディレクトリに置くだけで大丈夫です。

6. gemを公開する

せっかく作成したプラグインなので、公開したい場合があると思います。gemはGitリポジトリを使用して共有が可能です。


他のアプリケーションで使用するには、まずgemのコードをリモートリポジトリにコミットし、その後gemを使用したいアプリケーションのGemfileに書いてbundle installすることで利用できます。

gem 'yaffle', git: 'git://github.com/yaffle_watcher/yaffle.git'

この時コードのURLや、ローカルのディレクトリの場合はパスを忘れず書きましょう。


もしgemを正式なリリースとして一般公開するなら、RubyGemsでパブリッシュします。詳細はこちら(英語です。)


ここまでで直接開発に関わる説明は終わりです。 7項は「デプロイするならきちんとドキュメントを書きましょう」という内容なので省略します。

まとめ

基本的にはアプリの開発と同じですね。あとはファイル構成や、gemspec、本アプリでの使用方法などでつまずかなければいけそうです。


私の場合は自分のサイトにブログ機能を追加することが目的なので、よりホストアプリに寄り添ってるっぽいエンジン(多分似たようなものだと思うのですが)を使って開発していこうと思います。


とはいえ、プラグインが作れるようになるとできることの幅が広がりそうなので、サラッとでも勉強しておくと良さそうですね。

Rails プラグインを学んでみる。(前編)

f:id:song-of-life1352607:20170411225447p:plain

こんにちは、マリンです。

ポートフォリオサイトのスマホ化がひと段落しましたので、今度はブログ機能をつけようと思います。

せっかくなので、以前ちょこっと触れたプラグインをやってみようと思い、現在RailsガイドさんのRails プラグイン作成入門を勉強しています。

ここまでの内容を勉強ノートとしてまとめ。

PluginとEngine

本文とは少し外れますが、前回触れたのがエンジンで、今勉強中なのがプラグインで・・・

何が違うの?と思ったのでちょっと調べました。

Plugin

  • コアフレームワークを拡張したり変更したりするのに使う。

  • gemとしてビルドするので、RubygemsとBundlerを使用して他のRailsアプリケーションと共有することが可能。

Engine

  • プラグイン同様ホストアプリに機能を提供するものだが、エンジンはよりアプリに近く、ホストアプリのミニチュアのようなもの。

  • 実際にはRailsアプリの方がエンジンを拡張したようなもので、Rails::ApplicationはRails::Engineから多くの振る舞いを継承している。

・・・イマイチなんですが、

どうやらよりホストアプリ寄りなのがエンジンで、いろんなアプリで使ってもらうのを想定しているのがプラグインかな?まぁ、名前的にも。

ということは今回はエンジンのが良さそうですが、

なんとなくプラグインの方が基本ぽかったのでひとまず続けることにしました。

1 設定

gem形式のプラグインを生成する

まずはプラグインのスケルトンを作成します。

*ちなみにスケルトンは骨組みです。私はなんか透過的な意味かと思ってました。

$ rails plugin new

このコマンドはあらゆるRails拡張機能の開発用スケルトンを生成するためのものです。

ここで生成したスケルトンには、ダミーのRailsアプリが入っているので、それを使って統合テストなんかも行うことが可能です。

ちなみにこのコマンドで生成されるファイルはこちら。

      create
          create  README.md
          create  Rakefile
          create  yaffle.gemspec
          create  MIT-LICENSE
          create  .gitignore
          create  Gemfile
          create  lib/yaffle.rb
          create  lib/tasks/yaffle_tasks.rake
    
          create  lib/yaffle/version.rb
          create  bin/test
          create  test/test_helper.rb
          create  test/yaffle_test.rb
          append  Rakefile
      vendor_app  test/dummy

2 新しく生成したプラグインをテストする

テストの前に、作成したプラグインディレクトリでbundle installします。

が、さらにその前に、.gemspecファイルの"TODO"の部分を修正してください。

プラグインではgemの依存関係?などをGemfileではなく~.gemspecファイルに記述します。

デフォルトのままだとこのファイルに記述できるs.homepageやらs.summaryやらの内容が"TODO"になっており、このままだとbundle installした時にエラーになってしまうので、ここを修正してください。

その後、プラグインディレクトリにてrake testです。

ダミーアプリの中ではrails test

プラグインディレクトリではrakeです。

3 コアクラスを拡張する

ここではプラグインが使用されるRailsアプリケーションのどこでも利用可能なメソッドを追加する方法を学びます。

  • 追加先 : Stringクラス

  • 追加メソッド : to_squawk

テストファイルの作成

この教材はテスト駆動方式で進めているので、まずテストからです。

core_ext_test.rbファイルを作成し、以下のテストを書きます。

require 'test_helper'
 
class CoreExtTest < ActiveSupport::TestCase
  def test_to_squawk_prepends_the_word_squawk
    assert_equal "squawk! Hello World", "Hello World".to_squawk
  end
end

まだメソッドが実装されていないのでエラーになります。

メソッドの実装

メソッドを実装してテストが通るようにします。

<require 'yaffle/core_ext'を追加>

# lib/yaffle.rb

require 'yaffle/core_ext'

module Yaffle
end

プラグインではまずlib直下のyaffle.rb(ファイル名はそれぞれです)が呼び出されます。

そのためまずはここで呼び出して欲しいファイル名を記述。

<メソッドの実装>

# lib/yaffle/core_ext.rb

String.class_eval do
  def to_squawk
    "squawk! #{self}".strip
  end
end

前述のrequireで呼び出されるファイルにメソッドを定義します。

メソッドを使ってみる

ここまででto_squawkメソッドが使用できるようになったので、

rails consoleで試してみます。

rails consoleを実行するには、ダミーアプリに移動してください。

$ cd test
$ cd dummy
$ bin/rails console
>> "Hello World".to_squawk
=> "squawk! Hello World"

長くなってきたので、今回はここまでにします。

次は「4.“acts_as"メソッドをActive Recordに追加する」から書きます。

サイトをスマホ対応にする

f:id:song-of-life1352607:20170221152659j:plain

こんにちは、マリンです。

今回作ったサイト、最初からレスポンシブ対応にしていたつもりだったのですが、

いざ本番で動かしてみたらスマホ対応にはなっていなかったようです。

そこで今回、Railsでサイトをスマホ対応にする方法を勉強したので書き綴っておきます。

Action Pack variants

まず、今回はgemは使わずに実装しました。違いがいまいちわからなかったので(笑)

Rails4.1から実装されましたAction Pack Variantsを使用します。

Action Pack Variantsとは、スマホタブレット、PCなどのブラウザごとに異なるテンプレートを使用したい場合に、

その振り分けを簡単に実現できるようにするためのRailsの機能です。

手順

Controller

class ApplicationController < ActionController::Base
  before_action :set_request_variant

  private
    def set_request_variant
      case request.user_agent
      when /iPad/
        request.variant = :phone
      when /iPhone/
        request.variant = :phone
      when /Android/
        request.variant = :phone
      end
  end
end

application_controller.erbに上記を記述します。

Private内で振り分けるデバイスを指定します。

メソッド名はなんでも大丈夫ですが、「なんの時にはどのvariant」

ということは明示してあげてください。

後述しますが、私はここで一度失敗しました。

Views

variantごとに個別のテンプレートを用意します。

テンプレートのファイル名には、variantで指定した:~を記述します。

app/views/index.html.erb  #指定外のデバイス
app/views/index.html+phone.erb  #指定のデバイス

このようにすることで、Railsが自動的にデバイスごとにテンプレートを振り分けて表示してくれます。

失敗編

前述しましたが、指定の仕方が甘くて一度失敗しました。

最初のコード

class ApplicationController < ActionController::Base
  before_action :set_request_variant

private
    def set_request_variant
        request.variant = :phone
    end
end

最初はこれしか書いてませんでした。

これだとPCの場合でもスマホのテンプレートが使用されてしまっていました。

そこでこちらの記事を参考にさせていただき、

きちんとcase~whenを使用してそれぞれ指定し、

PCを入れないように記述するとうまくいきました。

satolabo.0t0.jp

まとめ

Bootstrapのレスポンシブ対応=スマホ対応と勘違いしていたことがそもそもの間違いでした。

でもこのVariantの導入でかなり楽にできるようになったようですね。

言語の発展で自動的にやってくれることがたくさんあるため、ついつい忘れてしまいますが、

本来ならコンピュータには1~10まで全て指示しなければいけないんですよね。

どこまでやってくれて、どこから自分でやらなければならないのか、

それもきちんと把握する必要がありそうです。

S3用のIAMを作る

f:id:song-of-life1352607:20170401112255j:plain

サイトの画像利用のためにAWSのS3を使っています。

そのままマスターのアカウントを使い続けるのはあまり良くないので、

S3用にIAMを使ってユーザーを追加しようと思います。

前提

  • すでに対象のバケットは作成済み

  • S3用なのでそれ以外の細かい設定はしません

手順

追加ユーザーの設定

(1)サービスの中からIAMを選択

(2)「ユーザーを追加」を選択

サイドメニューの「ユーザー」を選択して、「ユーザーを追加」をクリックします。

(3)ユーザー名、アクセスの種類などを指定

  • アクセスの種類

    • プログラムによるアクセス -> アクセスキーやシークレットキーを有効にします。

    • AWSマネジメントコンソールへのアクセス -> 追加ユーザーがコンソールへアクセスする際のパスワードを有効にします。 (これが無いと一度マスターから入ってパスワードを設定しなければならなくなります)

  • コンソールのパスワード

    • 最初のパスワードの生成方法を自動か手動か選べます。
  • パスワードのリセットが必要

    • 最初にログインした際、任意のパスワードに変更させるかさせないかの設定です。

S3用のポリシーの作成

ポリシーとは所定の書式に従い、1つ以上のアクセス許可を記述したドキュメントのことです。

(1)ポリシーの作成を選択

IAMを前項まで設定し、次に進むと以下のような画面に切り替わります。

ここで「既存のポリシーを直接アタッチ」を選択し、

ポリシー一覧の左上にある「ポリシーを作成」をクリックします。

(2)Policy Ganarator選択

「ポリシーの作成」画面の真ん中の項、「Policy Ganarator」を選択します。

(3)アクセス許可の編集

この編集を3パターン行います。

  1. 効果 -> 許可

    AWSサービス -> Amazon S3

    アクション -> 全てのアクション

    ARN -> arm:aws:s3:::バケット名

  2. 効果、AWSサービス、アクションまで 1 と同じ。

    ARN -> arm:aws:s3:::バケット/*

  3. 効果 -> 許可

    AWSサービス -> Amazon S3

    アクション -> ListAllMyBucktes

    ARN -> arm:aws:s3:::*

(4)ポリシーの確認

JSONファイルで色々設定もできるようです。

確認してOKなら右下の「ポリシーを作成」をクリック。

これでS3用のポリシー作成完了です。

こちらのサイトを参考にさせていただきました。

Amazon S3 専用のアカウントを作ってみよう (フェンリル | デベロッパーズブログ)

引き続きユーザーの設定

(1)作成したポリシーを選択

先ほどのポリシー一覧を下にスクロールしていくと作成したポリシーが出てきます。

これをチェック!

(2)確認

「次のステップ:確認」で確認画面が出ます。

OKなら「ユーザーの作成」

(3)アクセスキーとシークレットキーをメモ

*Herokuとかの設定で使うと思うのでメモっといた方が良いと思います。

詳しくはこちら

song-of-life.hatenablog.com

まとめ

AWSは慣れないとよくわからないですね・・・

細かく設定すればもっと色々できるんだと思いますが、

まだよくわかっていないので、今回はここまでで止めておきます。

関連記事

song-of-life.hatenablog.com

song-of-life.hatenablog.com

Amazon S3の新しいバケットを作る。

f:id:song-of-life1352607:20170401112255j:plain こんにちは、マリンです。

サイトでの画像登録用にAWSのS3を使ったので、

その時の手順をメモっときます。

song-of-life.hatenablog.com

前提

  • AWSのアカウントは取得済み

  • 途中の細かい設定はまだよくわからなので飛ばしてます。

手順

(1)サービスでS3を選択し、「バケットを作成する」をクリック。

(2)バケット名、リージョンを決定。

(3)プロパティとアクセス制限は「次へ」

*設定される方はきちんと設定してあげてください。私は飛ばしてます(笑)

(4)最後に確認して良ければ「バケットを作成」

*私の使ってるスクショの様子がおかしく、画像の下の方がダブってますが、本来はきちんと表示されています。見づらくて申し訳ありません。

まとめ

ただ作るだけなら特に問題はないですね。

リージョンの意味がわからなくて無駄に調べちゃいましたが…

次はこのS3ようのIAMを作ろうと思います。

画像が表示されないからfogとかS3とか使う。

f:id:song-of-life1352607:20170221152659j:plain

こんにちは、マリンです。

早速問題発生しました。

ポートフォリオの画像が、一瞬表示されるのに気づいたら消えてる!?

調べてみたら、Herokuは画像データを保存できないので、

クラウドストレージがマストだとのこと。

Rails Tutorialの13.4.4「本番環境での画像アップロード」ところです。

「上手くいかなかったらここ飛ばしちゃっていいよ〜👍」なんて書いてあったけど、

飛ばしたら全然ダメじゃん!!(えぇ。私は飛ばしましたとも。)

Heroku使って本番運用をお考えの方はサラッとでも見とくことをオススメします。

と言うことで、今更ながら13章を見直した話しです。

13.4.4 本番環境での画像アップロードです。 railstutorial.jp

fog

まずはfog gemから。Gemfileに追加して$bundle installです。

gem 'fog'

fogはRubyクラウドサービスを使いやすくするためのGemです。

次に本番環境での画像ファイルの保存場所を変更します。

app/uploaders/○○_uploader.rbを変更します。

  if Rails.env.production?
    storage :fog
  else
    storage :file
  end

論理値を返すproduction?メソッドで環境毎に保存先を変えられるようにします。

S3

S3はAWSのサービスの一つです。

*長くなるのでセットアップについては別記事で書こうと思います。

S3のセットアップが終了してからの設定です。

 1. config/initializerscarrier_wave.rbファイルを作ります。

$touch config/initializers/carrier_wave.rb

 2. CarrierWaveを通してS3を使う設定

if Rails.env.production?
  CarrierWave.configure do |config|
    config.fog_credentials = {
      # Amazon S3用の設定
      :provider              => 'AWS',
      :region                => ENV['S3_REGION'], 
      :aws_access_key_id     => ENV['S3_ACCESS_KEY'],
      :aws_secret_access_key => ENV['S3_SECRET_KEY']
    }
    config.fog_directory     =  ENV['S3_BUCKET']
  end
end

 3. Herokuの環境変数を設定

AWSでIAMを作成した際のアクセスキー、シークレットキー、バケット名、レギオン(地域)を入力。

$ heroku config:set S3_ACCESS_KEY="Accessキーを入力"
$ heroku config:set S3_SECRET_KEY="同様に、Secretキーを入力"
$ heroku config:set S3_BUCKET="Bucketの名前を入力"
$ heroku config:set S3_REGION="Regionの名前を入力"

 4. .gitignoreを編集

ローカルの画像ファイルをGitの対象から外します。

.
.
.
/public/uploads

 5. デプロイします

$git add -A
$git commit -m "hoge hoge"
$git push
$git push heroku
$heroku run pg:reset DATABASE
$heroku run rails db:migrate

データベースのリセットやサンプルデータがある場合などはご自身の環境に合わせて行ってください。

これで設定は完了です。

コケたところ

Herokuの環境変数についてよくわかっていなかったので、

REGIONの入力方法を間違えました。

さらにその間違えた変数を消去せずに正しいものを追加してしまったので、

ポートフォリオを全然追加できず、ハマってしまいました。

詳細

  • REGIONの値をS3の設定時に選択した「Asia pacific(Tokyo)」と入力。

  • 上記を削除することなく、追加で正規のリージョンを設定してしまう。

対処

 1. 状況確認

まず全体的に意味がわからなかったので、とりあえず一覧表示させて状況を確認しました。

 2. ダブり

上書きされてると勘違いしていたREGIONが正誤ダブっていることに気づいたので、

間違えた方を削除しました。

誤 -> Asia pacific(Tokyo)

正 -> ap-northeast-1

ちなみにS3でのRegionの表記についてはこちらに書かれています。

AWS のリージョンとエンドポイント - アマゾン ウェブ サービス

Herokuの環境変数について

一覧表示

$heroku config

変数名指定

$heroku config:get 環境変数名
$heroku config:set 環境変数名=値
$heroku config:unset 環境変数名

こちらのサイトを参考にさせていただきました。

chomast.com

まとめ

やはり一番大変だったのはAWS関係でした。

全く触ったことがない、名前しか知らないような状況でしたので・・・

今回行ったセットアップの方法については別記事で書こうと思います。

ここでは書かれていたことしかやっていないので氷山の一角だと思いますが、

かなり奥が深く、またとても便利なツールのようですね。

最近では結構必須な知識にもなっているようですし、

今までなんとなく避けていた分、これからきちんと勉強しようと思います。

追記

AWS系の方も記事書きましたのでよろしければどうぞ。

S3 song-of-life.hatenablog.com

完成しました〜!!

f:id:song-of-life1352607:20170323231451p:plain

MyWeb

いやぁ・・・長かったです。

たったこれだけのサイト作るのに1ヶ月かかってしまいましたorz

しかもほとんどtutorialパクっただけなのに・・・

しかし何はともあれ、無事完成し、デプロイも完了しました!

まだまだ改良の余地が満載なので、

今後少しずつでもよくしていこうと思います。

てか、仮でつけてた名前そのままであげちゃった・・・w

さて・・・次はアプリ作成に挑んでみようか?