gem作った
ブラウザで位置情報を簡単に取得できるgemを作りました。
geo_railsって名前です。
仕組みはシンプル。HTML5で導入されたGeolocation APIをjsで叩くコードをrenderでviewに埋め込んでいるだけです。
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のコードを探します。
ここの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を呼んでいます。
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を呼んでいます
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
Metalクラスをオーバーライドしています。
前半ではミドルウェア周りのコードですね。
中盤からのMetalクラスはAbstractController::Baseをオーバーライドしています。
ここではparamsとかparams=とかrequestとかが書いてあります。
おおよそrails_apiをマージしただけのような感じでした。 github.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ができるそうですね。楽しみです。
参考
コマンドメモ(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行目です。
抽象クラスは継承関係について、オブジェクト指向のデザインパターンです。
スーパークラスでは何もしないメソッドを定義して、同じ名前のメソッドを子クラスで作ることによりプログラムの構造を分かりやすくします。
以下は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メソッドは抽象メソッドと呼ばれます。抽象メソッドは必ず子クラスで実装されなければいけません。