20190228のRubyに関する記事は21件です。

railsでIndex name 'xxxxxxxxxxxxxxxxxxx' on table 'テーブル名' is too long; the limit is 64 characters

経緯

railsのmigrationで
Index name 'xxxxxxxxxxxxxxxxxxx' on table 'テーブル名' is too long; the limit is 64 charactersと怒られる
テーブル名、カラム名などなどが長くなり、自動付与されるインデックス名が長くなりすぎることが原因

対応

t.references :my_field, index: { name: 'my_index_name' }
でindex名を指定してやればOK!

add_indexでindexを別で設定する場合は以下の記事を参考
https://qiita.com/ezawa800/items/9a63a96fb36a7c1de04d

参考

http://stackoverflow.com/questions/28727240/migrations-t-references-doesnt-allow-index-name-to-be-specified

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Railsチュートリアルに再挑戦する

前に挫折してしまったRailsチュートリアルに再挑戦してみようと思います。

過度に完璧主義にならずに、適度にサボりながら周回を重ねていこうと思います。

testは後回しにしたり、課題を後回しにしたり、後回しでもいいと言われたところは後回しにしたり。

まずは一周して全体を掴んでいこうと思います。

1周目はCloud9で、2周目以降はローカル開発に挑戦していきたいですね。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RubyのライブラリをGoに移植したくなった話

TL;DR

  • Rubyのライブラリである、RedBlocks をGoに移植している話
  • Rubyの継承などはEmbeddingで解決した
  • 仕方なくReflectionを使ってしまったが案外速度は問題なさそう

何を移植したくなったか

RubyにはRedisのset/zsetの操作(SINTERSTORE, ZINTERSTORE)とデータの取得を宣言的に扱うためのライブラリであるRedBlocks というのが存在します。
Readmeからリンクされている[ブログ](を見てみると

class RegionSet < RedBlocks::Set
  def initialize(region)
    raise ArgumentError unless Project::REGIONS.include?(region)
    @region = region
  end

  def key_suffix
    @region
  end

  def get
    Project.where(region_cd: @region).pluck(:id)
  end
end
region1_set = RegionSet.new('hokkaido')
region2_set = RegionSet.new('kanto')
regions_set = RedBlocks::UnionSet.new([region1_set, region2_set])
regions_set.ids #=> [921, 324, 21, 39, 101]

http://takenos.link/post/173086712782/redis-sorted-set-in-oop

このような形で、北海道もしくは関東の募集(=Project)が取得できます。

実際Redisを直に触ってしまうとこのような計算はもっと複雑になってしまいます。
これを使い、Goでも同じように楽にRedisの操作がしたくなりました。

何が問題か

問題点は基本的に2つありました。
1. Rubyの継承をどうGoで再現するか
2. クラス名(GoではStruct名)をどう取得するか

基本的にRedBlocksは継承を前提としており、RedBlocks::Setの場合、3つのメソッドを定義するだけでidsといったメソッドが自動で生えてきます。
これをGoでどうやるべきでしょうか。

また、RedBlocksでは半自動でキー名を作成してきます。
基本的にキーはクラス名とユーザーが設定するキーからなります。RedBlocks::Set#key
そのうち、クラス名に関してはGoではどう取るべきかが悩ましいです。

できたもの

RedBlocksをGoで実装したものがredblocks-go になります。

先程のRegionを作り、北海道または関東のものを取ってくる例がこちらになります。

func NewRegionSet(region string) set.Set {
    return regionSetImp{region}
}

type regionSetImp struct {
    region string
}

func (r regionSetImp) KeySuffix() string {
    return r.region
}

func (r regionSetImp) Get(ctx context.Context) ([]set.IDWithScore, error) {
  ...
}

func (r regionSetImp) CacheTime() time.Duration {
    return time.Second * 10
}

store := store.NewRedisStore(newPool())
tokyo := compose.Compose(NewRegionSet("tokyo"), store)
osaka := compose.Compose(NewRegionSet("kanto"), store)

set := operator.NewUnionSet(store, time.Second*100, tokyo, kanto)
set.IDs(ctx, options.WithPagenation(0, -1))

どう移植したか

Rubyの継承をどうGoで再現するか

GoにはEmbeddingというのがあります。Goのドキュメントにもあります https://golang.org/doc/effective_go.html#embedding
redblocks-goの例を出すと、次のようになります

type Set interface {
    KeySuffix() string
    Get(ctx context.Context) ([]IDWithScore, error)
    CacheTime() time.Duration
    NotAvailableTTL() time.Duration // NotAvailableTTL < CacheTime. For processing
}

https://github.com/rerost/redblocks-go/blob/master/pkg/set/set.go

type ComposedSet interface {
    set.Set
    Key() string
    Update(ctx context.Context) error
    Available(ctx context.Context) (bool, error)
    Warmup(ctx context.Context) error
    IDs(ctx context.Context, opts ...options.PagenationOption) ([]store.ID, error)
    IDsWithScore(ctx context.Context, opts ...options.PagenationOption) ([]store.IDWithScore, error)
}

https://github.com/rerost/redblocks-go/blob/master/pkg/compose/set.go

このように、ユーザーが定義すべきものをSetというinterfaceにし、それをembedしたComposedSetというinterfaceを定義しています。
で、SetからComposedSetの必要なメソッドを定義する関数をComposeとしています。

クラス名(GoではStruct名)をどう取得するか

正直速度の観点から、Reflection をできるだけ使いたくないと思っていました。
しかし、実際クラス名の取得だけだとベンチマークを走らせてみると10%程度の違いしかなさそうなので問題ないと思っています。

~/.go/src/github.com/rerost/redblocks-go/pkg/compose (rerost/not-use-reflection)
$ go test -bench BenchmarkKey
goos: darwin
goarch: amd64
pkg: github.com/rerost/redblocks-go/pkg/compose
BenchmarkKey-4           5000000               277 ns/op
PASS
ok      github.com/rerost/redblocks-go/pkg/compose      1.870s

https://github.com/rerost/redblocks-go/blob/27bd7d9590e6de38653ebe9235b3584a259fec83/pkg/compose/update.go#L28

~/.go/src/github.com/rerost/redblocks-go/pkg/compose (master)
user $ go test -bench BenchmarkKey
goos: darwin
goarch: amd64
pkg: github.com/rerost/redblocks-go/pkg/compose
BenchmarkKey-4           5000000               309 ns/op
PASS
ok      github.com/rerost/redblocks-go/pkg/compose      2.060s

https://github.com/rerost/redblocks-go/blob/8a56b44f78b815bcf9cc2b9a4dad26e8ef457e2f/pkg/compose/update.go#L29

終わりに

RedBlocksのGo実装はまだ部分的に機能が足りないのですが、まあまあ使えるものにはなったかと思います。
以上です。 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

超初歩的なRubyのスクリプトで残業代を計算する。

元ネタはこちら
Rubyでユーザのキーボード入力を対話的に受け取るRubyスクリプト

最近、仕事する時間が長くなってきて、コンソール上でこっそり残業代を計算するスクリプトが欲しいなーと思ったのでRuby超初心者が書いてみました。

やりたかったこと

  • スマホを取り出して電卓アプリを起動するものもはや面倒なのでなるべくキーボードから手を離さずになにか計算したい。
  • RubyのMatzさんの講演会を聞いたのでとりあえずなんでもいいからRubyでなにか書きたかった。
  • まあ、とりあえずやることが大事というモチベーションを維持したい。

実行環境

OS: MacOS
Ruby: ruby 2.3.7

特にOSによる環境差異はないと思うのでRuby入れれば動くはず

ちょっと時間がかかったこと

問題点

gets.chompで対話的に入力値を取れるかな思ったけどString型になってしまうことを理解していなったので計算実行時こんなエラーが起きた

How_much_money.rb:51:in `calc_money': undefined local variable or method `"\u201D\u3042\u306A\u305F\u306E\u6B8B\u696D\u4EE3\u306F"' for #<HowMuchMoneyClass:0x00007fc50a8c4e58> (NameError)
        from How_much_money.rb:58:in `<main>'
MacBook-Pro:Ruby pcuser$ ruby How_much_money.rb
How_much_money.rb:51: syntax error, unexpected tIDENTIFIER, expecting keyword_end

解決策

計算のためには整数でないといけないのでInt型に変換するようにgets.to_iで数値を受け取って上げればおK

スクリプト本体

コンソールで使う場合は以下のように実行

$ ruby How_much_money.rb

今のこんな感じで出力されます。(要改良)

----------------------------
自分の残業代を計算しますか?
計算する場合は yes、キャンセルする場合は no と入力して下さい.
yes
  計算を実行します.
----------------------------
基準とする時給を入力してください
入力例:)1250
900
----------------------------
仕事をした時間を入力してください。
入力例:)80
40
あなたの残業代は 36000 円です

今の所こっそり感はゼロですが、まあ気にしない

特にひねりはないですがソースはこんな感じ

ruby How_much_money.rb
#!/bin/env ruby

## ----------------------------
# ユーザからのキーボードの入力を受け取り、
# yes と入力されたらスクリプトを実行する、no と入力されたらスクリプトを終了します.
## ----------------------------

class HowMuchMoneyClass

  def initialize

    puts <<-EOT
----------------------------
自分の残業代を計算しますか?
計算する場合は yes、キャンセルする場合は no と入力して下さい.
    EOT

  end

  def get_keyboard_input_yes_no

     case gets.chomp
       when "yes", "YES", "y"
         puts "  計算を実行します."
       when "no", "NO", "n"
         puts "  スクリプトを終了します."
         exit 1
       else
         puts "  yes または no を入力して下さい."
         confirm = HowMuchMoneyClass.new
         confirm.get_keyboard_input_yes_no
     end
   end

   def calc_money
        # 計算する場所
        puts <<-EOT
----------------------------
基準とする時給を入力してください
入力例:)1250
   EOT
        base_monoey = gets.to_i
        puts <<-EOT
----------------------------
仕事をした時間を入力してください。
入力例:)80
   EOT
        money_time = gets.to_i
       #計算結果を返す
       result = base_monoey * money_time
       puts "あなたの残業代は #{result} 円です"
   end
end


confirm = HowMuchMoneyClass.new()
confirm.get_keyboard_input_yes_no
confirm.calc_money

感想

コンフィグファイル的なやつを入れれば、コンソールに金額とかの情報を載せずともできそうなので今度改良してみようかなと思いました。

シェルとかPythonとか自分の好きな言語でこういうツールを作ってみるのもいいかもしれないですね。

他の人みたいにエクセレントな設計とかできないので思いついたものから地味にやっていくのがいいかな

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

『オブジェクト指向設計実践ガイド』第1章まとめ

本投稿は書籍『オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方』を読んだ際にまとめた備忘録です。

オブジェクト指向設計とは「オブジェクト同士の依存関係を管理する」こと

【オブジェクト】
=>「それぞれが自身の振る舞いを持ち、そして互いに作用し合うもの」

【アプリケーションの設計が解決する問題】
=> アプリケーションの変更を容易にする

【アプリケーションの変更が困難な理由】
=> オブジェクト間の依存が変更の邪魔をする

【オブジェクト間の依存】
=> 部品Aが部品Bを「知っている」という状態のこと
=> 相手にメッセージを送るためにはこの「相手を知っている」状態である必要がある

【設計の実用的な定義】
=>「未来を受け入れるための選択肢を保護する」もの
=>「あとにでも」設計をできるようにすること、第一の目標は変更コストの削減

オブジェクト指向の設計者の道具 = 原則とパターン

【設計原則】
=> SOLID
1.単一責任(Single Responsibility)
2.オープン・クローズド(Open-Closed)
3.リスコフの置換(Liskov Substitution)
4.インターフェース分離(Interface Segregation)
5.依存性逆転(Dependency Inversion)

【設計(デザイン)パターン】

「オブジェクト指向ソフトフェア設計において遭遇する様々な問題に対して、簡単でかつ明瞭な解を与える」ものであり「設計プロダクトの柔軟性、モジュール性、再利用性、および理解のしやすさをより高める」ために使えるもの

【設計が失敗する原因】
1.設計が十分でない <= 設計が失敗する最初の原因
2.「良かれと思って設計しすぎる」 <= 多少の経験を積んだプログラマの失敗
3.設計の行為と実装の行為が乖離した時 <= 設計とは漸進的な発見のプロセスであり、繰り返しのフィードバックを頼りに進んでいくもの

【設計を行うタイミング】
=>詳細設計はしない。小さな追加の繰り返しで作っていくべきで、開発を繰り返すことで本当に必要なものに近づけていく(アジャイル開発?)

*詳細設計
=>提案されたアプリケーションのすべての機能、想定される未来の内部動作をすべて特定し、完全に文書化したもの

【詳細設計における問題点】
=>本当に必要な機能は作っていく中で新たに見つかるもので、詳細設計のような事前に考えたものでは本当に顧客に必要なものを満たせない

【「設計を判断する」】
=>設計の損益分岐点に従ってその程度を判断すること

例)
初学者の場合 => 設計努力をする意味はほとんどない
熟練者の場合 => 設計努力の意味は濃い 

=>設計者の目標は、機能あたりのコストが最も低い方法でソフトウェアを書くことであり、この決断は「プログラマのスキル」と「結果が出るまでの時間」による

第1章終わり:オブジェクト指向/オブジェクト指向設計の理解しやすい方法を考える

様々な書籍でオブジェクト指向について触れられていますが、よく見かけるのが「オブジェクト指向は手順ではなく操作するように扱えるもの」「ものとして扱う」みたいな記述が多くて「ん?」となることもあったので、自分なりの例をあげて今回のオブジェクト指向/オブジェクト指向設計の理解を確認してみます。

オブジェクト指向はサッカーゲームを例に考えてみると、理解しやすい気がします。

サッカーゲームの場合
オブジェクトA = 操作する選手
オブジェクトB = 自チームの操作している以外の選手
オブジェクトC = 敵チームの選手
オブジェクトD = 観客
オブジェクトA~Cの振る舞い = パス、シュート、ドリブル、ダッシュ、クリア、スライディングタックル..etc
オブジェクトDの振る舞い   = オブジェクトAが所属するチームを応援する

オブジェクトAは味方であるオブジェクトBにはメッセージは送れる(Bにパスする、等)
オブジェクトCは敵であるオブジェクトAにメッセージを送れる(Aにスライディングタックルする、等)
オブジェクトAは観客であるオブジェクトDにはメッセージを送れない(Dにパスする、等)
⇨選手であるオブジェクトAは観客であるオブジェクトDについての知識は持たせる必要はない

プログラミング難しい。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

オブジェクト指向/オブジェクト指向設計とはなんぞや?

本投稿は書籍『オブジェクト指向設計実践ガイド ~Rubyでわかる 進化しつづける柔軟なアプリケーションの育て方』を読んだ際にまとめた備忘録です。

オブジェクト指向設計とは「オブジェクト同士の依存関係を管理する」こと

【オブジェクト】
=>「それぞれが自身の振る舞いを持ち、そして互いに作用し合うもの」

【アプリケーションの設計が解決する問題】
=> アプリケーションの変更を容易にする

【アプリケーションの変更が困難な理由】
=> オブジェクト間の依存が変更の邪魔をする

【オブジェクト間の依存】
=> 部品Aが部品Bを「知っている」という状態のこと
=> 相手にメッセージを送るためにはこの「相手を知っている」状態である必要がある

【設計の実用的な定義】
=>「未来を受け入れるための選択肢を保護する」もの
=>「あとにでも」設計をできるようにすること、第一の目標は変更コストの削減

オブジェクト指向の設計者の道具 = 原則とパターン

【設計原則】
=> SOLID
1.単一責任(Single Responsibility)
2.オープン・クローズド(Open-Closed)
3.リスコフの置換(Liskov Substitution)
4.インターフェース分離(Interface Segregation)
5.依存性逆転(Dependency Inversion)

【設計(デザイン)パターン】

「オブジェクト指向ソフトフェア設計において遭遇する様々な問題に対して、簡単でかつ明瞭な解を与える」ものであり「設計プロダクトの柔軟性、モジュール性、再利用性、および理解のしやすさをより高める」ために使えるもの

【設計が失敗する原因】
1.設計が十分でない <= 設計が失敗する最初の原因
2.「良かれと思って設計しすぎる」 <= 多少の経験を積んだプログラマの失敗
3.設計の行為と実装の行為が乖離した時 <= 設計とは漸進的な発見のプロセスであり、繰り返しのフィードバックを頼りに進んでいくもの

【設計を行うタイミング】
=>詳細設計はしない。小さな追加の繰り返しで作っていくべきで、開発を繰り返すことで本当に必要なものに近づけていく(アジャイル開発?)

*詳細設計
=>提案されたアプリケーションのすべての機能、想定される未来の内部動作をすべて特定し、完全に文書化したもの

【詳細設計における問題点】
=>本当に必要な機能は作っていく中で新たに見つかるもので、詳細設計のような事前に考えたものでは本当に顧客に必要なものを満たせない

【「設計を判断する」】
=>設計の損益分岐点に従ってその程度を判断すること

例)
初学者の場合 => 設計努力をする意味はほとんどない
熟練者の場合 => 設計努力の意味は濃い 

=>設計者の目標は、機能あたりのコストが最も低い方法でソフトウェアを書くことであり、この決断は「プログラマのスキル」と「結果が出るまでの時間」による

オブジェクト指向/オブジェクト指向設計の理解しやすい方法を考える

様々な書籍でオブジェクト指向について触れられていますが、よく見かけるのが「オブジェクト指向は手順ではなく操作するように扱えるもの」「ものとして扱う」みたいな記述が多くて「ん?」となることもあったので、自分なりの例をあげて今回のオブジェクト指向/オブジェクト指向設計の理解を確認してみます。

オブジェクト指向はサッカーゲームを例に考えてみると、理解しやすい気がします。

サッカーゲームの場合
オブジェクトA = 操作する選手
オブジェクトB = 自チームの操作している以外の選手
オブジェクトC = 敵チームの選手
オブジェクトD = 観客
オブジェクトA~Cの振る舞い = パス、シュート、ドリブル、ダッシュ、クリア、スライディングタックル..etc
オブジェクトDの振る舞い   = オブジェクトAが所属するチームを応援する

オブジェクトAは味方であるオブジェクトBにはメッセージは送れる(Bにパスする、等)
オブジェクトCは敵であるオブジェクトAにメッセージを送れる(Aにスライディングタックルする、等)
オブジェクトAは観客であるオブジェクトDにはメッセージを送れない(Dにパスする、等)
⇨選手であるオブジェクトAは観客であるオブジェクトDについての知識は持たせる必要はない

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

deviseを日本語化する方法の備忘録

deviseを日本語化する方法の備忘録です。

gemをインストール

gemfile
gem 'devise-i18n'
gem 'devise-i18n-views'

$bundle installを実行する。

日本語翻訳ファイルを生成

$rails g devise:views:local ja

config/locales/devise.viwes.ja.yml
ja:
  activerecord:
    attributes:
      user:
        current_password: "現在のパスワード"
        email: "メールアドレス"
        password: "パスワード"
        password_confirmation: "確認用パスワード"
        remember_me: "ログインを記憶"
    models:
      user: "ユーザ"
  devise:
    confirmations:
      new:
        resend_confirmation_instructions: "アカウント確認メール再送"
    mailer:
      confirmation_instructions:
        action: "アカウント確認"
        greeting: "ようこそ、%{recipient}さん!"
        instruction: "次のリンクでメールアドレスの確認が完了します:"
      reset_password_instructions:
        action: "パスワード変更"
        greeting: "こんにちは、%{recipient}さん!"
        instruction: "誰かがパスワードの再設定を希望しました。次のリンクでパスワードの再設定が出来ます。"
        instruction_2: "あなたが希望したのではないのなら、このメールは無視してください。"
        instruction_3: "上のリンクにアクセスして新しいパスワードを設定するまで、パスワードは変更されません。"
      unlock_instructions:
        action: "アカウントのロック解除"
        greeting: "こんにちは、%{recipient}さん!"
        instruction: "アカウントのロックを解除するには下のリンクをクリックしてください。"
        message: "ログイン失敗が繰り返されたため、アカウントはロックされています。"
    passwords:
      edit:
        change_my_password: "パスワードを変更する"
        change_your_password: "パスワードを変更"
        confirm_new_password: "確認用新しいパスワード"
        new_password: "新しいパスワード"
      new:
        forgot_your_password: "パスワードを忘れましたか?"
        send_me_reset_password_instructions: "パスワードの再設定方法を送信する"
    registrations:
      edit:
        are_you_sure: "本当に良いですか?"
        cancel_my_account: "アカウント削除"
        currently_waiting_confirmation_for_email: "%{email} の確認待ち"
        leave_blank_if_you_don_t_want_to_change_it: "空欄のままなら変更しません"
        title: "%{resource}編集"
        unhappy: "気に入りません"
        update: "更新"
        we_need_your_current_password_to_confirm_your_changes: "変更を反映するには現在のパスワードを入力してください"
      new:
        sign_up: "アカウント登録"
    sessions:
      new:
        sign_in: "ログイン"
    shared:
      links:
        back: "戻る"
        didn_t_receive_confirmation_instructions: "アカウント確認のメールを受け取っていませんか?"
        didn_t_receive_unlock_instructions: "アカウントの凍結解除方法のメールを受け取っていませんか?"
        forgot_your_password: "パスワードを忘れましたか?"
        sign_in: "ログイン"
        sign_in_with_provider: "%{provider}でログイン"
        sign_up: "アカウント登録"
    unlocks:
      new:
        resend_unlock_instructions: "アカウントの凍結解除方法を再送する"

必要に応じてメッセージを変更する。

localeの設定

config/application.rb
config.i18n.default_locale = :ja

上のコードをconfig/application.rbに記述する。
localeを設定することで日本語化できるようです。

まとめ

以上が今回私が実施したdeviseを日本語化する方法です。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails で Web API 開発(Part. 5 R(CRUD) の実装)

はじめに

 本記事は、自身が今までの Ruby on Rails で開発してきた知識 / 知見の総まとめをおこなったものです。
 「ここは、もっとこうしたほうがいいよ!こういうものがあるよ!」というようなことがあれば、随時おしらせください!
 最終的なプロダクトは、 Rails API Sample に置いておきます。

各記事

記事の構成

前回の記事

 前回は、CORS の設定を行いました。

今回の記事

 今回は、実際に CRUD のうちの R の実装をおこなっていきたいと思います。

次回の記事

 次回は、ひきつづき CRUD のうちの CUD の実装をおこなっていきたいと思います。

API 仕様書 の作成

Swagger Editor の起動

 早速実装を…!といきたいのですが、ひとまず仕様書をちゃんと作ろうということで、 Swagger Editor を扱えるようにしていきましょう。
 以下のコマンドを実行しましょう。

$ docker pull swaggerapi/swagger-editor
$ docker run -d --rm -p 80:8080 swaggerapi/swagger-editor

 おそらく、 localhost にブラウザ上からアクセスすれば Swagger Editor が起動しているのを確認できるはずです。
 画面左部に以下を記述してみましょう。 swagger の記述方法については、解説しませんので今後記述していくうえで演繹的に感じていただけたらと思います。

# This Docs Information
swagger: "2.0"
info:
  version: "0.0.1"
  title: "Rails API Sample"

schemes:
- "http"
host: "localhost:40000"
basePath: "/api"

produces:
  - application/json

paths:
  /users:
    get:
      tags:
        - "users - action"
      summary: "User 全体取得 API"
      responses:
        200:
          description: "OK"
          schema:
            type: array
            items:
              type: object
              properties:
                id:
                  type: integer
                name:
                  type: string
                created_at:
                  type: string
                updated_at:
                  type: string

  /users/{id}:
    get:
      tags:
        - "users - action"
      summary: "User 全体取得 API"
      parameters:
        - in: path
          name: "id"
          type: integer
          required: true
      responses:
        200:
          description: "OK"
          schema:
            type: object
            properties:
              id:
                type: integer
              name:
                type: string
              created_at:
                type: string
              updated_at:
                type: string

 以下のような画面になるはずです。

Screen Shot 2019-02-28 at 19.31.10.png

yaml ファイルの保存

 また念の為、この yaml ファイル は、 docs 配下 に保存しておくことにします。

API の実装

 さて、それでは実際に API を実装していきましょう。

エラー処理機構の実装

 さっそく実装を!!といきたいのですが、まずは先にエラー処理機構を整えることにしましょう。

I18n で日本語化

 Rails の I18n という機能を用いて、Message の管理をおこなっていきます。
 まずは、以下のコマンドで、デフォルトの ja.yml ファイルをダウンロードしましょう。
 Mac の場合は、 $ brew install wget とかうてば wget コマンド うてるようになるのかな?

$ wget https://raw.github.com/svenfuchs/rails-i18n/master/rails/locale/ja.yml -P config/locales/ja/default.yml

 次に、 config/application.rb に追記し、起動時に locales 配下の *.yml が読み込まれるようにします。

  # I18n の設定
  config.i18n.default_locale = :ja
  config.i18n.load_path += Dir[Rails.root.join('config', 'locales', '**', '*.{rb,yml}').to_s]

 最後に、 config/locales/ja/error.yml を作成しましょう。この部分に、各エラーの情報を記載していきます.

ja:
  errors:
    ApiErrors::FilterParamsError:
      status_code: 400
      error_code: E001
      error_description: リクエストパラメーターが異常です。
    ApiErrors::GenerateJSONError:
      status_code: 500
      error_code: E002
      error_description: JSON 生成時にエラーが発生しました。
    other:
      status_code: 500
      error_code: E999
      error_description: サーバー内でエラーが発生しました。

エラー処理機構

 app/controllers/application_controller.rb を以下のように編集しましょう。
 この部分で、エラーを処理して response を返すようにします。また、先に記述した yml ファイル を読み込むような設定もこの部分でしています.

class ApplicationController < ActionController::API
  rescue_from Exception, with: :render_error

  private

  def render_error(error)
    error_description = I18n.t("errors.#{error.class}", default: :'errors.other')
    response = {
      error_code: error_description[:error_code],
      error_description: error_description[:error_description]
    }
    render json: response, status: error_description[:status_code]
  end
end

エラークラス

 最後に、今後発生するであろうエラークラスを app/errors/api_errors.rb に定義しておきます。

module ApiErrors
  class FilterParamsError < StandardError
  end

  class GenerateJSONError < StandardError
  end
end

Model の実装

 それでは、以下のような app/models/user.rbapp/models/micropost.rb を作成しましょう。

class User < ApplicationRecord
  self.table_name = 'users'

  has_many :microposts
end
class Micropost < ApplicationRecord
  self.table_name = 'microposts'

  belongs_to :user
end

 Validation 等のビジネスロジック(ビジネスロジックとは何か…。はおいておきます…。)は、いっさいかきません。
 Rails では、よく Controller 層 / Model 層の肥大化が問題となっているので、今回は Trailblazer という gem を扱います。

Routing の設定

 それでは、 config/routes.rb を以下のように編集しましょう。

Rails.application.routes.draw do
  namespace :api do
    resources :users, only: %i[show index]
  end
end

Operation(JSON 生成層) の実装

 名付けがよくわからなかったので、とりあえず Operation(JSON 生成層)の実装をおこなっていきます。
 以下のような全ての Operation が継承をする app/concepts/apprication_operation.rb を作成します。
 ここでは、Operation で引き起こされるであろう Error について記述しておきます。ここで引き起こされた Error は、 application_controller.rb で処理されます。
 また、日付を format する関数をここに記述しておきます.

class ApplicationOperation < Trailblazer::Operation
  protected

  def handle_filter_params_error!(*)
    raise ApiErrors::FilterParamsError
  end

  def handle_generate_json_error!(*)
    raise ApiErrors::GenerateJSONError
  end

  def simple_format_time(time)
    time.strftime('%Y-%m-%d %H:%M:%S')
  end
end

 それでは、具体的に show / index method の実装をおこなっていきます。
 以下のような app/concepts/users/operation/show.rbapp/concepts/users/operation/index.rb を作成します。

class Users::Operation::Show < ApplicationOperation
  step :filter_params
  failure :handle_filter_params_error!
  step :generate_json
  failure :handle_generate_json_error!

  private

  def filter_params(options, params, **)
    options['user'] = User.find_by(id: params[:id])
  end

  def generate_json(options, **)
    # 抽出するカラムを選択
    response = options['user'].slice(:id, :name, :created_at, :updated_at)

    # value を整形
    response[:created_at] = simple_format_time(response[:created_at])
    response[:updated_at] = simple_format_time(response[:updated_at])

    # json を生成
    options['json'] = response.to_json
  end
end
class Users::Operation::Index < ApplicationOperation
  step :generate_json
  failure :handle_generate_json_error!

  private

  def generate_json(options, **)
    # 抽出するカラムを選択
    response = User.all.pluck(:id, :name, :created_at, :updated_at)

    # hash 形式に変更
    keys = %i[id name created_at updated_at]
    response.map! do |response|
      Hash[*[keys, response].transpose.flatten]
    end

    # value を整形
    response.map! do |response|
      response[:created_at] = simple_format_time(response[:created_at])
      response[:updated_at] = simple_format_time(response[:updated_at])
      response
    end

    # json を生成
    options['json'] = response.to_json
  end
end

Controller の実装

 最後に Controller を実装すれば、おしまいですね!
 以下のような app/controllers/api/users_controller.rb を作成しましょう。

class Api::UsersController < ApplicationController
  def show
    result = Users::Operation::Show.call(
      id: params[:id]
    )
    render json: result['json'], status: 200
  end

  def index
    result = Users::Operation::Index.call()
    render json: result['json'], status: 200
  end
end

おわりに

 今回は、 CRUD の R だけをとりあえず実装しようと思ったのですが、色々とやることがありましたね…。次回は、 Rails で Web API 開発(Part. 6 CUD(CRUD) の実装) をおこなっていきます!

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails で Web API 開発(Part. 4 CORS 関連)

はじめに

 本記事は、自身が今までの Ruby on Rails で開発してきた知識 / 知見の総まとめをおこなったものです。
 「ここは、もっとこうしたほうがいいよ!こういうものがあるよ!」というようなことがあれば、随時おしらせください!
 最終的なプロダクトは、 Rails API Sample に置いておきます。

各記事

記事の構成

前回の記事

 前回は、DB の設定を行いました。

今回の記事

 今回は、API を実装するうえで欠かせない CORS の設定をおこなっていきます。

次回の記事

 次回は、実際に CRUD のうちの R の実装をおこなっていきたいと思います。

CORS の設定

Rack Cors

 今回、CORS の設定を行うために、 Rack Cors という gem を扱います。最初の Gemfile に記述済みですので、今回新しくインストールのために何かやる必要はないです。
 さて、おそらくすでに config/initializers/cors.rb というファイルがあるかと思います。このファイルを以下のように編集しましょう。
 $ docker-compose down; docker-compose up を行えば、設定が反映されるはずです。

Rails.application.config.middleware.insert_before 0, Rack::Cors do
  allow do
    # 全ての origin(Host) からの リクエストを許可する。
    origins '*'
    # /api/ 以下全てのリソースに対して、
    resource '/api/*',
             # 本サーバに対するリクエストにどんな header もつけてもよいとする。
             headers: :any,
             # 本サーバに対するリクエストに以下の method であれば許可をする。
             methods: %i[get, post, put, patch, delete, options, head],
             # 本サーバからのレスポンスの HTTP ヘッダとして公開することを許可する。
             expose:  %i[]
  end
end

おわりに

 ちょっと今回は、はやくなりすぎてしまいました…。次回は、Rails で Web API 開発(Part. 5 R(CRUD) の実装)を行います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails で Web API 開発(Part. 3 DB 関連)

はじめに

 本記事は、自身が今までの Ruby on Rails で開発してきた知識 / 知見の総まとめをおこなったものです。
 「ここは、もっとこうしたほうがいいよ!こういうものがあるよ!」というようなことがあれば、随時おしらせください!
 最終的なプロダクトは、 Rails API Sample に置いておきます。

各記事

記事の構成

前回の記事

 前回は、Docker 上で開発をするうえでの下準備を行いました。

今回の記事

 今回は、前回開発した Docker 上で DB の設定を行い、実際に curl コマンド が通るところまで行います。

次回の記事

 次回は、API を実装するうえでかかせない CORS の設定をおこなっていきたいと思います。

ER 図の作成

MySQL Workbench

 ER 図を作成するために、今回は MySQL Workbench を使用します。今回は、使用方法については控えますが(ごにょごにょして)以下のような ER 図を作成しました。(今後、設計図等はリポジトリの docs 配下 に置いておくことにします。)

  • DB 名: rails_api_sample

Screen Shot 2019-02-27 at 15.02.56.png

 また、MySQL Workbench の機能を利用して、以下のような SQL 文を作成しました。

-- MySQL Script generated by MySQL Workbench
-- Wed Feb 27 15:05:04 2019
-- Model: New Model    Version: 1.0
-- MySQL Workbench Forward Engineering

SET @OLD_UNIQUE_CHECKS=@@UNIQUE_CHECKS, UNIQUE_CHECKS=0;
SET @OLD_FOREIGN_KEY_CHECKS=@@FOREIGN_KEY_CHECKS, FOREIGN_KEY_CHECKS=0;
SET @OLD_SQL_MODE=@@SQL_MODE, SQL_MODE='TRADITIONAL,ALLOW_INVALID_DATES';

-- -----------------------------------------------------
-- Schema rails_api_sample
-- -----------------------------------------------------

-- -----------------------------------------------------
-- Schema rails_api_sample
-- -----------------------------------------------------
CREATE SCHEMA IF NOT EXISTS `rails_api_sample` DEFAULT CHARACTER SET utf8 ;
USE `rails_api_sample` ;

-- -----------------------------------------------------
-- Table `rails_api_sample`.`users`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `rails_api_sample`.`users` (
  `id` BIGINT NOT NULL AUTO_INCREMENT,
  `name` VARCHAR(64) NOT NULL,
  `created_at` DATETIME NOT NULL,
  `updated_at` DATETIME NOT NULL,
  PRIMARY KEY (`id`))
ENGINE = InnoDB;

-- -----------------------------------------------------
-- Table `rails_api_sample`.`microposts`
-- -----------------------------------------------------
CREATE TABLE IF NOT EXISTS `rails_api_sample`.`microposts` (
  `id` BIGINT NOT NULL AUTO_INCREMENT,
  `content` TEXT NOT NULL,
  `created_at` DATETIME NOT NULL,
  `updated_at` DATETIME NOT NULL,
  `user_id` BIGINT NOT NULL,
  PRIMARY KEY (`id`),
  INDEX `fk_microposts_users_idx` (`user_id` ASC),
  CONSTRAINT `fk_microposts_users`
    FOREIGN KEY (`user_id`)
    REFERENCES `rails_api_sample`.`users` (`id`)
    ON DELETE NO ACTION
    ON UPDATE NO ACTION)
ENGINE = InnoDB;

SET SQL_MODE=@OLD_SQL_MODE;
SET FOREIGN_KEY_CHECKS=@OLD_FOREIGN_KEY_CHECKS;
SET UNIQUE_CHECKS=@OLD_UNIQUE_CHECKS;

テーブルの作成

 実際に、Docker 上の DB に接続しテーブルを作成していきましょう。今回は、 Sequel Pro という MySQL の可視化ツールを用いたいと思います。
 前回の作業をおこなっていれば、以下のような設定で疎通が確認できるはずです。

Screen Shot 2019-02-27 at 16.46.07.png

 先ほど作成した SQL 文を(Sequel Pro 上で)実際に入力することでテーブルが作成できることが確認できます。
 以下は、 SQL 文 入力をし、実行をおこなった後の画面になります。

Screen Shot 2019-02-27 at 16.47.53.png

database.yml の編集

 Docker Compose 上で環境変数を流しているために、以下のような設定を行うことで疎通が行えるはずです。
 記述した後に、再度 $ docker-compose down; docker-compose up を行えば、 $ curl localhost:40000 で疎通確認が行えるはずです。

default: &default
  adapter:  mysql2
  encoding: utf8
  pool:     5
  host:     <%= ENV['MYSQL_HOST'] %>
  username: <%= ENV['MYSQL_USER'] %>
  password: <%= ENV['MYSQL_PASSWORD'] %>
  port:     <%= ENV['MYSQL_PORT'] %>
  database: rails_api_sample

development:
  <<: *default

test:
  <<: *default
  database: rails_api_sample_test

production:
  <<: *default

Migration の管理

 DB というものは、実装をしていくうえでよく変わりうるものです。上記の ER図 を用いて運用上でうまい感じにやっていくというのもできないこともないですが、ちょっと面倒です。
 今回、Migration の管理を行うために、 Ridgepole という gem を扱います。最初の Gemfile に記述済みですので、今回新しくインストールのために何かやる必要はないです。

Ridgepole タスクの作成

 Ridgepole を扱うための Rake タスクを作成してきましょう。今回は、以下のようなファイルを作成します。
 若干コメント文にもありますが 既存の DB から Schemafile を作成することと、またその逆ができるようになります。

# (lib/tasks/ridgepole.rake)

# Schema -> DB
# ./bin/bundle exec rails ridgepole:apply "env"
#
# ex) ./bin/bundle exec rails ridgepole:apply RAILS_ENV=development
#
# DB -> Schema
# ./bin/bundle exec rails ridgepole:export "env"
#
# ex) ./bin/bundle exec rails ridgepole:export RAILS_ENV=development

namespace :ridgepole do
  task export: :environment do
    options = [
      '--export',
      '--split',
      "--output #{schemafile_path}"
    ]
    exec_ridgepole(options)
  end

  task apply: :environment do
    options = [
      '--apply',
      "--file #{schemafile_path}"
    ]
    exec_ridgepole(options)
  end

  def exec_ridgepole(options)
    yml_file_path = Rails.root.join('config', 'database.yml')
    default_options = [
      "--env #{Rails.env}",
      "--config #{yml_file_path}"
    ]
    sh("bundle exec ridgepole #{default_options.join(' ')} #{options.join(' ')}")
  end

  def schemafile_path
    Rails.root.join('db', 'schemas', 'Schemafile')
  end
end

既存の DB から Schemafile を作成 / 更新

 以下のコマンドを実行することで、DB の作成 / 更新を行えるようになっているはずです。作成された Schemafile は、 db/schemas に保存されているはずです。

$ docker-compose run --rm app ./bin/bundle exec rails ridgepole:export RAILS_ENV=development

既存の Schemafile から テーブル を作成 / 更新

 以下のコマンドを実行することで、テーブルの作成 / 更新を行えるようになっているはずです。

$ docker-compose run --rm app ./bin/bundle exec rails ridgepole:apply RAILS_ENV=development

おわりに

 今回は、DB 周りのことに関してお話しました。次回は、Rails で Web API 開発(Part. 4 CORS 関連)を行います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails で Web API 開発(Part. 2 Docker 関連)

はじめに

 本記事は、自身が今までの Ruby on Rails で開発してきた知識 / 知見の総まとめをおこなったものです。
 「ここは、もっとこうしたほうがいいよ!こういうものがあるよ!」というようなことがあれば、随時おしらせください!
 最終的なプロダクトは、 Rails API Sample に置いておきます。

各記事

記事の構成

前回の記事

 前回は、Mac 上でとりあえず $ rails server が動くところまでやりました。

今回の記事

 今回は、Docker Container 上で $ rails server が動くようにします。

次回の記事

 次回は、しっかりと DB 部分の実装をおこなっていきます。

各種 Docker ファイルの作成

docker-compose.yaml の作成

 以下のような docker-compose.yaml を作成します。

version: '3'
services:
  db:
    image: mysql:5.7
    volumes:
      - db_data:/var/lib/mysql
    ports:
      - "40050:3306"
    restart: always
    # 環境変数は、Docker 上から流すことにします。
    environment:
      TZ: "Asia/Tokyo"
      MYSQL_ROOT_PASSWORD: rails_api_sample
      MYSQL_DATABASE: rails_api_sample
      MYSQL_USER: rails_api_sample
      MYSQL_PASSWORD: rails_api_sample

  app:
    build: .
    volumes:
      - .:/rails_api_sample
    depends_on:
      - db
    ports:
      - "40000:3000"
    tty: true
    stdin_open: true
    # 環境変数は、Docker 上から流すことにします。
    environment:
      TZ: "Asia/Tokyo"
      MYSQL_HOST: db
      MYSQL_DB: rails_api_sample
      MYSQL_PORT: 3306
      MYSQL_USER: rails_api_sample
      MYSQL_PASSWORD: rails_api_sample

volumes:
  db_data: {}

Dockerfile の作成

 以下のような Dockerfile を作成します。

FROM ruby:2.6.1

RUN mkdir -p /rails_api_sample
WORKDIR /rails_api_sample

RUN apt-get update -qq && \
    apt-get install -y build-essential mysql-client nodejs tzdata

COPY Gemfile* /rails_api_sample/

RUN mkdir -p /rails_api_sample/bin
COPY bin/* /rails_api_sample/bin/

RUN ./bin/bundle install --path vendor/bundle

COPY . /rails_api_sample

# Add a script to be executed every time the container starts.
COPY entrypoint.sh /usr/bin/
RUN chmod +x /usr/bin/entrypoint.sh
ENTRYPOINT ["entrypoint.sh"]
EXPOSE 3000

CMD ["./bin/bundle", "exec", "rails", "server", "-p", "3000", "-b", "0.0.0.0", "-e", "development"]

Docker 上で rails server

docker-compose up

 以下のコマンドを実行します。

$ docker-compose build

 次に、以下のコマンドを実行します。

$ docker-compose run --rm app ./bin/bundle install --path vendor/bundle

 最後に、以下のコマンドを実行します。

$ docker-compose up

 以下のようになったら、とりあえず完了です!( curl コマンド 等で確認してもおそらく DB 周りの設定ができていないのでちゃんとはかえってこないです…。)

Screen Shot 2019-02-27 at 16.40.12.png

各種コマンド

 覚えておいたほうがよいコマンドに関して少しだけ記述しておきます。

$ docker-compose up : docker-compose を起動します。
$ docker-compose down : docker-compose を停止します。
$ docker-compose run --rm ${SERVICE_NAME} ${COMMAND} : ${SERVICE_NAME} 内で、指定のコマンドを実行します。 ex)$ docker-compose run --rm app bash
$ docker attach ${CONTAINER_NAME} : ${CONTAINER_NAME} 内に、入れます。次の例では、 binding.pry 等を実行する際によく使います。 ex)$ docker attach rails_api_sample_app

おわりに

 まだまだ DB 関連が整っていないです…。次回は、Rails で Web API 開発(Part. 3 DB 関連)を行います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Rails で Web API 開発(Part. 1 概要)

はじめに

 現在、執筆中です。
 本記事は、自身が今までの Ruby on Rails で開発してきた知識 / 知見の総まとめをおこなったものです。
 「ここは、もっとこうしたほうがいいよ!こういうものがあるよ!」というようなことがあれば、随時おしらせください!
 最終的なプロダクトは、 Rails API Sample に置いておきます。

対象

  • Mac での開発者
  • ある程度 Rails / Docker の知識があるとよいかな…。と

各記事

記事の構成

今回の記事

 今回は、全体の記事について記述します。また、とりあえずローカルで $ rails server コマンドが動くところまで記述します。

次回の記事

 Docker を用いた開発を行う上での下準備をおこなっていきます。

主に扱うもの

  • Ruby(ver. 2.6.1) + Ruby on Rails(ver. 5.2.2)
  • Docker + Docker-Compose(コンテナ 関連)
  • MySQL + MySQL Workbench + Sequel Pro + Ridgepole (DB 関連)
  • Rspec + Factory Girl + Shoulda Matcher(テスト 関連)

Rails プロジェクトの作成

Version

 先述のとおり、Ruby / Ruby on Rails の各 Version は、以下の通りです。

Screen Shot 2019-02-27 at 12.31.54.png
Screen Shot 2019-02-27 at 12.35.05.png

rails new

 以下のコマンドを実行しましょう。( DBには、MySQL を使用しAPI モードでMinitest は使わない ようにするオプション。)

$ rails new rails_api_sample --database=mysql --api -T
$ cd rails_api_sample

Gemfile の編集

 今回扱う Gemfile は、以下のようにしています。

source 'https://rubygems.org'
git_source(:github) { |repo| "https://github.com/#{repo}.git" }

ruby '2.6.1'

gem 'rails', '5.2.2'

# DB(MySQL) の設定に必要な gem
gem 'mysql2'

# JSON の管理に必要な gem
gem 'jbuilder'

# 'rails server' 起動時に必要な gem
gem 'bootsnap', require: false
gem 'puma'

# CORS の設定に必要な gem
gem 'rack-cors'

# Migration の管理に必要な gem
gem 'ridgepole'

# Trailblazer を扱うのに必要な gem
gem 'reform-rails'
gem 'trailblazer-loader'
gem 'trailblazer-rails'

group :development, :test do
  # 便利コマンド 'binding.pry' を使用するのに必要な gem
  gem 'pry-byebug'
  gem 'pry-doc'
  gem 'pry-rails'

  # ソースコードが綺麗かどうか Check する gem
  gem 'rubocop'
end

group :development do
  # ファイルの変更を監視する gem
  gem 'listen'

  # 起動を早くするために必要な gem
  gem 'spring'
  gem 'spring-watcher-listen'
end

group :test do
  # TEST 用 DB の管理をする gem
  gem 'database_rewinder'

  # TEST 用 データの管理をする gem
  gem 'factory_bot_rails'
  gem 'faker'

  # Rspec で TEST をするために必要な gem
  gem 'rspec-json_matcher'
  gem 'rspec-rails'
  gem 'rspec_junit_formatter'

  # モデルの関連を TEST するために必要な gem
  gem 'shoulda-matchers'
end

bundle install

 以下のコマンドを実行しましょう。( vendor/bundle 配下にインストール するオプション。)

$ bundle install --path vendor/bundle

rails server

 以下のコマンドを実行しましょう。( $ rails s$ rails server の省略コマンド。 -p はポート指定。-b はIP Address指定。-e は環境指定。

$ bundle exec rails s -p 3000 -b '127.0.0.1' -e 'development'

 以下のようになったら、とりあえず完了です!( curl コマンド 等で確認してもおそらく DB 周りの設定ができていないのでちゃんとはかえってこないです…。)

Screen Shot 2019-02-27 at 14.36.50.png

おわりに

 今回は、とりあえず $ rails server コマンドまでを行いました。次回は、Rails で Web API 開発(Part. 2 Docker 関連)を行います。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

bundle installしたら、Traceback (most recent call last):が出た時の対処

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby if文,case文

動作環境はMacとなります。

if文

条件によって処理を出し分けたいときに使う

記述方法

if 条件1
条件 1が真(true)だった場合の処理
elsif 条件2
条件2が真(true)だった場合の処理
elsif 条件3
条件3が真(true)だった場合の処理
else
上記のいずれにも当てはまらなかった場合の処理
end

条件が真になったタイミングで処理を抜ける。
後の処理は実行されない。

※elsifの綴りはelseifではないため、注意


実践1

テストの点数が
90点以上 大変よくできました
80点以上 よくできました
60点以上 普通です
60点未満 頑張りましょう

と出力されるプログラムを実際に書いてみる

if.rb
score = x(任意の数字を入れる)

if score >= 90
  puts "大変よくできました"
elsif score >= 80
  puts "よくできました"
elsif score >= 60
  puts "普通です"
else
  puts "頑張りましょう"
end

処理結果

$ruby if.rb
普通です  #score60点以上
$ruby if.rb
頑張りましょう   #score60点未満(59点以下)
$ruby if.rb
大変よくできました  #score90点以上
$ruby if.rb
よくできました  #score80点以上
kouya:ruby_proje

#条件が真(true)になった段階でif文を抜けている。
#また、59点以下の場合はelse内の処理が実行されている。

実践2

catならmeow
dogならbowwow
cowならmoomoo
上記以外ならNot found

と出力されるプログラム

if2.rb
animal = X(任意の変数が入る)

if animal == "cat"
  puts "meow"
elsif animal == "dog"
  puts "bowwow"
elsif animal == "cow"
  puts "moomoo"
else
  puts "Not found"
end

# =は右辺を左辺に代入するとき ==は右辺と左辺が等しいときに使用するので注意

出力結果


$ ruby if2.rb
meow #animalがcat
$ ruby if2.rb
bowwow #animalがdog
$ ruby if2.rb
moomoo #animalがcow
$ ruby if2.rb
Not found " #animalが上記以外

実践3

遊園地の入場料のプログラム
12歳以上 入場料 5,000円
6歳以上12歳未満 入場料 2,500円
6歳未満 1,000円

if3.rb
age = x(任意の値が入る)

if age >= 12
  puts 5000
elsif age >= 6
  puts 2500
else age < 6
  puts 1000
end

出力結果

kouya:ruby_projects toripurug884$ ruby if3.rb
1000 #6歳未満
kouya:ruby_projects toripurug884$ ruby if3.rb
5000 #12歳以上
kouya:ruby_projects toripurug884$ ruby if3.rb
2500 #6歳未満12歳以上

unless文

記述方法

unless 条件
条件が偽(false)だった場合の処理
else
上記に当てはまらなかった場合の処理
end

※unlessにelsifは書かない。

条件が偽になったタイミングで処理を抜ける。
後の処理は実行されない。


nが0で無かった場合、出力するプログラムを書く

if文の場合

unless.rb
n = 1

if !n.zero?
  puts "Not zero"
end


unless文の場合

unless.rb
n = 1
unless n.zero?
  puts "This is zero"
end

$ ruby unless.rb
Not zero
Not zero

0の場合に出力するプログラムを unless文で追加

unless.rb
n = 0

unless n.zero?
  puts "Not zero"
else
  puts "This is zero"
end

出力結果

unless.rb
$ ruby unless.rb
This is zero

unlessは無理に使う必要はなく、ifでも読み易ければOK

case文

記述方法

case 対象のオブジェクトや式
when
値1に一致する場合の処理
when
値2に一致する場合の処理
when
値3に一致する場合の処理
else
上記のいずれにも該当しない場合
end


実践

下記のプログラムを実装

誕生石がrubyだった場合 July
誕生石がperidotだった場合 August
誕生石がsapphireだった場合 September
上記意外だった場合 Not found

ifで書いた場合

case.rb
stone =  x(任意の値を代入)

if stone == "ruby"
  puts "July"
elsif stone == "peridot"
  puts "August"
elsif stone == "sapphire"
  puts "September"
else
  puts "Not Found"
end

case.rb
$ ruby case.rb
July #ruby
$ ruby case.rb
August #peridot
$ ruby case.rb
September #sappire
$ ruby case.rb
Not Found #上記以外

case文で書いた場合

case.rb
stone = x (任意の値が入る)

case stone
when "ruby"
  puts "July"
when "peridot"
  puts "August"
when "sapphire"
  puts "September"
else
  puts "Not found"
end

出力結果

$ ruby case.rb
July #ruby
$ ruby case.rb
August #peridot
$ ruby case.rb
September #sapphire
$ ruby case.rb
Not found #上記以外

if文で書くより、caseで書いた方がシンプルでわかりやすいことがある。
特に、複数の条件に分岐する場合はcaseの方が見やすい場合が多い。

まとめ

複数人で開発場合、特ににコードの見易さやメンテナンス性が重要視される
自分にしかわからないコードを書くのではなく、相手にわかりやすいコードを書くように心がける必要がある。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

なぜコスプレ?

日本の最高のゲームショーケースでよく見られるように、今年の東京ゲームショーは、さまざまなニンテンドー(および非ニンテンドー)のフランチャイズからの素晴らしいコスプレでした。 会社の最も有名な顔のいくつか、そしてもちろん少数のカルト的なお気に入りを含む、すべてのビッグネームが表示されていました。

ここにあなたの鑑賞の喜びのためのイベントからの最高のfgo コスプレ 衣装の一部があります。 下記のコメントを投稿して、個人的なお気に入りをお知らせください。私たちの集団であると一緒に、トランスフェムと卓越した卓越性のために、ナイトライフに安全な空間を提供するために努力しています。ここで最高のダンスミュージック、見事なルックス、そして変貌する装飾の要素を地下の奇妙な雰囲気にまとめたいと思います。

https://www.sukicos.com/kamigame-costume-fun.html

何を着ている?

私達は両方とも私達の魅力の空想を完了するためにビンテージミンクを着ています。ゲームは常に冒険リンクをたどりますが、彼が途中で出会った様々な異なるキャラクター(そして彼が取った様々な形態)は、自家製のファッションに目を向けた人々に多くのインスピレーションを提供します。 才能のあるファンのこの多様なコミュニティを祝うために、私たちは地球上のコスプレイヤーから私たちのお気に入りのゼルダをテーマにした10のコスプレを集めました。

何に影響を受けていますか?

私たちはいたるところからインスピレーションを得ています。ポップカルチャー、自然、芸術、社会正義など私たちはしばしば80年代のファッションの派手な色と角度、そしてディスコ時代の輝きと魅力に引き付けられています。私達は、シェール、グレースジョーンズ、プリンス、アマンダルポア、リベレースのような星やスタイルアイコンに触発されています…しかし、ほとんどの場合、私たちはクィアの歴史やシルビアリベラ、マーシャP.ゲイ解放戦線の設立を手助けした活動家たちは、ストーンウォール暴動の間の中心的人物であった。彼らは我々のコミュニティが今日のものになる道を開いた。

https://www.sukicos.com/costume-re-zero.html

今まで聞いたことがある初心者のための最高のコスプレ衣装のヒントの1つは分岐についてです。大会で他人と話すのを恐れないでください。信じられないほどのコスプレをした人を見かけたら、ぜひ、彼らに近づいて彼らを褒め、どうやってそれを作ったのか尋ねてください。

尋ねることに恥ずべきことはありません、そしておそらくあなたはたいていのコスプレイヤーが何らかのガイダンスを共有して幸せであることに気付くでしょう。実際、コスプレはあなたが参加できる最も歓迎されるコミュニティの1つであると多くの人が言うかもしれません。あなたが縫うことを学びたいのなら、それはあなたがあなたを教えることができる他の人に手を差し伸べるためにうまくいくでしょう。

「お金と名声のためではなく、あなたがそれを愛するからそれをしなさい」。有名なコスプレイヤーはいましたか?もちろんです。彼らの素晴らしい衣装のためにお金を稼ぐことになったコスプレイヤーはいましたか?はい。しかし、彼らはまた、彼らが技術に情熱を持っているのでコスプレをする傾向があります。次のになりたい初心者のための最高のコスプレのヒントの一つは、それは良い考えではないかもしれないということです。プロのコスプレは非常に競争力があり、それはまだ何よりもまずコミュニティです。さらに、それはその技術を非常に真剣に考えるコミュニティです。私はコスプレの相互愛が最近の記憶に一緒に2つのより記憶に残るビデオゲームの悪人をもたらしたものになると予測することはなかったでしょう。 恐らく彼らが熱望している自警団員を街の通りから取り除くことができるように団結しているのは、ホラーゲーム役目はロシアのコスプレイヤーです。 ここでは堅実なコスプレが行われていますが、このデュオはコスプレの世界ではあまり見られないシリーズに取り組むためのボーナスポイントを獲得します。

https://www.sukicos.com/fate-grand-order-cosplay-series.html

ここで、私たちから逃げるための同じように思い出深いバディを与えてくれることを願っています。おそらく人類史上最大の悲劇となりました。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby から FFI で Go にバイト列のポインタを渡す

Ruby を書いていると、バイナリ処理を Go で書いて FFI 呼び出すことでいい感じにしたくなることがあります。

バイナリの受け渡しをする際は、String を使うと NULL 文字で終端されてしまう 1 ため、ポインタで渡してあげる必要があります。

C での書き方は ffi/ffi の wiki に載っていますが、Go での書き方は調べてもあまり出てこないので、ここに書いておきます。

main.go
package main

import (
    "C"
    "bytes"
    "unsafe"
)

//export hoge
func hoge(pointer unsafe.Pointer, size C.int) *C.char {
    // ポインタからバイト列を取り出す
    var data []byte = C.GoBytes(pointer, size)

    // バイト列を使って何か処理する
    var result string = do_something(data)

    // Go の string を C の string に変換して返す
    return C.CString(result)
}

func do_something(data []byte) string {
    return "xxx"
}

func main() {
}
hoge.rb
require 'ffi'

module Hoge
  extend FFI::Library

  # 上記の Go を shared library にコンパイルしたファイル
  ffi_lib 'hoge.so'

  attach_function :hoge, [:pointer, :uint], :string

  class << self
    # ポインタを渡す必要があるので private にしてインターフェイスを call_hoge に統一する
    private :hoge

    # 文字列をポインタに変換してから hoge を呼ぶ
    def call_hoge(string)
      mem_buf = FFI::MemoryPointer.from_string(string)
      hoge(mem_buf, data.bytesize)
    end
  end
end

  1. Binary data · ffi/ffi Wiki https://github.com/ffi/ffi/wiki/Binary-data 

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

RubyでXMLから欲しい情報のハッシュを作成

RubyでXMLから欲しい情報のハッシュを作成

もし、もっと良い方法があればコメントください!!

PC環境

  • Mac OS Mojave 10.14.1
  • Ruby 2.4.0

何がしたいのか

ゲスエンジニアのとださんが先日rubyの問題をnoteで出題していました。

上記noteは以下のurlを参照ください。
https://note.mu/cohki0305/n/n339931e3f705

Rubyで天気予報のプログラムを作成するのですが、

ライブドアのお天気サービスのAPIを用いるとき、

http://weather.livedoor.com/forecast/rss/primary_area.xml  にて

あらかじめ場所のcity idを知っていないといけません。

調べたい場所が変わるごとにcity idを設定し直すのは面倒です。

だったら、

http://weather.livedoor.com/forecast/rss/primary_area.xml の情報から

場所をキー、city idを値としたハッシュを作成してしまえば手間が省けるのではないかと考えました。

hash.png

実際のハッシュ作成プログラム

実際に作成したプログラムです。

require 'open-uri'
require "rexml/document"
require 'active_support'
require 'active_support/core_ext'

xml_url = open("http://weather.livedoor.com/forecast/rss/primary_area.xml").read.toutf8
xml_doc = REXML::Document.new(xml_url)
xml_hash = Hash.from_xml(xml_doc.to_s)
city_id_hash = {}
xml_hash["rss"]["channel"]["source"]["pref"].each do |key|
  count = 0
  #ハッシュ作成
  while key["city"][count] != nil do
    city_id_hash[key["city"][count]["title"]] = key["city"][count]["id"] 
    count += 1
  end
end

#確認用
puts city_id_hash

実行結果は次の通りです。

{"稚内"=>"011000", "旭川"=>"012010", "留萌"=>"012020", "網走"=>"013010", "北見"=>"013020", "紋別"=>"013030", "根室"=>"014010", "釧路"=>"014020", "帯広"=>"014030", "室蘭"=>"015010", "浦河"=>"015020", "札幌"=>"016010", "岩見沢"=>"016020", "倶知安"=>"016030", "函館"=>"017010", "江差"=>"017020", "青森"=>"020010", "むつ"=>"020020", "八戸"=>"020030", "盛岡"=>"030010", "宮古"=>"030020", "大船渡"=>"030030", "仙台"=>"040010", "白石"=>"040020", "秋田"=>"050010", "横手"=>"050020", "山形"=>"060010", "米沢"=>"060020", "酒田"=>"060030", "新庄"=>"060040", "福島"=>"070010", "小名浜"=>"070020", "若松"=>"070030", "水戸"=>"080010", "土浦"=>"080020", "宇都宮"=>"090010", "大田原"=>"090020", "前橋"=>"100010", "みなかみ"=>"100020", "さいたま"=>"110010", "熊谷"=>"110020", "秩父"=>"110030", "千葉"=>"120010", "銚子"=>"120020", "館山"=>"120030", "東京"=>"130010", "大島"=>"130020", "八丈島"=>"130030", "父島"=>"130040", "横浜"=>"140010", "小田原"=>"140020", "新潟"=>"150010", "長岡"=>"150020", "高田"=>"150030", "相川"=>"150040", "富山"=>"160010", "伏木"=>"160020", "金沢"=>"170010", "輪島"=>"170020", "福井"=>"180010", "敦賀"=>"180020", "甲府"=>"190010", "河口湖"=>"190020", "長野"=>"200010", "松本"=>"200020", "飯田"=>"200030", "岐阜"=>"210010", "高山"=>"210020", "静岡"=>"220010", "網代"=>"220020", "三島"=>"220030", "浜松"=>"220040", "名古屋"=>"230010", "豊橋"=>"230020", "津"=>"240010", "尾鷲"=>"240020", "大津"=>"250010", "彦根"=>"250020", "京都"=>"260010", "舞鶴"=>"260020", "神戸"=>"280010", "豊岡"=>"280020", "奈良"=>"290010", "風屋"=>"290020", "和歌山"=>"300010", "潮岬"=>"300020", "鳥取"=>"310010", "米子"=>"310020", "松江"=>"320010", "浜田"=>"320020", "西郷"=>"320030", "岡山"=>"330010", "津山"=>"330020", "広島"=>"340010", "庄原"=>"340020", "下関"=>"350010", "山口"=>"350020", "柳井"=>"350030", "萩"=>"350040", "徳島"=>"360010", "日和佐"=>"360020", "松山"=>"380010", "新居浜"=>"380020", "宇和島"=>"380030", "高知"=>"390010", "室戸岬"=>"390020", "清水"=>"390030", "福岡"=>"400010", "八幡"=>"400020", "飯塚"=>"400030", "久留米"=>"400040", "佐賀"=>"410010", "伊万里"=>"410020", "長崎"=>"420010", "佐世保"=>"420020", "厳原"=>"420030", "福江"=>"420040", "熊本"=>"430010", "阿蘇乙姫"=>"430020", "牛深"=>"430030", "人吉"=>"430040", "大分"=>"440010", "中津"=>"440020", "日田"=>"440030", "佐伯"=>"440040", "宮崎"=>"450010", "延岡"=>"450020", "都城"=>"450030", "高千穂"=>"450040", "鹿児島"=>"460010", "鹿屋"=>"460020", "種子島"=>"460030", "名瀬"=>"460040", "那覇"=>"471010", "名護"=>"471020", "久米島"=>"471030", "南大東"=>"472000", "宮古島"=>"473000", "石垣島"=>"474010", "与那国島"=>"474020"}

なかなかの力技のような気はしますが、とりあえずはできました。

作成に至るまでの試行錯誤

まず

xml_url = open("http://weather.livedoor.com/forecast/rss/primary_area.xml").read.toutf8
xml_doc = REXML::Document.new(xml_url)
xml_hash = Hash.from_xml(xml_doc.to_s)

http://weather.livedoor.com/forecast/rss/primary_area.xml の情報をハッシュに変換します。

次に

city_id_hash = {}
xml_hash["rss"]["channel"]["source"]["pref"].each do |key|
  count = 0
  #ハッシュ作成
  while key["city"][count] != nil do
    city_id_hash[key["city"][count]["title"]] = key["city"][count]["id"] 
    count += 1
  end
end

で地名とそれに対応するcity idだけを抜き出してハッシュを作成しています。
作成中にはどのキーに地名やcity idが埋まっているかわからないので、

xml_hash["rss"]["channel"]["source"]["pref"]

に関しては、xml_hash.keysxml_hash["rss"].keysを何回も繰り返して地道に見つけていきました笑

while文の条件、key["city"][count] != nilはなぜこのようにしているかというと、
都道府県ごとに所有している地名が異なるからです。
この実装については実際にputs key["city"]をループ中に挿入して動作を確認していただくと理解できると思います。ぜひ考えてみてください。

現状の問題点

実行速度が遅い気がする。

もっと良い方法があるのならばぜひコメントください!

まとめ

今回の実装のポイントは

1. xmlをhashとして読み込む
2. 読み込んだhashのデータ構造を調査する
3. 欲しい情報を抜き出し、求めていたハッシュを完成させる

でした。

参考記事

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby 演算子,真偽値

動作環境はMacとなります。

比較演算子

演算子による値の比較
2つの値の大小と同じ値かを調べる方法

比較演算子の記号
※マークダウン方式の影響で、より大きいの記述がおかしくなってます。

より大きい > ⇦これ
= 以上
< より小さい
<= 以下
== 等しい
!= 等しくない


式が成り立つ場合 true
式が成り立たない場合falseを返す


irb#1(main):001:0> 1 < 2
=> true
irb#1(main):002:0> 1 <= 2
=> true
irb#1(main):003:0> 1 > 2
=> false
irb#1(main):004:0> 1 >= 2
=> false
irb#1(main):005:0> 1 == 2
=> false
irb#1(main):006:0> 1 != 2
=> true

演算子の優先順位

irb#1(main):007:0> 1.0 + 2.0 * 3.0 / 4.0 
=> 2.5
irb#1(main):008:0> (1.0 + 2.0) * 3.0 / 4.0 #()で優先順位を上げる
=> 2.25

高い ::
[]
+(単項) ! ~
**
-(単項)
* / %
+ -
<< >>
&
| ^
> >= < <=
<=> == === != =~ !~
&&
||
.. ...
?:(条件演算子)
=(+=, -= ... )
not
低い and or


数値と文字列は暗黙的に変換されない

数値と文字列を連結するとエラー

irb#1(main):009:0> 1 + "1"
TypeError: String can't be coerced into Integer
    from (irb#1):9:in `+'
    from (irb#1):9

文字列を数値に変換

irb#1(main):011:0> 1 + "1".to_i #整数クラスに変換
=> 2
irb#1(main):012:0> 1 + "1.1".to_f #浮動小数点クラスに変換
=> 2.1

出力された!


数値を文字列に変換

irb#1(main):015:0> num = 10
=> 10

irb#1(main):021:0> "Number name is " + num
TypeError: no implicit conversion of Integer into String
    from (irb#1):21:in `+'
    from (irb#1):21
#numが変数のため、わかりにくいが文字列と数値の足し算はエラー

irb#1(main):020:0> "Number name is " + num.to_s
=> "Number name is 10"

#数値を文字列に変換

出力された!


インクリメントとディクリメント

・インクリメント
数値に1を足す

・ディクリメント
数値から1を引く

他のプログラミング言語のように++や--で表すことができない点に注意!

irb#1(main):022:0> n = 1
=> 1
irb#1(main):023:0> n = n + 1
=> 2
irb#1(main):024:0> n += 1
=> 3
irb#1(main):025:0> n = n -  1
=> 2
irb#1(main):026:0> n -= 1
=> 1

#どちらの表記でも大丈夫だが、+=や-=で表した方がスマート

真偽値

Rubyでは、nilかfalseの場合は偽(false)
それ以外は真(true)

trueの例

  • trueそのもの・・・true
  • 全ての数値・・・1,1.5,2/3
  • 全ての文字列 ・・・"false","true",""(空文字)

論理演算子

&&や||で複数の条件の式を真偽値で返すことが可能になる。

論理積
&&(かつ)=全ての条件が真の場合trueを返す

論理和
||(または)=少なくとも一つの条件が当てはまれば真になり、trueを返す

and,or,not

&&,||,!とほぼ同じ役割
ただし計算の優先順位が異なるため、式に混在させないこと


irb(main):001:0> t1 = true
=> true
irb(main):002:0> t2 = true
=> true
irb(main):003:0> f1 = false
=> false
irb(main):004:0> f2 = false
=> false
irb(main):005:0> t1 and t2
=> true
irb(main):006:0> t1 and f1
=> false
irb(main):007:0> t1 or t2
=> true
irb(main):008:0> f1 or f2
=> false
irb(main):009:0> !t1 || t1 #trueとfalse
=> true

#混在させると非常にわかりにくくなるため注意
irb(main):010:0> not t1 || t1
=> false
irb(main):011:0> t1 || t2 && f1 #&&の方が優先順位が高いため、t2とf1の計算が先にされた後、t1との和が計算される。
=> true
irb(main):012:0> t1 or t2 and f1 #orとandの優先順位は等しいので、左から計算
=> false

#()で囲ったりわかりやすくすることは複数人で開発するときには欠かせないスキル
irb(main):013:0> (t1 || t2) && f1 
=> false

まとめ

リファレンスに書いてある優先順位が高いものと低いものを混在させると非常にわかりにくい。
また、優先順位を上げたいときは()でくくるなど、直感的でわかりやすいプログラムを書くことが重要。

シンプルに記述するように心がけよう!

参考文献

Ruby 2.6.0 リファレンスマニュアル 演算子式

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

Ruby オブジェクト指向、数値、文字列

動作環境はMacとなります。
主に自分の勉強用メモとして残しています。

オブジェクト指向プログラミング言語

  • コンピュータ・プログラミングの概念の一つ。
  • オブジェクト指向の概念や手法を取り入れたもの。
  • プログラムを、データとその振る舞いが結びつけられたオブジェクトの集まりとして構成する。

オブジェクトとは?
データと処理の集まりのこと。文字列、配列、数値、nilなど全てオブジェクト。

オブジェクト指向とは?
オブジェクト同士が相互に関係しあうことで、システムの振る舞いを捉える考え方。

クラス
オブジェクトの設計図

インスタンス
クラス(設計図)から作成した、実態。

メソッド

  • クラスの中に定義されていて、複数の処理を1つにまとめて、扱いやすくしたもの
  • クラスに所属する関数のようなもの

数値(Numeric)

Numericは親クラス

  • Numeric ・・・ 数値クラス 
    • Integer ・・・ 整数クラス(整数を扱うクラス)
    • Float ・・・浮動小数点クラス(小数点以下を扱うクラス)

動的型付け

変数に格納した時に自動的に型が設定されるため、明示的にクラスを書く必要はない。

例) 
i = 1はOK
int i = 0はNG


四則演算 

irb(main):045:0> 1 + 1
=> 2
irb(main):046:0> 10 - 1
=> 9
irb(main):047:0> 13 * 2 #掛け算
=> 26
irb(main):048:0> 100 / 5 #割り算
=> 20
irb(main):049:0> x = 1 #xに1を代入
=> 1
irb(main):050:0> - x #入れた変数に-をつけると符号を反転させることができる
=> -1

割り算の注意点


irb(main):053:0* 3 / 2 #小数点の計算がされない
=> 1

#どちらかまたはどちらの数値にも小数点をつける必要がある
irb(main):054:0> 3.0 / 2
=> 1.5
irb(main):055:0> 3 / 2.0
=> 1.5
irb(main):056:0> 3.0 / 2.0
=> 1.5
irb(main):001:0> 10 % 3 #剰余
=> 1
irb(main):002:0> 10 ** 3 #べき乗
=> 1000

irb(main):004:0> x = 5 #5を代入
=> 5
irb(main):005:0> x.to_f #小数に変換
=> 5.0
irb(main):006:0> x.to_f / 7 #5を小数に変換し、7で割る
=> 0.7142857142857143
irb(main):007:0> x / 7   #5 / 7の出力結果 ※小数点は出力されない
=> 0

#クラスの確認
irb(main):008:0> 1.class
=> Integer
irb(main):009:0> 1.1.class 
=> Float

#クラスのメソッドの確認
irb(main):010:0> 1.methods
=> [:%, :&, :*, :+, :-, :/, :<, :>, :^, :|, :~, :-@, :**, :<=>, :<<, :>>, :<=, :>=, :==, :===, :[], :inspect, :size, :succ, :to_int, :to_s, :to_i, :to_f, :next, :div, :upto, :chr, :ord, :coerce, :divmod, :fdiv, :modulo, :remainder, :abs, :magnitude, :integer?, :floor, :ceil, :round, :truncate, :odd?, :even?, :downto, :times, :pred, :bit_length, :digits, :to_r, :numerator, :denominator, :rationalize, :gcd, :lcm, :gcdlcm, :+@, :eql?, :singleton_method_added, :i, :real?, :zero?, :nonzero?, :finite?, :infinite?, :step, :positive?, :negative?, :quo, :arg, :rectangular, :rect, :polar, :real, :imaginary, :imag, :abs2, :angle, :phase, :conjugate, :conj, :to_c, :between?, :clamp, :instance_of?, :kind_of?, :is_a?, :tap, :public_send, :public_method, :singleton_method, :remove_instance_variable, :define_singleton_method, :method, :instance_variable_set, :extend, :to_enum, :enum_for, :=~, :!~, :respond_to?, :freeze, :object_id, :send, :display, :nil?, :hash, :class, :singleton_class, :clone, :dup, :itself, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variable_get, :instance_variables, :instance_variable_defined?, :!, :!=, :__send__, :equal?, :instance_eval, :instance_exec, :__id__]

文字列(String)

文字列はシングルクオートかダブルクオートで囲む。

挙動の違い


irb(main):006:0> "abcde"
=> "abcde"
irb(main):007:0> 'abcde'
=> "abcde"

#この時点での違いはない。



irb(main):001:0> puts "Ruby\nProgramming"
Ruby
Programming
=> nil
irb(main):002:0> puts 'Ruby\nProgramming'
Ruby\nProgramming
=> nil

#ダブルクオートで囲むと、文字列の途中で改行される
#シングルクオートで囲むと、改行されない

※バックスラッシュはoptionキー+¥キー

irb(main):003:0> first_name = "Yuta"
=> "Yuta"
irb(main):004:0> last_name = "Nakamura"
=> "Nakamura"
irb(main):006:0> "My name is #{first_name} #{last_name}"
=> "My name is Yuta Nakamura"
irb(main):007:0> 'My name is #{first_name} #{last_name}'
=> "My name is \#{first_name} \#{last_name}"

#ダブクオートで囲むと#{}で囲んだ文字列が式展開される
#シングルクオートではそのまま表示される

まとめ
ダブルクオートを使う場合・・・式展開、改行したい時
シングルクオートを使う場合・・・それ以外


その他文字列の使い方

irb(main):010:0> puts "kengo" + "kaneki"
kengokaneki
=> nil
irb(main):011:0> puts "kengo" + " " + "kaneki"
kengo kaneki

#スペースを開けたい場合、" "を連結

破壊的メソッド  メソッド名の最後に!を記述

irb(main):017:0> name = 'ken'
=> "ken"
irb(main):018:0> puts name
ken
=> nil

#upcaseメソッドの破壊的メソッド
irb(main):019:0> name.upcase
=> "KEN"
irb(main):020:0> name
=> "ken"
#変数の中身に変化はない


irb(main):021:0> name.upcase!
=> "KEN"
irb(main):022:0> name
=> "KEN"
#変数の中身自体が大文字に書き換わる

クラスの確認

irb(main):023:0> "ken".class
=> String

メソッドの確認


irb(main):024:0> "ken".methods
=> [:include?, :%, :*, :+, :to_c, :unicode_normalize, :unicode_normalize!, :unicode_normalized?, :count, :partition, :unpack, :unpack1, :sum, :next, :casecmp, :casecmp?, :insert, :bytesize, :match, :match?, :succ!, :+@, :-@, :index, :rindex, :<=>, :replace, :clear, :upto, :getbyte, :==, :===, :setbyte, :=~, :scrub, :[], :[]=, :chr, :scrub!, :dump, :byteslice, :upcase, :next!, :empty?, :eql?, :downcase, :capitalize, :swapcase, :upcase!, :downcase!, :capitalize!, :swapcase!, :hex, :oct, :split, :lines, :reverse, :chars, :codepoints, :prepend, :bytes, :concat, :<<, :freeze, :inspect, :intern, :end_with?, :crypt, :ljust, :reverse!, :chop, :scan, :gsub, :ord, :start_with?, :length, :size, :rstrip, :succ, :center, :sub, :chomp!, :sub!, :chomp, :rjust, :lstrip!, :gsub!, :chop!, :strip, :to_str, :to_sym, :rstrip!, :tr, :tr_s, :delete, :to_s, :to_i, :tr_s!, :delete!, :squeeze!, :each_line, :squeeze, :strip!, :each_codepoint, :lstrip, :slice!, :rpartition, :each_byte, :each_char, :to_f, :slice, :ascii_only?, :encoding, :force_encoding, :b, :valid_encoding?, :tr!, :encode, :encode!, :hash, :to_r, :<, :>, :<=, :>=, :between?, :clamp, :instance_of?, :kind_of?, :is_a?, :tap, :public_send, :public_method, :singleton_method, :remove_instance_variable, :define_singleton_method, :method, :instance_variable_set, :extend, :to_enum, :enum_for, :!~, :respond_to?, :object_id, :send, :display, :nil?, :class, :singleton_class, :clone, :dup, :itself, :taint, :tainted?, :untaint, :untrust, :untrusted?, :trust, :frozen?, :methods, :singleton_methods, :protected_methods, :private_methods, :public_methods, :instance_variable_get, :instance_variables, :instance_variable_defined?, :!, :!=, :__send__, :

空白文字の使い方

irb(main):025:0> 1 + 2
=> 3
irb(main):026:0> 1+2 
=> 3
irb(main):027:0> 1                  +2             +    3
=> 6

#スペースはいくら開けても開けなくても挙動に違いはない。
基本的にはスペースを一文字ずつ空けるのが定説
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

条件分岐の組み替え(Recompose Conditional)

image.png

1つずつリファクタリング技法まとめ
個人的に簡単かつ取り入れ易いと思うものから

目的

すぐ引き出せるようにする

基本作業サイクル

  • システムを動かして仕様を精査
  • テストメソッドを作成
  • テストの失敗を確認
  • テストの成功を確認
  • 小さい変更、随時テスト実行(失敗確認->成功確認)
  • 最後テスト実行
  • 最後動作確認

条件分岐の組み替え(Recompose Conditional)とは

言語独自の構文要素を使うこと
イディオム(条件などに使用できる記号)など完結に実現できるものを使う

ポイント

  • 記述量が減る
  • 誰がみても分かるかという意味ではRubyの習熟度に依存する部分がややある

  • 三項演算子はイディオムのORの演算子を利用して表現できる
user.age = age ? age : '年齢不詳'

   ↓

user.age = age || '年齢不詳'
  • returnはガード節(早期return)以外基本いらない
  • 条件式の中行われている処理が1つだけならば、return if構文を使用できる
def talk(age)
  if age < 2
    'おぎゃー'
  else
    '話せるよ'
  end
end

   ↓

def talk(age)
  return 'おぎゃー'if age < 2
  '話せるよ'
end

書籍情報

Jay Fields (著), Shane Harvie (著), Martin Fowler (著), Kent Beck (著),
長尾 高弘(訳), リファクタリング:Rubyエディション
https://amzn.to/2VlyWML

雑感

凝りすぎずにシンプルさと読み易さを意識する。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

条件分岐の組み替え(Recompose Conditional)

image.png

1つずつリファクタリング技法まとめ
個人的に簡単かつ取り入れ易いと思うものから

目的

すぐ引き出せるようにする

基本作業サイクル

  • システムを動かして仕様を精査
  • テストメソッドを作成
  • テストの失敗を確認
  • テストの成功を確認
  • 小さい変更、随時テスト実行(失敗確認->成功確認)
  • 最後テスト実行
  • 最後動作確認

条件分岐の組み替え(Recompose Conditional)とは

言語独自の構文要素を使うこと
イディオム(条件などに使用できる記号)など完結に実現できるものを使う

ポイント

  • 記述量が減る
  • 誰がみても分かるかという意味ではRubyの習熟度に依存する部分がややある

  • 三項演算子はイディオムのORの演算子を利用して表現できる
user.age = age ? age : '年齢不詳'

   ↓

user.age = age || '年齢不詳'
  • returnはガード節(早期return)以外基本いらない
  • 条件式の中行われている処理が1つだけならば、return if構文を使用できる
def talk(age)
  if age < 2
    'おぎゃー'
  else
    '話せるよ'
  end
end

   ↓

def talk(age)
  return 'おぎゃー'if age < 2
  '話せるよ'
end

書籍情報

Jay Fields (著), Shane Harvie (著), Martin Fowler (著), Kent Beck (著),
長尾 高弘(訳), リファクタリング:Rubyエディション
https://amzn.to/2VlyWML

雑感

凝りすぎずにシンプルさと読み易さを意識する。

  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む