なべしす

独立系SI企業から完全異業種のベンチャー企業に転職、社内のITインフラを整えるべく颯爽と登場した自称天才プログラマー俺のハートフルブログ

Rails MWS APIを利用するまでの事前準備

MWSとは

AmazonマーケットプレイスWebサービスの略です。
自分が販売をしているAmazon各プラットフォーム対して以下のようなことができます。

  • レポートの取得
  • 出品や商品情報の更新削除などができるフィードのサブミット
  • 商品情報の取得
  • などなど

詳しくは公式リファレンスを読みましょう。
結構色々なことができます

MWSを利用するのに必要な情報

開発者ではない場合(複数セラーを扱わない場合)

MWSを利用するには以下の3つのキーが必要です。

  • Seller ID(出品者ID)
  • AWS Access Key ID(AWSアクセスキーID)
  • Secret Key(秘密キー)

取得方法は以下の記事を参照にしてください。
amazonのMWSアカウント情報はセラーセントラルから確認できるんですけど

開発者の場合(複数セラーを扱う場合)

弊社のWebアプリケーションlismoaの様に複数のセラーの情報を扱う場合には上記の3つの他に

が必要になります。

アプリ開発者のDeveloper ID(開発者ID)とセラーのアカウントを紐付けてAuthTokenを取得します。
公式リファレンスに紐付ける方法は記載されていますが、正直分かり辛いと思います。
百聞は一見にしかずです
黙って一回自分自身を紐付けて見ましょう。
https://sellercentral.amazon.co.jp/gp/mws/registration/register.html

Railsで簡単にMWSを使えるgem「peddler」を導入する

MWSを生で扱うのはPOSTリクエストを魔改造する必要があり、かなりハードルが高いです
peddlerというgemを使いましょう。便利ですので
https://github.com/hakanensari/peddler
導入方法はREADMEにある通りいつもの感じです。

APIリファレンスはこちら
http://www.rubydoc.info/gems/peddler/toplevel

client作成のサンプル

peddlerではAPI Sectionごとにclientというオブジェクトを作成してsection以下の機能を利用するという流れになります。
API Sectionというのはこのレイヤーですね。

f:id:oredeki:20171108115821p:plain:w300 f:id:oredeki:20171108115816p:plain

今回は「商品(Products)」のclientを作成するサンプルです。
先ほど取得した複数のキー使用します。

@client = MWS.products(
  primary_marketplace_id: 'marketplace_id',
  aws_access_key_id: 'aws_access_key_id',
  aws_secret_access_key: 'aws_secret_access_key',
  merchant_id: 'seller_id',
  auth_token: 'auth_token' # 開発者ではない場合は不要
)

準備完了

これでMWSの多彩な機能を使う準備は完了です!
今後はその多彩な機能の紹介や注意点などを記事にしていこうと考えていますのでお楽しみに!

Pairsで「初回デート費用は男性が全て払う」を選択している人に直接聞いてみた

どんなマインドでそんなの選択してるんだ?

先月ふと思ったんですよ。。。

もしかして何かの隠語なのか?
業界的に暗黙の了解みたいなものがあるのか?

おそらく何もないでしょう。
ただただそういう考えの女性なんでしょうね!
でもモヤモヤするのでいつか聞ける機会があれば聞いてみたいなーと思っていました。

時は満ちた

先日ついにマッチングしたんですよ!
Pairsで「初回デート費用は男性が全て払う」を選択している女性と。
(ぼくは足跡ついた人全てに「いいね」してるので好みな人じゃなくてもマッチングします。)
f:id:oredeki:20171028181108p:plain:w300
※プライバシーのため色々白塗りしています。

早く聞きたい早く聞きたい

適度に挨拶を済ませていざ!
f:id:oredeki:20171028181758p:plain:w300
丁寧かつフランクかつ失礼のないように!!

返ってきた!

期待はずれの答えが返ってきましたね...
f:id:oredeki:20171028182200p:plain:w300


...ん?

f:id:oredeki:20171028181839p:plain


うそやん...

突っ込め突っ込め

f:id:oredeki:20171028182740p:plain:w300

返ってこなくなったんごー(^q^)

絶対なんかあるやろ。
「初回デートで奢らない懐の狭い男はないです」
とか
「男はATMだと思ってます」
とか
「私にはおごって当然でしょ?」
とかあるでしょ黒いところが!!

まとめ

人間は都合が悪いことはごまかす

今後

進展があったらまた記事書きますねw

GoogleAppsScript JavaScriptを用いてCSVをローカルに書き出す実装

概要

おおよそソースを見たらわかるようにしているので説明はふわっとです。

GAS側のメソッドと機能

メソッド 機能
onOpen() メニューバーのその他に「CSVで出力」を追加
csvDownload() CSVで出力」をクリックされるとCSVをダウンロード
getData() GASからJSにシートのデータを渡す
getFileName() 出力名を取得(アクティブシート名+現在日時)

JS側のポイント

  • window.onloadで読み込み時にファンクションを実行
  • GAS側からダウンロードデータを作りダウンロードリンクを作成
  • 擬似的にlinkオブジェクトを作成してクリックさせる
  • google.script.host.close();でダウアログを閉じる

実装

// メニューバーにカスタムメニューを追加
function onOpen() {
  var spreadsheet = SpreadsheetApp.getActiveSpreadsheet();
  var entries = [{
    name : "CSVで出力",
    functionName : "csvDownload"
  }];
  spreadsheet.addMenu("その他", entries);
};

// JSを用いてCSVファイルをダウンロードする
function csvDownload() {
  // dialog.html をもとにHTMLファイルを生成
  // evaluate() は dialog.html 内の GAS を実行するため( <?= => の箇所)
  var html = HtmlService.createTemplateFromFile("dialog").evaluate();
  // 上記HTMLファイルをダイアログ出力
  SpreadsheetApp.getUi().showModalDialog(html, "ダウンロードなう");
}

// JS側で使用
function getData() {
  // スプレッドシート上の値を二次元配列の形で取得
  var sheet = SpreadsheetApp.getActiveSheet();
  var values = sheet.getDataRange().getValues();

  // 二次元配列をCSV形式のテキストデータに変換
  var dataArray = [];
  for (var i = 0; i < values.length; i++) {
    dataArray.push(values[i].join(","));
  }
  return dataArray.join("\r\n");  // 改行コードは windows を想定
}

// JS側で使用
// ファイル名の設定はここで!!
function getFileName() {
  var spreadSheet = SpreadsheetApp.getActiveSpreadsheet();
  var sheet = spreadSheet.getActiveSheet();
  var now = new Date();
  var datetime = Utilities.formatDate( now, 'Asia/Tokyo', 'yyyyMMddHHmm');
  // アクティブシート名+現在日時
  return sheet.getName() + '_' + datetime + '.csv';
}
<!DOCTYPE html>
<html>
  <head>
    <base target="_top">
    <script type='text/javascript'>    
      window.onload = function(){
        handleDownload();
      }
      function handleDownload() {
        // 出力データを GAS から取得する
        var content = <?= getData(); ?>;
        var blob = new Blob([ content ], { "type" : "text/csv"});
        var link = document.createElement('a');
        // ファイル名を GAS から取得する
        link.download = <?= getFileName(); ?>;
        link.href = window.URL.createObjectURL(blob);
        link.click();
        // ダイアログを閉じる
        google.script.host.close();
      }
  </script>
  </head>
</html>

所感

  • ダイアログが一瞬開いてしまうのは仕方がない
  • VBAの80倍なじみやすい

参考

GASで文字コード指定してファイルを書き出す
https://qiita.com/jsoizo/items/948e23231417d203eefa
ローカルにファイルダウンロード
http://googleappsscript.hatenablog.com/entry/2017/09/06/120000

Rails rubyzipでディレクトリを圧縮してダウンロードする

やりたいこと

こんな感じのディレクトリを含むデータを
スクリーンショット 2017-10-06 18.12.50.png
圧縮したい!!
スクリーンショット 2017-10-06 18.13.11.png

使用するgem

rubyzip
導入はREADMEを読みましょう。

ziprubyってのもあるから間違えないように。

rubyzipの基本的な使い方

READMEに書いてある基本的なzip作成方法は以下の通り
Basic zip archive creation

require 'rubygems'
require 'zip'

folder = "Users/me/Desktop/stuff_to_zip"
input_filenames = ['image.jpg', 'description.txt', 'stats.csv']

zipfile_name = "/Users/me/Desktop/archive.zip"

Zip::File.open(zipfile_name, Zip::File::CREATE) do |zipfile|
  input_filenames.each do |filename|
    # Two arguments:
    # - The name of the file as it will appear in the archive
    # - The original file, including the path to find it
    zipfile.add(filename, File.join(folder, filename))
  end
  zipfile.get_output_stream("myFile") { |os| os.write "myFile contains just this" }
end

手始めにサンプルを少しいじってローカルにある画像ファイル数個を圧縮してみました。
簡単にできました。
その時こう思ったのです。
「あ、これは勝ちましたね。」と...

そして英語だからってまともにREADMEを読まずに、zipfile.addディレクトリ渡せばできるんじゃ?
という軽い気持ちで多方面からaddを試してみたんですが、ことごとく敗北。
負けました。

実装

readmeの少し下を見てみましょう。
Zipping a directory recursively....ん?流れ変わったな?
再帰的にディレクトリを圧縮する」by Google翻訳
以下ソース

require 'zip'

# This is a simple example which uses rubyzip to
# recursively generate a zip file from the contents of
# a specified directory. The directory itself is not
# included in the archive, rather just its contents.
#
# Usage:
#   directory_to_zip = "/tmp/input"
#   output_file = "/tmp/out.zip"
#   zf = ZipFileGenerator.new(directory_to_zip, output_file)
#   zf.write()
class ZipFileGenerator
  # Initialize with the directory to zip and the location of the output archive.
  def initialize(input_dir, output_file)
    @input_dir = input_dir
    @output_file = output_file
  end

  # Zip the input directory.
  def write
    entries = Dir.entries(@input_dir) - %w(. ..)

    ::Zip::File.open(@output_file, ::Zip::File::CREATE) do |io|
      write_entries entries, '', io
    end
  end

  private

  # A helper method to make the recursion work.
  def write_entries(entries, path, io)
    entries.each do |e|
      zip_file_path = path == '' ? e : File.join(path, e)
      disk_file_path = File.join(@input_dir, zip_file_path)
      puts "Deflating #{disk_file_path}"

      if File.directory? disk_file_path
        recursively_deflate_directory(disk_file_path, io, zip_file_path)
      else
        put_into_archive(disk_file_path, io, zip_file_path)
      end
    end
  end

  def recursively_deflate_directory(disk_file_path, io, zip_file_path)
    io.mkdir zip_file_path
    subdir = Dir.entries(disk_file_path) - %w(. ..)
    write_entries subdir, zip_file_path, io
  end

  def put_into_archive(disk_file_path, io, zip_file_path)
    io.get_output_stream(zip_file_path) do |f|
      f.write(File.open(disk_file_path, 'rb').read)
    end
  end
end

「Copy from here」と書いていますし言われるがままにこのクラスをプロジェクトに配置します。
すると、素晴らしい。
インスタンスを作成してwriteメソッドを呼び出すだけで指定したフォルダがzipになるのです。

zip_file_generator = ZipFileGenerator.new(input_dir, output_file)
zip_file_generator.write
# ディレクトリのzipができる

後はコントローラーでsend_fileを使ってダウンロードさせるだけです。

send_file output_file

所感

  • READMEはヨンダホウガイイデスヨ
  • なんでgemのライブラリに含んでくれないんですかねえ
  • rubyzipの使い方は結構ググっても出てこない

Rails Stripe サブスクリプション決済実装 プランの登録

概要

Stripeでのサブスクリプション決済実装をRailsで行う場合、
公式で提供されているgemを使用することになると思います。

前回はAPIを使うまでの事前準備を記事にしました。

oredeki.hatenablog.com

今回はサブスクリプションプランをStripeに登録する方法を紹介します。

管理ページからプラン追加

Stripe管理ページでSubscriptions > Plansよりプランの一覧が閲覧できます。
管理ページからプランを追加する場合はこの画面のNewより追加できます。 スクリーンショット 2017-09-30 12.49.35.png こんな感じです。 スクリーンショット 2017-09-30 12.52.17.png

トライアル期間が365日あるが、その後は1日毎に100万円かかる悪魔的プランです。

Railsからプラン追加

アプリケーション側から先ほどのような悪魔的なプランを登録してみます。
プランを追加するにはモジュールはStripe::Planクラスのcreateメソッドを使用します。
前回Stripeモジュールを使用できる設定は完了させているので、単純にメソッドに適切な引数を設定するだけでプランは作成されます。
公式リファレンス:Create a plan

実装

  def create_plan
    Stripe::Plan.create(
      id:                   'test_plan',
      name:                 'Test Plan',
      amount:               1000000,
      currency:             'jpy',
      interval:             'day',
      interval_count:       1,
      trial_period_days:    365,
      statement_descriptor: 'sswatanabe'
      metadata: {
        are_you_from_chicago: "No i'm from NewYork"
      }
    )
  end

各パラメータの意味は名前の通りです。
英語に馴染みのない僕にはstatement_descriptorがなんのことか分からなかったのですが、
リファレンスを読んだところ、「クレジットの明細に表示される名前」らしいです。
metadataでは、好きな情報を持たせることができます。
アプリケーション側のPlan IDを登録する時などに活用できます。

確認

API側からも確認できますが、
手っ取り早くStripe管理ページでみてみましょう。
無事追加されています。 スクリーンショット 2017-09-30 13.38.56.png 詳細も正常ですね。 スクリーンショット 2017-09-30 13.39.07.png

所感

........アプリケーション側からプラン追加するってなかなか壮大なシステムでしか使わないよな。

参考

Redmine プラグイン 今更ながらEasyガントチャートを導入してみた

はじめに

Easyガントチャートとは

ガントチャートを直感的にいじることができるプラグインです。
https://www.easyredmine.com/redmine-gantt-plugin
今回は無料版を導入しました。

導入の経緯

プロジェクトを管理していたらリスケすることって多々あるじゃないですか。
その度にチケットの開始日と期限をいちいち更新するのって結構手間ですよね。
親チケレベルでリスケしようとしたら10個とか20個とかのチケットの開始日と期限を変えないといけないわけです。
もっと手軽に開始日と期限を更新したい!と思ってこのプラグインの導入を決めました。

導入手順

プラグインファイルを取得する

こちらから無料版のプラグインをダウンロードします。
https://www.easyredmine.com/redmine-gantt-plugin スクリーンショット 2017-09-08 11.48.51.png

Redmineプロジェクトに配置

ダウンロードしたzipをredmine/htdocs/pluginsに展開します。

bundle install

いつものようにpluginsに配置して再起動したらエラーとなったので調べた結果、
bundle installが必要なようです。
easy_ganttに移動してbundle installをしましよう。

db:migrate

bundle installして一安心し、再起動したらまたエラーとなったので調べた結果、
db:migrateも必要なようです。
rake --trace db:migrate RAILS_ENV=productionしましょう。

再起動

最後に再起動して完了です。
Redmine上の 管理>プラグイン で確認しましょう。

設定

各プロジェクトでEasyガントを有効にする

設定>モジュール でEasyガントを有効にします。 スクリーンショット 2017-09-08 11.55.54.png 有効にしたら、プロジェクトメニューにEasyガントチャートが追加されます。

実際のプロジェクトデータを取り込む

Easyガントチャートに移動すると、サンプルデータが表示されていると思うので(初回だけ)、
実際のプロジェクトデータを取り込むボタンを押して実際のプロジェクトデータを取り込む。

動作確認

こんな感じに移動できます。 タイトルなし.gif

惜しいところ

  • 期間の拡張はできない(横に広げれない)
  • おそらくデフォルトのテーマにしかデザインの完全な対応はしていない

Rails Stripe サブスクリプション決済実装 初期設定をしてAPIを使える状態にする

概要

Stripeでのサブスクリプション決済実装をRailsで行う場合、
公式で提供されているgemを使用することになると思います。

実装に当たって
Stripe::Plan.create()
Stripe::Customer.create()
のようなAPIメソッドを使用していくことになるのですが、
今回はAPIを使える状態にするまでの初期設定についてまとめました。

事前準備

いつもの

Gemfileに gem 'stripe'
を追加して
bundle install
しておきましょう。

確認

rails cでStripeメソッドが使えるか試しておきましょう。
Stripe::Customer.listは登録されている顧客の情報リストを取得するメソッドです。

irb(main):001:0> Stripe::Customer.list
Stripe::AuthenticationError: No API key provided. Set your API key using "Stripe.api_key = <API-KEY>". You can generate API keys from the Stripe web interface. See https://stripe.com/api for details, or email support@stripe.com if you have any questions.

当たり前ですが、エラーとなります。

APIを使用するのに必要なアカウント情報

必要な情報は

  • Publishable key
  • Secret key

といった2つのkeyです。

Stripe管理画面の以下の箇所より取得できます。(色々白抜きしてます) スクリーンショット 2017-09-06 17.30.17.png

開発フェーズの場合、Test用のkeyを使用するようにしましょう。

設定記述

stripe-rubyのREADMEにはこう書かれています。
https://github.com/stripe/stripe-ruby#usage

require "stripe"
Stripe.api_key = "sk_test_..."

# list charges
Stripe::Charge.list()

# retrieve single charge
Stripe::Charge.retrieve(
  "ch_18atAXCdGbJFKhCuBAa4532Z",
)

もちろんこれでもAPIは使用できます。
しかし、必要な箇所で毎回

require "stripe"
Stripe.api_key = "sk_test_..."

と書くのは効率がいいとは言えませんし、セキュリティ的にもアウトです。

なので、今回は以下の2つのファイルにkey情報を記述します。

  • config/secrets.yml
  • config/initializers/stripe.rb

secrets.ymlについては以下の記事を参照してください。
Rails 5.1のencrypted secretsの運用について考える

config/secrets.yml

stripe_publishable_keyとstripe_secret_keyを定義します。
環境に合わせてdevelopment、test、productionは切り替えてください。

development:
  # stripe
  stripe_publishable_key: pk_test_********************
  stripe_secret_key: sk_test_********************

config/initializers/stripe.rb

デプロイ時にStripeの定義を読み込みます。

Rails.configuration.stripe = {
  publishable_key: Rails.application.secrets.stripe_publishable_key,
  secret_key: Rails.application.secrets.stripe_secret_key
}

Stripe.api_key = Rails.application.secrets.stripe_secret_key

準備完了

以上でAPIを使用する準備は完了となります。
簡単ですね!!

確認

先ほどと同じメソッドを使って確認します。
(サーバー再起動してくださいね)

irb(main):001:0> Stripe::Customer.list
=> #<Stripe::ListObject:*****> JSON: {
  "object": "list",
  "data": [....],
  "has_more": true,
  "url": "/v1/customers"
}

正常なレスポンスが返ってきました。
(Customerがemptyの場合は見え方が違うかも…)

参考