- 投稿日:2019-03-10T23:26:00+09:00
RubyはオワコンらしいからRubyの代替を考えるよ
オワコンだと言う人がいる1ので、今のうちに代替を探しておくことにする。
バージョンを明記しない場合は最新バージョンを使うことを前提とする。また、JavaScriptについては、AltJSが前提のライブラリを使用する場合を除き、AltJSは除外している。JavaScriptの部分はTypeScriptでもCoffeeScriptでも好きなものに置き換えても良いものとする。また、汎用プログラミング言語では無い物は斜体にしてある。
他に候補があれば、コメントで教えて欲しい。詳しくないものも載せているので、間違っているというのがあれば教えて欲しい。
Rubyでできるもの
Rubyが得意なものというかRubyの用途してよく選ばれるものという感じ。
ワンライナー
Rubyでは
-e
オプションでスクリプトをコマンドの引数として書ける。また、-n
や-p
と言った行毎の処理に適したオプションが用意されており、いわゆるワンライナーが作りやすいように工夫が行われている。
- UNIXコマンドの組合せ
- Perl 5
- Perl 6
スクリプトによるテキスト処理
標準入出力だけの処理やファイルのテキスト処理を行う場合でも、複雑になる場合はワンライナーでは厳しくなる。その場合でも本格的なスクリプトを書く必要が出てくる。
たぶん、Rubyが一番得意な分野のはず。標準入出力やファイルの扱いに工夫がされており、強力な正規表現も用意されている。あえてRubyが苦手になりそうな所は大量ファイルに対する並列処理ぐらいだ。それも、ただ時間がかかるだけなので、短い処理時間が求められなければ、特に問題にならない。
- シェル(bash or tcsh, zsh, etc.)
- Perl 5
- Perl 6
- Python
CUIアプリケーション
Rubyでは複数のプログラムからなるより複雑なCUIアプリケーションも作れる。有名所ではVagrantやChefがある。利点として、RubyをDSLとして採用しやすいという所がある。
- Perl 5
- Perl 6
- Python
- Go
- C
- C++
DSL
言ってみればアプリケーションの動作を決定づける設定ファイルである。ただ、iniファイルやレジストリ等は単純なキーバリューの紐付けしか出来ない。より複雑で構造化された設定を行えるように汎用言語が使われる事がある。そういったものがDSLである。
Rubyで言えばRakefileやGemfileがあたる。これらはRubyスクリプトそのものであるがアプリの動作を決定する設定ファイルとも言える。
- YAML
- JSON
- XML
- JavaScript
- Lua
プログラム拡張(アプリ内言語・マクロ)
プログラムの拡張に使う言語。Luaが大流行した時期もあった。Rubyも負けておらず、例えば…たと…え…、ごほん、とにかくRubyでも使える。2
- Python
- JavaScript
- Lua
オフィススイート操作
そもそもMS OfficeもLibre Officeもマクロ言語としてRubyをサポートしていない。しかし、Rubyでもwin32oleを使えばMS Officeは操作可能であるため、マクロのような処理が出来ないわけではない。また、Office Open XMLやOpenDocumentを直接読み取りや書き込みできるライブラリも(全てを網羅しているわけではないが)いくつかある。
- VBA
- Python
- JavaScript
Webアプリ
設置型
CGIやPHPは置くだけで動作する。アプリケーションの利用者から見るとこれほど大きな利点はない。よくわからないコマンドやサービス起動と言った難しいことはやりたくないのだ。
Rubyは昔からCGIとして動作することが出来た。ただ、標準のcgiライブラリのできがいいとは言い難いが、本当に簡易なものであれば、十分とも言える。
- PHP
- Perl 5
- Perl 6
- Python
Webサーバー型
Rubyには標準ライブラリとしてwebrickがあるし、pumaをはじめとしたWebサーバーはいくつもある。rackを使えばいろいろな組合せも出来る。でも、大抵は後述のフレームワークを使う。
- JavaScript + http(Node.js標準)
- Go
シンフレームワーク
Ruby on Railsの影に隠れてしまっているようだが、Rubyには強力なシンフレームワークSinatraがある。簡易なものであれば十分とも言える。
- JavaScript + Express
フルスタックフレームワーク
RubyでWebアプリのフルスタックフレームワークといえばRuby on Rails一択と言ってもいい。RubyはRuby on Railsで一躍有名になったが、Ruby on Rails以前から使っているマニアな人達はいたわけだ。そういった人達にとってRuby on Railsがなくなっても特に変わらないと思う。
- PHP + Larabel
- Python + Django
- Scala + Play
- Elixir + Phoenix
組み込み
Rubyには組み込み向けのサブセットであるmrubyがある。
- アセンブリ
- C
- C++
- Arduino
2Dゲーム
WindowsのみとなるがDXRubyを使えば簡易な2Dゲームは作れる。また、あるバージョンのRPGツクールはRubyのフレームワークになっている。
- C# + Unity
- Hot Soup Processor
- JavaScript + RPGツクール
ビジュアルプログラミング
低学年向けのプログラミング教材として注目されているビジュアルプログラミング。もちろんRubyにもSmalrubyというビジュアルプログラミングが用意されている。
- Squeak + Scratch
- Python + BlockPy
Rubyでできないことはないもの
Rubyでできないことはないが、あまり得意ではないもの。現在の所、あえて使うべきでは無いだろう。
デスクトップアプリ
Rubyにも多くのGUIライブラリがある。しかし、標準的なものというものが無く、どれも一長一短である。かつてはTcl/Tkが標準ライブラリとして同梱されていたが、いまではそれすらもなくなってしまった。
- C++ + Qt
- C# + WPF (Windows)
- C# + Xamarin
- Scala or Kotlin + OpenJFX
- JavaScript + Electron
3Dゲーム
OpenGLを駆使すれば出来ないことはないらしい。ただ、応答速度がシビアなゲームは無理。
- C# + Unity
- C++ + Unreal Engine
非同期処理
IOが発生する大量の軽量処理を行う場合はシングルスレッドな軽量処理が有利である。Rubyも一応Fiberという軽量スレッドが用意されており、ノンブロッキングIO(標準で非同期IOはない)もあるので、非同期処理と似たようなことは出来るし、そのようなライブラリもある。ただ、Rubyとしては非同期処理は主流では無い。
- JavaScript
- Go
- Python
標準環境での単体実行可能アプリ
OSインストール直後から単体で実行出来るというのは一つの利点となり得る。Windowsではocraという手段もないことはない。なお、Macだと(バージョンは古いが)Rubyが標準ではいる。Linuxはディストリビューションによるが、最小構成ではだいたい入らない。
- C
- Go
データサイエンス
データ分析からいわば機械学習やディープラーニングなど流行りのAIに通じるもの。RubyにもSciRubyやNumoといったプロジェクトがあるのだが、ライブラリが十分揃っているとは言い難い。
- Python
- R
- C++
スマホアプリ
一応RubyMotionと言う手段がある。どこまで出来るのかは不明。
- Kotlin
- Swift
- JavaScript
- C#
Rubyでできないもの
Rubyではほぼできないもの。頑張ればできるかも知れないが、途中で壁にぶつかる可能性もある。
Webフロントエンド
Rubyそのものではないが、Rubyとほぼ同じ文法であるOpalというものがある。Opal向けのフレームワークとしてはHyperstack3がある。他にもRubyっぽい文法のCrystalがWebAssemblyに対応しようとしているようだが、ちょっとこっちはよくわからない。
どちらにしても、Rubyそのものがフロントエンドの言語にはなれない。
- Opal + Hyperstack
- JSX(with Flow) or TypeScript or CoffeScript + React
- JavaScript + Vue.js
- TypeScript + Angular JS
- Elm
高速・省メモリアプリ
Rubyは低速だし、メモリ消費量も多い。何度も呼び出され、速度や省メモリが求められるような場合はRubyを選択することはできない。
- C
- C++
- Go
- Rust
- C#
- Kotlin
- Swift
- Crystal
汎用ライブラリ
汎用ライブラリとしてはCのインターフェースが提供できることが最低条件である。また、そう言ったライブラリは画像や動画の加工等の非常に重い処理であり、それなりの速度が求められる。場合によっては、メモリ容量も少ない方が良い。
- C
- C++
- Go
- Rust
VM用ライブラリ
JVMや.NET用のライブラリのことである。同じVMを使用した言語から利用できるようにしなければならない。JRubyやIronRubyを使えばできないことはなさそうだが、現実的ではない。
- JVM
- Scala
- Kotlin
- .NET
- C#
- F#
スケールアウト可能な並列処理
処理が軽量であればシングルスレッドな非同期処理で十分だが、一つ一つの処理が重く、また、応答時間が非常にシビアな場合は非同期処理では間に合わなくなってしまう。マルチコアをフルで生かせる並列処理と複数サーバーで分散できるスケーラビリティが必要になってくる。
RubyのスレッドはGVLがあるため、マルチコアの恩恵は限定的である。一応マルチコアにはフォークという手段がないわけではないが、このレベルの処理は速度も求められるため、そもそも追いつかないと思われる。
- Scala
- Kotlin
- Elixir
OS
OSが無い環境、つまり、C/C++で言いうフリースタンディング環境でも動作できなければOSの基幹部分は作れない。なぜなら、OS自体はOSが無い環境で動作しなければならないからである。mrubyはOS依存では無いが、OSが作れるかは不明。
- アセンブリ
- C
- C++
- 投稿日:2019-03-10T23:23:05+09:00
Rubyのアクセサメソッドを理解した
Rubyの初心者ですが、どーもアクセサメソッドに関して附に落ちなかったのですが、ようやく理解できたので噛み砕いてご紹介。
※表現の仕方に不備があれば教えて下さいm(_ _)mまずアクセサメソッドには3つの種類があります。
attr_reader #rede(読む) attr_writer #write(書く) attr_accessor #access(読むと書くの両方)それでは一つづつ見ていきましょう。
attr_reader
まずは読むためのアクセサメソッドです。
Productというclassを作って、商品名(name)を作成して、商品名を表示させます。
class Product def initialize(name) @name = name end end product = Product.new('pen') puts product.name #=> ndefined method `name' for #<Product:0x007fa59b0dc620 @name="pen"> (NoMethodError)これだけではエラーになります。
Product
というclassを作って、initialize
で@nameインスタンス変数に代入しています。
product = Product.new('pen')
でインスタンスを作成し、product
という変数にインスタンスを代入。
puts product.name
でpenと表示させようとしましたがエラー。rubyではそのままだとインスタンス変数にアクセスできないのです。
では表示させるためのインスタンスメソッドを作成してみましょう。class Product def initialize(name) @name = name end def name @name end end product = Product.new('pen') puts product.name #=>pen無事に表示させることができました。
しかし、表示させるためだけのメソッドを毎回作成するのは面倒ですよね。。。そこで登場するのが
attr_reader
です。class Product attr_reader :name def initialize(name) @name = name end end product = Product.new('pen') puts product.name #=>penこれでわざわざ表示させるためのメソッドを作成する必要がなくなりました。
これで簡単にインスタンス変数をread、読む事ができました。attr_writer
attr_readerが理解できれば簡単です。
インスタンス変数を変更できるよう(write,書く)になります。
まずはattr_writer
を使わない場合はわざわざそのためのメソッドを作らなければなりません。class Product attr_reader :name def initialize(name) @name = name end def name=(value) @name = value end end product = Product.new('pen') product.name = 'note' puts product.name #=>noneこれも面倒ですよね。
attr_writer
で一発解決。class Product attr_reader :name attr_writer :name def initialize(name) @name = name end end product = Product.new('pen') product.name = 'note' puts product.name #=>noneattr_accessor
attr_reader
とattr_writer
の両方の役割を果たすのがattr_accessor
。
attr_reader
とattr_writer
の意味がわかれば簡単ですね。class Product attr_accessor :name def initialize(name) @name = name end end product = Product.new('pen') product.name = 'note' puts product.name #=>none引っかかってた点と、まとめ
class User def initialize(name) @name = name end def hello "Heloo, I am #{@name}" end end user = User.new('atsushi') p user.hello #=> Heloo, I am atsushi例えば上記の様なコードがあるときに、なんでこの場合はアクセサメソッドいらないの?
なんで?
attr_reader
いるんじゃないの?
だってインスタンス変数を参照してるじゃん。
と思っていましたが、違います。
なぜならhelloメソッドで直接インスタンス変数を参照しているからです。
だからアクセサメソッドは必要ありません。class User attr_reader :name def initialize(name) @name = name end end user = User.new('atsushi') p user.nameしかし上記のような場合は
attr_reader :name
が必要です。
なぜなら、user = User.new('atsushi')
はuserにインスタンスを代入しています。
userという変数からインスタンス変数を参照する必要があるため、アクセサメソッドが必要なわけです。
クラスの外部からインスタンス変数の参照するために必要なのです。アクセサメソッドは、外部インスタンスのインスタンス変数を参照したり変更するために定義する。
というところでしょうか。さぁまだまだ初心者なのでがんばります。
- 投稿日:2019-03-10T23:19:51+09:00
Gmail API 過去1ヶ月分のメールを全て取得する (Ruby)
@koshi_life です。
Gmail APIでメールを取得する機会があり、過去メールの取得の仕方調べたので備忘します。
流れは、
1.messages:list APIを使って、対象のメッセージIDを取得し、
2.messages:get API を使い、メッセージIDのメール内容を取得する
の2ステップです。※ スレッドID事に取得したいシーンは threads:list、Thread:get で取得が良さげ。
サンプルコード (Ruby)
fetch_gmail.rb# 指定のクエリでメッセージID一覧を返却します。 # # client: Google::Apis::GmailV1::GmailService # query: Gmail search operators refer to https://support.google.com/mail/answer/7190 def get_all_messages(client, query, max_results = 100) messages = [] next_page_token = nil loop do if next_page_token.present? result = client.list_user_messages('me', q: query, max_results: max_results, page_token: next_page_token) else result = client.list_user_messages('me', q: query, max_results: max_results) end messages += result.messages if result.messages.present? # result.next_page_token が存在していたら次のデータ有り next_page_token = result.next_page_token break if next_page_token.blank? end messages end # 過去1ヶ月分の全メールを取得し、`do_something`をコールします。 # # client: Google::Apis::GmailV1::GmailService def main(client) query = 'newer_than:1m' # クエリ: 直近1ヶ月分 messages = get_all_messages(client, query) messages.each do |msg| # メール取得 gmail = client.get_user_message('me', msg.id) # # do_somthing(gmail) # end end例外処理と
get_all_messages
内のresult.messages
が大量データを扱う時、メモリ占有率など気になりましたが、サンプルコードのため考慮していません。コード内のポイントとしては、メッセージID一覧取得する
get_all_messages
内でresult.next_page_token
の存在有無確認で
次のデータの存在有無を確認している点ですかね。参考
- 投稿日:2019-03-10T22:54:57+09:00
オフラインリアルタイムどう書く E31 の実装例(Ruby)
問題の名前 : ぐるぐる数
問題 : http://nabetani.sakura.ne.jp/hena/orde31rotnum/
実装リンク集 : https://qiita.com/Nabetani/items/d139d5ef70aa23c2d038次回のイベントは 4/6 最終回にするかも。
see https://yhpg.doorkeeper.jp/events/88379で。
やや遅い実装
まずは、やや遅い実装。
ruby# frozen_string_literal: true require "json" def next_vals(b,n) [n, (n+1)%b] end def build_number(b, head, size, states) (size-1).times.with_object([head]) do |ix,o| candidates = next_vals(b, o.last).sort o.push candidates[:low==states[ix] ? 0 : 1] end end def undigits(b,d) d.reverse_each.inject(0) do |acc,n| acc*b+n end end def build_states(b, digits) r=digits.each_cons(2).with_object([]){ |(p,q),o| candidates = next_vals( b, p ) o << %i(low high above)[candidates.count{ |e| e<q }] break o unless candidates.include?(q) } r + [:low]*(digits.size-1-r.size) end def guru(b,digits) states = build_states(b, digits) above = states.index(:above) return build_number(b, digits.first, digits.size, states) unless above # 繰り上がり処理がある n = undigits(b,digits[0,above+1].reverse) d = (n+1).digits(b).reverse head = guru(b,d) head + [next_vals( b, head.last).min] * (digits.size-above-1) end def sup(b,x) undigits(b, guru(b, x.digits(b).reverse).reverse) end def solve_impl( b, x, y ) x = sup(b,x) count = 0 while x<=y count+=1 x = sup(b,x+1) end count end def solve( src ) sb, *sxy = src.split(",") b = sb.to_i xy = sxy.map{ |e| e.to_i(b) } if b==2 (xy[1] - xy[0]+1).to_s else solve_impl( b, *xy ).to_s end end if __FILE__==$PROGRAM_NAME json = File.open( ARGV[0], &:read) data = JSON.parse( json, symbolize_names:true ) data[:test_data].map{ |number:, src:, expected:| actual = solve( src ) ok = expected==actual p [ (ok ? "ok" : "**NG**"), number, src, expected, actual ] ok }.tap{ |r| puts( r.all? ? "everything is okay" : "something wrong" ) } end
ruby 1.rb data.json
のように実行する。「次に大きいぐるぐる数」という計算ができればよいというアイディアだけど、MikuriyaHiroshi さんのアイディアの方が平易なコードになっていいかもね。
ただ。
b
が 2 のときはすべての数がぐるぐる数なので、この計算をするとえらく時間がかかる。
そこでb
が 2 のときは単に引き算するというショートカットをしている。手元のマシンで3秒弱。
速い実装
つづいてちゃんとした実装。
ruby# frozen_string_literal: true require "json" def state(b, x) x.each_cons(2).map{ |prev,cur| lambda{ |lo,hi| return :below if cur<lo return :lo if cur==lo return :mid if cur<hi return :hi if cur==hi return :above }[*[ prev, (prev+1) % b ].sort] } end def count_state( s ) return 1 if s.empty? case s[0] when :below then 0 when :lo then count_state(s.drop(1)) when :mid then 2**(s.size-1) when :hi then count_state(s.drop(1)) + 2**(s.size-1) when :above then 2**s.size end end # x 以下の ぐるぐる数の個数 def guru_count( b, x ) # x.size 未満の桁数 s0 = (1...x.size).sum{ |keta| (b-1)*(2**(keta-1)) } # x.size 桁で、先頭が 1..(x.first-1) s1 = (x.first-1)*(2**(x.size-1)) # 先頭が x.first s2 = count_state( state(b, x) ) [s0, s1, s2].sum end def solve( src ) sb,sx,sy = src.split(",") b = sb.to_i x = (sx.to_i(b) - 1).digits(b).reverse y = sy.to_i(b).digits(b).reverse ycount = guru_count(b,y) xcount = guru_count(b,x) ( ycount - xcount ).to_s end if __FILE__==$PROGRAM_NAME json = File.open( ARGV[0], &:read) data = JSON.parse( json, symbolize_names:true ) data[:test_data].map{ |number:, src:, expected:| actual = solve( src ) ok = expected==actual p [ (ok ? "ok" : "**NG**"), number, src, expected, actual ] ok }.tap{ |r| puts( r.all? ? "everything is okay" : "something wrong" ) } endまずはこの手の問題の定石。
「0<=x<=y で、x<=i<=y である i について C(i)が真になる i の数は?」みたいな問題は、しばしば「0<=x で、0<=i<x である i について C(i)が真になる i の数」を求める関数を書くと楽になる。で。
問題はその『「0<=x で、0<=i<x である i について C(i)が真になる i の数」を求める関数』の実装。例えば。
455734 ( 10進数、6桁 )
よりも小さいぐるぐる数の数を数えたい。そうすると、これはa) 1桁〜5桁のぐるぐる数 の数
b) 1〜3で始まる6桁のぐるぐる数 の数
c) 4で始まる6桁のぐるぐるで455734より小さいものの数の三種類の合計になる。a と b は簡単に求まる。
c はc1) 44 で始まる 6桁のぐるぐる数 の数
c2) 45 で始まる 455734 より小さい 6桁のぐるぐる数 の数の2つに分けられる。
c1 は簡単に求まる。
c2 は「5で始まる 5桁のぐるぐる数」の数と同じになるので、再帰呼出しで解決できるというわけ。
これを実行すると ruby自体の起動時間に隠れるぐらいの時間で計算が終わる。まとめ
会場の方の反応を見ると、やや難しかった模様。
わりときれいな、それでいてオリジナリティのある問題だと思ったんだけど、どうだろう。次回 4月6日(土曜)は、最終回になるかもしれない(ならないかもしれない)です。
ご興味がお有りなら是非。
- 投稿日:2019-03-10T22:33:21+09:00
http://localhost:3000/に特定のページを表示させたい(ルートへのルーティング設定)
専門的には、ルートへのルーティング設定をするというらしいです。
本当の初心者にとっては、そのワードすら知らないのでggrksと言われてもなかなか辛いところありますよね。。。ルートへのルーティング設定とは
Cloud9環境では、
https://〜.amazonaws.com/
Vagrant環境では、
http://localhost:3000/
にアクセスした時に、デフォルトで表示される「Welcome aboard」のページではなく、
自分で作ったページなり、アクションなりを表示させたい時にする設定のことです。ルートへのルーティング設定を行う
早速やっていきましょう!
コントローラーとアクションを作る
ターミナルに下記のコードをうちます。
$ rails g controller コントローラー名 アクション名
ルートファイルを編集する
「config/routes.rb」ファイルに下記のコードを書き込みます。
root :to => 'コントローラ名#アクション名'
例えば、
http://localhost:3000/
にアクセスした時に、
indexページを表示させたい場合は、
root :to => 'コントローラ名#index'
と書きます。これだけです。
もう少し理解が進んだら、ルートとは、ルーティング設定とは、というところも細かく書きたいです。
RESTfulなルーティングを設定するというところも書きたい。。
でも今は、これが精一杯なのでご勘弁ください。
- 投稿日:2019-03-10T21:29:01+09:00
Rails: 5分で住所を自動入力しよう
もっと住所入力を楽にしてみない?
CtoCサービスのWebアプリを作る時、大抵のアプリには住所入力の項目があるかと思います。
その時に、自分で一から実装するのもいいけどめんどくさい時ってありますよね?
そんなあなたに5分で実装出来るプラグイン「gem」を教えたい。手順
まず最初に、下記で必要なjsファイルをダウンロードしてください。
ファイルは、assets/javascript/に置いてください。jquery.jpostal.js
1. 練習なので$rails g scaffold User
を事前に作っておきます。
2. 次にgem
を追加していきます。必須gem
gem 'jp_prefecture' gem 'jquery-rails'3. 次に
bundle install
をしましょ
4. ここでは、mifrationファイルを作っていきます。
$rails g migration AddColumnsToUsers postcode:integer
class AddColumnsToUsers < ActiveRecord::Migration[5.1] def change add_column :users, :postcode, :integer add_column :users, :prefecture_code, :integer add_column :users, :address_city, :string add_column :users, :address_street, :string add_column :users, :address_building, :string end end5.
$ rails db:migrate
をしましょ。
6. modelを編集していきます。
app/models/user.rbclass User < ApplicationRecord include JpPrefecture jp_prefecture :prefecture_code def prefecture_name JpPrefecture::Prefecture.find(code: prefecture_code).try(:name) end def prefecture_name=(prefecture_name) self.prefecture_code = JpPrefecture::Prefecture.find(name: prefecture_name).code end end7. Viewファイルを編集しています。
app/views/users/_form.html.erb<h2>Your address</h2> <p>zip code</p> <%= form.text_field :postcode %> <p>prefecture</p> <%= form.text_field :prefecture_code, collection: JpPrefecture::Prefecture.all, :value_method => :name, include_blank: '都道府県' %><br> <p>city</p> <%= form.text_field :address_city %> <p>street</p> <%= form.text_field :address_street %> <p>builbding name</p> <%= form.text_field :address_building %> <div class="actions"> <%= form.submit %>8. 次に、jQueryでjpostalメソッドの呼び出しをしていきます。
app/assets/javascripts/user.coffee$ -> $("#user_postcode").jpostal({ postcode : [ "#user_postcode" ], address : { "#user_prefecture_code" : "%3", "#user_address_city" : "%4", "#user_address_street" : "%5%6%7" } })こうすると、郵便番号を入力すると反映されるかと思います。
9. 最後に、Controllerでストロングパラメーターを設定します。
app/controllers/users_controller.rbdef zipedit params.require(:user).permit(:postcode, :prefecture_name, :address_city, :address_street, :address_building) endすでに、関連する記事がいくつかありますが更新日が古かったので個人的メモのついでにアップデートしてみました。
こんな感じで、誰かの助けになればと思います。なお、最短でやって5分なので思いのほか時間がかかっても責めないでないください。
- 投稿日:2019-03-10T21:29:01+09:00
Rails: 5分で住所を自動入力かしよう
もっと住所入力を楽にしてみない?
CtoCサービスのWebアプリを作る時、大抵のアプリには住所入力の項目があるかと思います。
その時に、自分で一から実装するのもいいけどめんどくさい時ってありますよね?
そんなあなたに5分で実装出来るプラグイン「gem」を教えたい。手順
まず最初に、下記で必要なjsファイルをダウンロードしてください。
ファイルは、assets/javascript/に置いてください。jquery.jpostal.js
1. 練習なので$rails g scaffold User
を事前に作っておきます。
2. 次にgem
を追加していきます。必須gem
gem 'jp_prefecture' gem 'jquery-rails'3. 次に
bundle install
をしましょ
4. ここでは、mifrationファイルを作っていきます。
$rails g migration AddColumnsToUsers postcode:integer
class AddColumnsToUsers < ActiveRecord::Migration[5.1] def change add_column :users, :postcode, :integer add_column :users, :prefecture_code, :integer add_column :users, :address_city, :string add_column :users, :address_street, :string add_column :users, :address_building, :string end end5.
$ rails db:migrate
をしましょ。
6. modelを編集していきます。
app/models/user.rbclass User < ApplicationRecord include JpPrefecture jp_prefecture :prefecture_code def prefecture_name JpPrefecture::Prefecture.find(code: prefecture_code).try(:name) end def prefecture_name=(prefecture_name) self.prefecture_code = JpPrefecture::Prefecture.find(name: prefecture_name).code end end7. Viewファイルを編集しています。
app/views/users/_form.html.erb<h2>Your address</h2> <p>zip code</p> <%= form.text_field :postcode %> <p>prefecture</p> <%= form.text_field :prefecture_code, collection: JpPrefecture::Prefecture.all, :value_method => :name, include_blank: '都道府県' %><br> <p>city</p> <%= form.text_field :address_city %> <p>street</p> <%= form.text_field :address_street %> <p>builbding name</p> <%= form.text_field :address_building %> <div class="actions"> <%= form.submit %>8. 次に、jQueryでjpostalメソッドの呼び出しをしていきます。
app/assets/javascripts/user.coffee$ -> $("#user_postcode").jpostal({ postcode : [ "#user_postcode" ], address : { "#user_prefecture_code" : "%3", "#user_address_city" : "%4", "#user_address_street" : "%5%6%7" } })こうすると、郵便番号を入力すると反映されるかと思います。
9. 最後に、Controllerでストロングパラメーターを設定します。
app/controllers/users_controller.rbdef zipedit params.require(:user).permit(:postcode, :prefecture_name, :address_city, :address_street, :address_building) endすでに、関連する記事がいくつかありますが更新日が古かったので個人的メモのついでにアップデートしてみました。
こんな感じで、誰かの助けになればと思います。なお、最短でやって5分なので思いのほか時間がかかっても責めないでないください。
- 投稿日:2019-03-10T20:59:37+09:00
プログラミング学習 記録 Ruby
- 投稿日:2019-03-10T18:02:19+09:00
①連続した数を配列に入れる。②文字列からn個抜き出すと何通りあるか?
①1〜5の連続した数を配列に入れる
m = (1..5) => 1..5 m = (1..5).to_a => [1,2,3,4,5]to_aを使うと配列に変換される。
②文字列から何個抜き出すと何通りあるか?
文字列.combination
[1,2,3,4]の中からn個抜き取る。重複しない組み合わせ。逆順含まない(例[1,2][2,1]は含まない)n = 2 [1,2,3,4].combination(n).to_a => [[1, 2], [1, 3], [1, 4], [2, 3], [2, 4], [3, 4]]文字列.permutation
[1,2,3,4]の中からn個抜き取る。重複しない組み合わせ。逆順含む(例[1,2][2,1]含む)n = 2 [1,2,3,4].permutation(n).to_a => [[1, 2], [1, 3], [1, 4], [2, 1], [2, 3], [2, 4], [3, 1], [3, 2], [3, 4], [4, 1], [4, 2], [4, 3]]
- 投稿日:2019-03-10T17:40:43+09:00
fields_forにハマった
日記に画像を登録するのに、diaryモデルとimageモデルを使用して実装。
画像の登録はcarrierwaveを使いました。
別モデルのデータを表示するにはアソシエーションとfields_forでのフォーム生成が必要。
アソシエーション
diary:image = 1:多 の関係性なので
diary.rbhas_many :images, dependent: :destroy #オプション(dependent: :destroy)で日記が消えたらそれに関連する画像も消える設定にした。image.rbbelongs_to :diaryリファレンスキー追加
ターミナルrails g migration add_references_to_images diary:referencescreate_images.rbclass CreateImages < ActiveRecord::Migration[5.2] def change create_table :images do |t| t.string :name t.references :diary, foreign_key: true #追記 t.timestamps end end endadd_references_to_images.rbclass AddReferencesToImages < ActiveRecord::Migration[5.2] def change add_reference :images, :diary, foreign_key: true end endこれでimagesテーブルにdiary_idが入る。
gemインストール
Gemfilegem 'carrierwave'ターミナルbundle installuploader追加
ターミナルrails g uploader imageimage.rbmount_uploader :name, ImageUploader #nameカラムに画像のデータが入ります。入力フォーム作成
_form.html.erb(diary)<%= form_for(@diary) do |form| %> <% if @diary.errors.any? %> <div id="error_explanation"> <%= pluralize(angel.errors.count, "error") %> prohibited this diary from being saved: <div class="field"> <%= form.label :date %> <%= form.date_field :date, value: Time.now.strftime("%Y-%m-%d") %> </div> <div class="field"> <%= form.label :content %> <%= form.text_area :content %> </div> #異なるモデル(imageモデル)をいじるときはform.fields_forを使う <%= form.fields_for :images do |image| %> <div class="field"> <%= image.label :image %> #表示名 <%= image.file_field :name %> #ファイル選択ボックスを生成し、データの送り場所を指定 <%= image.hidden_field :id %> </div> <% end %> <div class="actions"> <%= form.submit %> </div> <% end %> <%= link_to 'Back', diaries_path %>accepts_nested_attributes_for
diaries_controller.rbclass DiariesController < ApplicationController def new @diary = current_user.diaries.new @diary.images.build end def create @diary = current_user.diaries.build(diary_params) if @diary.save redirect_to @diary, notice: 'diary was successfully created.' else render :new end end private def diary_params params.require(:diary).permit(:date ,:gift ,:content ,:user_id ,:angel_id, images_attributes: [:id, :name, :_destroy])#編集や削除の際にid,;_destroyが必要らしい end enddiary.rbaccepts_nested_attributes_for :imagesモデルでaccepts_nested_attributes_forを指定することでコントローラのstrongparameter内でimages_attributeを使ってカラムを指定することができる
画像の表示
show.html.erb<%= image_tag @diary.images.first.name.to_s %>
- 投稿日:2019-03-10T16:29:45+09:00
Factory_Botで undefined method '×××' in '○○○' factoryが発生する場合
RailsでFactory Botを使う時にタイトルのエラーが発生したので、原因と対応のメモです。
バージョン
Ruby 2.5.1
Rails 5.2.2
Factory Bot 5.0.1エラーメッセージ
具体的には以下のエラーメッセージが出力されました。
/vendor/bundle/ruby/2.5.0/gems/factory_bot-5.0.1/lib/factory_bot /definition_proxy.rb:97:in `method_missing': undefined method 'name' in 'user' factory (NoMethodError)解決方法
エラー発生時のファクトリ
spec/factories/user.rbFactoryBot.define do factory :user do name "Beer Lover" email "beerlover@example.com" password "beerlover" end end↓修正版
spec/factories/user.rbFactoryBot.define do factory :user do name {"Beer Lover"} email {"beerlover@example.com"} password {"beerlover"} end end属性名に設定する値は{}で囲う必要があります。
Factory Botのバージョンが5.0.0からは{}で囲わないとエラーがでるように変更されたみたいですね。
- 投稿日:2019-03-10T15:23:41+09:00
[Ruby] rbenv install
環境
Mac OS Mojava 10.14.3
rbenv install
一旦不要になったバージョンを削除
$ rbenv versions scripts $ rbenv versions system * 2.5.1 (set by /Users/harigaik/.rbenv/version) $ rbenv uninstall -f 2.5.1 $ rbenv rehashrbenv install
Homebrew でもインストール可能であるが、最新版であれば、clone で更新(とりあえず、そっちでやってみる)
$ brew install rbenv # or $ git clone git://github.com/sstephenson/rbenv.git ~/.rbenv $ mkdir -p ~/.rbenv/plugins $ git clone https://github.com/sstephenson/ruby-build.git ~/.rbenv/plugins/ruby-build.bash_profile 設定
$ vim ~/.bash_profile # 以下を最後の行に追加 export PATH="$HOME/.rbenv/bin:$PATH" eval "$(rbenv init -)" $ source ~/.bash_profile関連パッケージインストール & Ruby インストール
関連パッケージインストール
$ brew install openssl $ brew install readline $ brew install libiconv $ brew install libxml2インストール可能な Ruby バージョンをチェック
$ rbenv install -lreadline, openssl, iconv のパス指定で Ruby install(2.5.1)
$ RUBY_CONFIGURE_OPTS="--with-readline-dir=$(brew --prefix readline) --with-openssl-dir=$(brew --prefix openssl) --with-iconv-dir=$(brew --prefix libiconv) --with-xml2-include=$(brew --prefix libxml2)/include/libxml2" rbenv install 2.5.1 $ rbenv rehash # インストールした Ruby のバージョンのリストを確認 $ rbenv versions * system (set by /Users/harigaik/.rbenv/version) 2.5.1 # インストールした Ruby を有効にする $ rbenv global 2.5.1 system * 2.5.1 (set by /Users/harigaik/.rbenv/version)SSL証明書のインポート
$ ruby -ropenssl -e "p OpenSSL::X509::DEFAULT_CERT_FILE" $ sudo curl "https://curl.haxx.se/ca/cacert.pem" -o /usr/local/etc/openssl/cert.pemBundlerのインストール
GEMでRuby環境にbundlerのみをインストールする
$ rbenv exec gem install bundler $ rbenv rehash # bundler の確認 $ rbenv exec gem list $ rbenv exec gem which bundlerRailsのローカルインストールとRailsプロジェクトの作成
$ cat << EOS > Gemfile source "http://rubygems.org" gem "rails", "5.1.6" EOS $ bundle install --path vendor/bundle # rails を確認 $ bundle list # railsでプロジェクト(今回の例では”example”)を作成 # vendor/bundle に入ったgemを使ってコマンドを実行したい場合は上記のように bundle exec 〜 のようにする # --skip-bundle の指定を忘れないように!そうでないと bundle install が発動し、Ruby環境にgemがインストールされてしまう! $ bundle exec rails new example --skip-bundle # railsをローカルインストールするために使ったbundlerの環境と、ローカルのrailsを削除 $ rm -f Gemfile $ rm -f Gemfile.lock $ rm -rf .bundle $ rm -rvf vendor/bundle $ rm -rvf vendorRailsプロジェクトの環境セットアップ
$ cd example $ bundle install --path vendor/bundle -> エラーの場合は「bundle install エラー対応」を参照 # .gitignore に追加 $ echo '/vendor/bundle' >> .gitignorebundle install エラー対応
bundler 2.0.1 問題(1.16.1 にダウングレード)
can't find gem bundler (>= 0.a) with executable bundle 対応
$ cat Gemfile.lock ・ BUNDLED WITH 1.16.6 $ gem install bundler -v 1.16.1 $ gem uninstall bundler -v 2.0.1Nokogiri インストールできない問題
macOS SierraでNokogiriがインストールできない問題の解決方法
-> 推奨設定を実施$ brew install libxml2 $ bundle config build.nokogiri --use-system-libraries --with-xml2-include=$(brew --prefix libxml2)/include/libxml2Railse server 起動
$ bundle exec rails server資料
- ails開発環の構築(複数バージョン共存可能)(Homebrew編)
- rbenv に関する資料
- bundle install で nokogiri をインストールしろと言われる解決方法
- macOS SierraでNokogiriがインストールできない問題の解決方法
- can't find gem bundler (>= 0.a) with executable bundle 対応
- bundler 2.0.1 問題(1.16.1 にダウングレード)
- 投稿日:2019-03-10T14:05:33+09:00
Rubyでインターフェースクラスを実現する
はじめに
こちらの記事でRubyのインターフェースクラスについて触れていたところ、@yuukive さんよりアドバイスいただいたので記事化。
ありがとうございます!インターフェースクラスを実現する
定義
例えば
encode
というメソッドを持つインターフェースクラスEncoderIF
を実現したいとします。
ここで使えるのがNotImplementedErrorというクラス。こんな感じで定義してあげます。EncoderIF.rbclass EncoderIF def encode() raise NotImplementedError.new("#{self.class}##{__method__} が実装されていません") #FFmpeg等、エンコーダーに合わせた処理を実装する。 end end継承先の実装
後は普通に継承してメソッドを実装すればOKです。ここで、うっかりencodeの実装を忘れたりすると、
EncoderIFFailed.rb$LOAD_PATH.push("./") require 'EncoderIF' class EncoderIFFailed < EncoderIF end実行時にエラーを返してくれます。
$ ruby Main.rb Traceback (most recent call last): 1: from Main.rb:5:in `<main>' /home/XXX/EncoderIF.rb:3:in `encode': EncoderIFFailed#encode が実装されていません (NotImplementedError)もちろんencodeメソッドを実装すれば正しく動作するようになります。
EncoderIFSuccess.rb$LOAD_PATH.push("./") require 'EncoderIF' class EncoderIFSuccess < EncoderIF def encode() puts("実装しました") end end$ ruby Main.rb 実装しました最後に
インターフェースクラスっていいよね!
- 投稿日:2019-03-10T13:40:14+09:00
Rubyで言語処理100本ノックやってみた
はじめに
『プロを目指す人のためのRuby入門』――通称チェリー本の著者である伊藤淳一(@jnchito)さんに、Twitterで「プログラミング初心者なのですが、何か人に見せられるものを作りたいです。何がいいでしょうか?」とアバウト極まる質問をしたところ、伊藤さんが勧めてくれたのが「言語処理100本ノック 2015」でした。(伊藤さん、ありがとうございます!)
実際にやってみたので、何番煎じか分かりませんが、投稿してみます。
(言語処理本100ノック 2015:http://www.cl.ecei.tohoku.ac.jp/nlp100/)00. 文字列の逆順
str = "stressed" puts str.reverse #=> desserts
reverse
メソッドを知っていれば解けるので簡単ですね。01. 「パタトクカシーー」
str = "パタトクカシーー" puts str[0] + str[2] + str[4] + str[6] #=> パトカー
+
ではなく<<
を使っても同じ出力が得られます。ただし<<
は+
と違い、レシーバそのものの値を変更してしまう、いわゆる「破壊的メソッド」です。02. 「パトカー」+「タクシー」=「パタトクカシーー」
str_one = 'パトカー' str_two = 'タクシー' idx_one = 1 idx_two = 0 while idx_one <= 7 str_one.insert(idx_one, str_two[idx_two]) idx_one += 2 idx_two += 1 end puts str_one #=> パタトクカシーーなんだか不細工なコードで、もっと上手いやり方がある気がします……。
03. 円周率
str = "Now I need a drink, alcoholic of course, after the heavy lectures involving quantum mechanics." scanned = str.scan(/\w+/) circular_constant = scanned.map { |n| n.size } p circular_constant #=> [3, 1, 4, 1, 5, 9, 2, 6, 5, 3, 5, 8, 9, 7, 9]
scan
メソッドめちゃくちゃ便利~って感じです。面白い問題ですね。04. 元素記号
str = "Hi He Lied Because Boron Could Not Oxidize Fluorine. New Nations Might Also Sign Peace Security Clause. Arthur King Can." scanned = str.scan(/\w+/) ary = [1, 5, 6, 7, 8, 9, 15, 16, 19] hash = {} scanned.each.with_index(1) do |item, idx| if ary.include?(idx) hash[item.slice(0, 1)] = idx else hash[item.slice(0, 2)] = idx end end puts hash #=> {"H"=>1, "He"=>2, "Li"=>3, "Be"=>4, "B"=>5, "C"=>6, "N"=>7, "O"=>8, "F"=>9, "Ne"=>10, "Na"=>11, "Mi"=>12, "Al"=>13, "Si"=>14, "P"=>15, "S"=>16, "Cl"=>17, "Ar"=>18, "K"=>19, "Ca"=>20}かなり苦戦しましたが、解けたときはものすごく気持ちがよかったです。
最初はif
elsif
if
elsif
……と書き連ねたせいで60行近いクソオブクソコードになりましたが、include?
メソッドを見つけることができてコンパクトになりました。
each_with_index
とeach.with_index
が別物だというのも面白いですね。後者は引数に数字を入れることで、配列の添え字がどこから開始するか指定してやることができます。
(参考:https://qiita.com/gestalt/items/d2c663b4be524581747e)おわりに
解いていて楽しい問題ばかりで面白かったです。リファレンスとにらめっこして「使えそうなメソッドはないか」と考える時間もためになりました。とりあえず5問だけやりましたが、また挑戦したいです。
(100問ぜんぶはやらないとは思いますが)
「もっとうまいやり方があるぞ」とかあったらマサカリ投げてくれると喜びます。
ありがとうございました。
- 投稿日:2019-03-10T12:17:43+09:00
【Ruby】N桁のランダムな数字を生成するワンライナー
N桁のランダムな数字の文字列を生成するワンライナー
random_gen.rbdef random_number_generator(n) ''.tap { |s| n.times { s << rand(0..9).to_s } } end random_number_generator(4) # "3416" random_number_generator(10) # "1890841679"sampleを使うパターンはこちらの記事に記載されています。
https://qiita.com/shu_0115/items/fcdacebba263a7e8922d
- 投稿日:2019-03-10T10:33:43+09:00
[Ruby] Array から Hash を生成
概要
このコードの動作は?
array = [] n.times do array << [:a, :b].zip(gets.split().map(&:to_i)).to_h endarray # => [{:a=>1, :b=>2}, {:a=>3, :b=>4}]動作
# zip でまとめる a = [:a, :b].zip([1, 2]) # => [[:a, 1], [:b, 2]] # Hash に変換 h = a.to_h # => {:a=>1, :b=>2}サンプル
array = [1, 2] [:p, :b].zip(array).to_h # => {:p=>1, :b=2}array = [1, 2, 3].map { |id| User.new(id) } array.map(&:id).zip(array).to_h => {1=>#<User:0x00007fc97da12950 @id=1>, 2=>#<User:0x00007fc97da12928 @id=2>, 3=>#<User:0x00007fc97da12900 @id=3>} class User attr_accessor :id def initialize(id) @id = id end end資料
- 投稿日:2019-03-10T08:37:14+09:00
ループからコレクションクロージャメソッドへ(Replace Loop with Collection Clousure Method)
1つずつリファクタリング技法まとめ
個人的に簡単かつ取り入れ易いと思うものから目的
すぐ引き出せるようにする
基本作業サイクル
- システムを動かして仕様を精査
- テストメソッドを作成
- テストの失敗を確認
- テストの成功を確認
- 小さい変更、随時テスト実行(パターン追加失敗確認->成功確認)
- 最後テスト実行
- 最後動作確認
ループからコレクションクロージャメソッドへ(Replace Loop with Collection Clousure Method)とは
each系の処理をmap系の処理にすること
ブロック内でのループ処理の記述から、メソッドチェーンを実現可能な記法にすることポイント
- Ruby特有のリファクタリングともいえそう
- 繰り返し処理した結果を返却したい場合に、ループの外から変数として渡す必要がなくなる
- 複数の処理をループ内で行っている場合は、メソッドチェーンで実現できる
例
fruits = [] foods.each do |food| fruits << food.values_at( :apple, :orange ) end↓
fruits = foods.map {|food| food.values_at( :apple, :orange ) }書籍情報
Jay Fields (著), Shane Harvie (著), Martin Fowler (著), Kent Beck (著),
長尾 高弘(訳), リファクタリング:Rubyエディション
https://amzn.to/2VlyWML雑感
チェーンが長すぎるとわかり易いとはいえなくなるので、コレクションクロージャ単体をメソッドとして切り出す方が良さそう
- 投稿日:2019-03-10T02:56:08+09:00
binding.pryでデバッグする
binding.pryを使うためにGemをインストール。
Gemfilegroup :development do gem 'pry-rails' gem 'pry' endbundle installを実行する。
$ bundle installコンソールを立ち上げる。
$ rails cデバックしたいブレークポイントにbinding.pryと書く。
viewの場合は、<% binding.pry %>サーバーを起動し、ブレイクポイントを設定したアクションに遷移するとコンソールがとまる。
binding.pryから抜けるコマンド
[1] pry(Hoge)> exitbinding.pryから強制的に抜けるコマンド
プロセスを終了してしまうため、rails sが落ちる。[2] pry(Hoge)> exit![3] pry(Hoge)> !!!