20190310のRubyに関する記事は18件です。

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++

  1. 私が言っているわけではない。 

  2. 例を募集中。 

  3. 旧々名React.rb、旧名Hyperloop、また名前変わった。 

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

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

#=>none

attr_accessor

attr_readerattr_writerの両方の役割を果たすのがattr_accessor
attr_readerattr_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という変数からインスタンス変数を参照する必要があるため、アクセサメソッドが必要なわけです。
クラスの外部からインスタンス変数の参照するために必要なのです。

アクセサメソッドは、外部インスタンスのインスタンス変数を参照したり変更するために定義する。
というところでしょうか。

さぁまだまだ初心者なのでがんばります。

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

Gmail API 過去1ヶ月分のメールを全て取得する (Ruby)

@koshi_life です。

Gmail APIでメールを取得する機会があり、過去メールの取得の仕方調べたので備忘します。

流れは、
1.messages:list APIを使って、対象のメッセージIDを取得し、
2.messages:get API を使い、メッセージIDのメール内容を取得する
の2ステップです。

※ スレッドID事に取得したいシーンは threads:listThread: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の存在有無確認で
次のデータの存在有無を確認している点ですかね。

参考

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

オフラインリアルタイムどう書く 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日(土曜)は、最終回になるかもしれない(ならないかもしれない)です。
ご興味がお有りなら是非。

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

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なルーティングを設定するというところも書きたい。。
でも今は、これが精一杯なのでご勘弁ください。

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

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 
end 

5. $ rails db:migrateをしましょ。
6. modelを編集していきます。
app/models/user.rb

class 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

end

7. 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.rb

def zipedit
  params.require(:user).permit(:postcode, :prefecture_name, :address_city, :address_street, :address_building)
end

すでに、関連する記事がいくつかありますが更新日が古かったので個人的メモのついでにアップデートしてみました。
こんな感じで、誰かの助けになればと思います。

なお、最短でやって5分なので思いのほか時間がかかっても責めないでないください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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 
end 

5. $ rails db:migrateをしましょ。
6. modelを編集していきます。
app/models/user.rb

class 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

end

7. 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.rb

def zipedit
  params.require(:user).permit(:postcode, :prefecture_name, :address_city, :address_street, :address_building)
end

すでに、関連する記事がいくつかありますが更新日が古かったので個人的メモのついでにアップデートしてみました。
こんな感じで、誰かの助けになればと思います。

なお、最短でやって5分なので思いのほか時間がかかっても責めないでないください。
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

プログラミング学習 記録 Ruby

学習内容

プロを目指す人の為のRuby入門

railsチュートリアル1周目が終わった今、もう一度復習し直す

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

①連続した数を配列に入れる。②文字列から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]]
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

fields_forにハマった

日記に画像を登録するのに、diaryモデルとimageモデルを使用して実装。

画像の登録はcarrierwaveを使いました。

別モデルのデータを表示するにはアソシエーションとfields_forでのフォーム生成が必要。

アソシエーション

diary:image = 1:多 の関係性なので

diary.rb
has_many :images, dependent: :destroy
#オプション(dependent: :destroy)で日記が消えたらそれに関連する画像も消える設定にした。
image.rb
belongs_to :diary

リファレンスキー追加

ターミナル
rails g migration add_references_to_images diary:references
create_images.rb
class 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
end
add_references_to_images.rb
class AddReferencesToImages < ActiveRecord::Migration[5.2]
  def change
    add_reference :images, :diary, foreign_key: true
  end
end

これでimagesテーブルにdiary_idが入る。

gemインストール

Gemfile
gem 'carrierwave'
ターミナル
bundle install

uploader追加

ターミナル
rails g uploader image
image.rb
mount_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.rb
class 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
end
diary.rb
accepts_nested_attributes_for :images

モデルでaccepts_nested_attributes_forを指定することでコントローラのstrongparameter内でimages_attributeを使ってカラムを指定することができる

画像の表示

show.html.erb
<%= image_tag @diary.images.first.name.to_s %>
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む

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.rb
FactoryBot.define do
  factory :user do
    name "Beer Lover"
    email "beerlover@example.com"
    password "beerlover"
  end
end

↓修正版

spec/factories/user.rb
FactoryBot.define do
  factory :user do
    name {"Beer Lover"}
    email {"beerlover@example.com"}
    password {"beerlover"}
  end
end

属性名に設定する値は{}で囲う必要があります。
Factory Botのバージョンが5.0.0からは{}で囲わないとエラーがでるように変更されたみたいですね。

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

[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 rehash

rbenv 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 -l

readline, 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.pem

Bundlerのインストール

GEMでRuby環境にbundlerのみをインストールする

$ rbenv exec gem install bundler
$ rbenv rehash

# bundler の確認
$ rbenv exec gem list
$ rbenv exec gem which bundler

Railsのローカルインストールと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 vendor

Railsプロジェクトの環境セットアップ

$ cd example
$ bundle install --path vendor/bundle
-> エラーの場合は「bundle install エラー対応」を参照

# .gitignore に追加
$ echo '/vendor/bundle' >> .gitignore

bundle 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.1

Nokogiri インストールできない問題

macOS SierraでNokogiriがインストールできない問題の解決方法
-> 推奨設定を実施

$ brew install libxml2
$ bundle config build.nokogiri --use-system-libraries --with-xml2-include=$(brew --prefix libxml2)/include/libxml2

Railse server 起動

$ bundle exec rails server

資料

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

Rubyでインターフェースクラスを実現する

はじめに

こちらの記事でRubyのインターフェースクラスについて触れていたところ、@yuukive さんよりアドバイスいただいたので記事化。
ありがとうございます!

インターフェースクラスを実現する

定義

例えばencodeというメソッドを持つインターフェースクラスEncoderIFを実現したいとします。
ここで使えるのがNotImplementedErrorというクラス。こんな感じで定義してあげます。

EncoderIF.rb
class 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
実装しました

最後に

インターフェースクラスっていいよね!

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

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_indexeach.with_indexが別物だというのも面白いですね。後者は引数に数字を入れることで、配列の添え字がどこから開始するか指定してやることができます。
(参考:https://qiita.com/gestalt/items/d2c663b4be524581747e)

おわりに

解いていて楽しい問題ばかりで面白かったです。リファレンスとにらめっこして「使えそうなメソッドはないか」と考える時間もためになりました。とりあえず5問だけやりましたが、また挑戦したいです。
(100問ぜんぶはやらないとは思いますが)
「もっとうまいやり方があるぞ」とかあったらマサカリ投げてくれると喜びます。
ありがとうございました。

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

【Ruby】N桁のランダムな数字を生成するワンライナー

N桁のランダムな数字の文字列を生成するワンライナー

random_gen.rb
def 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

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

[Ruby] Array から Hash を生成

概要

このコードの動作は?

array = []
n.times do
  array << [:a, :b].zip(gets.split().map(&:to_i)).to_h
end
array
  # => [{: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

資料

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

ループからコレクションクロージャメソッドへ(Replace Loop with Collection Clousure Method)

image.png

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

雑感

チェーンが長すぎるとわかり易いとはいえなくなるので、コレクションクロージャ単体をメソッドとして切り出す方が良さそう

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

binding.pryでデバッグする

binding.pryを使うためにGemをインストール。

Gemfile
group :development do
  gem 'pry-rails'
  gem 'pry'
end

bundle installを実行する。

$ bundle install

コンソールを立ち上げる。

$ rails c

デバックしたいブレークポイントにbinding.pryと書く。
viewの場合は、<% binding.pry %>

サーバーを起動し、ブレイクポイントを設定したアクションに遷移するとコンソールがとまる。

binding.pryから抜けるコマンド

[1] pry(Hoge)> exit

binding.pryから強制的に抜けるコマンド
プロセスを終了してしまうため、rails sが落ちる。

[2] pry(Hoge)> exit!
[3] pry(Hoge)> !!!
  • このエントリーをはてなブックマークに追加
  • Qiitaで続きを読む