プログラミングを完全に理解したエンジニアのメモ

チラ裏レベルのことしか書いてないインターネットの藻屑

deviseでユーザー招待機能の追加

したいこと

  • 登録されているユーザーがメアドを入力して送信すると招待用URLが記載されているメールを送信できる
  • 招待用URLを踏むとパスワード設定画面に遷移できる
  • パスワードが完了すると登録が完了する

=>slackに招待用URLで人追加する時と同じ

businesschatmaster.com

前提

  • deviseは導入済み
  • deviseを適用しているモデルはuser
  • viewとcontrollerもdeviseディレクトリでなくusersディレクトリ配下にある
  • devise.rbはconfig.scoped_views = trueになっている
  • 開発環境でのメール確認はletter_openerletter_opener_webを導入済み

実装方法

github.com

Gemfile

追加してbundle install

gem 'devise_invite'

rails command

$ rails generate devise_invitable:install
$ rails generate devise_invitable user
$ bundle exec rake db:migrate

models/user.rbの変更、migrationファイル、viewファイルができる

models/user.rb

これが既存

class User < ApplicationRecord
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable
end

修正後

class User < ApplicationRecord
  devise :invitable, :database_authenticatable, :registerable,
         :recoverable, :rememberable, :trackable, :validatable,
         invite_for: 2.weeks
end

routes.rb

devise_for :users, controllers: {
    sessions:      'users/sessions',
    passwords:     'users/passwords',
    registrations: 'users/registrations',
    invitations: 'users/invitations' # 追加
}

確認

  • http://localhost:3000/users/invitation/newにアクセスして、メールフォームにメアドを入れて送信すると、root_pathにリダイレクトする。
  • シークレットブラウザでhttp://localhost:3000/letter_openerにアクセスすると招待メールがきていて、"Accept invitation"を押すと、http://localhost:3000/users/invitation/accept?invitation_token=XXXXXXXXXXXXXXに飛んで、パスワード設定画面が表示される。
  • パスワードを設定するとログインできる

参考URL

github.com

railsの開発でメール送信テスト

gemfile

developmentのグループに以下を追加

gem 'letter_opener'
gem 'letter_opener_web'

routes.rb

以下を追加

mount LetterOpenerWeb::Engine, at: '/letter_opener' if Rails.env.development?

development.rb

config/environments/development.rb

以下を追加

config.action_mailer.delivery_method = :letter_opener_web
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }

動作確認

http://localhost:3000/users/password/new

にアクセスしてパスワードリセットのメールを送信してみる。

http://localhost:3000/letter_opener

にアクセスすると送信されたメールの一覧が見れる

enumを日本語化するenum_help

topic.rb

class Topic < ApplicationRecord
  enum status: {
    draft: 0,
    published: 1,
    privated: 2
  }
end

gem

gem 'enum_help'

ja.yml

config/locales/ja.yml

ja:
  enums:
    topic:
      status:
        draft: 下書き
        published: 公開
        privated: 非公開

application.rb

config/application.rb

config.i18n.default_locale = :ja

を追加

RailsにreCAPTCHAを導入する

問題

問い合わせフォームにスパムが多くなったのでreCAPTCHAを入れる

サイト登録

www.google.com にアクセスして「Get reCAPTCHA」をクリック

f:id:fujitora:20180305103249p:plain

「Register a new site」のフォームに必要事項を入力 Site keyとSecret keyが出る。

注意点としては「Domains」にはサイトのURLとlocalhostを追加すること

ambethia/recaptcha

github.com このgemを使う

導入

gem

dotenv-railsとrecaptchaを入れる

gem "recaptcha", require: "recaptcha/rails"
gem 'dotenv-rails'

Gemfileに追加してbundle install

env

.env

RECAPTCHA_SITE_KEY='6Lxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'
RECAPTCHA_SECRET_KEY='6Lxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx'

recaptcha.rb

config/initializers/recaptcha.rb

Recaptcha.configure do |config|
  config.site_key  = ENV['RECAPTCHA_SITE_KEY']
  config.secret_key = ENV['RECAPTCHA_SECRET_KEY']
  # Uncomment the following line if you are using a proxy server:
  # config.proxy = 'http://myproxy.com.au:8080'
end

view

form_forの中のreCAPTCHAを表示させたいところに追加する

= recaptcha_tags

認証されたcallbackを受け取ってsubmitボタンのdisable外したい時は

= recaptcha_tags callback: 'recaptchaCallbackFunction'

にする

controller

createメソッドに追加する

if verify_recaptcha(model: @contact) && @contact.save
  # 成功した時の処理(メール送信とか)
  redirect_to "/contact", notice: '送信成功'
else
  render 'new'
end

callbackを受けてdisable属性とdisableクラスを外す

application.js

var recaptchaCallbackFunction = function () {
  $('#js-recaptcha').removeClass('disable');
  $('#js-recaptcha').prop('disabled', false);
};

jsで追加した要素にイベントが発生しない

$(document).on 'click', '.tab-pane.active .image-garally img', ->
    console.log $(this)
    return

もしくは親要素の下のクラスという指定の仕方をするとうまく動く

$('.tab-pane.active').on 'click', '.image-garally img', ->
  console.log 'boxをクリックしました!'
  return

rake db:migrate:resetとrake db:seedをまとめて実行してくれるrake taskを作成

$ vi lib/tasks/db.rake
namespace :db do
  desc "Run 'db:migrate:reset' and 'db:seed'"
  task resetup: ['db:migrate:reset', 'db:seed']
end

fukajun/rails-db-resetupgithub.com をコピペしただけ

ただstarの低いgemを入れたくなかっただけ

bundle installでcapybara-webkitのエラー

railsでcloneしてbundle install したらエラーがでた

An error occurred while installing capybara-webkit (1.7.1), and Bundler cannot continue.
Make sure that `gem install capybara-webkit -v '1.7.1'` succeeds before bundling.

解決方法

github.com

macのバージョンがmacOS Sierraなのでこれ https://github.com/thoughtbot/capybara-webkit/wiki/Installing-Qt-and-compiling-capybara-webkit#macos-sierra-1012-el-capitan-1011-and-yosemite-1010

$ brew install qt@5.5
$ echo 'export PATH="$(brew --prefix qt@5.5)/bin:$PATH"' >> ~/.bashrc
$ brew link --force qt@5.5

コピペ用の進捗バー

css

.progressbar {
  width: 100%;
  height: 20px;
  margin-bottom: 20px;
  overflow: hidden;
  background-color: #f5f5f5;
  border-radius: 4px;
  -webkit-box-shadow: inset 0 1px 2px rgba(0,0,0,.1);
  box-shadow: inset 0 1px 2px rgba(0,0,0,.1);
  .progress {
    background-color: #5cb85c;
    height: 20px;
    &-0 {
      width: 0%;
    }
    &-20 {
      width: 20%;
    }
    &-40 {
      width: 40%;
    }
    &-60 {
      width: 60%;
    }
    &-80 {
      width: 80%;
    }
    &-100 {
      width: 100%;
    }
  }
}

html

<div class="progressbar">
  <div class="progress progress-0">

<div class="progressbar">
  <div class="progress progress-20">

<div class="progressbar">
  <div class="progress progress-40">

<div class="progressbar">
  <div class="progress progress-60">

<!-- (略) -- >

イメージ

f:id:fujitora:20171120174719p:plain

ジーニアスバーでは教えてくれなかったワイモバイルのsimでiPhone8に乗り換えてAPN設定してもネットが使えない問題の解決法

問題

ワイモバイルのiPhone5SからsimフリーiPhone8に乗り換えるとネットが使えなかった 公式のプロファイルを入れたけど、ネットに繋がらなかった。 simはn141

www.ymobile.jp

解決方法

iOS用APN構成プロファイルジェネレータで非公式なプロファイルを作る 注意点としてsafariでアクセスすること。(chromeでアクセスしてもできない) iOS用APN構成プロファイルジェネレータ

項目は以下の通り

APN: kqtqjs
ユーザー名: tnsrknk
パスワード: cmtknrn
認証タイプ: PAP

プロトコル、プロキシは空欄でOK

で再起動。

再起動してなかったために1時間近く手間取った。。。 電波の名前はymobileになるかとおもったけどsoftbankのままだった

非公式プロファイルなので自己責任で

slimテンプレートに変えた時にkaminariでhtmlが表示される

症状

途中からslimテンプレートに変えたらhtmlが表示されるようになった

f:id:fujitora:20170924190105p:plain

解決方法

github.com

app/view/kaminari/_paginator.html.slimを書き換える

デフォルト

= paginator.render do
  ul.pagination
    = first_page_tag unless current_page.first?
    = prev_page_tag unless current_page.first?
    - each_page do |page|
      - if page.left_outer? || page.right_outer? || page.inside_window?
        = page_tag page
      - elsif !page.was_truncated?
        = gap_tag
    = next_page_tag unless current_page.last?
    = last_page_tag unless current_page.last?

修正後

= paginator.render do
  ul.pagination
    == first_page_tag unless current_page.first?
    == prev_page_tag unless current_page.first?

    - each_page do |page|
      - if page.left_outer? || page.right_outer? || page.inside_window?
        == page_tag page
      - elsif !page.was_truncated?
        == gap_tag

    == next_page_tag unless current_page.last?
    == last_page_tag unless current_page.last?

Railsでパソコンとスマホでviewを分ける

Gemfile

gem 'rack-user_agent'

app/controllers/application_controller.rb

上でbefore_actionで判定させる。

before_action :check_user_agent_for_mobile

判定のメソッド

def check_user_agent_for_mobile
    if request.from_smartphone?
      request.variant = :mobile
    end
  end

view

index.html.erbがPCで表示されてindex.html+mobile.erbがSPで表示されるようになる。

AWS EC2のiptableとSELinuxをオフにする

EC2でRailsのサイト公開してもアクセスできなかった原因

初期状態

$ getenforce
Enforcing

効いている。

$ sudo service iptables status
Table: filter
Chain INPUT (policy ACCEPT)
num  target     prot opt source               destination
1    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0           state RELATED,ESTABLISHED
2    ACCEPT     icmp --  0.0.0.0/0            0.0.0.0/0
3    ACCEPT     all  --  0.0.0.0/0            0.0.0.0/0
4    ACCEPT     tcp  --  0.0.0.0/0            0.0.0.0/0           state NEW tcp dpt:22
5    REJECT     all  --  0.0.0.0/0            0.0.0.0/0           reject-with icmp-host-prohibited

Chain FORWARD (policy ACCEPT)
num  target     prot opt source               destination
1    REJECT     all  --  0.0.0.0/0            0.0.0.0/0           reject-with icmp-host-prohibited

Chain OUTPUT (policy ACCEPT)
num  target     prot opt source               destination

効いている。

SELinuxのオフ

$ vi /etc/selinux/config
SELINUX=enforcing

SELINUX=disabled

にする

iptable

$ sudo service iptables stop

carrierwaveのテンプレ

いまさらながらだが、、、一応rails5

Gemfile

gem 'carrierwave'
gem 'rmagick'

controler

$ rails g uploader image

app/uploaders/image_uploader.rbが作成される

$ rails g controller image

app/controllers/images_controller.rbが作成される

modle

$ rails g model image --skip-migaration
      invoke  active_record
      create    app/models/image.rb
      invoke    test_unit
      create      test/models/image_test.rb
      create      test/fixtures/images.yml

DB

ridgepoleなのでSchemafileに

create_table "images", force: true do |t|
  t.string "path"
  t.datetime "created_at"
  t.datetime "updated_at"
end

を追加してridgepoleコマンド

準備終了

ここまでで一通りファイル作ったからあとは書くだけ

app/models/image.rb

class Image < ApplicationRecord
  mount_uploader :path, ImageUploader
end

app/controllers/images_controller.rb

class ImagesController < ApplicationController
  def upload
    image = Image.create(path: params[:file])
    respond_to do |format|
      format.html { render json: {path: image.path.url}}
      format.json { render json: {path: image.path.url}}
    end
  end
end

config/routes.rb

post '/image/upload' => 'images#upload'

app/views/topics/_form.html.erb

$(document).on 'turbolinks:load', ->
  $('input.js-upload-image').on 'change', ->
    fileList = @files
    i = 0
    l = fileList.length
    while l > i
      fileReader = new FileReader
      fileReader.onload = ->
        dataUri = @result
        return
      fileReader.readAsDataURL fileList[i]
      i++

    formData = new FormData
    formData.append 'file', $('input.js-upload-image')[0].files[0]
    jQuery.each $('input.js-upload-image')[0].files, (i, file) ->
    $.ajax
      url: '/image/upload'
      type: 'POST'
      data: formData
      cache: false
      processData: false
      contentType: false
      context: this
      success: (msg) ->
        msg = JSON.parse(msg)
        domains = msg.path.split('/')
        filename = domains[domains.length - 1]
        hostname = window.location.origin
        console.log hostname + msg.path
        $('input#office_thumb').val hostname + msg.path
        return
    return

app/assets/javascript/topics.coffee

<div class="form-group">
  <%= form_tag image_upload_path, multipart: true do %>
  <%= file_field_tag 'image', class: 'js-upload-image' %>
  <% end %>
</div>

Railsで使ってるckeditorにボタン追加する

したいこと

wordpressのadd quick tagみたいにボタン押すとhtmlがテキストエリアに入るようにしたい

英語ばっかだったから日本語でメモ

ckedior

gem

github.com

リファレンス

CKEditor 4 Documentation

やり方

app/assets/javascripts/ckeditor/config.js

CKEDITOR.editorConfig = function( config )
{
   config.extraPlugins = 'defaulttable';

   config.toolbar = 'MyToolbar';
   config.toolbar_MyToolbar =
   [
   ['Defaulttable'],
   [ 'Source', '-', 'Save', 'NewPage', 'Preview', 'Print', '-', 'Templates' ],
   [ 'Cut', 'Copy', 'Paste', 'PasteText', 'PasteFromWord', '-', 'Undo', 'Redo' ],
   [ 'Find', 'Replace', '-', 'SelectAll', '-', 'Scayt' ],
   [ 'Form', 'Checkbox', 'Radio', 'TextField', 'Textarea', 'Select', 'Button', 'ImageButton', 'HiddenField' ],
   '/',
   ['Bold', 'Italic', 'Underline', 'Strike', 'Subscript', 'Superscript', '-', 'RemoveFormat' ],
   [ 'NumberedList', 'BulletedList', '-', 'Outdent', 'Indent', '-', 'Blockquote', 'CreateDiv', '-', 'JustifyLeft', 'JustifyCenter', 'JustifyRight', 'JustifyBlock', '-', 'BidiLtr', 'BidiRtl', 'Language' ],
   [ 'Link', 'Unlink', 'Anchor' ],
   '/',
   [ 'Image', 'Flash', 'Table', 'HorizontalRule', 'Smiley', 'SpecialChar', 'PageBreak', 'Iframe' ],
   [ 'Styles', 'Format', 'Font', 'FontSize' ],
   [ 'TextColor', 'BGColor' ],
   [ 'Maximize', 'ShowBlocks' ],
   [ '-' ],
   [ 'About' ],
   [ 'car-table' ]
   ];
};

config.extraPluginsで追加するプラグインの名前(プラグインのjsファイルを置くディレクトリ名になる)を指定

config.toolbar_MyToolbarの一番初めに'Defaulttableを追加

app/assets/javascripts/ckeditor/plugins/defaulttable/plugin.js

ディレクトリに気をつける。

CKEDITOR.plugins.add('defaulttable',
{
  init: function (editor) {
    var pluginName = 'defaulttable';
    editor.ui.addButton('Defaulttable',
    {
        label: 'My New Plugin',
        command: 'InsertTable',
        icon: 'http://www.hogehoge.com/images/hoge.png'
    });
    var cmd = editor.addCommand('InsertTable', { exec: InsertTable });
}
});

function InsertTable(e) {
    e.insertHtml('<table><tr><td>hoge</td></tr><tr><td>hoge</td></tr></table>' );
}