- 投稿日:2019-06-07T23:39:02+09:00
ゲッターとセッターについて
1.ゲッターとは
ゲッターとは,インスタンス変数をクラス内から参照するメソッドのことをいう。
以下のようなコードにおいて、本来Rubyではインスタンス変数の値はクラス内からでしか取得することはできない。class Movie def initialize(name) @name = name end end obj1 = Movie.new('Forrest Gump') p obj1.name #=>undefined method `name' for #<Movie:0x00007fd4558526f0 @name="Forrest Gump">そこで、それを可能にするため、クラス内にインスタンス変数を参照する専用のメソッド「ゲッター」を定義する。
class Movie def initialize(name) @name = name end def getName #「ゲッター」 @name end end obj1 = Movie.new('Forrest Gump') p obj1.getName #=> 'Forrest Gump'2.セッターとは
セッターとは、インスタンス変数をクラス内で更新するメソッドのことをいう。
以下のようなコードにおいて、セッターにおいてもインスタンス変数の更新もクラス内でしかでない。class Movie def initialize(name) @name = name end end obj1 = Movie.new('Forrest Gump') p obj1.name = 'Fight Club' #=>undefined method `name=' for #<Movie:0x00007ffd4a83b400 @name="Forrest Gump"> (NoMethodError)そこで、それを可能にするため、クラス内にインスタンス変数の内容を更新する専用のメソッド「セッター」を定義する。
class Movie def initialize(name) @name = name end def changeName=(name) #「セッター」 @name = name end end obj1 = Movie.new('Forrest Gump') obj1.changeName = 'Fight Club' p obj1.getName #=> 'Fight Clubこの時、セッターの定義において、
メソッド名の右側に「=()」が必要となる。
以上
- 投稿日:2019-06-07T22:57:01+09:00
Railsチュートリアル3日目:1章クリア!
今日やったこと
・1章初めからやり直し
・1章クリア!初めからやり直したら、Gitの設定がうまくいった!
昨日はマージをしようとしたら、.c9/metadata...というファイルが邪魔をしてマージ出来ませんでした。.gitのファイル位置がおかしいことに気づき、今日はgit initする時のディレクトリに注意しながら、最初から1章をやり直してみました!
その結果、無事にマージすることが出来ました!
ec2-user:~/environment/hello_app (master) $ git branch * master modify-README ec2-user:~/environment/hello_app (master) $ git merge modify-README Updating f807c10..fd0d9f8 Fast-forward README.md | 9 +++++++++ 1 file changed, 9 insertions(+)その後は順調に進んで、1章をクリアすることができました!
(写真撮り損ねた…)ただ、また疑問が出てきて、cloud9を閉じてからもう一回herokuに入ろうとすると、
ec2-user:~/environment/hello_app (master) $ heroku --versionbash: heroku: command not foundあれ?herokuのバッシュが消えてる?
まだcloud9の仕様がわからず、一度閉じたら何がリセットされて何がそのままの設定で残っているのか、全然分かっていない気がします。
明日はこの辺りをググってみてcloud9やバッシュなど全体的に学んでいきたいと思いました。
- 投稿日:2019-06-07T21:01:37+09:00
rails モデルについて
modelの作成
$ rails g model モデル名上記をを実行するとマイグレーションファイルが作成される。
これを使うことでSQL文を書かずして、railsからmysqlなどのデータベースを操作することができる。作成するだけでは操作できない。マイグレーションファイルの実行
$ rails db:migrateこのコマンドでデータベースにモデル名のテーブルが作成される。
railsのルールとして一度実行されたファイルに変更をしてはならない。
データベースに変更を加えたい場合、rails db:migrate:resetでもう一度作り直すかカラムを追加する方法がある。カラムの追加
$ rails g migration Addカラム名Toテーブル名テーブル名はモデル名の小文字複数形ですが、上記ではキャメルケース記法なので大文字になります。
- 投稿日:2019-06-07T20:12:53+09:00
[error]rails aborted! I18n::MissingTranslationData: translation missing: en.faker.name.first_name
rails aborted!I18n::MissingTranslationData: translation missing: en.faker.name.first_name
bundle exec rails db:reset
しようとしたら、上記のエラーがでた解決方法
下記をコピペ*日本語での設定とかそこらへん
local.rbrequire 'i18n' I18n.locale # => :en I18n.locale = :ja I18n.locale # => :ja require 'faker' Faker::Config.locale # => :ja Faker::Internet.email # => ".@.com"で
$ bundle exec rails db:reset
- 投稿日:2019-06-07T20:12:53+09:00
[rails error]rails aborted! I18n::MissingTranslationData: translation missing: en.faker.name.first_name
rails aborted!I18n::MissingTranslationData: translation missing: en.faker.name.first_name
bundle exec rails db:reset
しようとしたら、上記のエラーがでた解決方法
下記をコピペ*日本語での設定とかそこらへん
local.rbrequire 'i18n' I18n.locale # => :en I18n.locale = :ja I18n.locale # => :ja require 'faker' Faker::Config.locale # => :ja Faker::Internet.email # => ".@.com"で
$ bundle exec rails db:reset
- 投稿日:2019-06-07T18:18:32+09:00
RailsでTwitterログイン認証を実装
RailsアプリでTwitterログインを実装する方法をまとめます。
deviseの導入
メールアドレスやSNSアカウントを用いてのログインを、簡単に実装するGem「devise」と、OAuth認証に必要なGem「omniauth」「omniauth-twitter」を導入します。
Gemfile.rbgem 'devise' gem 'omniauth' gem 'omniauth-twitter'続いて、bundle installを行います。
bundle install※ライブラリはサーバーの起動時に読み込まれるので、ここでいったんサーバーを停止し、再起動しましょう。
deviseの設定
アプリケーションにdeviseをインストールします。
rails g devise:install1から4までのステップが書かれた英文が表示されるので、指示通りにコーディングします。
デフォルトURL
config.action_mailer.default_url_options = { host: 'localhost', port: 3000 }をconfig/environments/development.rbに追加します。
config/environments/development.rbRails.application.configure do # Settings specified here will take precedence over those in config/application.rb. ・・・(略:場所はどこでも良いです) config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } endroot_urlを指定
ルートURLにアクセスした際のページを作成します。
rails g controller Pages indexPagesコントローラーには、indexアクションを、ビューファイルindex.html.erbを追加しています。
フラッシュメッセージを指定
ログインが成功した際に表示する、フラッシュメッセージを、application.html.erbの
下に指定します。app/views/layouts/application.html.erb・・・(略) </head> <body> <p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p> <%= yield %> </body>devise用のviewを作成
rails g devise:viewsapp/views/devise/以下にファイルが作成されます。
Userモデルを作成
rails g devise User※モデル名なので単数形
これにより、dbフォルダ下にマイグレーションファイルが作成されます。
ユーザーモデルを編集
user.rbのdevise:以下に「:omniauthable」を追加します。
model/user.rbdevise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :omniauthableデータベースにカラムを追加
deviseはデフォルトでメールアドレスとパスワードした扱うことができません。ツイッターログインで扱うデータのカラムをあらかじめ追加しておく必要があります。
以下のカラムを追加します。
- ユーザーID(uid)
- プロバイダー(provider)
- アカウント名(name)
- アカウントID(nickname)
- 場所(location)
- 画像(image)
rails g migration AddColumnsToUsers uid:string provider:string name:string nickname:string location:string image:stringここまで終わったら、以下マイグレーションを実行します。
rails db:migrateTwitter Developerの登録
API Keyと、Secret Keyを取得します。(省略)
取得した2つのキーを貼り付け
config/initializer/devise.rbDevise.setup do |config| ・・・(略) config.omniauth :twitter, 'API key(各自取得したキーを入れてください)', 'API secret(各自取得したキーを入れてください)' endUserモデルを編集する
model/user.rbclass User < ApplicationRecord # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable, :trackable and :omniauthable devise :database_authenticatable, :registerable, :recoverable, :rememberable, :validatable, :omniauthable def self.find_for_oauth(auth) user = User.where(uid: auth.uid, provider: auth.provider).first unless user user = User.create( uid: auth.uid, provider: auth.provider, email: User.dummy_email(auth), password: Devise.friendly_token[0, 20], image: auth.info.image, name: auth.info.name, nickname: auth.info.nickname, location: auth.info.location ) end user end private def self.dummy_email(auth) "#{auth.uid}-#{auth.provider}@example.com" end endTeitter認証の際に取得した各データを、データベースに格納しています。メールアドレスがない場合はdevise側でエラーが発生してしまうので、ダミーのアドレスを作成しています。
コールバックの設定
app/controllers/以下に「users」フォルダを作成し、その下に「omniauth_callbacks_controller.rb」ファイルを作成します。
omniauth_callbacks_controller.rbは以下のように編集します。
omniauth_callbacks_controller.rbclass Users::OmniauthCallbacksController < Devise::OmniauthCallbacksController def twitter callback_from :twitter end private def callback_from(provider) provider = provider.to_s @user = User.find_for_oauth(request.env['omniauth.auth']) if @user.persisted? print("persisted true") flash[:notice] = I18n.t('devise.omniauth_callbacks.success', kind: provider.capitalize) sign_in_and_redirect @user, event: :authentication else print("persisted false") session["devise.#{provider}_data"] = request.env['omniauth.auth'] redirect_to controller: 'sessions', action: 'new' end end endルーティングの設定
config/route.rbのdevise_forを、以下のように編集します。
config/route.rbdevise_for :users, controllers: { omniauth_callbacks: 'users/omniauth_callbacks' }ビューファイルを整える
ビューファイルを以下のように整えます。
index.html.rb<% if user_signed_in? %> <%=current_user.email%> <%=current_user.name%> <%=current_user.nickname%> <%=current_user.image%> <%=current_user.location%> <%= link_to "ログアウト", destroy_user_session_path, method: :delete %> <% else %> <%= link_to "ログイン", new_user_session_path %> <%= link_to " Twitterでログイン/登録", user_twitter_omniauth_authorize_path %> <% end %>以上でツイッター認証ログインが可能になりました。
- 投稿日:2019-06-07T18:13:52+09:00
Ruby on Railsを開発する時にいれておきたいAtomのパッケージ
ファイル検索・移動系
Rails Transporter
Controller/Model/View間の移動をコマンド一つできる。
RubyMineにもそんな機能があったからAtomにもないかなと思って探したら発見。
ControllerからViewを探すのは結構めんどくさいからかなり重宝している。atom-fuzzy-grep
高速簡易グレップ。
Atomの標準でプロジェクト内全体の検索はできるが、このfuzzy-grepはより早く検索したい時におすすめ。
advanced-open-file
現在いるファイルから別のファイルを検索したい時に使える。
RailsだとViewでpartialの中身を確認したくて、別のファイルに移動する時に使える。
Viewって似たようなファイル名が多いから、grepしても検索結果が多くて判別できないため、これを使っている。goto-definition
関数定義場所のファイルにジャンプする。
私は
atom-fuzzy-grep
でも十分かなと思うが、直接関数に飛んでくれるのでたまに使う。Git系
git-blame
行毎に最終変更者が確認できる。
その名の通り、誰かのせいしたい時に使う。(笑)
git-time-machine
ファイルの過去の変更履歴を時系列で確認できる。
git-blameでは追えない犯人を追う時に使う。
その他
platformio-ide-terminal
Atom上にターミナルを開く。
初回起動の場所をプロジェクトのrootパス等に設定できるため、gitのコマンド等使いたい時に使用。
ちょっと重い。。(更新中。。)
- 投稿日:2019-06-07T17:44:23+09:00
cloud9→ローカルに移したら写真投稿ができなくなった
はじめに
SNS風の写真投稿アプリを久々にローカルで確認してみたところ、肝心の写真投稿がバリデーションに引っかかり、投稿はもちろん、テストなども通らなくなってしまっていました。
原因
結果を言ってしまうと、
Minimagick
を使うのに必要なImageMagick
を、ローカルでインストールしていませんでした。cloud9時代に
ImageMagick
をインストールした自体あまり覚えていなかったので、気付くのが遅れてしまいました。$ brew install imagemagickこれで今まで通り、問題なく写真投稿できるようになりました!
参考
Rails gem MiniMagick を利用して画像ファイルをリサイズする
RubyでImageMagick使うにはMiniMagickの方がよい
- 投稿日:2019-06-07T13:39:25+09:00
[Ruby]コピペでOpenWeatherMap
OpenWeatherMapからAPIで天気情報を取得する*ターミナル実行版とwebアプリ(Rails)実装版
OpenWeatherMapの登録は下記参照
https://qiita.com/nownabe/items/aeac1ce0977be963a740ターミナル実行版
OpenWeatherMap.rbAPI_KEY = "xxxxxxxxxxxxxxxxxxxxxxxxxxx" BASE_URL = "https://api.openweathermap.org/data/2.5/forecast/" require "json" require "open-uri" response = open(BASE_URL + "?id=1859171&APPID=#{"xxxxxxxxxxxxxxxxxxxxxxxxxxx"}") puts JSON.pretty_generate(JSON.parse(response.read))ターミナルで実行
[*注意]ruby -v '2.6.0'
以上だとエラーになるターミナル$ ruby OpenWeatherMap.rb //実行実行結果
{ "cod": "200", "message": 0.0073, "cnt": 40, "list": [ { "dt": 1559887200, "main": { "temp": 296.86, "temp_min": 296.86, "temp_max": 301.4, "pressure": 1002.13, "sea_level": 1002.13, "grnd_level": 980.88, "humidity": 67, "temp_kf": -4.54 }, "weather": [ { "id": 500, "main": "Rain", "description": "light rain", "icon": "10d" } ], "clouds": { "all": 94 }, "wind": { "speed": 4.82, "deg": 213.786 }, "rain": { "3h": 1.376 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-07 06:00:00" }, { "dt": 1559898000, "main": { "temp": 294.7, "temp_min": 294.7, "temp_max": 298.1, "pressure": 1001.03, "sea_level": 1001.03, "grnd_level": 980.11, "humidity": 80, "temp_kf": -3.4 }, "weather": [ { "id": 500, "main": "Rain", "description": "light rain", "icon": "10d" } ], "clouds": { "all": 100 }, "wind": { "speed": 2.71, "deg": 213.267 }, "rain": { "3h": 0.188 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-07 09:00:00" }, { "dt": 1559908800, "main": { "temp": 294.43, "temp_min": 294.43, "temp_max": 296.7, "pressure": 1001.62, "sea_level": 1001.62, "grnd_level": 980.95, "humidity": 68, "temp_kf": -2.27 }, "weather": [ { "id": 804, "main": "Clouds", "description": "overcast clouds", "icon": "04n" } ], "clouds": { "all": 100 }, "wind": { "speed": 5.25, "deg": 249.352 }, "sys": { "pod": "n" }, "dt_txt": "2019-06-07 12:00:00" }, { "dt": 1559919600, "main": { "temp": 293.37, "temp_min": 293.37, "temp_max": 294.5, "pressure": 1001.61, "sea_level": 1001.61, "grnd_level": 980.99, "humidity": 69, "temp_kf": -1.13 }, "weather": [ { "id": 804, "main": "Clouds", "description": "overcast clouds", "icon": "04n" } ], "clouds": { "all": 100 }, "wind": { "speed": 5.86, "deg": 252.839 }, "sys": { "pod": "n" }, "dt_txt": "2019-06-07 15:00:00" }, { "dt": 1559930400, "main": { "temp": 292.8, "temp_min": 292.8, "temp_max": 292.8, "pressure": 1000.95, "sea_level": 1000.95, "grnd_level": 980.71, "humidity": 75, "temp_kf": 0 }, "weather": [ { "id": 804, "main": "Clouds", "description": "overcast clouds", "icon": "04n" } ], "clouds": { "all": 100 }, "wind": { "speed": 5.69, "deg": 248.281 }, "sys": { "pod": "n" }, "dt_txt": "2019-06-07 18:00:00" }, { "dt": 1559941200, "main": { "temp": 292.3, "temp_min": 292.3, "temp_max": 292.3, "pressure": 1001.49, "sea_level": 1001.49, "grnd_level": 980.83, "humidity": 82, "temp_kf": 0 }, "weather": [ { "id": 802, "main": "Clouds", "description": "scattered clouds", "icon": "03d" } ], "clouds": { "all": 45 }, "wind": { "speed": 5.06, "deg": 241.092 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-07 21:00:00" }, { "dt": 1559952000, "main": { "temp": 296.5, "temp_min": 296.5, "temp_max": 296.5, "pressure": 1002.88, "sea_level": 1002.88, "grnd_level": 981.36, "humidity": 62, "temp_kf": 0 }, "weather": [ { "id": 803, "main": "Clouds", "description": "broken clouds", "icon": "04d" } ], "clouds": { "all": 58 }, "wind": { "speed": 3.98, "deg": 249.845 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-08 00:00:00" }, { "dt": 1559962800, "main": { "temp": 296.2, "temp_min": 296.2, "temp_max": 296.2, "pressure": 1003.73, "sea_level": 1003.73, "grnd_level": 981.81, "humidity": 70, "temp_kf": 0 }, "weather": [ { "id": 803, "main": "Clouds", "description": "broken clouds", "icon": "04d" } ], "clouds": { "all": 67 }, "wind": { "speed": 1.5, "deg": 340.308 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-08 03:00:00" }, { "dt": 1559973600, "main": { "temp": 295.4, "temp_min": 295.4, "temp_max": 295.4, "pressure": 1004.25, "sea_level": 1004.25, "grnd_level": 982.75, "humidity": 72, "temp_kf": 0 }, "weather": [ { "id": 500, "main": "Rain", "description": "light rain", "icon": "10d" } ], "clouds": { "all": 72 }, "wind": { "speed": 4.61, "deg": 357.365 }, "rain": { "3h": 0.438 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-08 06:00:00" }, { "dt": 1559984400, "main": { "temp": 293.048, "temp_min": 293.048, "temp_max": 293.048, "pressure": 1004.99, "sea_level": 1004.99, "grnd_level": 983.92, "humidity": 76, "temp_kf": 0 }, "weather": [ { "id": 802, "main": "Clouds", "description": "scattered clouds", "icon": "03d" } ], "clouds": { "all": 31 }, "wind": { "speed": 4.5, "deg": 0.42 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-08 09:00:00" }, { "dt": 1559995200, "main": { "temp": 289.7, "temp_min": 289.7, "temp_max": 289.7, "pressure": 1006.06, "sea_level": 1006.06, "grnd_level": 985.13, "humidity": 88, "temp_kf": 0 }, "weather": [ { "id": 802, "main": "Clouds", "description": "scattered clouds", "icon": "03n" } ], "clouds": { "all": 42 }, "wind": { "speed": 2.18, "deg": 6.09 }, "sys": { "pod": "n" }, "dt_txt": "2019-06-08 12:00:00" }, { "dt": 1560006000, "main": { "temp": 289.72, "temp_min": 289.72, "temp_max": 289.72, "pressure": 1006.28, "sea_level": 1006.28, "grnd_level": 985.69, "humidity": 86, "temp_kf": 0 }, "weather": [ { "id": 803, "main": "Clouds", "description": "broken clouds", "icon": "04n" } ], "clouds": { "all": 83 }, "wind": { "speed": 1.76, "deg": 334.496 }, "sys": { "pod": "n" }, "dt_txt": "2019-06-08 15:00:00" }, { "dt": 1560016800, "main": { "temp": 288.042, "temp_min": 288.042, "temp_max": 288.042, "pressure": 1005.9, "sea_level": 1005.9, "grnd_level": 984.97, "humidity": 92, "temp_kf": 0 }, "weather": [ { "id": 803, "main": "Clouds", "description": "broken clouds", "icon": "04n" } ], "clouds": { "all": 57 }, "wind": { "speed": 1.71, "deg": 359.597 }, "sys": { "pod": "n" }, "dt_txt": "2019-06-08 18:00:00" }, { "dt": 1560027600, "main": { "temp": 289.247, "temp_min": 289.247, "temp_max": 289.247, "pressure": 1006.09, "sea_level": 1006.09, "grnd_level": 985.07, "humidity": 86, "temp_kf": 0 }, "weather": [ { "id": 804, "main": "Clouds", "description": "overcast clouds", "icon": "04d" } ], "clouds": { "all": 87 }, "wind": { "speed": 2.01, "deg": 356.186 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-08 21:00:00" }, { "dt": 1560038400, "main": { "temp": 294.176, "temp_min": 294.176, "temp_max": 294.176, "pressure": 1006.33, "sea_level": 1006.33, "grnd_level": 985.33, "humidity": 63, "temp_kf": 0 }, "weather": [ { "id": 803, "main": "Clouds", "description": "broken clouds", "icon": "04d" } ], "clouds": { "all": 56 }, "wind": { "speed": 3.34, "deg": 17.913 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-09 00:00:00" }, { "dt": 1560049200, "main": { "temp": 295.8, "temp_min": 295.8, "temp_max": 295.8, "pressure": 1005.59, "sea_level": 1005.59, "grnd_level": 984.65, "humidity": 59, "temp_kf": 0 }, "weather": [ { "id": 803, "main": "Clouds", "description": "broken clouds", "icon": "04d" } ], "clouds": { "all": 69 }, "wind": { "speed": 4.09, "deg": 35.192 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-09 03:00:00" }, { "dt": 1560060000, "main": { "temp": 298.405, "temp_min": 298.405, "temp_max": 298.405, "pressure": 1004.47, "sea_level": 1004.47, "grnd_level": 983.01, "humidity": 53, "temp_kf": 0 }, "weather": [ { "id": 802, "main": "Clouds", "description": "scattered clouds", "icon": "03d" } ], "clouds": { "all": 44 }, "wind": { "speed": 2.69, "deg": 34.783 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-09 06:00:00" }, { "dt": 1560070800, "main": { "temp": 294.41, "temp_min": 294.41, "temp_max": 294.41, "pressure": 1004.04, "sea_level": 1004.04, "grnd_level": 982.98, "humidity": 72, "temp_kf": 0 }, "weather": [ { "id": 500, "main": "Rain", "description": "light rain", "icon": "10d" } ], "clouds": { "all": 31 }, "wind": { "speed": 4, "deg": 19.591 }, "rain": { "3h": 0.25 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-09 09:00:00" }, { "dt": 1560081600, "main": { "temp": 289.351, "temp_min": 289.351, "temp_max": 289.351, "pressure": 1005.35, "sea_level": 1005.35, "grnd_level": 984.46, "humidity": 89, "temp_kf": 0 }, "weather": [ { "id": 801, "main": "Clouds", "description": "few clouds", "icon": "02n" } ], "clouds": { "all": 20 }, "wind": { "speed": 2.87, "deg": 11.416 }, "rain": { }, "sys": { "pod": "n" }, "dt_txt": "2019-06-09 12:00:00" }, { "dt": 1560092400, "main": { "temp": 288.4, "temp_min": 288.4, "temp_max": 288.4, "pressure": 1005.21, "sea_level": 1005.21, "grnd_level": 984.18, "humidity": 90, "temp_kf": 0 }, "weather": [ { "id": 800, "main": "Clear", "description": "clear sky", "icon": "01n" } ], "clouds": { "all": 6 }, "wind": { "speed": 2.52, "deg": 7.258 }, "sys": { "pod": "n" }, "dt_txt": "2019-06-09 15:00:00" }, { "dt": 1560103200, "main": { "temp": 287.939, "temp_min": 287.939, "temp_max": 287.939, "pressure": 1004.63, "sea_level": 1004.63, "grnd_level": 983.37, "humidity": 94, "temp_kf": 0 }, "weather": [ { "id": 800, "main": "Clear", "description": "clear sky", "icon": "01n" } ], "clouds": { "all": 5 }, "wind": { "speed": 2.32, "deg": 12.261 }, "sys": { "pod": "n" }, "dt_txt": "2019-06-09 18:00:00" }, { "dt": 1560114000, "main": { "temp": 289.358, "temp_min": 289.358, "temp_max": 289.358, "pressure": 1005.1, "sea_level": 1005.1, "grnd_level": 983.94, "humidity": 90, "temp_kf": 0 }, "weather": [ { "id": 800, "main": "Clear", "description": "clear sky", "icon": "01d" } ], "clouds": { "all": 7 }, "wind": { "speed": 2.3, "deg": 16.687 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-09 21:00:00" }, { "dt": 1560124800, "main": { "temp": 296.343, "temp_min": 296.343, "temp_max": 296.343, "pressure": 1005.53, "sea_level": 1005.53, "grnd_level": 984.13, "humidity": 66, "temp_kf": 0 }, "weather": [ { "id": 800, "main": "Clear", "description": "clear sky", "icon": "01d" } ], "clouds": { "all": 4 }, "wind": { "speed": 2.04, "deg": 45.815 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-10 00:00:00" }, { "dt": 1560135600, "main": { "temp": 298.408, "temp_min": 298.408, "temp_max": 298.408, "pressure": 1005, "sea_level": 1005, "grnd_level": 983.49, "humidity": 63, "temp_kf": 0 }, "weather": [ { "id": 500, "main": "Rain", "description": "light rain", "icon": "10d" } ], "clouds": { "all": 12 }, "wind": { "speed": 1.79, "deg": 21.528 }, "rain": { "3h": 2.125 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-10 03:00:00" }, { "dt": 1560146400, "main": { "temp": 296.257, "temp_min": 296.257, "temp_max": 296.257, "pressure": 1004.36, "sea_level": 1004.36, "grnd_level": 983.06, "humidity": 76, "temp_kf": 0 }, "weather": [ { "id": 501, "main": "Rain", "description": "moderate rain", "icon": "10d" } ], "clouds": { "all": 7 }, "wind": { "speed": 3.57, "deg": 21.13 }, "rain": { "3h": 6.437 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-10 06:00:00" }, { "dt": 1560157200, "main": { "temp": 293.947, "temp_min": 293.947, "temp_max": 293.947, "pressure": 1005.03, "sea_level": 1005.03, "grnd_level": 983.77, "humidity": 84, "temp_kf": 0 }, "weather": [ { "id": 501, "main": "Rain", "description": "moderate rain", "icon": "10d" } ], "clouds": { "all": 13 }, "wind": { "speed": 3.2, "deg": 21.107 }, "rain": { "3h": 3.75 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-10 09:00:00" }, { "dt": 1560168000, "main": { "temp": 290.3, "temp_min": 290.3, "temp_max": 290.3, "pressure": 1006.84, "sea_level": 1006.84, "grnd_level": 985.56, "humidity": 90, "temp_kf": 0 }, "weather": [ { "id": 500, "main": "Rain", "description": "light rain", "icon": "10n" } ], "clouds": { "all": 8 }, "wind": { "speed": 1.98, "deg": 7.495 }, "rain": { "3h": 0.188 }, "sys": { "pod": "n" }, "dt_txt": "2019-06-10 12:00:00" }, { "dt": 1560178800, "main": { "temp": 289.445, "temp_min": 289.445, "temp_max": 289.445, "pressure": 1007.43, "sea_level": 1007.43, "grnd_level": 986.32, "humidity": 92, "temp_kf": 0 }, "weather": [ { "id": 800, "main": "Clear", "description": "clear sky", "icon": "01n" } ], "clouds": { "all": 0 }, "wind": { "speed": 1.61, "deg": 13.709 }, "sys": { "pod": "n" }, "dt_txt": "2019-06-10 15:00:00" }, { "dt": 1560189600, "main": { "temp": 288.941, "temp_min": 288.941, "temp_max": 288.941, "pressure": 1007.49, "sea_level": 1007.49, "grnd_level": 986.05, "humidity": 92, "temp_kf": 0 }, "weather": [ { "id": 800, "main": "Clear", "description": "clear sky", "icon": "01n" } ], "clouds": { "all": 0 }, "wind": { "speed": 0.95, "deg": 22.657 }, "sys": { "pod": "n" }, "dt_txt": "2019-06-10 18:00:00" }, { "dt": 1560200400, "main": { "temp": 291.4, "temp_min": 291.4, "temp_max": 291.4, "pressure": 1008.18, "sea_level": 1008.18, "grnd_level": 986.81, "humidity": 88, "temp_kf": 0 }, "weather": [ { "id": 800, "main": "Clear", "description": "clear sky", "icon": "01d" } ], "clouds": { "all": 0 }, "wind": { "speed": 0.7, "deg": 67.893 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-10 21:00:00" }, { "dt": 1560211200, "main": { "temp": 296.4, "temp_min": 296.4, "temp_max": 296.4, "pressure": 1008.44, "sea_level": 1008.44, "grnd_level": 987.3, "humidity": 67, "temp_kf": 0 }, "weather": [ { "id": 800, "main": "Clear", "description": "clear sky", "icon": "01d" } ], "clouds": { "all": 1 }, "wind": { "speed": 1.97, "deg": 174.658 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-11 00:00:00" }, { "dt": 1560222000, "main": { "temp": 299.559, "temp_min": 299.559, "temp_max": 299.559, "pressure": 1007.41, "sea_level": 1007.41, "grnd_level": 986.23, "humidity": 50, "temp_kf": 0 }, "weather": [ { "id": 500, "main": "Rain", "description": "light rain", "icon": "10d" } ], "clouds": { "all": 6 }, "wind": { "speed": 2.13, "deg": 214.981 }, "rain": { "3h": 1.062 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-11 03:00:00" }, { "dt": 1560232800, "main": { "temp": 299.7, "temp_min": 299.7, "temp_max": 299.7, "pressure": 1006.16, "sea_level": 1006.16, "grnd_level": 984.58, "humidity": 46, "temp_kf": 0 }, "weather": [ { "id": 801, "main": "Clouds", "description": "few clouds", "icon": "02d" } ], "clouds": { "all": 20 }, "wind": { "speed": 3.15, "deg": 221.401 }, "rain": { }, "sys": { "pod": "d" }, "dt_txt": "2019-06-11 06:00:00" }, { "dt": 1560243600, "main": { "temp": 296.559, "temp_min": 296.559, "temp_max": 296.559, "pressure": 1006.38, "sea_level": 1006.38, "grnd_level": 985.28, "humidity": 65, "temp_kf": 0 }, "weather": [ { "id": 804, "main": "Clouds", "description": "overcast clouds", "icon": "04d" } ], "clouds": { "all": 95 }, "wind": { "speed": 1.96, "deg": 223.261 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-11 09:00:00" }, { "dt": 1560254400, "main": { "temp": 292.7, "temp_min": 292.7, "temp_max": 292.7, "pressure": 1007.86, "sea_level": 1007.86, "grnd_level": 986.74, "humidity": 79, "temp_kf": 0 }, "weather": [ { "id": 804, "main": "Clouds", "description": "overcast clouds", "icon": "04n" } ], "clouds": { "all": 97 }, "wind": { "speed": 1.2, "deg": 222.469 }, "sys": { "pod": "n" }, "dt_txt": "2019-06-11 12:00:00" }, { "dt": 1560265200, "main": { "temp": 291.041, "temp_min": 291.041, "temp_max": 291.041, "pressure": 1007.44, "sea_level": 1007.44, "grnd_level": 986.36, "humidity": 87, "temp_kf": 0 }, "weather": [ { "id": 803, "main": "Clouds", "description": "broken clouds", "icon": "04n" } ], "clouds": { "all": 77 }, "wind": { "speed": 0.8, "deg": 212.856 }, "sys": { "pod": "n" }, "dt_txt": "2019-06-11 15:00:00" }, { "dt": 1560276000, "main": { "temp": 290.141, "temp_min": 290.141, "temp_max": 290.141, "pressure": 1006.83, "sea_level": 1006.83, "grnd_level": 985.68, "humidity": 92, "temp_kf": 0 }, "weather": [ { "id": 802, "main": "Clouds", "description": "scattered clouds", "icon": "03n" } ], "clouds": { "all": 39 }, "wind": { "speed": 0.43, "deg": 109.654 }, "sys": { "pod": "n" }, "dt_txt": "2019-06-11 18:00:00" }, { "dt": 1560286800, "main": { "temp": 292.2, "temp_min": 292.2, "temp_max": 292.2, "pressure": 1007.35, "sea_level": 1007.35, "grnd_level": 986.15, "humidity": 84, "temp_kf": 0 }, "weather": [ { "id": 800, "main": "Clear", "description": "clear sky", "icon": "01d" } ], "clouds": { "all": 0 }, "wind": { "speed": 0.64, "deg": 102.032 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-11 21:00:00" }, { "dt": 1560297600, "main": { "temp": 297.3, "temp_min": 297.3, "temp_max": 297.3, "pressure": 1007.36, "sea_level": 1007.36, "grnd_level": 986.29, "humidity": 64, "temp_kf": 0 }, "weather": [ { "id": 800, "main": "Clear", "description": "clear sky", "icon": "01d" } ], "clouds": { "all": 0 }, "wind": { "speed": 1.85, "deg": 157.755 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-12 00:00:00" }, { "dt": 1560308400, "main": { "temp": 299.986, "temp_min": 299.986, "temp_max": 299.986, "pressure": 1006.59, "sea_level": 1006.59, "grnd_level": 985.33, "humidity": 55, "temp_kf": 0 }, "weather": [ { "id": 800, "main": "Clear", "description": "clear sky", "icon": "01d" } ], "clouds": { "all": 2 }, "wind": { "speed": 1.6, "deg": 241.438 }, "sys": { "pod": "d" }, "dt_txt": "2019-06-12 03:00:00" } ], "city": { "id": 1859171, "name": "Kobe-shi", "coord": { "lat": 34.6913, "lon": 135.183 }, "country": "JP", "timezone": 32400 } }webアプリ(Rails)実装版
weather.js$(document).ready(function() { $.post("http://api.openweathermap.org/data/2.5/weather?id=" + '1850147' + "&appid=xxxxxxxxxxxxxxxxxxxxxxxxxxx&lang=ja&units=metric", function(json){ $("#weather").html(json.weather[0].description); $("#humidity").html(json.main.humidity); $("#temp").html(Math.round(json.main.temp)); switch (json.weather[0].main){ case 'Clouds': $("#weatherMark").html("<img src='http://openweathermap.org/img/w/04d.png' >"); break; case 'Snow': $("#weatherMark").html("<img src='http://openweathermap.org/img/w/13d.png' >"); break; case 'Rain': $("#weatherMark").html("<img src='http://openweathermap.org/img/w/09d.png' >"); break; case 'Clear': $("#weatherMark").html("<img src='http://openweathermap.org/img/w/01d.png' >"); break; case 'Fog': $("#weatherMark").html("<img src='http://openweathermap.org/img/w/50d.png' >"); break; case 'Mist': $("#weatherMark").html("<img src='http://openweathermap.org/img/w/50n.png' >"); break; case 'Haze': $("#weatherMark").html("<img src='http://openweathermap.org/img/w/50d.png' >"); break; default: $("#weatherMark").html("<img src='http://openweathermap.org/img/w/01n.png' >"); } } ); });_weather.html<h2>東京の天気</h2> <div> <span id = "weather" class="bold"></span> </div> <div> <span id = "weatherMark" class="bold"></span> </div> <p id="icon"></p> <div>気温<span id = "temp" class="bold"></span>℃</div> <div>降水確率<span id = "humidity" class="bold"></span> %</div>参考サイト
http://www.torutsume.net/openweathermap/
https://qiita.com/nownabe/items/aeac1ce0977be963a740
- 投稿日:2019-06-07T12:24:10+09:00
AWS LambdaでNokogiriを動かす
近々、AWS Lambdaを使って簡易的なクローラを作る予定があったので
Lambda上でNokogiriが使えるか 検証しました。開発環境
ローカルの開発環境はMac、クローラはRubyで実装します。
ポイント
クローリングに必要となるGemである「Nokogiri」はネイティブライブラリを含むGemであるため、
Mac上でビルドしてAWSへデプロイすると「cannot load such file -- nokogiri
」が発生します。これを回避するため、AWS SAMを用いて雛形作成した上で、
「sam build --use-container
」を実行しAmazonLinux2のDockerコンテナ上でビルドを行いました。AWS SAM CLIでHello World
AWS SAM CLIを使ってRubyのLambdaハンドラの雛形を作成します。
アプリの名前が実際に作ろうとしているクローラの名前になっていますので、適宜置き換えてください。$ sam init --runtime ruby2.5 --name sample_app $ cd sample_app $ sam build $ sam local invoke HelloWorldFunction --event event.json参考: https://aws.amazon.com/jp/about-aws/whats-new/2018/04/aws-sam-cli-releases-new-init-command/
Nokogiriのインストール
今回はLambda上でスクレイピングツールであるNokogiriを動かす必要があります。
このGemはネイティブライブラリが含まれるため、ローカル環境(今回はMac)でインストールするとLambda上でエラーとなります。が、AWS SAM CLIは以下コマンドでネイティブライブラリも含めて良い感じにビルドしてくれるコマンドを提供してくれています。便利ですね。
hello_world/Gemfilesource "https://rubygems.org" gem "httparty" gem "nokogiri" # 追記 gem "robotex" # 追記追記先はルートのGemfileではありませんので注意してください。
cannot load such file -- nokogiri
が起こる原因となります。Nokogiriを使うLambdaハンドラを実装
動作確認のためLambdaハンドラの処理も修正します。
hello_world/app.rbrequire "bundler/setup" Bundler.require URL='https://www.google.com/' def lambda_handler(event:, context:) charset = nil robotex = Robotex.new robotex.allowed?(URL) robotex.delay!(URL) html = open(URL) do |f| charset = f.charset f.read end doc = Nokogiri::HTML.parse(html, nil, charset) { statusCode: 200, body: { page_title: doc.title, }.to_json } endローカルの開発環境で動作確認
ビルドして実行してみましょう。
GoogleのTOPページのタイトルが取得できました。$ sam build --use-container $ sam local invoke HelloWorldFunction --event event_file.json {"statusCode":200,"body":"{\"page_title\":\"Google\"}"}デプロイ
AWS SAM CLIを使ってデプロイします。
$ sam package --output-template-file output.yaml --s3-bucket sample_app $ sam deploy --template-file output.yaml --stack-name sample_app --capabilities CAPABILITY_NAMED_IAMただし、
sam package
に--template-file
オプションを指定すると、vendor配下がアップロードされない問題が発生しました。--template-file
は設定しないようにしましょう。AWSコンソールにログインして、Lambdaを実行すると無事成功しました。
参考
https://aws.amazon.com/jp/blogs/developer/announcing-ruby-build-support-for-aws-sam-cli/
- 投稿日:2019-06-07T11:29:45+09:00
HomebrewをMacに初めてインストール。エラー発生。そして解決できました。
Rubyを少し勉強した初心者です。
今までCloud9でrubyを書いていました。書籍 「プロを目指す人のためのRuby入門」を購入し、自身のMACにrubyをインストールしようとしたのですが、エラーでインストールできませんでした。
いろいろ調べた結果、インストールに成功したので、ここにその方法を記載します。環境 Mac OS Mojave 10.14.5
Homebrewのインストール
https://brew.sh/index_ja.html
このサイトより
/usr/bin/ruby -e "$(curl -fsSL https://raw.githubusercontent.com/Homebrew/install/master/install)"
このキーワードをターミナルで実行しました。自動的にインストールされていきます。
パスワードを聞かれるのでこのMacのパスワードを入れます。
Xcodeが必要とのことでポップアップが立ち上がりました。
XcodeをダウンロードするためにApp StoreからXcodeをダウンロードしました。
Xcodeのインストールは成功しました。
しばらくするとこのような文字が帰ってきます。
xcode-select: error: invalid developer directory '/Library/Developer/CommandLineTools'
Failed during: /usr/bin/sudo /usr/bin/xcode-select --switch /Library/Developer/CommandLineToolsエラーということで、Homebrewのインストールはできませんでした。
いろいろ調べましたが、回答がみつからず1日経過。
ターミナルの基礎を勉強し直しましたがこれといって解決できず。
エラーの文章をそのままネットで調べると下記のサイトへ到着。
https://discourse.brew.sh/t/xcode-select-error-invalid-developer-directory-library-developer-commandlinetools-failed-during-usr-bin-sudo-usr-bin-xcode-select-switch-library-developer-commandlinetools/693/40そこに記載されていたのは、アップルのデベロッパーツールからcommand lineをインストールするようにとのことだったので、下記URLにアクセスし、『command line tools (macOS 10.14) for Xcode』をダウンロードし、インストール。
https://developer.apple.com/download/more/再度ターミナルで、Homebrewのインストールのスクリプトを実行すると
無事にできました。解決できて非常にうれしかったです。
誰かの参考になれば幸いです。
Rubyの基礎勉強がんばります。
皆さんよろしくお願い致します。
初めての投稿でした。
- 投稿日:2019-06-07T09:39:58+09:00
Serverspec(Specinfra)にcheck_is_installed_by_pip2を実装しました
TL;DR
serverspecでは、pip2コマンドでモジュールをチェックする機能が実装されていないので、独自でリソースを実装し、以下のように利用できるようにする。
describe package(module_name) do it { should be_installed.by('pip2') } it { should be_installed.by('pip2').with_version(version) } end追記
check_is_installed_by_pip2
を実装して、SpecinfraのGitHubにPull requestを出したらマージされたので、独自実装する必要がなくなりました。
https://github.com/mizzy/specinfra/pull/688経緯
serverspecで、pip2, pip3のモジュールをそれぞれチェックしたかった。
チェック対象のサーバーは、pip
、pip3
コマンドではpip3が呼ばれ、pip2
コマンドではpip2が呼ばれるようにしている。しかし、serverspecでは、以下のように、pip、pip3コマンドでのチェックしかできない。
# check_is_installed_by_pip it { should be_installed.by('pip') } # check_is_installed_by_pip3 it { should be_installed.by('pip3') }理由は、specinfraでは、
check_is_installed_by_pip
とcheck_is_installed_by_pip3
しか用意されていないからだ。これではpip2のチェックができない。
そこで、check_is_installed_by_pip2を独自で用意し、serverspecで以下のように定義できるようにする。
it { should be_installed.by('pip2') }独自リソースを作成する
packageリソースを拡張するため、
check_is_installed_by_pip2
を定義する。参考:specinfra/lib/specinfra/command/base/package.rb
check_is_installed_by_pip2を定義
以下のファイルに
check_is_installed_by_pip2
を定義する。
spec/lib/extension/specinfra/command/base/package.rbspec/lib/extension/specinfra/command/base/package.rbclass Specinfra::Command::Base::Package < Specinfra::Command::Base class << self def check_is_installed_by_pip2(name, version=nil) regexp = "^#{name}" cmd = "pip2 freeze | grep -iw -- #{escape(regexp)}" cmd = "#{cmd} | grep -w -- #{escape(version)}" if version cmd end end endspec/lib以下のファイルがautoloadされるようにする
spec/libの*.rbのファイルがautoloadされるように、以下の一行をspec/spec_helper.rbに追加する。
spec/spec_helper.rbDir[File.expand_path "#{__dir__}/lib/**/*.rb"].each{|f| require(f)}specファイルで呼び出す
be_installed.by('pip2')
とすることで、check_is_installed_by_pip2
が呼び出される。spec/sample/sample_spec.rbrequire 'spec_helper' describe package('boto3') do it { should be_installed.by('pip2') } it { should be_installed.by('pip2').with_version('1.9.156') } end追記
check_is_installed_by_pip2
を実装して、SpecinfraのGitHubにPull requestを出したらマージされたので、独自実装する必要がなくなりました。
https://github.com/mizzy/specinfra/pull/688参考にしたserverspec, specinfraのファイル
- check_is_installed_by_pip(), check_is_installed_by_pip3()が定義されているファイル
- be_installedが定義されているファイル
- be_installedで呼んでいるinstalled?が定義されているファイル
その他参考記事
- 投稿日:2019-06-07T09:14:41+09:00
ActiveRecordのinclude、joins、eager_load、preloadの表まとめ
表題の件について完全に自分用の備忘録としてまとめる
メソッド JOIN associationのキャッシュ クエリ数 その他 joins LEFT JOIN × 1 JOINしたテーブルで絞込ができる。 eager_load LEFT OUTER JOIN ○ 1 JOINしたテーブルで絞込ができる。 preload - ○ 複数 preloadしたテーブルによって絞り込もうとすると、例外を投げる。 includes - - - eager_load か preloadの動きをする それぞれ使いどころがあると思うが、個人的には使っていてeager_loadが全体的に一番使いやすいのではと思う。
- 投稿日:2019-06-07T09:01:53+09:00
【Rails】バッテリーホイミ機能を実装しました
個人開発のWebアプリ「まちかどルート」v6.1に、名づけて《バッテリーホイミ機能》なるものを実装したときのメモです。
TL; DR
まちかどルートは
「道ばたのゴミを拾おう」
「お年寄りに席をゆずろう」
「〇〇に行って〇〇しよう」 など。みんなの日常からちょっとした一日一善やおつかいなどをお題にしてサブクエストを作りクリアしあうことで世界がちょっと明るくなる、そんなリアルRPG系Webアプリです。
そしてユーザーには、レベルや経験値のほかHP (ヒットポイント:ドラクエのアレ)という属性値を持たせています。
Battery Status API
どうやらPCとAndroidのChrome限定のようですが、バッテリーの状態変化でJavaScriptを発火させることのできるBattery Status APIというものがあります。
今回はそれを応用してみました。
こんな流れです。
1. まちかどルートを開く
2. 充電を始める
3. APIが充電を検知したことを画面に表示
4. バッテリー残量値を取得
5. 「ホイミ!」ボタンを押す
6. バッテリー残量値をHPに加算してDBに保存view / JavaScript
index.html.erb<div id="posts"> # ここに下記のJavaScriptからDOM要素が挿入されます </div>application.jsnavigator.getBattery().then(function(battery) { // 充電を検知したら発火 battery.onchargingchange = function(){ // 充電開始(true)のときだけ処理 if (battery.charging === true) { // viewのid="posts"のところにDOM要素を挿入 var element = document.getElementById("posts"); element.insertAdjacentHTML("afterbegin", " <p>充電を検知</p> <form action='battery' accept-charset='UTF-8' method='get'> <input name='utf8' type='hidden' value='✓'> <input type='text' id='battery' name='battery' readonly=''> </input> <button name='button' id='battery_btn' type='submit' disabled> ホイミ! </button> </form> "); } } // 充電の残量が変化したら発火 battery.onlevelchange = function(){ // 充電の残量値を入力フォームに表示 document.getElementById( "battery" ).value = battery.level * 100; // 入力フォームのdisabledを無効にして「ホイミ!」ボタンを押せるようにする document.getElementById( "battery_btn" ).disabled = ""; } });controllerでホイミ!
posts_controller.rbdef battery # viewから送られてきたバッテリー残量値をHPに加算してDB保存 if params[:battery] != nil battery = params[:battery] @current_user.hp += battery.to_i @current_user.save flash[:notice] = "HPが回復しました!" redirect_to root_path else flash[:error] = "呪文が失敗しました!" redirect_to root_path end endルーティング
config\routes.rbget 'battery' , as: 'battery', to: 'posts#battery'あとがき
とてもザックリな感じになってしまいましたが、要点は上記のとおりです。HPが残り少なくなったらバッテリーを充電して「ホイミ!」
けっこう楽しいです。
オープンソース開発用のリポジトリを公開しました。今回の件、こちらにもアップしてあります。
https://github.com/west2538/machiroute_oss
- 投稿日:2019-06-07T08:23:43+09:00
Sonic Piによる音楽生成
概要
コードを書くことで音楽を生成できるシステム環境「Sonic Pi」(ソニックパイ)のノウハウをストックしていきます。
学び始めたばかりの初心者なので、学習してアップデートがあれば随時、加筆修正をしていく予定です。Sonic Piとは
Rubyの文法をベースとしたコーディングで音楽を作って鳴らせるシステム環境。
以下の公式サイトからインストールしただけですぐに使えます。・公式サイト
Sonic Pi - The Live Coding Music Synth for Everyone
https://sonic-pi.net/DAWとどう違う?
LogicやCUBASEなどのいわゆるDAWと比較した際のコードによる音楽生成の特徴は以下のようなものがあると感じます。
繰り返しが主体
└例えば、単純に何かのメロディーを入力していくという場合、DAWであればピアノロールで意図した音程、場所、長さなどをコントルールして打ち込みことができますが、コーディングによる入力はそれなりの手間がかかります。
反面、繰り返しの処理は得意なので、ループ構造をメインとして、その中身に色々な工夫を足していくことで音楽を生成するということが基本路線かなと感じます。アルゴリズムを駆使できる
└プログラミングで音楽を生成するので、何らかのアルゴリズムを作ってそれをループさせることで、ミニマルミュージックのような規則性のある音楽を少ない工数で作れる可能性がある。乱数や変数を駆使して動的な音楽を作れる
└プログラムで乱数を扱うことで、繰り返しの度に生成される音やエフェクトかかり方が変わるような「変化し続ける音楽」を作れるというのは大きな特徴かなと思います。
- 投稿日:2019-06-07T08:23:43+09:00
Sonic Piによる音楽生成の基礎
概要
コードを書くことで音楽を生成できるシステム環境「Sonic Pi」(ソニックパイ)のノウハウをストックしていきます。
私も学び始めたばかりの初心者なので、学習して知識がアップデートされたら加筆修正をしていく予定です。Sonic Piとは
Rubyの文法をベースとしたコーディングで音楽を作って鳴らせるシステム環境。
以下の公式サイトからインストールしただけですぐに使えます。・公式サイト
Sonic Pi - The Live Coding Music Synth for Everyone
https://sonic-pi.net/DAWとどう違う?
LogicやCUBASEなどのいわゆるDAWと比較した際のコードによる音楽生成の特徴は以下のようなものがあると感じます。
繰り返しが主体
└例えば、単純に何かのメロディーを入力していくという場合、DAWであればピアノロールで意図した音程、場所、長さなどをコントルールして打ち込みことができますが、コーディングによる入力はそれなりの手間がかかります。
反面、繰り返しの処理は得意なので、ループ構造をメインとして、その中身に色々な工夫を足していくことで音楽を生成するということが基本路線かなと感じます。アルゴリズムを駆使できる
└プログラミングで音楽を生成するので、何らかのアルゴリズムを作ってそれをループさせることで、ミニマルミュージックのような規則性のある音楽を少ない工数で作れる可能性がある。乱数や変数を駆使して動的な音楽を作れる
└プログラムで乱数を扱うことで、繰り返しの度に生成される音やエフェクトかかり方が変わるような「変化し続ける音楽」を作れるというのは大きな特徴かなと思います。音を鳴らす
use_bpm 120 # BPMを指定する。デフォルトは60。 use_synth :piano # 使用するシンセを選択。デフォルトは「:beep」。 play 60 # 指定のノート番号の音を鳴らす。60はドの音。 sleep 1 # 休符を指定。1拍休み。 play :C4 # 英語の音名で音程を、後ろの数値でオクターブを指定。「:」が必要。 sleep 2 # 2拍休み。 play :Cs4 # Cシャープを鳴らす。 sleep 0.5 # 半拍休み。 play :Db4 # Dフラットを鳴らす。 sleep 0.25 # 16分休み。 play [:C4, :E4, :G4] # []で囲うことで音を同時に鳴らせる。Cのコードを鳴らしている。ノート番号と音名の対応は以下を参照。
ノート番号を覚えるのは大変なので基本的には英語表記で書くとよいと思います。・MIDIノート番号と音名、周波数の対応表
http://www.asahi-net.or.jp/~HB9T-KTD/music/Japan/Research/DTM/freq_map.htmlイテレーション(反復)
# 4回繰り返す 4.times do play :C4 sleep 1 end
[回数].times ~ end
で囲うことで反復処理を行える。# 反復の中に反復をネスト 4.times do 2.times do play :C4 sleep 1 end 2.times do play :D4 sleep 1 end endこのように、繰り返しの中に入れ子で繰り返しを入れることもできる。
ループ
# 無限ループ loop do play :C4 sleep 1 end play :D4 # 上でループが動いているので実行されない。
loop do ~ end
で囲うことで無限にループを繰り返します。
基本的にStopボタンを押すまで永遠と繰り返し続けます。