20200331のRubyに関する記事は15件です。

dockerでGemfile(gem追加)更新したら後、データベースが消えた、gemが反映しない

サーバーを再起動してますか????

#dockerではない通常開発の場合

dockerなし
control c でサーバー終了
rails s

これでgem反映します。

ということはdockerもサーバーを再起動しなければいけない。

dockerあり
$ docker-compose restart

これで反映されます。

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

RubyのFloat(arg, exception: true)をbuiltinで再実装してみた

はじめに

この記事は以前書いた以下の記事で紹介したbuiltinを使ってFloat(arg, exception: true)というメソッドを再定義してみた記事になります。

Ruby(とC)でRubyを実装してみた(builtinで遊んでみた)

やっていることは前回の記事とおおよそ同じです。前回と異なる点は、特定のクラスのメソッドではなく、どこでも呼び出すことができるメソッドをbuiltinで再定義してみたという点です。

前回の記事を書いた後、raiseFloatなどのメソッドをbuiltin対応ができるのか気になり試してみた感じですね。

結論から言うと、そういったグローバルなメソッドもbuiltinで対応することができるようです(意図した挙動かは確認とっていないので、推奨されたものではないかもしれませんが……)

builtinって?

builtinとはRuby(とC)でRuby自体を実装するというものです。詳しくは前回の記事を読んでいただければと思います。

Ruby(とC)でRubyを実装してみた(builtinで遊んでみた)

環境構築

前回の記事の環境をそのまま使用しています。特に大きな変更点などもありません。

Ruby(とC)でRubyを実装してみた(builtinで遊んでみた)

やってみた

Floatメソッドの実装場所を探す

まずはFloatメソッドが実装されているソースを探します。Rubyのメソッドは大体こんなかんじで定義されています。

rb_define_method(rb_mKernel, "to_s", rb_any_to_s, 0);

第一引数がメソッドが定義されているクラス/モジュールの変数になります。大体、rb_cArrayなどのようにクラス名やモジュール名で作成されています。第二引数の"to_s"はRuby側で呼び出されるメソッド名になっています。第三引数はメソッドが呼び出された際に実行するCの関数で、最後の引数がメソッドが受け取る引数の数を指定しています。

今回、再実装したいのはFloatメソッドですのでRubyのソースコード内からgit grep \"Float\"などで検索してみましょう。
するとobject.cで以下のように実装されているようです。

rb_define_global_function("Float", rb_f_float, -1);

それでは再実装していきます。

Floatメソッドの再実装

前回の記事ではHash#deleteを再実装していました。またbuiltin対応のため、common.mkなどの修正していました。
ですが、現在のRubyのmasterのコードではobject.cで実装されていたKernel#cloneをbuiltinに対応させているので、それらの修正は必要ありません。

修正が必要なソースはobject.ckernel.rbの二つになります。

object.cの修正

object.cではまずFloatメソッドを定義している以下のコードを削除します。

void
InitVM_Object(void)
{
    Init_class_hierarchy();

    // 省略
-    rb_define_global_function("Float", rb_f_float, -1);
    // 省略
}

次に、Floatメソッドの処理を定義しているrb_f_floatを以下のように修正します。

- /*    
-  *  call-seq: 
-  *     Float(arg, exception: true)    -> float or nil 
-  *    
-  *  Returns <i>arg</i> converted to a float. Numeric types are    
-  *  converted directly, and with exception to String and  
-  *  <code>nil</code> the rest are converted using 
-  *  <i>arg</i><code>.to_f</code>.  Converting a String with invalid   
-  *  characters will result in a ArgumentError.  Converting    
-  *  <code>nil</code> generates a TypeError.  Exceptions can be    
-  *  suppressed by passing <code>exception: false</code>.  
-  *    
-  *     Float(1)                 #=> 1.0   
-  *     Float("123.456")         #=> 123.456   
-  *     Float("123.0_badstring") #=> ArgumentError: invalid value for Float(): "123.0_badstring"   
-  *     Float(nil)               #=> TypeError: can't convert nil into Float   
-  *     Float("123.0_badstring", exception: false)  #=> nil    
-  */   

static VALUE    static VALUE
- rb_f_float(int argc, VALUE *argv, VALUE obj)
+ rb_f_float(rb_execution_context_t *ec, VALUE main, VALUE arg, VALUE opts)
{
-    VALUE arg = Qnil, opts = Qnil; 
-
-    rb_scan_args(argc, argv, "1:", &arg, &opts);   
-    return rb_convert_to_float(arg, opts_exception_p(opts));       
+   return rb_convert_to_float(arg, opts_exception_p(opts));
}

builtinを使い、キーワード引数を受け取るのでint argc, VALUE *argvではなくVALUE main, VALUE arg, VALUE optsとしています。またVALUE mainはグローバルなメソッドが暗黙の裡に受け取っている引数を代わりに受け取るために追加しています。

これでobject.cでの修正は完了です!

kernel.rbの修正

Rubyでは、どこからでも呼び出すことができるグローバルなメソッドはKernelモジュールにて再定義することができます。

たとえば、putsメソッドは以下のようにモンキーパッチすることができます。

module Kernel
   def puts *args
      p "hoge" 
   end
end


puts :ho
#=> "hoge" と表示される

これを使い、kernel.rb内に以下のようにFloatメソッドを追加します。

module Kernel
+ 
+   #
+   #  call-seq:
+   #     Float(arg, exception: true)    -> float or nil
+   #
+   #  Returns <i>arg</i> converted to a float. Numeric types are
+   #  converted directly, and with exception to String and
+   #  <code>nil</code> the rest are converted using
+   #  <i>arg</i><code>.to_f</code>.  Converting a String with invalid
+   #  characters will result in a ArgumentError.  Converting
+   #  <code>nil</code> generates a TypeError.  Exceptions can be
+   #  suppressed by passing <code>exception: false</code>.
+   #
+   #     Float(1)                 #=> 1.0
+   #     Float("123.456")         #=> 123.456
+   #     Float("123.0_badstring") #=> ArgumentError: invalid value for Float(): "123.0_badstring"
+   #     Float(nil)               #=> TypeError: can't convert nil into Float
+   #     Float("123.0_badstring", exception: false)  #=> nil
+   #
+   def Float(arg, exception: true)
+     __builtin_rb_f_float(arg, exception)
+   end
+ 
  #
  #  call-seq:
  #     obj.clone(freeze: nil) -> an_object
  #
  #  Produces a shallow copy of <i>obj</i>---the instance variables of
  #  <i>obj</i> are copied, but not the objects they reference.
  #  #clone copies the frozen value state of <i>obj</i>, unless the
  #  +:freeze+ keyword argument is given with a false or true value.
  #  See also the discussion under Object#dup.
  #
  #     class Klass
  #        attr_accessor :str
  #     end
  #     s1 = Klass.new      #=> #<Klass:0x401b3a38>
  #     s1.str = "Hello"    #=> "Hello"
  #     s2 = s1.clone       #=> #<Klass:0x401b3998 @str="Hello">
  #     s2.str[1,4] = "i"   #=> "i"
  #     s1.inspect          #=> "#<Klass:0x401b3a38 @str=\"Hi\">"
  #     s2.inspect          #=> "#<Klass:0x401b3998 @str=\"Hi\">"
  #
  #  This method may have class-specific behavior.  If so, that
  #  behavior will be documented under the #+initialize_copy+ method of
  #  the class.
  #
  def clone(freeze: nil)
    __builtin_rb_obj_clone2(freeze)
  end
end

先ほどbuiltin対応させたrb_f_float__builtin_rb_f_floatで呼び出しています。これでkernel.rbでの修正はOKです。

最後に

あとはmakemake install を実行してビルドできればbuiltinでの実装は完了です。

おわりに

こんな感じでFloatIntegerなどのメソッドをbuiltin対応できそうです。パッチとして送るかは別として、結構応用できそうだったのが面白かったですね。

ちなみに、以下のようなベンチマークを試してみたところパフォーマンス向上は期待できそうにないかなという感じでした。

benchmark:
  float: "Float(42)"
  float_true: "Float(42, exception: true)"
  float_false: "Float(42, exception: false)"
loop_count: 10000

以下結果

Calculating -------------------------------------
                     compare-ruby  built-ruby
               float      37.495M     15.878M i/s -     10.000k times in 0.000267s 0.000630s
          float_true       1.109M      1.211M i/s -     10.000k times in 0.009015s 0.008261s
         float_false       1.227M      1.191M i/s -     10.000k times in 0.008150s 0.008395s

Comparison:
                            float
        compare-ruby:  37495314.0 i/s
          built-ruby:  15878056.0 i/s - 2.36x  slower

                       float_true
          built-ruby:   1210507.2 i/s
        compare-ruby:   1109250.0 i/s - 1.09x  slower

                      float_false
        compare-ruby:   1226948.7 i/s
          built-ruby:   1191128.5 i/s - 1.03x  slower

仮にFloatなどのメソッドをbuiltin対応する場合はパフォーマンスよりもコードのメンテナンス性などを重視することになるのかなと思いますね。

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

仮想環境でRails API × Nuxt.jsのアプリケーション開発をしたいが、まずブラウザに表示されない!

自己紹介

  • 現在プログラミングの学習中の者です
  • 言い回しや知識に関して、諸所間違い等あるかと思います
  • その際は、ご指摘いただけますと幸いです

やりたいこと

RailsをAPIとして使用し、Nuxt.jsをフロント側に使用する開発において、
それらを仮想環境で開発を開始すること。

具体的には、@saongtx7様が書いてくださった
こちらの記事を仮想環境で進めたいと思ったことがきっかけです。

[入門]Rails API × Nuxt SPA × Firebase Authで作る Todo Appチュートリアル
https://qiita.com/saongtx7/items/d97ef5aec393e704fd3f

本当に素晴らしいチュートリアルでした。
この場を借りて感謝申し上げます。

問題点

私の場合このような問題が起こりました。

  • Rails側でポート指定をしてもブラウザに表在されない(rails s -b 5000)
  • Rails側でポートを指定せずに起動した場合、Nuxt.jsとポート番号が競合する

結論

1.Vagrantfileへ追記をする

まず、仮想環境のあるディレクトリに移動してください。
Vagrantfileがあると思いますのでテキストエディターで起動し、以下の追記をお願いします。

Vagrantfile

//省略
  config.vm.network "forwarded_port", guest: 3000, host: 3000
  config.vm.network "forwarded_port", guest: 5000, host: 5000 #追記!
//省略

2.Railsで起動をする際に-b 0.0.0.0を指定する。

大前提としてrails new --apiができており、アプリケーションのディレクトリは完成しているものとします。所定の場所に移動したら、ターミナルで以下を入力し、起動してください。

rails s -b 0.0.0.0 -p 5000

3.Nuxt.jsのpackage.jsonを書き換える

こちらも大前提として、npx create-nuxt-appができているものとします。所定の場所に移動する前に、テキストエディターでディレクトリを開き、直下のpackage.jsonを開いてください。

package.json
//省略

  "scripts": {
    "dev": "HOST=0.0.0.0 PORT=3000 nuxt", //変更後
    "build": "nuxt build",
    "start": "nuxt start",
    "generate": "nuxt generate"
  },

//省略

これができたら、通常通り
npm run dev
(yarnでインストールしている方は違うかも…?)

そして、locallhost:3000にアクセス。
自分はこれで解決し、無事仮想環境でもRails/Nuxtの同時起動ができました!

参考

初学者ながら加筆させていただいた部分としましては、

  • Rails s時の書き方
  • Vagrantfileの書き方

以上になります。

超独学プログラマ様の以下の記事に救われました。
本当に何日も悩みましたので・・・

Nuxt.jsからRailsへ、初めてのapi通信でHelloを表示しよう
https://blog.cloud-acct.com/posts/spa-nuxt-firstapi/

最後に、初投稿となりますので至らない点もあるかと思います。
何かお気づきの点がございましたら、後学のためご指摘をお願いいたします。
この記事が誰かのためになれば幸いです。

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

railsのルーティングからOpenAPI(V3)ドキュメントを自動生成・管理するツールを作成し、10ヶ月間会社で運用した話(下期編)

前回、railsのルーティングからOpenAPI(V3)ドキュメントを自動生成・管理するツールを作成し、4ヶ月間会社で運用した話(開発秘話もあるよ)
という記事で下期の進捗も報告すると書いたのでその記事になります。

使用したツール

自分で作ったr2-oasです。

4ヶ月の実績

※会社の許可をとりデータを載せております。

全体の実績

赤線が目標ライン。目標は、5個/週 です。

スクリーンショット 2020-03-30 11.04.36.png

各週の実績

10月の中旬に進捗がないのは、他のプロジェクトを引き継ぐために必死だったからです。
着手する余力がなかったです。12/30の週に進捗がないのは正月休み。その他進捗がなかったのは

スクリーンショット 2020-03-30 11.04.54.png

個人の実績

チームとの話し合いでAPIドキュメント書いていくのはシンドイとなったので個人でコツコツやってました。

私の9ヶ月の進捗実績は、7.56個/週でした。
下期だけの進捗だと 3.8個/週でした。

最初、週10個/週を目標にやっていたおかげもあってそんなにひどい結果にならずにすんだとはいえ、目標未達成です。

残念です。

スクリーンショット 2020-03-30 10.57.51.png

進捗管理表

これまで乗せてきたグラフや表はこの進捗管理表から自動生成されてます。
pathsファイル というのがrailsで言うところのコントローラーに対応してます。
後で紹介しますが、r2-oas は「コントローラー毎にAPIドキュメントが書ける」特徴があります。

スクリーンショット 2020-03-30 11.02.38.png

まとめ

下期は個人でコツコツやってました。途中でなげだすのはよくないと思うので、上司と話あって継続で5個/週でやっていきます。

項目 実績/目標
個人合計(個) 189個
個人平均(個/週) 7.58個/週
下期個人平均(個/週) 3.8個/週

r2-oasのチュートリアル

r2-oas ってなんだよ。って人向けのチュートリアルです。r2-oasが得意とすることは既に完成されたAPIのAPIドキュメントを自動で作成することです。そしてコードベースで書いていかないので、途中からでも始められるし、途中でやめやすいというのが特徴です。

rails6でAPIドキュメントを作ってみましょう。

SwaggerUIやSwaggerEditorで開いたりする場合は以下の準備が必要です。

$ brew cask install chromedriver
$ docker pull swaggerapi/swagger-ui:latest
$ docker pull swaggerapi/swagger-editor:latest

rails newしたら、Gemfileのdevelopmentに r2-oas を追加してください。

group :development do
 gem 'r2-oas'
end

準備

$ rails _6.0.0_ new example-600 -d mysql --skip-bundle
$ cd example-600
$ bundle install --path vendor/bundle
$ # mysql2のエラーが出るときは以下を実行して、bundle installをやり直す。
$ # bundle config --local build.mysql2 "--with-ldflags=-L/usr/local/opt/openssl/lib"

scaffoldで適当にルーティングを作成する。

$ bundle exec rails g scaffold user name:string age:integer
$ bundle exec rails g scaffold task status:string content:string
$ bundle exec rails g scaffold Api/V1/Account status:string content:string
$ bundle exec rails g scaffold Api/V2/CustomPost status:string content:string

一旦コミットする

$ git init
$ git add . && git commit -nm "initial commit "
$ # OpenAPI(V3)形式のドキュメントの雛形生成
$ bundle exec rake routes:oas:docs
# ドキュメントはコロコロ変わるのでgitignoreに追加する。
$ echo 'oas_docs/oas_doc.yml' >> .gitignore
$ # 一旦コミットする
$ git add . && git commit -nm "generate docs"

SwaggerEditor(UI)で開く。 PATHS_FILE という環境変数を指定したほうが最小限のドキュメントだけビルドされるので動作は軽いです。

$ bundle exec rake routes:oas:editor
$ # もしくは
$ # こんな風にpathsファイル毎に開くこともできます。環境変数のPATHS_FILEを指定します。
$ PATHS_FILE=oas_docs/src/paths/task.yml bundle exec rake routes:oas:editor

ここでは PATHS_FILE を指定して開いてみましょう。

image.png

  • GET /taskssummary を「タスク一覧取得」に修正
  • GET /tasks422 に関しての情報をすべて削除

以上ができたら、ターミナルに戻り Ctrl+C を押してみましょう。

wait for signal trap ...
^CI, [2020-03-30T11:52:20.903111 #21328]  INFO -- : [Analyze OAS file] start
I, [2020-03-30T11:52:20.908564 #21328]  INFO -- : [Analyze OAS file (tags)] start
I, [2020-03-30T11:52:20.910406 #21328]  INFO -- :   Write schema file:  /Users/yukihirop/RubyProjects/example-600/oas_docs/src/tags.yml
I, [2020-03-30T11:52:20.910440 #21328]  INFO -- : [Analyze OAS file (tags)] end
I, [2020-03-30T11:52:20.910450 #21328]  INFO -- : [Analyze OAS file (paths)] start
I, [2020-03-30T11:52:20.915803 #21328]  INFO -- :   Write schema file:  /Users/yukihirop/RubyProjects/example-600/oas_docs/src/paths/task.yml
I, [2020-03-30T11:52:20.915851 #21328]  INFO -- : [Analyze OAS file (paths)] end
I, [2020-03-30T11:52:20.915865 #21328]  INFO -- : [Analyze OAS file (components)] start
I, [2020-03-30T11:52:20.915883 #21328]  INFO -- : [Analyze OAS file (components/schemas)] start
I, [2020-03-30T11:52:20.918129 #21328]  INFO -- :   Write schema file:  /Users/yukihirop/RubyProjects/example-600/oas_docs/src/components/schemas/task.yml
I, [2020-03-30T11:52:20.918168 #21328]  INFO -- : [Analyze OAS file (components/schemas)] end
I, [2020-03-30T11:52:20.918182 #21328]  INFO -- : [Analyze OAS file (components/requestBodies)] start
I, [2020-03-30T11:52:20.920313 #21328]  INFO -- :   Write schema file:  /Users/yukihirop/RubyProjects/example-600/oas_docs/src/components/requestBodies/task.yml
I, [2020-03-30T11:52:20.920364 #21328]  INFO -- : [Analyze OAS file (components/requestBodies)] end
I, [2020-03-30T11:52:20.920381 #21328]  INFO -- : [Analyze OAS file (components/securitySchemes)] start
I, [2020-03-30T11:52:20.920889 #21328]  INFO -- : [Analyze OAS file (components/securitySchemes)] end
I, [2020-03-30T11:52:20.920908 #21328]  INFO -- : [Analyze OAS file (components/parameters)] start
I, [2020-03-30T11:52:20.921348 #21328]  INFO -- : [Analyze OAS file (components/parameters)] end
I, [2020-03-30T11:52:20.921365 #21328]  INFO -- : [Analyze OAS file (components/responses)] start
I, [2020-03-30T11:52:20.921804 #21328]  INFO -- : [Analyze OAS file (components/responses)] end
I, [2020-03-30T11:52:20.921820 #21328]  INFO -- : [Analyze OAS file (components/examples)] start
I, [2020-03-30T11:52:20.922285 #21328]  INFO -- : [Analyze OAS file (components/examples)] end
I, [2020-03-30T11:52:20.934988 #21328]  INFO -- : [Analyze OAS file (components/headers)] start
I, [2020-03-30T11:52:20.940521 #21328]  INFO -- : [Analyze OAS file (components/headers)] end
I, [2020-03-30T11:52:20.940773 #21328]  INFO -- : [Analyze OAS file (components/links)] start
I, [2020-03-30T11:52:20.944804 #21328]  INFO -- : [Analyze OAS file (components/links)] end
I, [2020-03-30T11:52:20.944943 #21328]  INFO -- : [Analyze OAS file (components/callbacks)] start
I, [2020-03-30T11:52:20.945420 #21328]  INFO -- : [Analyze OAS file (components/callbacks)] end
I, [2020-03-30T11:52:20.945540 #21328]  INFO -- : [Analyze OAS file (components)] end
I, [2020-03-30T11:52:20.945569 #21328]  INFO -- : [Analyze OAS file] end
I, [2020-03-30T11:52:21.162340 #21328]  INFO -- : container id: fddeedcd579c4e53ec549778ae5dc3888796b011543e504f77b93706d4dac232 removed
I, [2020-03-30T11:52:21.162463 #21328]  INFO -- : [R2-OAS] end

こんな感じにログが流れて差分を確認すると以下の様になっているはずです。

image

これをcommitしましょう。

git add . && git commit -nm "Write GET /tasks"

このようにAPIドキュメントを書いていきます。

完成したAPIドキュメントだけ表示したい

そういう場合もあるでしょう。そのためにr2-oasには、.paths ファイルに完成したpathsのyamlファイルを書いていきます。

今、oas_docs/src/paths/task.yml が完成したとすると、

# pathsディレクトリ以下の相対パスを書きます。
echo 'task.yml' >> .paths

こうすることで環境変数のPATHS_FILEが指定される時以外は、oas_docs/src/paths/task.ymlに関してのAPIドキュメントに関してだけしか表示されなくなります。

$ bundle exec rake routes:oas:ui
I, [2020-03-30T12:00:39.428227 #22297]  INFO -- : [R2-OAS] start
I, [2020-03-30T12:00:39.543178 #22297]  INFO -- : [Generate OAS schema files] start
I, [2020-03-30T12:00:39.543224 #22297]  INFO -- : [Generate OAS schema files] end
I, [2020-03-30T12:00:39.543237 #22297]  INFO -- : [Generate OAS docs from schema files] start
I, [2020-03-30T12:00:39.543595 #22297]  INFO -- :  Use schema file:     /Users/yukihirop/RubyProjects/example-600/oas_docs/src/openapi.yml
I, [2020-03-30T12:00:39.543683 #22297]  INFO -- :  Use schema file:     /Users/yukihirop/RubyProjects/example-600/oas_docs/src/external_docs.yml
I, [2020-03-30T12:00:39.543797 #22297]  INFO -- :  Use schema file:     /Users/yukihirop/RubyProjects/example-600/oas_docs/src/tags.yml
I, [2020-03-30T12:00:39.543882 #22297]  INFO -- :  Use schema file:     /Users/yukihirop/RubyProjects/example-600/oas_docs/src/info.yml
I, [2020-03-30T12:00:39.544091 #22297]  INFO -- :  Use schema file:     /Users/yukihirop/RubyProjects/example-600/oas_docs/src/servers.yml
I, [2020-03-30T12:00:39.544328 #22297]  INFO -- :  Use schema file:     /Users/yukihirop/RubyProjects/example-600/oas_docs/src/paths/task.yml
I, [2020-03-30T12:00:39.544437 #22297]  INFO -- :  Use schema file:     /Users/yukihirop/RubyProjects/example-600/oas_docs/src/components/schemas/task.yml
I, [2020-03-30T12:00:39.544537 #22297]  INFO -- :  Use schema file:     /Users/yukihirop/RubyProjects/example-600/oas_docs/src/components/requestBodies/task.yml
I, [2020-03-30T12:00:39.552871 #22297]  INFO -- : [Generate OAS docs from schema files] end

wait for single trap ...

image.png

pahtsファイルの一覧が知りたい

PATHS_FILEを指定して書くと軽くなるといってもpathsファイルのyamlファイルのpathを調べるのがだるい。
その問題のために一覧を出すコマンドがあります。(ただ動作が遅いのでshellスクリプトで解決したがいいかもしれません。)

$ bundle exec rake routes:oas:paths_ls
/Users/yukihirop/RubyProjects/example-600/oas_docs/src/paths/action_mailbox/ingresses/postmark/inbound_email.yml
/Users/yukihirop/RubyProjects/example-600/oas_docs/src/paths/action_mailbox/ingresses/sendgrid/inbound_email.yml
/Users/yukihirop/RubyProjects/example-600/oas_docs/src/paths/action_mailbox/ingresses/mandrill/inbound_email.yml
/Users/yukihirop/RubyProjects/example-600/oas_docs/src/paths/action_mailbox/ingresses/mailgun/inbound_email.yml
/Users/yukihirop/RubyProjects/example-600/oas_docs/src/paths/action_mailbox/ingresses/relay/inbound_email.yml
/Users/yukihirop/RubyProjects/example-600/oas_docs/src/paths/active_storage/disk.yml
/Users/yukihirop/RubyProjects/example-600/oas_docs/src/paths/active_storage/representation.yml
/Users/yukihirop/RubyProjects/example-600/oas_docs/src/paths/active_storage/blob.yml
/Users/yukihirop/RubyProjects/example-600/oas_docs/src/paths/active_storage/direct_upload.yml
/Users/yukihirop/RubyProjects/example-600/oas_docs/src/paths/user.yml
/Users/yukihirop/RubyProjects/example-600/oas_docs/src/paths/api/v1/account.yml
/Users/yukihirop/RubyProjects/example-600/oas_docs/src/paths/api/v2/custom_post.yml
/Users/yukihirop/RubyProjects/example-600/oas_docs/src/paths/task.yml
/Users/yukihirop/RubyProjects/example-600/oas_docs/src/paths/rails/conductor/action_mailbox/inbound_email.yml
/Users/yukihirop/RubyProjects/example-600/oas_docs/src/paths/rails/conductor/action_mailbox/reroute.yml

cool-pecoを使った便利なスクリプト

pecoを便利につかうためのcool-pecoを使って便利スクリプトを書いておくといいです。

api-editorapi-ui で pathsファイルを選択してEnterでSwaggerを開くことができるようになります。

pecoでAPIドキュメント編集

#pecoでAPIドキュメント編集
function peco-api-editor(){
  local unit_paths_file_path=$(find . -type f -name "*.yml" | grep paths | peco)
  if [ -n "$unit_paths_file_path" ]; then
    res="PATHS_FILE=$unit_paths_file_path bundle exec rake routes:oas:editor"
  fi
  _cool-peco-insert-command-line $res
}
alias api-editor=peco-api-editor

pecoでAPIドキュメント閲覧

#pecoでAPIドキュメント閲覧
function peco-api-ui(){
  local unit_paths_file_path=$(find . -type f -name "*.yml" | grep paths | peco)
  if [ -n "$unit_paths_file_path" ]; then
    res="PATHS_FILE=$unit_paths_file_path bundle exec rake routes:oas:ui"
  fi
  _cool-peco-insert-command-line $res
}
alias api-ui=peco-api-ui

おわりに

次回は9月に進捗を報告します。
目標は 5個/週 です。

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

【超初心者向け】RubyをインストールしたらRubyで遊んでみよう!!その1

※本記事はRubyがインストールされた前提の記事です。

Rubyをインストールしたあと、とにかくRubyをいろいろ触ってみて慣れていくための記事です。お役に立てば幸いです。

環境

Ruby 2.5.1
MacOS Mojave Ver.10.14.6

インタラクティブRuby(irbコマンド)

ターミナルから直接Rubyのプログラムを動かす「対話シェル」機能を起動させます。irbは「interactive Ruby」の略です。この機能は本来の手順である
【コードを記述】▶︎【ファイルを実行】ではなく、
【コードを常に読み込み】▶︎【実行】してくれる為Rubyの簡単な動作確認などに重宝します。

 操作法

MacOSの場合、Terminalを立ち上げてirbと入力し、enterキーを押します。

Terminal
irb(main):001:0>

このような画面になったら「対話シェル」が起動しています。次に"Hello World"と入力してください。

irb(main):001:0> "Hello World"
=> "Hello World"

と表示されていれば成功です。お疲れ様でした。

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

Rails ウィザード形式導入について 1

はじめに

Qiitaサボっておりましたが、学習は継続してしっかりやっております!
むしろチーム開発を通じて、アプリ開発が楽しくてたまらない状態です!
しかし、サボりっぱなしは気持ちよくないので、リスタートさせてください(泣)

上述の通り、チーム開発でフリマサイトを開発致しました。
その際、ユーザーの新規登録画面でウィザード形式を導入致しましたので、内容を整理します。
もうすでにご存知の方、省略の仕方等ご存知でしたら、ご教授願います。

ウィザード形式とは

  • 世に出ているアプリ等でも見かける画面が切り替わりながらユーザー等の登録を行うもの
  • ウィザード形式とは、対話するように順番に操作が進んでいくインターフェースのこと

なぜウィザード形式が必要なのか

  • 使いやすくなる
  • UXのクオリティが高まる

私自身も「使いやすい」、「何がどこにあるのか」が分かり易いアプリは、いい印象です。
え? そんなところをクリックできたのか!
マイページに飛んで、また入力か。

それらを回避できるイメージです!

前提条件

  • 今回はuser情報に併せて、destination(住所)情報をウィザード形式で入力できる
  • deviseは導入済み
  • deviseのデフォルト状態での新規登録・ログイン機能は実装済み

では早速!

destinationsモデルの設定

  • destinations(住所)情報の実装をするので、マイグレファイルを設定します!
db/migrate/20XXXXX.rb
class CreateDestinations < ActiveRecord::Migration[5.2]
  def change
    create_table :destinations do |t|
      t.integer :post_code
      t.text :destination
      t.references :user
      t.timestamps
    end
  end
end

バリデーション、アソシエーションの設定

  • まずはdestinationの方から!
app/models/destination.rb
class Destination < ApplicationRecord
  belongs_to :user, optional: true
  validates :post_code, :destination ,presence: true
end
  • 続いて、userの方も!
app/models/user.rb
class User < ApplicationRecord
  # Include default devise modules. Others available are:
  # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable
  devise :database_authenticatable, :registerable,
         :recoverable, :rememberable, :validatable
  validates :name, :age ,presence: true
  has_one :destination
end

deviseのカスタマイズ

deviseのコントローラの作成

  • 編集(ウィザード形式実装のため)できるようにする為に、必要となります。
% rails g devise:controllers users

しかし、このままでは、devise管理化のコントローラーが呼ばれてしまいます。

呼びたいのはDevise::RegistrationsControllerを継承したUsers::RegistrationsControllerです!

% rails routes 

で確認してくださーい!

例えばですが、controller#Actionのdevise/registrations#newです。

ルーティングの設定

  • どのコントローラを参照するのか設定
config/routes.rb
Rails.application.routes.draw do
  devise_for :users, controllers: {
    registrations: 'users/registrations',
  }
  root to: "top#index"
end

ここでもう一度

% rails routes 

で確認してくださーい!

rails routesをすると、参照するコントローラが変更されていることが確認できます。

例えばですが、
controller#Actionのdevise/registrations#new

controller#Actionのusers/registrations#new
になっているかと思います!

今行ったことは、Devise::RegistrationsControllerを継承したUsers::RegistrationsControllerを作成したイメージです。

続きは次回!

さいごに

日々勉強中ですので、随時更新します。
皆様の復習にご活用頂けますと幸いです。

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

everydayrailsで Selenium::WebDriver::Error::WebDriverError: がでた (geckodrive)

macosです。

追記: すいません。この記事ではfirefoxを使えるようにしたのですが、chromeを使うべきだそうです。

「ドライバのダウンロード」

Unable to find Mozilla geckodriver. 
というエラー文が出ていたので、(実際に出たエラー文cpし忘れたのでこれじゃないかもしれませんがほぼこれです) geckodriver をダウンロードする。

ダウンロードは
https://github.com/mozilla/geckodriver/releases
このURLからした。

macos,linux,winなどいくつか種類があるので自分のPCにあったのを選ぶ。

「ドライバをPATHに置く」

とりあえず初心者でPATHの設定が初めてだったのでディレクトリをDesktopに作成し、そこにブラウザでダウンロードしたgeckodriveを入れといた。

実際にPATHを通す

ターミナルで一応
$ echo $PATH
でPATHを確認(しなくていいけど)すると

/いつも通り出てくるデフォルトのPATH/
になってる

$ export PATH=$PATH:/Users/ユーザー名/Desktop/geckodriverを入れたディレクトリ名
でPATHを追加

もう一回
$ echo $PATH
で確認してみると

/いつも通り出てくるデフォルトのPATH/Users/ユーザー名/Desktop/geckodriveを入れたディレクトリ名
になってた。

これでPATHは通せたと思います。(違ったらすいません)

bin/rspec spec/features/tasks_spec.rb でテストしてみる

Could not find Firefox binary (os=macosx).

Firefoxが見つからないというエラー

$ brew cask install --appdir="/Applications" firefox

でHomebrewからダウンロード

これで
$ bin/rspec spec/features/tasks_spec.rb
が成功しました

/usr/local/bin に保存する

上記の環境変数PATHを通すやり方では、ターミナルを消したときこの設定も消えてしまうので

/usr/local/bin という場所に保存する

/usr/local/bin
はユーザが追加のパッケージをインストールした場合の実行ファイルを保存する場所らしい。

デフォルトでは存在しないので /usr/local/bin をsudoコマンドを使い作る

sudo mkdir /usr/local/bin

使えるようにしたいファイルを作成したPATHへ移動させる

sudo mv 移動させたいファイル名 /usr/local/bin/新しいファイル名

保存できたか確認する

which 確認したいファイル名

出力結果は

/usr/local/bin/geckodriver

なので保存できた

ターミナルを一回消して bin/rspec spec/features/tasks_spec.rb

通ったのでとりあえずオッケー。

環境変数PATHに関しては
https://reffect.co.jp/windows/full_understanding_mac
を参考にしました。ありがとうございました。

間違っている箇所など見つけた場合コメントしていただけたら幸いです。

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

Rails 404エラー画面を設定した時にrefileで設定した画像が表示されなくなる

概要

備忘録代わりに書かせていただきます

Railsでroutesの最下行に

routes.rb
get '*path', to: 'application#render_404',

でどのパスにもいかなかった場合に404エラーを独自で出力するように設定した場合に、
Refile等で指定されたattachment_image_tagの画像が表示されない。

原因

ログを見たらすぐに分かるんですけど、attachment_image_tagでURLを生成して、GETを送ってRoutesを通る時にrefileではroutesの設定が無いので、当てはまるパスがなく404のgetを返してしまうという話。

解決法

もっと綺麗なやり方があるのかもしれないですけど、とりあえずRoutesのconstraintsを使ってrequestオブジェクトのurlに"attachments/"が含まれている場合は404の行を通らないようにして回避しました。

もっといい方法100%あるような気がするので、よかったらコメントで教えていただけると嬉しいです。

routes.rb
class ErrorAvoid
  def initialize
    @url = "attachments/"
  end

  def matches?(request)
    @url.include?(request.url)
  end
end

Rails.application.routes.draw do
  get '*path', to: 'application#render_404',
    constraints: ErrorAvoid.new
end

とりあえず当面はこれで回避できます。

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

findとfind_byの違いを初心者がまとめてみました。

この記事ではfindfind_byの違いについて分かりやすく伝わるように書いてみようと思います。
わかりにくいところがあれば参考記事も確認して、しっかりと理解していただければ幸いです。

findとは

  • idを検索キーとして、取得したいモデルから特定のデータを取得する方法
  • findを使用する場合には、id以外では検索できない
  • 要するに、取得したいデータのidが分かっている場合に使用するといい
  • 検索の仕方は モデル名.find(id) 例:User.find(1)

使用方法

例えばUserモデルにあるtaroさんのデータが欲しいとします。
その場合、findで検索するとなると id で検索してあげる必要があります。

Userモデル

id name age
1 taro 23
2 jiro 20
3 sabu 18
User.find(1)
→ユーザモデル(User)から、idが1(find.(1))のものを取得

こうすればtaroさんのインスタンス(すぐに使用できるデータ)が取得できます。
挙動は

2.5.3 :001 > user1 = User.find(1)
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 1], ["LIMIT", 1]]
 => #<User id: 1, name: "taro", age: 23, created_at: "2020-03-31 02:27:04", updated_at: "2020-03-31 02:27:11"> 

このように流れてきます。
そしてこのようにすればidが1(taroさん)のageだけを取り出すことができます。

2.5.3 :002 > user1.age
 => "23" 

find_byとは

  • id以外のカラムを指定して、1つのデータを取得する方法(idでも検索可能)
  • id以外の値が分かっているときに使用できる
  • 複数の条件指定が可能
  • 値として返ってくるのは、条件にヒットした最初の1件のみ
  • 検索の方法は モデル名.find_by(カラム名: 値) 例:User.find_by(name: "taro")

使用方法

例えばUserモデルにあるjiroさんのデータが欲しいとします。
その場合、find_byで検索するとなると カラム名: 値 で検索して上げる必要があります。

Userモデル

id name age
1 taro 23
2 jiro 20
3 sabu 18
User.find_by(name: 'jiro')
→ユーザモデル(User)から名前がjiro(name: 'jiro')のデータを取得

と言う風になります。こうすればnameがjiroさんのデータを取り出すことができます。
挙動は

2.5.3 :001 > user = User.find_by(name: 'jiro')
  User Load (0.1ms)  SELECT  "users".* FROM "users" WHERE "users"."name" = ? LIMIT ?  [["name", "jiro"], ["LIMIT", 1]]
 => #<User id: 2, name: "jiro", age: 20, created_at: "2020-03-31 02:27:26", updated_at: "2020-03-31 02:27:26"> 

このように流れてきます。そして、find_byでは 値として返ってくるのは条件にヒットした最初の1件のみ が返ってきます。
どういうことかと言うと
年齢が20才の2人がいたとします。

Userモデル

id name age
1 taro 23
2 jiro 20
3 sabu 20

このような場合にfind_byを使用して User.find_by(age: 20) としても sabuさんのデータは取れません。

2.5.3 :001 > user = User.find_by(age: 20)                                                                                                                         
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."age" = ? LIMIT ?  [["age", 20], ["LIMIT", 1]]
 => #<User id: 2, name: "jiro", age: 20, created_at: "2020-03-31 02:27:26", updated_at: "2020-03-31 02:27:26"> 

idを順にみていくので、ageが20である最初のデータはidが2のjiroさんになってしまいます。なので、sabuさんのデータを取ろうと思うと、別の条件でとるしかないということになります。

また、 複数の条件指定が可能 というのはこのようにします。
例えば、兵庫県出身の20才のデータを取得したい時には

Userモデル

id name age pref
1 taro 23 大阪
2 jiro 20 東京
3 sabu 20 兵庫
2.5.3 :002 > user = User.find_by(age: 20, pref: '兵庫')                                                                                                           
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."pref" = ? AND "users"."pref" = ? LIMIT ?  [["age", 20], ["pref", "兵庫"], ["LIMIT", 1]]
 => #<User id: 3, name: "sabu", age: 20, pref: "兵庫", created_at: "2020-03-31 02:27:35", updated_at: "2020-03-31 03:49:24"> 

このようにすれば、複数条件に当てはまる最初のデータが返ってきます。
また、find_byで取り出したデータもインスタンスなので、このようにすれば指定のカラムのデータを取得することができます。

2.5.3 :003 > user.name
 => "sabu" 

データが見つからなかった時の違い

findの場合

例えば下のようなUserモデルがあったとして、間違えて User.find(4)と存在しないidを指定したとします。
Userモデル

id name age
1 taro 23
2 jiro 20
3 sabu 20
2.5.3 :004 > user = User.find(4)
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."id" = ? LIMIT ?  [["id", 4], ["LIMIT", 1]]
Traceback (most recent call last):
        1: from (irb):4
ActiveRecord::RecordNotFound (Couldn't find User with 'id'=4)

このように返ってきます。

ActiveRecord::RecordNotFound (Couldn't find User with 'id'=4)

ここでは、idが4のデータは見つかりませんでした。といった エラー が返ってきます。エラーが返ってくるとエラー画面が表示され、動かなくなってしまいます。

find_byの場合

同じUserモデルを使用して 間違えて User.find_by(name: 'gonta') と存在しないnameを指定したとします。

2.5.3 :005 > user = User.find_by(name: 'gonta')
  User Load (0.2ms)  SELECT  "users".* FROM "users" WHERE "users"."name" = ? LIMIT ?  [["name", "gonta"], ["LIMIT", 1]]
 => nil 

このように エラー ではなく nil が返ってきます。
nilが返ってくるということは、エラー画面は表示されずに処理が進みます。

要するにどういうことか

例えばこのようなfindを使ったif文を書いたとします。

user = User.find(4)

if user
 puts 'ユーザが見つかりました。'
else
 put 'ユーザが見つかりませんでした。'
end

この場合、userには何も代入されておらず、エラー画面が表示されて動きません。
しかし、find_byを使ったif文にすると

user = User.find_by(id: 4)

if user
 puts 'ユーザが見つかりました。'
else
 put 'ユーザが見つかりませんでした。'
end

この場合には、userにはnilが代入されていることになり、if文のelseが適応となって ユーザが見つかりませんでした と出力されることになります。

まとめ

findとfind_byの違いってサービスを開発してみるまでは、正直意味が分かりませんでした。
違いをしっかり理解して使っていくようにしましょう。
最後まで読んでいただきありがとうございました。:relaxed:

参考にした記事

find、find_by、whereの違い
【Rails初心者必見!】ひたすら丁寧にデータ取得を説明(find, where)
大変参考になりました。ありがとうございます。

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

heroku で Active Storage を通して GCS に接続時 Neither PUB key nor PRIV key: nested asn1 error が発生する

*ローカル環境では正しく接続できている前提です

ローカルで動いてもherokuで動かない

ローカル環境では正常に接続できていてもherokuにデプロイすると
Neither PUB key nor PRIV key: nested asn1 error
が発生する
エラー時のstorage.yml

storage.yml
google:
  service: GCS
  credentials:
    type: "service_account"
    project_id: ""
    private_key_id: "<%= Rails.application.credentials.private_key_id %>"
    private_key: "<%= Rails.application.credentials.private_key %>"
    client_email: ""
    client_id: ""
    auth_uri: ""
    token_uri: ""
    auth_provider_x509_cert_url: ""
    client_x509_cert_url: ""
  project: "My First Project"
  bucket: "hogehuga"

credentialsを利用して秘密鍵を渡していた
herokuでコンソールを叩いても正常に読み込めている

heroku側で改行がうまくいかないという情報もあったので
private_key: "<%= Rails.application.credentials.private_key.gsub("\\n", "\n") %>"
に変更したが、同様のエラーが発生した

解決策 直接渡す

storage.ymlを

    private_key_id: "<%= ENV['PRIVATE_KEY_ID'] %>"
    private_key: "<%= ENV['PRIVATE_KEY'] %>"

heroku config:set
でherokuの環境変数に直接秘密鍵をそのまま挿入
これで正常に接続でき、画像の保存と読み込みが行えます

結論

credentials から値を持ってくるときにheroku側で何らかの想定しない処理が行われてしまっていると思われる


解決はしましたが、詳しい原因が分かっておりません
詳細がわかる方いましたらコメント頂けると幸いです

参考: https://qiita.com/syossan27/items/f67cc3fd24e90b2a819c

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

あなたの日本語をRails語に翻訳する辞書 データベース編

はじめに

TECH CAMPというプログラミング教室に40日ほど通った添野です。
本日はRailsで重要な概念「データベース」という概念を説明していきます。

皆さんはRailsにおける「データベース」が何なのか説明できるでしょうか?
この記事を読めば、かなり具体的に説明できるようになります。

※MySQLの使用を前提に記事を書いています

結論

データベースとは「配列に入った配列に入ったハッシュ」です。
わかりやすく説明するため、徐々に進化させて説明していきます。


雑魚敵。ドラクエでいう「スライム」です。
最終的にビューで表示したいのはこいつです。

りんご



配列
雑魚敵の集合体です。ドラクエでいう「キングスライム」です。

[りんご , ゴリラ , ラッパ]

簡略化すると:
[A,B,C]



配列に入った配列
キングスライムの集合体です。キングスライムって進化すんの・・・?
Rubyというゲームの場合は無限に進化します。

[ [りんご , みかん , バナナ] , [ゴリラ , サル] , [ラッパ , ピアノ, トランペット , ギター] ]

簡略化すると:
[ [A,B,C] , [D,E,F] , [G,H,I] ]



ハッシュ
ラスボスです。ドラクエでいうゾーマです。
Railsではこいつを「レコード」と呼びます。

{名前: りんご, 値段: 100 , 色: 赤}

簡略化すると:
{ A:a, B:b , C:c }



配列に入ったハッシュ
歴代ドラクエのラスボスが合体した恐るべき存在です。
レコードがいっぱい並びます。つまり、Railsではこいつを「テーブル」と呼びます。

[ {名前: りんご, 値段: 100 , 色: 赤} , {名前: みかん, 値段: 200 , 色: 橙} , {名前: バナナ, 値段: 60 , 色: 黄} ]

簡略化すると:
[ {A:a, B:b, C:c} , {D:d, E:e, F:f} , {G:g, H:h, I:i} ]



配列に入った配列に入ったハッシュ
様々なゲームの歴代ラスボスを集めてそれぞれ合体させた存在を、さらに合体させた存在です。恐るべき最終形態。テーブルがいっぱい並びます。つまり、こいつがRailsにおける「データベース」の正体です。

[ [ {名前: りんご, 値段: 100 , 色: 赤} , {名前: みかん, 値段: 200 , 色: 橙} , {名前: バナナ, 値段: 60 , 色: 黄} ] 
, [ {名前: ゴリラ, 年齢: 30 , 性別:オス , 性格: 乱暴} , {名前: サル, 年齢: 10 , 性別:メス , 性格: おとなしい} ] 
, [ {名前: ラッパ, 種別: 金管楽器} , {名前: ピアノ, 種別: 弦楽器} , {名前: トランペット, 種別: 金管楽器 } , {名前:ギター, 種別: 弦楽器} ] ]

簡略化すると:
[ [ {A:a, B:b, C:c} , {D:d, E:e, F:f} , {G:g, H:h, I:i} ]
, [ {J:j, K:k, L:l} , {M:m, N:n, O:o} , {P:p, Q:q, R:r} ]
, [ {S:s, T:t, U:u} , {V:v, W:w, X:x} , {Y:y, Z:z, Ω:ω} ] ]



さて、「データベース」のことが分かってきたのではないでしょうか。
上記の「配列に入った配列に入ったハッシュ」を少し分解してみると、以下のように分けられます。


「果物」が入ってる配列

[ {名前: りんご, 値段: 100 , 色: 赤} , {名前: みかん, 値段: 200 , 色: 橙} , {名前: バナナ, 値段: 60 , 色: 黄} ] 

「動物」が入ってる配列

[ {名前: ゴリラ, 年齢: 30 , 性別:オス , 性格: 乱暴} , {名前: サル, 年齢: 10 , 性別:メス , 性格: おとなしい} ] 

「楽器」が入っている配列

[ {名前: ラッパ, 種別: 金管楽器} , {名前: ピアノ, 種別: 弦楽器} , {名前: トランペット, 種別: 金管楽器 } , {名前:ギター, 種別: 弦楽器} ]

これらがRailsにおいて、上から順に「果物テーブル」「動物テーブル」「楽器テーブル」と呼ばれるのです。

つまり、Railsのデータベース用語をまとめて説明すると、

  • レコードは「ハッシュ
  • テーブルは「配列に入ったハッシュ
  • データベースは「配列に入った配列に入ったハッシュ

です。

日本語をRails語に翻訳する

それでは、実際にデータベースから値を取り出します。
様々なゲームのラスボスが集結合体した恐るべき存在を、勇者の剣で「スライム」になるまで切り刻みます。

今回のゴールは「果物テーブル」から「りんご」「みかん」「バナナ」を取り出すことにします。
まずはデータベースから「果物テーブル」を取り出す必要がありますね。

日本語:データベースから「果物テーブル」を取り出す→ Rails語:「果物.all」

(テーブル名).allとすると、そのテーブルだけ取り出せます。
まずは「いろんなゲームのラスボス集団」を「ドラクエのラスボス集団」まで刻みます。

果物.all

結果:
[ {名前: りんご, 値段: 100 , : } , {名前: みかん, 値段: 200 , : } , {名前: バナナ, 値段: 60 , : } ]

次は「果物テーブル」から「レコード」を取り出す必要があります。

日本語:「果物テーブル」から「レコード」を取り出す→ Rails語:「果物.all.each do |f| f end」

(テーブル名).all.each do |変数| 変数 endとすると、全てのレコードを取り出すことができます。この.each doというのは重要な呪文なので覚えましょう。読み方は「ドットイーチドゥー」です。.each do|変数|変数は適当に決めちゃってオッケーです。今回はfにしました。これを使って「ドラクエのラスボス集団」を「ドラクエのラスボス1体」まで刻みます。

果物.all.each do|f|
  puts f
end

結果:
{名前: りんご, 値段: 100 , : }
{名前: みかん, 値段: 200 , : }
{名前: バナナ, 値段: 60 , : }

あとは出てきたラスボスがスライムになるまで切り刻むのみです。

日本語:「レコード」から「名前」を取り出す→ Rails語:「果物.all.each do |f| f.名前 end」

(テーブル名).all.each do |変数| 変数.(カラム名) endと書くと指定したカラムのレコードを取り出せます。ここでいう「カラム」はハッシュにおける「キー」のことです。つまり今回は「名前」です。

果物.all.each do|f|
  puts f.名前
end

結果:
りんご
みかん
バナナ

おめでとうございます。ついに「果物テーブル」から「りんご」「みかん」「バナナ」を取り出すことに成功しました。実際に使う上では、ビューファイル上では以下のように記載します。

ERBの場合

index.html.erb
<% 果物.all.each do|f| %>
  <%= f.名前 %>
<% end %>

hamlの場合

index.html.maml
- 果物.all.each do|f|
  = f.名前

他にも、pictweetで「Tweetsテーブルのnameカラムの情報を一覧表示したい」なら、コードは以下になります。

ERBの場合

index.html.erb
<% Tweet.all.each do|t| %>
  <%= t.name %>
<% end %>

hamlの場合

index.html.maml
- Tweet.all.each do|t|
  = t.name

まだ少しだけしっくりこないかもしれません。
私のpictweetだとTweet.allじゃなくて@tweetsみたいな書き方だよ!という方。
コントローラーのファイルを開くと、こんな感じのモノが書かれてないでしょうか。

def index
  @tweets = Tweet.all
end

これはデータベースからTweetsテーブルを取り出して@tweetsという変数(配列)にぶち込め!という意味です。MVCの流れって「コントローラ」の後に「ビュー」なので、先にこれが読み込まれているのです。だからビューで@tweetsというものを、Tweet.allの代わりに使えます。

そしてビューにはこんな事を書きますね。

<% @tweets.each do |tweet| %>
  <div class="content_post" style="background-image: url(<%= tweet.image %>);">
    <%= simple_format(tweet.text) %>
    <span class="name">
      <%= tweet.name %>
    </span>
  </div>
<% end %>

ゴチャゴチャしてますが、見るべきところはちゃんと決まっています。
まずは1行目の@tweets.each do |tweet|。これはラスボス集団をラスボス一体まで刻む処理でした。

そしてよく見てください、3行目と5行目です。tweet.textとか、tweet.nameって書いてあります。
これはレコードからカラムを取り出す、つまりラスボスを雑魚敵になるまで刻む処理です。

tweet.textの起源を辿って書くとTweet.all.each do|tweet| tweet.text endです。
これを日本語→Rails語訳を使って逆翻訳すると

「データベースのTweetsテーブルから、textカラムを取り出せ」

になる訳です。これにて、データベースにおけるRails語を日本語訳することも可能になりました。

まとめ

データベースとは「レコードというハッシュテーブルという配列に入ったものをデータベースという配列に入れたもの」です。

取り出し方

「データベースから〇〇テーブルを取り出す」
〇〇.all 

「データベースから取り出した〇〇テーブルから、レコードを取り出す」
〇〇.all.each do |変数| 変数 end

「データベースから取り出した〇〇テーブルのレコードから、××カラムを取り出す」
〇〇.all.each do |変数| 変数.×× end

あとがき

いかがでしたでしょうか。他にもwhereメソッドやparamsといった概念がデータベースの理解には必要ですが、全てはここに書いてある知識がベースとなった概念です。記事の内容を理解することができれば、自ずと他の概念も理解できるようになるはずです。

この記事が、皆様のご理解の一助となりましたら幸いです。

それでは。


添野文哉(@enjoy_omame)
https://twitter.com/enjoy_omame

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

vistaでkicad その3

概要

vistaでkicadやってみた。
sketchupで、vrml書いて、kicadで読み込んで見た。

環境

windows vista 32bit
kicad 4.0.4
sketchup 8

sketchupでbox書く。

image

skp_to_vrml.rbで出力。

#VRML V2.0 utf8
Shape {
  geometry IndexedFaceSet {
    coord Coordinate {
      point [
      1.000000 0.000000 -1.000000 ,
      0.000000 0.000000 -0.000000 ,
      0.000000 0.000000 -1.000000 ,
      0.000000 0.000000 -0.000000 ,
      1.000000 0.000000 -1.000000 ,
      1.000000 0.000000 -0.000000 ,
      1.000000 1.000000 -0.000000 ,
      0.000000 1.000000 -1.000000 ,
      0.000000 1.000000 -0.000000 ,
      0.000000 1.000000 -1.000000 ,
      1.000000 1.000000 -0.000000 ,
      1.000000 1.000000 -1.000000 ,
      1.000000 1.000000 -0.000000 ,
      0.000000 0.000000 -0.000000 ,
      1.000000 0.000000 -0.000000 ,
      0.000000 0.000000 -0.000000 ,
      1.000000 1.000000 -0.000000 ,
      0.000000 1.000000 -0.000000 ,
      0.000000 1.000000 -1.000000 ,
      0.000000 0.000000 -0.000000 ,
      0.000000 1.000000 -0.000000 ,
      0.000000 0.000000 -0.000000 ,
      0.000000 1.000000 -1.000000 ,
      0.000000 0.000000 -1.000000 ,
      0.000000 1.000000 -1.000000 ,
      1.000000 0.000000 -1.000000 ,
      0.000000 0.000000 -1.000000 ,
      1.000000 0.000000 -1.000000 ,
      0.000000 1.000000 -1.000000 ,
      1.000000 1.000000 -1.000000 ,
      1.000000 0.000000 -1.000000 ,
      1.000000 1.000000 -0.000000 ,
      1.000000 0.000000 -0.000000 ,
      1.000000 1.000000 -0.000000 ,
      1.000000 0.000000 -1.000000 ,
      1.000000 1.000000 -1.000000 ,
      ]
    }
    coordIndex [
    0, 1, 2, -1,
    3, 4, 5, -1,
    6, 7, 8, -1,
    9, 10, 11, -1,
    12, 13, 14, -1,
    15, 16, 17, -1,
    18, 19, 20, -1,
    21, 22, 23, -1,
    24, 25, 26, -1,
    27, 28, 29, -1,
    30, 31, 32, -1,
    33, 34, 35, -1,
    ]
  }
  appearance Appearance {
    material Material {}
  }
}

kicadでvrmlを読み込む。

image

以上。

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

Kinx 実現技術 - Fiber

Fiber

はじめに

「見た目は JavaScript、頭脳(中身)は Ruby、(安定感は AC/DC)」 でお届けしているスクリプト言語 Kinx。作ったものの紹介だけではなく実現のために使った技術を紹介していくのも貢献。その道の人には当たり前でも、そうでない人にも興味をもって貰えるかもしれない。

前回のテーマは Garbage Collection。今回のテーマは Fiber。

Fiber はこれまで使ったことがなかったのだが、途中で関数を中断できる機能は結構便利かも。効果的な使い道は 誰か教えてください

尚、今回の記事を書いている途中で当初考えたやり方ではうまく行かないケースがあり、急遽修正しました。今回は反省の意味を含め、その流れも書いておきます。その方が何やっているか分かりやすそうというのもあり。恥ずかしすぎるが致し方ない。むむむ、見落とした…

実現方法

当初の発想

「基本、その時のフレームと実行再開位置が保存されていればうまくいくんじゃね?」

結果

「うまくいったぜ!チョロいもんだ」

本記事を書いてる途中

「マジか、動かねえ。何でだ?つか、あぁ、なるほど。当たり前だ。なぜ気付かなかった俺。」

改めた発想

「スタック状態と実行再開位置が保存されていればうまくいくに違いない」

結果

「かぁさん、俺やったよ!」

やったこと

当初

  • yield の時の フレーム次回開始位置 を呼び出し元関数オブジェクトに保存。
  • return の際、フレーム情報はクリアせず実行開始位置だけクリアして リターン。これは後で使う。
  • 次回の関数呼び出しの際、関数オブジェクトに フレーム情報があれば yield 後の再開とみなす。
  • 再開と判断した後、実行開始位置がクリアされて いれば return したということで FiberException 例外を発行。

何がダメ?

  • Fiber 中にさらに関数呼び出しされてそこで yield された場合、期待する関数オブジェクトにコンテキストが保存されない。
  • そらそうだ。結局、Fiber 中のスタック状態は全部保存しとかないとダメだった。

具体的にはこの記事の最後の Ruby の例が動かなかったんですね。

修正

  • 関数オブジェクトに ファイバーであるという情報ファイバー開始時のフレーム以降のスタック実行位置 を保持できるフィールドを追加。
  • yield の際、ファイバー開始時のフレーム以降のスタックをコピー し、次の実行開始位置 を記録してリターン。
  • return の際、ファイバーである情報はクリアせず実行開始位置だけクリアして リターン。これは後で使う。
  • 次回の関数呼び出しの際、関数オブジェクトがファイバーとなって いたら yield 後の再開とみなす。
  • 再開と判断した後、実行開始位置がクリアされて いれば return したということで FiberException 例外を発行。
  • resume する際、どこからファイバーをスタートさせたかの情報 をスタック上に残しておく(コピー開始地点の記録)。

解説

スタック状態について

当初はローカル変数がフレームに格納されているので、フレームを復元すれば良いと単純に考えてしまった。こんな感じ。

stack1.png

ところが、Fiber 中に関数呼び出しするとこんな感じに。これは正しく動かない。

stack2.png

これを正しく動かすためにはこうしないといけない。

stack3.png

今回は、スタックにマーキングするようにした。例外スタックのように実行コンテキストに分離する手もあるが何度も保存・復元する必要があるのでスタックのほうが楽。どちらにしても特急で直したためコード的に若干無駄があるので、後でリファクタリングします。

命令追加

もしかしたらもっと簡単にできたかもしれないが、ファイバー開始マークをするためだけの命令が1つ追加されています。もうちょっと汎用的にしたほうが良かったかな。まぁ、まずは動くことが重要なのでこれで。後でリファクタリングもできるし。

謎の特殊命令 _coroutine キーワードが追加されていますが、当然、通常 使ってはいけません 。以下のようになっており、実際、return とほぼ同じ動きをしますが、式を評価する前にスタック上に ファイバー開始したよ! というマークをスタックに残す動作をします。このマークを頼りに、yield するときにどこまでのスタックを保存しておくのかをサーチします。

Fiberクラスの内部定義(ちょっと違うけどイメージ)
class Fiber(fiber) {
    public resume(...arg) {
        _coroutine fiber(...arg);
    }
}

書いててキーワードの位置が意味的に合っていない気がしたので、修正するかも。内部仕様なので、勿論動作は変わらずですが。

GC 対応

あと、忘れてはいけないのが GC でのマーキング。関数オブジェクトがスタックを持っている場合、そこからの参照先にも全部マークさせに行きます。

全部合わせると

具体的な差分は以下です。

はー、やっちまった感が半端ない。一応、直して動くことは確認したけれど。

isAlive が必要とか、ファイバー例外まわりが多少おかしいので、もう少し修正する予定です。

サンプル

Fibonacci

私自身は 良い 使い道がイマイチ(モヤっと)しているのですが、まずはどこかにあった Fiber によるフィボナッチ数列を Kinx で実現してみましょう。

こんな感じに無限ループで yield

var fib = new Fiber(&{
    var a = 0, b = 1;
    while (true) {
        yield b;
        [a, b] = [b, a + b];
    }
});

var r = 35.times().map(&(i) => fib.resume());
r.each(&(v, i) => System.println("fibonacci[%2d] = %7d" % i % v));

実行。

fibonacci[ 0] =       1
fibonacci[ 1] =       1
fibonacci[ 2] =       2
fibonacci[ 3] =       3
fibonacci[ 4] =       5
fibonacci[ 5] =       8
fibonacci[ 6] =      13
fibonacci[ 7] =      21
fibonacci[ 8] =      34
fibonacci[ 9] =      55
fibonacci[10] =      89
fibonacci[11] =     144
fibonacci[12] =     233
fibonacci[13] =     377
fibonacci[14] =     610
fibonacci[15] =     987
fibonacci[16] =    1597
fibonacci[17] =    2584
fibonacci[18] =    4181
fibonacci[19] =    6765
fibonacci[20] =   10946
fibonacci[21] =   17711
fibonacci[22] =   28657
fibonacci[23] =   46368
fibonacci[24] =   75025
fibonacci[25] =  121393
fibonacci[26] =  196418
fibonacci[27] =  317811
fibonacci[28] =  514229
fibonacci[29] =  832040
fibonacci[30] = 1346269
fibonacci[31] = 2178309
fibonacci[32] = 3524578
fibonacci[33] = 5702887
fibonacci[34] = 9227465

よっしゃ、グッジョブ!

Ruby の例をいくつか

Ruby 2.7.0 Fiberクラス より。

Ruby
f = Fiber.new do
  n = 0
  loop do
    Fiber.yield(n)
    n += 1
  end
end

5.times do
 p f.resume
end

#=> 0
    1
    2
    3
    4

これを素直に Kinx に書き直してみる。

Kinx
var f = new Fiber(&{
  var n = 0;
  while (true) {
    yield n;
    n++;
  }
});

5.times(&{
 System.println(f.resume());
});

実行。

0
1
2
3
4

お・ん・な・じー。も一つの例も。

Ruby
def enum2gen(enum)
  Fiber.new do
    enum.each{|i|
      Fiber.yield(i)
    }
  end
end

g = enum2gen(1..100)

p g.resume  #=> 1
p g.resume  #=> 2
p g.resume  #=> 3

ただし、Range オブジェクトは今はないので代替手段で。enum は Kinx では予約語なので変数名も変えておきます。

Kinx
function enum2gen(enumArray) {
  return new Fiber(&{
    enumArray.each(&(i) => {
      yield i;
    });
  });
}

var g = enum2gen(100.times().map(&(i) => i+1));

System.println(g.resume());
System.println(g.resume());
System.println(g.resume());

実行。

1
2
3

やったね。

これが動かなかった…

おわりに

やはりそもそも Fiber を使ったことがない、というのが敗因でしょう。色々使ってみよう。

結局 協調 スレッドなので、実際にスレッドである必要はないし、単一スレッドの中でのコンテキスト(スタック状態)を関数オブジェクトに保持して切り替えている、といった実装になっているだけ。スレッドという言葉に惑わされなければこれで悪くない気がしているのですが、何か根本的なところで思い違いをしているようであれば、ぜひぜひ ご指摘ください

ということで、最初に紹介しようとしたものは紹介しましたね。一先ず、ライブラリの使い方とか、サンプルコードとかを紹介していくフェーズに戻ろうかと思います。

では、次回。

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

VS codeの初期設定とRuby on Railsの環境設定

初期設定につまづくとかなり時間をロスするので、まとめることにしました

VSCodeの初期設定

VSCode(Visual Studio Code)をインストール

  1. VSCodeダウンロードページを開く スクリーンショット 2020-03-31 5.50.34.png
  2. Download for Macをクリックして、ダウンロードを完了します

VS Codeに拡張機能を追加

追加する拡張機能

  • Japanese Language Pack for Visual Studio Code →日本語表記にします。

スクリーンショット 2020-03-31 5.56.45.png

  • Ruby→Rubyの構文をチェックし、間違った箇所を指摘。

スクリーンショット 2020-03-31 5.56.59.png

  • HTML Snippets→HTMLタグ、CSSタグの入力を補完。

スクリーンショット 2020-03-31 5.56.37.png

  • Better Haml

スクリーンショット 2020-03-31 5.56.22.png

  • zenkaku→全角スペースを知らせてくれます。

スクリーンショット 2020-03-31 5.57.16.png

  • vscode-icons →言語ごとにファイルのアイコンを変えてくれます。
  • Docker

スクリーンショット 2020-03-31 5.56.29.png

実際にインストールする

  1. VS Codeを開く スクリーンショット 2020-03-31 5.53.49.png
  2. 拡張機能を追加するためのボタンを押す Image from Gyazo
  3. 追加したい拡張機能を検索する

Image from Gyazo
4. インストールする
Image from Gyazo
5. 反映させるためにvs codeを再起動「command + q」で終了し再度、起動する

自動保存する設定

記述したのに、保存できていないと泣くので、自動保存するように設定しましょう
1. vs codeの設定を開く

Image from Gyazo
2. ユーザー設定がひらけているか確認
スクリーンショット 2020-03-31 6.20.28.png
3. Files: Auto Save「onFocusCange」が選択されていれば完了

半角スペースの見分けがつくようにする

  1. VSCodeのサイドバーより、「管理」→ 「設定」の順に選択。
  2. 開いたページで「ユーザー設定」タブを開く。
  3. Editor: Tab Sizeを2に設定。 Editor: Render Whitespaceで「all」を選択。

Finderの設定

1.FInderを開く!
スクリーンショット 2020-03-31 6.29.45.png
2.Finderを選択の上、画面左上の「Finder」をクリック
スクリーンショット 2020-03-31 6.29.58.png
3.環境設定をクリック
4.サイドバーに「owner」を追加
スクリーンショット 2020-03-31 6.30.11.png
5.Finderにownerの表示が出る
スクリーンショット 2020-03-31 6.32.21.png

Ruby on Railsの開発環境を整える

ターミナル
#ホームディレクトリに戻す
$ cd
ターミナル
$ xcode-select --install

Homebrewをインストール

ターミナル
$ ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"

途中でエンターキーを押して、進める必要があるので、押し忘れないでください。

インストールされたか確認
:ターミナル
$ brew -v
Homebrew 1.8.0など表示される

Homebrewのアップデート

ターミナル
$ brew update

Homebrewの権限を変更

ターミナル
$ sudo chown -R `whoami`:admin /usr/local/bin

Rubyをインストール

ターミナル
$ brew install rbenv ruby-build

rbenvをどこからも使用できるようにしよう

ターミナル
$ echo 'eval "$(rbenv init -)"' >> ~/.bash_profile

bash_profileの変更を反映

ターミナル
$ source ~/.bash_profile

readlineをinstall
ターミナルのirb上で日本語入力を可能にする設定を行うために、以下のコマンドでインストール

ターミナル
$ brew install readline

readlineをどこからも使用できるようにしよう

ターミナル
$ brew link readline --force

rbenvを利用してRubyをインストール

ターミナル
$ RUBY_CONFIGURE_OPTS="--with-readline-dir=$(brew --prefix readline)"
$ rbenv install 2.5.1

利用するRubyのバージョンを指定

ターミナル
$ rbenv global 2.5.1

rbenvを読み込んで変更を反映

ターミナル
$ rbenv rehash

Rubyのバージョンを確認

ターミナル
$ ruby -v

さきほど指定したバージョンになっていれば、OKです。

MySQLを用意

ターミナル
$ brew install mysql@5.6

MySQLの自動起動設定

MySQLは本来であればPC再起動のたびに起動し直す必要がありますが、面倒なので自動化

ターミナル
$ mkdir ~/Library/LaunchAgents 
$ ln -sfv /usr/local/opt/mysql\@5.6/*.plist ~/Library/LaunchAgents
$ launchctl load ~/Library/LaunchAgents/homebrew.mxcl.mysql\@5.6.plist 

どこからもmysqlを利用できるように設定

ターミナル
# mysqlのコマンドを実行できるようにする
$ echo 'export PATH="/usr/local/opt/mysql@5.6/bin:$PATH"' >> ~/.bash_profile
$ source ~/.bash_profile
# mysqlのコマンドが打てるか確認する
$ which mysql
# 以下のように表示されれば成功
/usr/local/opt/mysql@5.6/bin/mysql

Railsを用意

bundlerをインストール

Rubyの拡張機能(gem)を管理するためのbundler(バンドラー)をインストール

ターミナル
$ gem install bundler

Railsをインストール

ターミナル
# インストール
$ gem install rails --version='5.2.3'

# 再読み込みして反映
$ rbenv rehash

# 成功しているかバージョン確認
$ rails -v

Appを保存するためのフォルダ(ディレクトリ)を作成する

ターミナル
#ホームディレクトリに移動
$ cd ~

#projectsディレクトリの作成
$ mkdir projects

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

[devise]現在ログインしているユーザ名が表示されない場合の解決策

環境

バージョン
ruby 2.6.5
rails 5.2.4.2
devise 4.7.1

目標

現在ログインしているユーザの名前を表示させたい。

問題

@yasuno0327氏が公開していたdevise にusername カラムを追加し、usernameを登録できるようにする。を参考にして実装したところ以下のようなエラーを吐いた。

Error
undefined method `username' for nil:NilClass

(意訳)
NilClassのnilオブジェクトに`username`メソッドは定義されてないよ

解決策

_current_username.html.erb
<% if user_signed_in? %>
  <!-- 変更前-->
  <%= @user.username %>
    <!-- 変更後-->
    <%= current_user.username %>
<% end %>

解決に至るまでに引っかかったこと

1.エラー文はどういう意味を指しているのか?

どうやら、@userがnilになっていることを指しているらしい。

参考

https://qiita.com/gonzaemon111/items/a6d9f3ad0645037125c2

2.@userがnilになっているのは何故?

インスタンス変数のスコープ外だから値が参照されない?
(正直、理解があやふやです。自分なりにかみ砕けたら、追記なり修正なり行います。)

[復習]各変数のスコープについて

種別 説明 文法
ローカル変数 それぞれのメソッドの中だけで存在しており、メソッドの処理が終了すると失われてしまう変数です。 通常の変数通り
インスタンス変数 インスタンスに属する変数で、インスタンスの中からであれば、どのメソッドからでも参照・変更できます。インスタンスごとに作られる変数ですので、インスタンスごとに違う値を保持しています。インスタンス生成後、最初に代入された時点で生成されますが、メソッド内でしか生成できませんので、注意が必要です。 変数名の前に「@」
クラス変数 クラスで共通の変数として生成されますので、同じクラスから生成されたインスタンスすべてで共通の値を持つことになります 変数名の前に「@@」
クラスインスタンス変数 クラスメソッドからのみアクセスできる変数で、メソッド内ではなく、クラス定義内に定義しなければいけません。クラスが定義されると同時に生成され、クラス変数同様、1つのクラスで共通の値を持っています 変数名の前に「@」

引用

https://web-camp.io/magazine/archives/15956

3.current_userと@userは何が違うのか?

メソッドか変数かの違い(...だと認識してます。)

current_user @user
deviseのヘルパーメソッド userのインスタンス変数
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む