RubyでConnpassのAPIを叩く
はじめに
RubyでCLIアプリを作成し始めました。
ConnpassのイベントをGoogleカレンダーに登録する感じのを作ろうと思います。
便利なものになるかはわかりません。(そもそもCLIアプリな時点で)
完成形は置いといてまずはHttpsでConnpassのAPIを叩くところを作りました。
API呼び出し
APIの仕様に沿って作成しました。
すごいAPIがシンプルなので難しいところはないのですが、APIの呼び出しがHTTPSになると以外に情報がなかったので呼び出し部分を抜粋で載せておきます。
特に外部ライブラリ使わなくても標準のnet/httpでも十分って感じでした。
def self.get(params) begin validation params # パラメータのバリデーションチェック呼び出し params = URI.encode_www_form params uri = URI.parse("https://connpass.com/api/v1/event/?#{params}") response = Net::HTTP.start(uri.host, uri.port, :use_ssl => true) { |http| request = Net::HTTP::Get.new uri http.request request } JSON.parse response.body rescue ArgumentError => e e.message rescue IOError => e e.message rescue Timeout::Error => e e.message rescue JSON::ParserError => e e.message rescue => e e.message end end
パラメータのバリデーションチェックも簡単にですが実装しています。
引っかかった場合はArgumentErrorを返す感じです。
エラー処理部分は今はエラーメッセージを返す感じにしてますが、そのままエラーを呼び出しもとに投げる形に変えるかも。
RubyのHashとSymbolでハマったこと
事象
Thorのオプション機能を使ったときにハマりました。
コマンド入力のオプションで設定した内容はoptionsというHashクラスに入るのですが、以下がうまくいきませんでした。
p options[:a_key] # =>"a_value" hash.each do |key, value| case key when :a_key puts value # =>表示されない end end p optins # =>{"a_key"=>"a_value"}
結果
最後の1行でお気づきの方もいると思いますが、
結果から言うとoptionsはHashクラスではありませんでした。
私が勝手にHashクラスだと思いこんでいただけです。
p options.class # =>Thor::CoreExt::HashWithIndifferentAccess
SymbolとStringを区別せずに同一のキーとして扱うクラスらしい。(Railsとかでも提供がある)
eachメソッドのkeyにはSymbolではなくStringが入るためcase分岐しませんでした。
以下とかとすごく似て見えるけど違う。
というか引数に型(クラス)を意識しないRubyの特徴だから類似も含めて気をつけねば。
options = {"a_key" => "a_value"} p options # =>{"a_key"=>"a_value"} p options["a_key"] # =>"a_value" p options[:a_key] # =>nil options_symbol = {:a_key => "a_value"} p options_symbol # =>{:a_key=>"a_value"} p options_symbol["a_key"] # =>nil p options_symbol[:a_key] # =>"a_value"
Rubyでテストコードの作成
はじめに
Rubyでテストコードのお試しをしました。
Rubyでのテスティングフレームワークは何種類かあります。
以下あたりが人気っぽいです。
- RSpec
- Minitest
今回はRSpecを選びました。
選ぶ基準はここを参考にさせていただきました。(情報量の多さとか使われている多さ等々)
今読んでいるパーフェクトRubyではRubyに同梱されているtest-unitの説明のみだったため、
RSpecで書いてみた内容をメモして置きます。
RSpec導入
Rubyでの導入は簡単です。
というか前回のCLIアプリケーションにも導入しています。
前回の記事の通り以下の初期実行にてテスティングフレームをRSpecを選択していれば -t オプションをつけたときに勝手に入ります。
bundle gem sample -b -t
もしここでRSpecを選んでなかったよって人は以下で導入できます。
bundle gem sample -b -t rspec
このコマンド後RSpecの設定ファイル(.rspecとspec/spec_helper.rb)も作成されてます。
途中からって人は多分.gemspecに書くとかすればいける気しますが、試してません。
またテストコードも一部自動で作成済みです。
spec/sample_spec.rbはlib/sample.rbに対するテストコードで、バージョンがnilでないこととfalseがtrueであることを確認しています。
RSpec.describe Sample do it "has a version number" do expect(Sample::VERSION).not_to be nil end it "does something useful" do expect(false).to eq(true) end end
テストコードの実行
試しにこの状態ままで実行してみます。
実行のコマンドと実行結果は以下です。
$ bundle exec rspec Sample has a version number does something useful (FAILED - 1) Failures: 1) Sample does something useful Failure/Error: expect(false).to eq(true) expected: true got: false (compared using ==) # ./spec/sample_spec.rb:7:in `block (2 levels) in <top (required)>' Finished in 0.04696 seconds (files took 0.40854 seconds to load) 2 examples, 1 failure Failed examples: rspec ./spec/sample_spec.rb:6 # Sample does something useful
もちろん後者は失敗しました。
テストの作成
続いてはテストを自分で作成してみます。
前回CLIアプリケーションのお試しで作ったSampleプロジェクトを使います。
Thor部分はテストし辛いので少しソースをいじります。
lib/sample/cli.rbを以下のように修正します。
require "sample" require "thor" +require "sample/greeting" module Sample class Cli < Thor desc "hello {name}", "Hello {name}!" def hello(name) - puts "Hello #{name}!" + puts_str = Sample::Greeting.hello_create(name) + puts puts_str end end end
lib/sample/greeting.rbを作成します。
require "sample" module Sample class Greeting def self.hello_create(name) "Hello #{name}!" end end end
見てわかるとおり挙動は変えてません。
テストするためだけにGreetingクラスを作成しました。
このGreetingクラスに対するテストコードを書きます。
rspec/sample/greeting_spec.rbを作成します。
このコードはhello_createメソッドの挙動を確認しています。最後だけわざと失敗するように作成しました。
require "sample/greeting" RSpec.describe Sample::Greeting do it "return Hello Tanaka!" do expect(Sample::Greeting.hello_create("Tanaka")).to eq("Hello Tanaka!") end it "return Hello 田中!" do expect(Sample::Greeting.hello_create("田中")).to eq("Hello 田中!") end it "test failed" do expect(Sample::Greeting.hello_create("山田")).to eq("Hello 田中!") end end
テストを実行してみます。
新たに作成したテストが追加で実行できていることが確認できると思います。
$ bundle exec rspec Sample::Greeting return Hello Tanaka! return Hello 田中! test failed (FAILED - 1) Sample has a version number does something useful (FAILED - 2) Failures: 1) Sample::Greeting test failed Failure/Error: expect(Sample::Greeting.hello_create("山田")).to eq("Hello 田中!") expected: "Hello 田中!" got: "Hello 山田!" (compared using ==) # ./spec/sample/greeting_spec.rb:12:in `block (2 levels) in <top (required)>' 2) Sample does something useful Failure/Error: expect(false).to eq(true) expected: true got: false (compared using ==) # ./spec/sample_spec.rb:7:in `block (2 levels) in <top (required)>' Finished in 0.05989 seconds (files took 0.43274 seconds to load) 5 examples, 2 failures Failed examples: rspec ./spec/sample/greeting_spec.rb:11 # Sample::Greeting test failed rspec ./spec/sample_spec.rb:6 # Sample does something useful
今回のテストコードはものすごい基本的な文字列確認ですが、RSpecで提供されているメソッドをいろいろ使えば柔軟にテストを書くことができると思います。
RubyでのCLIアプリケーション開発
はじめに
rubyもなんとなくわかって来たのと開発環境もそこそこ準備が整って来たので、お試しも含めて何か作っていこうと思います。
railsはまだ触ってないのでまずはCLIアプリケーションからと思ってその環境について調べました。
こんな感じを基本構成としようと思ってます。
rubyプロジェクトの作成
rubyプロジェクトフォルダの基本構成はgemの構成に従うのがスタンダードっぽい。
gemの雛形づくりはbundleでできるんだけどそのbundleのrubyバージョンはrbenvが握っているためまずはrbenvでbundleのコマンドを実施する前にバージョンを設定します。
$ rbenv global 2.5.1
もちろんこのバージョンのrubyにbundleが入っている必要があります。
このあとはgemの雛形をつくります。
このときgitの設定でnameとかemailとか設定しておく必要があります。このへんの情報を使うので。ないとどうなるかは試してません。
$ bundle gem sample -b -t reating gem 'sample'... Do you want to generate tests with your gem? Type 'rspec' or 'minitest' to generate those test files now and in the future. rspec/minitest/(none): rspec Do you want to license your code permissively under the MIT license? This means that any other developer or company will be legally allowed to use your code for free as long as they admit you created it. You can read more about the MIT license at https://choosealicense.com/licenses/mit. y/(n): y MIT License enabled in config Do you want to include a code of conduct in gems you generate? Codes of conduct can increase contributions to your project by contributors who prefer collaborative, safe spaces. You can read more about the code of conduct at contributor-covenant.org. Having a code of conduct means agreeing to the responsibility of enforcing it, so be sure that you are prepared to do that. Be sure that your email address is specified as a contact in the generated code of conduct so that people know who to contact in case of a violation. For suggestions about how to enforce codes of conduct, see https://bit.ly/coc-enforcement. y/(n): n
-bのオプションは実行ファイルの作成をする、-tのオプションはテストファイルを作成をするになります。
最初だけこんな感じでいろいろ聞かれます。
- テスティングフレームワークはrspecとminitestどっち?→rspec(私の回答)
- MITライセンスでいいか?→y(私の回答)
- code of conduct に関するファイルを作成する?→n(私の回答)
Contributor Code of Conduct
code of conductについてはこちらがわかりやすいです。
プロジェクトの雛形として以下のファイルが作成されます。
create sample/Gemfile create sample/lib/sample.rb create sample/lib/sample/version.rb create sample/sample.gemspec create sample/Rakefile create sample/README.md create sample/bin/console create sample/bin/setup create sample/.gitignore create sample/.travis.yml create sample/.rspec create sample/spec/spec_helper.rb create sample/spec/sample_spec.rb create sample/LICENSE.txt create sample/exe/sample
更にプロジェクトのrubyのバージョンを固定するために以下をしておきます。(あまり強制力はないです。)
$ cd sample/ $ rbenv local 2.5.1
最後にbundle installとかするときにエラーとならないようにsample.gemspecを修正しておきます。 TODOがあるとエラーになります。
- spec.summary = %q{TODO: Write a short summary, because RubyGems requires one.} - spec.description = %q{TODO: Write a longer description or delete this line.} - spec.homepage = "TODO: Put your gem's website or public repo URL here." + spec.summary = "sample application" + spec.description = "sample application" + spec.homepage = "https://github.com/ruihub/"
ちなみにhomepageに適当な文字列いれたらだめでした。どこまでかわからないですが、存在するURLをいれておくのが無難です。
CLIアプリケーションの作成
CLIアプリケーションにはThorというgemを使います。
gemの追加に関してはsample.gemspecにgemを追記します。
spec.add_development_dependency "bundler", "~> 1.16" spec.add_development_dependency "rake", "~> 10.0" spec.add_development_dependency "rspec", "~> 3.0" + spec.add_dependency "thor"
これで後ほどのgemコマンドでthorが追加されます。うしろに「, "-> バージョン"」をつければバージョンも固定できます。
developmentがついているの開発時のみにしか使わないgemです。
次にCLIクラスを作成します。
参考先に従ってlib/パッケージ名/cli.rbとしました。
ThorではThorを継承したクラスに宣言したpubulicメソッドがコマンドとなるようです。
メソッド上部にdescで"コマンドの使い方","コマンド説明"を記載するこでhelpコマンドでその内容が確認できます。
require "sample" require "thor" module Sample class Cli < Thor desc "hello {name}", "Hello {name}!" def hello(name) puts "Hello #{name}!" end end end
続いてlib/パッケージ名.rbにcli.rbをrequireするよう修正します。
実行ファイルから呼び出されるファイルがこのファイルになります。
require "sample/version" + require "sample/cli" module Sample # Your code goes here... end
実行ファイルはこれまた参考の更に参考先の意見をもとにexe/以下に配置することにしました。
#!/usr/bin/env ruby require "sample" + Sample::Cli.start
アプリケーションの実行
まずはbunde installを実行してThor含め必要なgemをとってきます。
ここで忘れてはいけないのが今回に限った話ではないけどこのままbunde installを実行してしまうとグローバルにgemをインストールしてしまうので、プロジェクトフォルダ以下に配置するようにオプション --path ディレクトリ をつけてプロジェクトディレクトリ配下の指定のディレクトリにgemをインストールします。
ディレクトリはvendor/bundleとするのが一般的なようです。
$ bundle install --path vendor/bundle $ ls -l total 48 -rw-r--r-- 1 rui staff 161 7 10 21:33 Gemfile -rw-r--r-- 1 rui staff 651 7 11 20:11 Gemfile.lock -rw-r--r-- 1 rui staff 1073 7 10 21:33 LICENSE.txt -rw-r--r-- 1 rui staff 1324 7 10 21:33 README.md -rw-r--r-- 1 rui staff 117 7 10 21:33 Rakefile drwxr-xr-x 4 rui staff 128 7 10 21:33 bin drwxr-xr-x 3 rui staff 96 7 11 20:10 exe drwxr-xr-x 4 rui staff 128 7 11 19:59 lib -rw-r--r-- 1 rui staff 1545 7 10 22:22 sample.gemspec drwxr-xr-x 4 rui staff 128 7 10 21:33 spec drwxr-xr-x 3 rui staff 96 7 11 20:11 vendor
その後は以下でコマンド一覧の確認
$ bundle exec exe/sample Commands: sample hello {name} # Hello {name}! sample help [COMMAND] # Describe available commands or one specific command
ヘルプの参照
$ bundle exec exe/sample help hello Usage: sample hello {name} Hello {name}!
実装したhelloコマンドの実行
$bundle exec exe/sample hello Ruby Hello Ruby!
という感じでThorを使うことによってCLIアプリケーションが簡単に作成できました!
参考
Rubyの開発環境構築
どうでもいい話
前回の記事でDockerを使用して開発環境を構築をしようと言っていたが、あれはなしとします。
一旦Mac内に環境を構築しようと思います。
方向転換した理由としては事前にいろいろ調べて先人様たちを参考にさしていただこうと思ったのですが、
実例として多かったのは作成済みのアプリケーションの開発環境として扱うパターンで一からアプリケーションを立ち上げるパターンがなかったのと、
Rubyの知識が乏しいので利点等が理解できませんでした。
DockerはもっとRubyを理解してから実施します。
Macでの開発環境構成
とりあえず基本的なところをいれます。
- rbenv
- ruby
- gem
- bundler
開発環境構築手順
Homebrew(Rubyとは関係ないけど)
いきなり記載のないものから。
Macのパッケージマネージャー。
今回はこれでRuby向けのアプリケーションの管理を行うことにするから記載。
特にRuby用ってことではないので、説明は省略します。この辺みてやるだけです。
rbenv
Rubyのバージョン管理アプリケーション。
あと一緒にruby-buildもいれる。これはruby installコマンドを提供するプラグイン。
インストール
$ brew install rbenv ruby-build
いまのRubyのバージョン確認。
$ rbenv versions * system (set by ~/.rbenv/version)
入れられるRubyの確認。いろいろ出てくるからそこから選びます。
$ rbenv install -l
今回はバージョン2.5.1をインストール。
$ rbenv install 2.5.1 $ rbenv versions * system (set by ~/.rbenv/version) 2.5.1
使用するrubyを2.5.1に変更。
$ rbenv global 2.5.1 $ rbenv versions system * 2.5.1 (set by /Users/rui/.rbenv/version)
バージョン確認。
$ ruby -v ruby 2.3.3p222 (2016-11-21 revision 56859) [universal.x86_64-darwin17]
あれ?ってなったのでいろいろ調べるとこれで解決。
$ rbenv init # Load rbenv automatically by appending # the following to ~/.bash_profile: eval "$(rbenv init -)"
.bash_profileにeval "$(rbenv init -)"を追記しろとのことです。
.bash_profileがなければ作って記載し、読み込み。
$touch .bash_profile $vi .bash_profile eval "$(rbenv init -)" $source ~/.bash_profile
$ ruby -v ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-darwin17]
バージョンOK。
gem
Rubyのライブラリやアプリケーションはgemとよばれており、
そのgemをダウンロードしたり、インストールしたりできます。
rubyに内包されているのですでに使えます。
bundler
アプリケーションに必要なgemの種類やそのバージョンを管理してくれます。
bundler自身もgemなのでgemのインストールコマンドを使ってインストール。
$ gem install bundler
まとめ
一旦は基本的なところは抑えたので、これをベースにいろいろ実験と、何か作ってみようと思います。
使い方等で詰まったり、なにか追加でいれたほうが良さそうなものがあったら記事書いてきます。
Docker入れてみました
どうでもいい話
開発環境をリニューアルしたくてMac買いました。
iPhoneアプリとか開発したいなーという感じです。
この機会にDocker入れて開発環境の構築を検討してみようかと思い、とりあえずまだ触りだけだけどメモ。
インストール
Docker for Macをインストールします。
公式サイトからダウンロードだけど、アドレス、アカウント、パスが必要なアカウント登録が必要でした。
ちなみに複数のコンテナを管理するDocker ComposeもすでにDocker for Mac入ってます。
Install Compose on macOS
Docker for Mac and Docker Toolbox already include Compose along with other Docker apps, so Mac users do not need to install Compose separately.
起動
バージョン確認のコマンドでちゃんと起動していることが確認できました。
$ docker version Client: Version: 18.03.1-ce API version: 1.37 Go version: go1.9.5 Git commit: 9ee9f40 Built: Thu Apr 26 07:13:02 2018 OS/Arch: darwin/amd64 Experimental: false Orchestrator: swarm Server: Engine: Version: 18.03.1-ce API version: 1.37 (minimum version 1.12) Go version: go1.9.5 Git commit: 9ee9f40 Built: Thu Apr 26 07:22:38 2018 OS/Arch: linux/amd64 Experimental: true
Dockerの用語の解釈
いろいろ調べた結果のあくまで個人的な解釈です。ご意見あればください。
イメージ
オブジェクト指向のクラスのようなものという解釈。アプリケーションが動くための情報が提供される感じ。
コンテナ
オブジェクト指向のインスタンスのようなものという解釈。イメージをもとに作成されてアプリケーションが動く実際の環境。
Dockerfile
イメージの作り(動作?)を記載するもの。ソースコード的な位置づけ?
触ってみる
イメージをとってくる
試しにrubyのイメージをとってくる。
とってくる先はデフォルトではDockerHub。
$docker pull ruby
とってきているイメージの確認。
$docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE ruby latest 857bc7ff918f 2 weeks ago 869MB
特に指定がない場合は最新(latest)がとって来れるもよう。バージョンを指定したい場合はruby:{バージョン}でいける。
コンテナを作る
とってきたrubyのイメージからコンテナを作成してみる。
$docker run ruby ruby --version ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux]
これはrubyのイメージからコンテナを作成し、ruby --versionコマンドを実行したというコマンド。
ちなみにDockerHubにある最新(2018/6/27時点)のrubyのバージョンは2.5.1p57ということがわかった。
存在するコンテナの確認は以下のコマンド。
停止している(今回の場合はruby --versionが完了したら停止する)ものも含めての表示の場合は-aオプションが必要。
$docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 6f5fbb0441bc ruby "ruby --version" 6 minutes ago Exited (0) 6 minutes ago loving_noyce
さらにもう一度同じコマンドでコンテナを作成した場合は別のコンテナが作成、実行される。
$ docker run ruby ruby --version ruby 2.5.1p57 (2018-03-29 revision 63029) [x86_64-linux] $ docker container ls -a CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES 9ed7ce97548e ruby "ruby --version" 2 seconds ago Exited (0) 1 second ago upbeat_yalow 6f5fbb0441bc ruby "ruby --version" 9 minutes ago Exited (0) 9 minutes ago loving_noyce
コンテナの削除
コンテナの削除は以下のコマンドで可能
$ docker container rm {CONTAINER ID}
イメージの削除
イメージの削除は以下のコマンドで可能
$ docker image rm {IMAGE ID}
Dockerfile
Dockerfileを作成してrubyにてHello World!を出力してみます。
Dockerfileを作成する用の空のディレクトリを切り(推奨 ※公式も)、以下のファイルを配置します。
今回はそのディレクトリをカレントディレクトリにします。
hello.rb
puts "Hello World!"
Dockerfile
FROM ruby ADD hello.rb /test/ WORKDIR /test ENTRYPOINT ["ruby", "hello.rb"]
ちなみにDockerfileの意味は以下の通りです。
命令 | 解説 |
---|---|
FROM | ベースのイメージを記載します。 |
ADD | ホストPCのファイルをイメージにコピーします。 |
WORKDIR | 以下ENTRYPOINT等のコマンドを実行するディレクトリ |
ENTRYPOINT | docker run時の実行コマンド |
Dockerfileをもとにイメージを作成します。
コマンドの最後のドットはDockerdileの場所を指定(今回はカレントディレクトリ)。
$ docker build .
作成されればこんな感じで確認できます。
今回はオプションをつけてないので、REPOSITORYもTAGもnoneです。
$ docker image ls REPOSITORY TAG IMAGE ID CREATED SIZE <none> <none> b8c5ba4a7800 13 minutes ago 869MB ruby latest 857bc7ff918f 2 weeks ago 869MB
ちなみにDockerfileの記載ミスでbuildが途中で失敗した場合もイメージが作成されていたので、その場合は要注意。
最後にrunコマンドでコンテナを作成し、実行します。(IMAGE IDで実行)
$ docker run b8c5ba4a7800 Hello World!
終わりに
触りとしてはそれなりにやれたかな思ってます。
感覚はつかめたのでこれをベースにrubyの開発環境構築を検討しようかな。