DesignAssembler

備忘録に近い

gem作った

ブラウザで位置情報を簡単に取得できるgemを作りました。

geo_railsって名前です。

github.com

仕組みはシンプル。HTML5で導入されたGeolocation APIをjsで叩くコードをrenderでviewに埋め込んでいるだけです。

f:id:hyottokoaloha:20160402234808p:plain

gem install geo_rails

で入ります。

困ったこと

  • 命名 分かりやすい名前って難しい

  • リリースできない問題

$ gem push geo_api-0.0.5.gem
Pushing gem to https://rubygems.org...
You do not have permission to push to this gem.

gem push fails / Problems / Discussion Area - RubyGems.org Support

どうやら他のgemと名前が衝突したため起きているようです。

もっと分かりやすいエラーにしてほしい。

$ gem push pkg/geo_rails-0.0.5.gem
Pushing gem to https://rubygems.org...
Successfully registered gem: geo_rails (0.0.5)

やること

  • jsコードを何とかする
  • オプション増やす
  • READMEの添削
  • テスト書く
  • エラーハンドリング部分のリファクタリング

AvtionViewの流れ

ActionViewでhtmlタグが生成されるまでの流れを追いました。

とりあえずform_tagのコードを探します。

https://github.com/rails/rails/blob/477fae3eb3d3b3bfdbe28586fecb8578c0be4721/actionview/lib/action_view/helpers/form_tag_helper.rb

ここのL67:L74です

      def form_tag(url_for_options = {}, options = {}, &block)
        html_options = html_options_for_form(url_for_options, options)
        if block_given?
          form_tag_with_body(html_options, capture(&block))
        else
          form_tag_html(html_options)
        end
      end

このメソッドではform_tag_htmlを呼んでいます。

https://github.com/rails/rails/blob/b6c1ee0dfcb7ea8bfcac9daff0162876990665a3/actionview/lib/action_view/helpers/form_tag_helper.rb

        def form_tag_html(html_options)
          extra_tags = extra_tags_for_form(html_options)
          tag(:form, html_options, true) + extra_tags
        end

        def form_tag_with_body(html_options, content)
          output = form_tag_html(html_options)
          output << content
          output.safe_concat("</form>")
        end

このメソッドではtagを呼んでいます

https://github.com/rails/rails/blob/b6c1ee0dfcb7ea8bfcac9daff0162876990665a3/actionview/lib/action_view/helpers/tag_helper.rb

      def tag(name, options = nil, open = false, escape = true)
        "<#{name}#{tag_options(options, escape) if options}#{open ? ">" : " />"}".html_safe
      end

tag()でformタグが作られ、form_tag_with_bodyで中身と閉じタグが作られます。

Rails5.0.0.beta3のActionController::APIを使う

そろそろRC版が来るRails5のbeta3を触ってみました。

$ bundle init

でGemfileを生成します。

# A sample Gemfile
source "https://rubygems.org"

gem "rails", github: "rails/rails"

bundle installします。

$ bundle install

少し時間かかりますがインストールできます。

$ bundle exec rails -v
Rails 5.0.0.beta3

5.0.0.beta3です。

$ bundle exec rails new rails_5_api --api

createしてからremoveしてます。面白いですね。

サンプルアプリケーション

今日も犬です。

$ bin/rails g model dog name:string age:string
      invoke  active_record
      create    db/migrate/20160331101719_create_dogs.rb
      create    app/models/dog.rb
      invoke    test_unit
      create      test/models/dog_test.rb
      create      test/fixtures/dogs.yml
$ bin/rails db:migrate
== 20160331101719 CreateDogs: migrating =======================================
-- create_table(:dogs)
   -> 0.0053s
== 20160331101719 CreateDogs: migrated (0.0057s) ==============================

rakeじゃなくてrailsで通るようになってますね。

controllerを作ります

$ bin/rails g controller dog show

app/controller/application.rbを見ると、親クラスがActionController::APIになっていますね。

#app/controller/application.rb
class ApplicationController < ActionController::API
end

コンソールで適当にデータ作ります。

サーバーを立ち上げます。

$ bin/rails s -p 3007
=> Booting Puma
=> Rails 5.0.0.beta3 application starting in development on http://localhost:3007
=> Run `rails server -h` for more startup options
=> Ctrl-C to shutdown server
Puma starting in single mode...
* Version 3.2.0 (ruby 2.2.2-p95), codename: Spring Is A Heliocentric Viewpoint
* Min threads: 5, max threads: 5
* Environment: development
* Listening on tcp://localhost:3007
Use Ctrl-C to stop

お、Webrickじゃなくなってますね。
ActionCableはシングルスレッドじゃダメらしいのでマルチスレッドOKなPumaになったようです。

routesとかcontrollerとかよしなにしてリクエスト投げます

$ curl localhost:3007/dog/1
{"id":1,"name":"inu1","age":"3","created_at":"2016-03-31T10:22:01.682Z","updated_at":"2016-03-31T10:22:01.682Z"}

返ってきました。

ソースコード読む

とりあえずソースコード読みます。
ActionController::API

github.com

Metalクラスをオーバーライドしています。

前半ではミドルウェア周りのコードですね。

中盤からのMetalクラスはAbstractController::Baseをオーバーライドしています。

ここではparamsとかparams=とかrequestとかが書いてあります。

おおよそrails_apiをマージしただけのような感じでした。 github.com

参考

totutotu.hatenablog.com

carrierwaveで画像アップロードAPIサーバー

carrierwaveを使って画像アップロードAPIサーバーを作りました。

どんなの作るか

犬の名前と画像が保存できるサービスのAPIを作ります。 必要なカラムはnameとpicです。

まずは犬データの作成です。名前と画像をPOSTで投げたら名前と画像URLがjsonで返ってきます。

次に犬データを見ます。URLで指定したidの犬をjsonで返ってきます。

作る

Gemfileに以下を追記します。

#Gemfile
gem 'carrierwave'

次にモデルの作成です。

$ rails g model dog name:string pic:string
$ rake db:migrate

nameカラムに犬の名前、picカラムに犬の写真を格納します。

$ rails g uploader DogPicture
      create  app/uploaders/dog_picture_uploader.rb

するとapp/uploaders/dog_picture_uploader.rbができます。

マウントします

#app/model/dog.rb
class Dog < ActiveRecord::Base
  mount_uploader :pic, DogPictureUploader
end

コントローラーを作成します

$ rails g controller dog create show

そしてapp/controllerにapiフォルダを作成し、そこに入れます。 こんな感じに書きました。

#app/controller/api/dog_controller.rb
module Api
class DogController < ApplicationController
  protect_from_forgery :only => ["create"]
  # to avoid error of CSRF

  def create
    @dog = Dog.new(dog_params)
    if @dog.save
      render json: {
        dog: {
          name: @dog.name,
          pic:  @dog.pic.url,
        },
        status: 200
      }
    else
      render json: {
        status: 400
      }
    end
  end

  def show
    @dog = Dog.find(params[:id])
    render json: {
      dog: {
        name: @dog.name,
        pic:  @dog.pic.url
      },
      status: 200
    }
  end

  private

    def dog_params
      params.permit(:name, :pic)
    end
end
end

次にconfig/routes.rbの編集です。以下を追加します。

#config/routes.rb
namespace :dog do
  resources :dog, only: [:show, :create]
end

次はオートロードの設定です。以下をconfig/application.rbに書きます。

config.autoload_paths << Rails.root.join('app', 'uploaders')

結果

サーバーを起動してcurlを投げます。

$ curl -F 'name=inu1' -F 'pic=@prof_mini.jpg' http://localhost:3000/api/dog
{"dog":{"name":"inu1","pic":"/uploads/dog/pic/1/prof_mini.jpg"},"status":200}

$ curl localhost:3000/api/dog/1
{"dog":{"name":"inu1","pic":"/uploads/dog/pic/1/prof_mini.jpg”},”status":200}

成功です。

Rails5ではrails_api gemが統合されてRails::APIができるそうですね。楽しみです。

参考

morizyun.github.io

コマンドメモ(vimやshell)

シェル

検索、grep

find ./ | grep aaaaaaa

ファイル内grep

find / -type f -print | xargs grep hogehoge /dev/null

コマンド履歴から実行

$ (Ctrl-r) (reverse-i-search) ‘bun’:bundle exec unicorn_rails -c config/unicorn.rb -E production -D

history | grep bundle

vim

vimシェルスクリプトを実行

:! ls

vimrcにシェルスクリプト実行を登録

Rubyを実行 nnoremap <C-e> :!ruby %

vimでシェルを起動

:sh

vimで単語補完

挿入モードで - Ctrl+P - Ctrl+N

vim複数窓開いて移動

:split で窓分割 <Ctr+w>h左に移動 <Ctr+w>j下 <Ctr+w>k上 <Ctr+w>l右 <Ctr+w>w-次の窓に移動

vim上で他のファイルを開いて編集

:edit ファイル名

vim上で新しいタブを開いて移動

:tabnew :gt

vimで文字痴漢

:%s/aaaaa/bbbbb/ で、aaaaaがbbbbbになる

vimでページ移動

Ctr+b:前のページ Ctr+f:次のページ

vimのカーソル移動

Shift+m:カーソルを中心行へ

vimコメントアウト

Ctr+vで短径選択モード Shift+iで入力モード

入力

vimの移動

複数行にわたる文の中での移動(ステップアップ) g+j上 g+k下

単語の末尾に進む、単語の先頭に戻る

e b

一行1文字検索

f+(検索する文字):右方向に一文字検索 F+(検索する文字):左方向に一文字検索

;で右に移動 ,で左に移動

abstract class

抽象クラスです。ActiveRecord::Baseなどがそうです。

>   ActiveRecord::Base.new
NotImplementedError: ActiveRecord::Base is an abstract class and cannot be instantiated.

「抽象クラスである」という判別がRubyによりなされているわけではなく、ActiveRecord::Baseは抽象クラスだからインスタンス化できないよ、と書いてます。このコードの50行目です。

github.com

抽象クラスは継承関係について、オブジェクト指向デザインパターンです。

スーパークラスでは何もしないメソッドを定義して、同じ名前のメソッドを子クラスで作ることによりプログラムの構造を分かりやすくします。

以下はRubyでの例です。

class AbstractClass
  def initialize
    raise "this class cannot be instantiated"
  end
  def bark
    raise "this is abstract class"
  end
end

class Dog < AbstractClass
  def bark
    puts "wan"
  end
end

class Cat < AbstractClass
  def bark
    puts "nya"
  end
end

> dog = Dog.new
> dog.bark
"wan"
> cat = Cat.new
> cat.bark
"nya"

ここで、AbstractClassのbarkメソッドは抽象メソッドと呼ばれます。抽象メソッドは必ず子クラスで実装されなければいけません。

参考

www.javadrive.jp

qiita.com