Twitter のアクセストークンをスクリプトで取得する

昨今、Twitter API の App 登録が審査など手順が増えたので、気軽に App を増やさなくなりました。そのため、既存の App に対して管理アカウントとは別のアカウントを認可させたいときがあります。 下記の Ruby スクリプトは gem oauth を使って、サーバを立てずにブラウザだけで認可してアクセストークンを取得するものです。認可後のリダイレクト時に付随するクエリ内容を適切に処理さえすれば、対象の Callback URL が 404 Not found であろうとも問題ないです。一時的にサーバを立てる必要もなく、一連の本来サーバサイドで行う処理をローカルから実行して処理できます。 なお Callback URL が自分の持ち物でないとセキュリティ上問題があるのは当然です。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 require 'uri' require 'oauth' CONSUMER_KEY = '<your consumer key>' CONSUMER_SECRET = '<your consumer secret>' CALLBACK_URL = '<your callback url>' consumer = OAuth::Consumer.new(CONSUMER_KEY, CONSUMER_SECRET, { site: "https://api.twitter.com", scheme: :header }) request_token = consumer.get_request_token(oauth_callback: CALLBACK_URL) req_url = request_token.authorize_url(oauth_callback: CALLBACK_URL) puts <<EOS Go to the following URL and authorize your account on a browser: #{req_url} Copy the redirected url from the browser and paste it: EOS input = STDIN.gets redirected_url = input.chomp rurl = URI.parse(redirected_url) params = {}.tap do |h| URI.decode_www_form(rurl.query).each do |entry| h[entry[0].to_sym] = entry[1] end end access_token = request_token.get_access_token(oauth_verifier: params[:oauth_verifier]) puts <<EOS =================================== user_id: #{access_token.params[:user_id]} screen_name: #{access_token.params[:screen_name]} Access Token: #{access_token.token} Access Token Secret: #{access_token.secret} =================================== EOS スクリプトを実行すると authorize URLが生成されるので、それを認可したいアカウントでサインイン済みのブラウザに貼り付けて移動します。そのまま Twitter の認可画面でボタンを押すと、本来自分のサービスのURLにリダイレクトされます。この状態でURL欄を中身コピーして、貼り付けます。そうすると続きを処理してアクセストークンして出力します。

2019年9月1日 · Toshimitsu Takahashi

RubyでYARD定義を使って実行時にメソッド引数と戻り値の型チェックを試みる

(Ruby のカンファレンスの度に、毎度話題になる?) Ruby に型が欲しい件ですが、個人的な見解を書いておこうと思います。ちなみに私は RubyKaigi 2018 に参加しておりません。Twitterのタイムラインでの賑わいを見ていただけです。 最近、Swift を触っててそのコンパイルの重さにうんざりしているので、 型推論 は現代のマシンスペックでは基本的に辛いと思っています(カフェでノマドコーディングしたいので)。またメタプログラミングし放題の Ruby に導入するのは困難という認識です。 では、どういうときに 型定義 が欲しくなるのか考えてみます。 コードを書いて実行して確認というトライアンドエラーを減らしたい、実行前にエラーをなるべく洗い出したい。 型定義起因による補完を効かせながらコーディングしたい。これはエディタやIDEのサポートも必要です。 1 について、Rubyの場合テストでカバレッジを稼いで、なるべくエラーの芽を潰しておこうとします。しかしユニットテストにおいて end-to-end テストが存在しない場合、単純なユーティリティ関数でなければ、モックやスタブに頼るようになりチェックが甘くなります。そのためテストは通過するものの、実際通しで動かしたとき想定とは異なる型のデータ受渡しが発生してしまうことがあります。 2 について、メソッド名や引数名から型をコードの読解で推測することは可能ですが、それなりの規模のアプリケーションやライブラリではコードコメントでドキュメント定義していないと(昔の自分や)他人の書いたコードを扱うのが困難になることが多いと思います。 そして例えば YARD で引数や戻り値の型をコメントに定義して、ドキュメントを生成したりIDEでコード補完に用いることが多いでしょう。 さてここから本題ですが、1 で型チェックしないで検証から漏れてしまう問題は、テストでメソッド呼び出しの引数と戻り値をよりチェックするアサーションを明示的に書いていく必要があります。一方 2 でYARDによるドキュメントの型定義は必ずしも実際のソースコードで走る処理と一致してるか保証されない問題もあります。 そこで『YARD定義によるメソッド引数と戻り値の型チェック』を実行時に行ってみたらどうかと考えてみました。 テストで型チェックのアサーションを減らせる。 YARD定義とソースコードが一致してるか検証できる。 Rubyのソースコードに余計な定義を挿し込むことはない。 YARDはテンプレートで書きだす直前の解析データを、YARD::Registry から参照する機能を提供しています。またあらゆるメソッド呼び出し(:call)と戻り(:return)は、TracePoint 機構を使ってフックすることができます。これを組み合わせれば、割と簡単に、実行時に定義どおりに適切な型の引数が指定されてメソッド呼び出され、適切な戻り値が返っているかをチェックすることできると思います。 ということで、試してみましょう。 lib/dog.rb これが YARD 定義を書いた検証対象のクラスになります。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 module Animal # # This class is Dog # class Dog # @param name [String] a name # @param weight [Numeric] weight def initialize(name, weight) @name = name @weight = weight @children = [] end # Add a child dog # # @param dog [Animal::Dog] a child dog def add_child(dog) @children.push(dog) end # Run. # # @param distance [Integer] # @return [String] message def run(distance) "#{@name} runs #{distance}." end # dummy method returns wrong type value # # @return [Integer] def dummy "a string" end end end definition.rb YARD::Registry から ClassObject と属する MethodObject を解析してチェックしやすい定義クラス MethodDefinition の集合 DefinitionStore に変換します。 ...

2018年6月3日 · Toshimitsu Takahashi

Ruby で Google Analytics から直近1時間のページビューランキングを取得する方法

サイトのアクセスランキングを作る方法はいろいろありますが、自前で HTTPサーバのログから計算するのは大変です。Google Analytics の API を使うとアクセス数が Ruby には Google Analytics API を操作できる Garb という便利なラッパーライブラリがあります。今回はこれを使います。 Garbのインストール gemで入れます。プロジェクトは GitHub で管理されています。 https://github.com/vigetlabs/garb # gem install garb Fetching: crack-0.3.1.gem (100%) Fetching: i18n-0.6.1.gem (100%) Fetching: activesupport-3.2.8.gem (100%) Fetching: garb-0.9.1.gem (100%) Successfully installed crack-0.3.1 Successfully installed i18n-0.6.1 Successfully installed activesupport-3.2.8 Successfully installed garb-0.9.1 4 gems installed スクリプト本体 それほど長くないので、まずソースコードを貼っておきます。 #!/usr/bin/ruby1.9.1 # coding: utf-8 require ‘rubygems’ require ‘garb’ require ‘uri’ Garb::Session.login(’<アカウント>’, ‘<パスワード>’) profile = Garb::Management::Profile.all.detect { |p| p.web_property_id = ‘<サイトプロパティID>’ p.id = ‘<アカウントID>’ } class PageTitle extend Garb::Model metrics :pageviews dimensions :hour, :page_path, :page_title end today = Time.now - 3600; start_date = today end_date = today hour = today.hour if hour < 10 hour = “0” + hour.to_s else hour = hour.to_s end cond = { :limit => 20, :sort => :pageviews.desc, :start_date => start_date, :end_date => end_date, :filters => { :hour.eql => hour } } rs = PageTitle.results(profile, cond) rs.each do |r| puts r.page_path # ページパス puts r.page_title # ページタイトル puts r.pageviews # ページビュー end ...

2012年9月5日 · Toshimitsu Takahashi

AKB48 の Google+ アクティビティデータを MongoDB で MapReduce してみた

MapReduce について実際やってみたことがなかったので、MongoDB で試しそうと思っていました。 そんななか、AKB48 の(18歳以上?の)メンバーが Google+ を開始しました。これで「バルス」以上に定時でかつてない負荷が Google+ にかかり始めたと思われます。ということで、扱うにはもってこいなデータなのでこれを使うことにしました。 Google+ API は今のところデータのGETしかできないようですが、それで充分です。 とりあえずメンバーの Googel+ のID(?)と名前をMongoDB のコレクションに突っ込みます。 それを元に定期的に各メンバーのアクティビティ(活動のエントリ)を取得しては、 JSON をそのままほぼそのまま別のコレクションに突っ込みました。 メンバーの取得とセット AKB48 Now on Google+ のHTMLを適当にパースして突っ込みました。 MongoDB シェルで見ると以下のように入ってます。 $ /proj/arble/mongodb/bin/mongo MongoDB shell version: 2.0.0 connecting to: test \> db.idols.find({}, { "_id":0, "id":1, "name":1 }) { "id" : "108406705498777962659", "name" : "板野友美" } { "id" : "112077362806147944184", "name" : "梅田彩佳" } { "id" : "105229500895781124316", "name" : "大島優子" } { "id" : "108367535733172853340", "name" : "大家志津香" } { "id" : "116324240483798147615", "name" : "大矢真那" } { "id" : "107135851528812577523", "name" : "小木曽汐莉" } { "id" : "111145641865855965824", "name" : "小野晴香" } { "id" : "110230842586402039931", "name" : "河西智美" } { "id" : "109547251260290757268", "name" : "柏木由紀" } { "id" : "108485060451296256117", "name" : "片山陽加" } has more Google+ からデータの取り込み Activities: list - Google+ Platform — Google Developers を使うと、ユーザのアクティビティがリストで取得できます。userId に上記の idを、collection に “public” と指定します。 ...

2011年12月11日 · Toshimitsu Takahashi

Ruby で Windows Azure Storage テーブルを操作するには

Github にある johnnyhalife/waz-storage · GitHub ライブラリを使うと簡単に操作できます。Windows Azure Storage REST API のラッパーになってます。 Rubygems でインストール $ sudo gem install waz-storage Fetching: mime-types-1.17.2.gem (100%) Fetching: rest-client-1.6.7.gem (100%) Fetching: ruby-hmac-0.4.0.gem (100%) Fetching: waz-storage-1.1.1.gem (100%) Successfully installed mime-types-1.17.2 Successfully installed rest-client-1.6.7 Successfully installed ruby-hmac-0.4.0 Successfully installed waz-storage-1.1.1 4 gems installed Installing ri documentation for mime-types-1.17.2... Installing ri documentation for rest-client-1.6.7... Installing ri documentation for ruby-hmac-0.4.0... Installing ri documentation for waz-storage-1.1.1... Installing RDoc documentation for mime-types-1.17.2... Installing RDoc documentation for rest-client-1.6.7... Installing RDoc documentation for ruby-hmac-0.4.0... Installing RDoc documentation for waz-storage-1.1.1... 認証 waz-storage ライブラリの認証方法は Base のクラスメソッド establish_connection! にアカウント名、アクセスキー、SSLを使うかどうかをハッシュで渡します。 ...

2011年12月10日 · Toshimitsu Takahashi

REXML と Libxml-Ruby とのエンティティ出力比較

REXML で大きな XML ファイルを処理したらあまりにも遅かった。Ruby で書かれているのでいたし方ないんですが。もちろん REXML は標準でついているし、手軽に使えて良いという大きなメリットがあるけど、行う処理が数時間レベルなので速くしたい。そこで Libxml http://libxml.rubyforge.org/ を試すことに。 行うのは XML → XML 変換なので、エンティティの出力が気になった。そこで次のようなコードを実行して、結果を比較してみる。 テストしたコード 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 #!/usr/bin/env ruby # require 'rubygems' require 'xml/libxml' require 'rexml/document' str =<<EOS <?xml version="1.0" encoding="UTF-8"?> <root> <title>Tosshi&apos;s メモ書き</title> <array> <item attr='&apos;'> &lt;html&gt; &apos;TAG&quot; </item> <item attr="&quot;"><!\[CDATA\[ CDATA &lt;html&gt; &apos;TAG&qout; <>"' \]\]></item> </array> </root> EOS puts <<EOS \[ libxml-ruby \] EOS xp = XML::Parser.new xp.string = str libxmldoc = xp.parse puts "document :" puts libxmldoc node = libxmldoc.root.find_first("title") print "Node :" puts node print "content :" puts node.content print "child.to_s:" puts node.child.to_s libxmldoc.root.find("array/item").each do |item| print "Node :" puts item print "content :" puts item.content print "child.to_s:" puts item.child.to_s end puts <<EOS \[ REXML \] EOS rexmldoc = REXML::Document.new(str) puts "Document :" puts rexmldoc el = rexmldoc.root.elements\["title"\] print "Element :" puts el print "text :" puts el.text print "get_text :" puts el.get_text rexmldoc.root.elements.each("array/item") do |item| print "Element :" puts item print "text :" puts item.text print "get_text :" puts item.get_text end 結果 libxml はアポストロフィとダブルクォートを必要なときだけ実体名にエスケープする。 REXML は属性値は固定でアポストロフィで囲い、アポストロフィとダブルクォートは必ず実体名にエスケープする。 $ ruby testxml.rb \[ libxml-ruby \] Document : <?xml version="1.0" encoding="UTF-8"?> <root> <title>Tosshi's メモ書き</title> <array> <item attr="'"> &lt;html&gt; 'TAG" </item> <item attr="&quot;"><!\[CDATA\[ CDATA &lt;html&gt; &apos;TAG&qout; <>"' \]\]></item> </array> </root> Node :<title>Tosshi's メモ書き</title> content :Tosshi's メモ書き child.to_s:Tosshi's メモ書き Node :<item attr="'"> &lt;html&gt; 'TAG" </item> content : <html> 'TAG" child.to_s: &lt;html&gt; 'TAG" Node :<item attr="&quot;"><!\[CDATA\[ CDATA &lt;html&gt; &apos;TAG&qout; <>"' \]\]></item> content : CDATA &lt;html&gt; &apos;TAG&qout; <>"' child.to_s:<!\[CDATA\[ CDATA &lt;html&gt; &apos;TAG&qout; <>"' \]\]> \[ REXML \] Document : <?xml version='1.0' encoding='UTF-8'?> <root> <title>Tosshi&apos;s メモ書き</title> <array> <item attr='&apos;'> &lt;html&gt; &apos;TAG&quot; </item> <item attr='&quot;'><!\[CDATA\[ CDATA &lt;html&gt; &apos;TAG&qout; <>"' \]\]></item> </array> </root> Element :<title>Tosshi&apos;s メモ書き</title> text :Tosshi's メモ書き get_text :Tosshi&apos;s メモ書き Element :<item attr='&apos;'> &lt;html&gt; &apos;TAG&quot; </item> text : <html> 'TAG" get_text : &lt;html&gt; &apos;TAG&quot; Element :<item attr='&quot;'><!\[CDATA\[ CDATA &lt;html&gt; &apos;TAG&qout; <>"' \]\]></item> text : CDATA &lt;html&gt; &apos;TAG&qout; <>"' get_text : CDATA &lt;html&gt; &apos;TAG&qout; <>"'

2008年1月11日 · Toshimitsu Takahashi

Ruby の REXML と RSS Maker で Amazon で売れてるオライリー本のフィードを簡単に作る

使用したライブラリ open-uri rexml/document rss/maker RSS Parser ※ We retire raa.ruby-lang.org という Amazon Web Service を簡単に使えるライブラリも存在するが、単にリストを取得するだけなので今回は見送った。 コード RSS 2.0 で出力する。RSS::Maker.make(version) で引数にバージョンを入れて指定する。 Description にイメージと著者、出版日、価格を入れている。Author は複数タグあるため、カンマで結合。 同じようなものを仕事で Java で作ったことがあったが、Ruby などの Light Weight 言語の手軽さを知ってしまうと戻れない。 結果: http://feed.tilfin.net/amazon/oreilly-bestseller.xml 下記のコードでは、ファイル書き出しをコメントアウトして、CGIとしての動作を有効にさせている。 1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 #!/usr/bin/env ruby # # Amazon O'Relly Sales Ranking # Rss feed # ################################# require 'open-uri' require 'rexml/document' require 'rss/maker' param = { 'Service' => 'AWSECommerceService', 'AWSAccessKeyId' => '☆アクセスキーID☆', 'AssociateTag' => '☆アソシエイトID☆', 'Operation' => 'ItemSearch', 'SearchIndex' => 'Books', 'ResponseGroup' => 'Medium', 'Sort' => 'salesrank', 'Publisher' => 'オライリー' } params = param.map do |key, value| "#{URI.encode(key)}=#{URI.encode(value)}" end.join("&") doc = nil open('http://webservices.amazon.co.jp/onca/xml?' \+ params) { |resp| doc = REXML::Document.new(resp) } rss = RSS::Maker.make("2.0") do |maker| maker.channel.title = "O'Reilly ベストセラー (Amazon)" maker.channel.description = "O'Reilly の Amazon.co.jp のセールストップ 10 を紹介" maker.channel.link = "http://feed.tilfin.net/amazon/orelly-bestseller.xml" number = 0 doc.elements.each("/ItemSearchResponse/Items/Item") do |item| itmat = item.elements\["ItemAttributes"\] number += 1 entry = maker.items.new_item entry.title = number.to_s + '. ' \+ itmat.elements\["Title"\].text entry.link = item.elements\["DetailPageURL"\].text desc = nil item.elements.each("MediumImage") { |img| desc = <<EOS <img src="#{img.elements\["URL"\].text}" width="#{img.elements\["Width"\].text}" height="#{img.elements\["Height"\].text}"><br> EOS } desc += itmat.elements.each("Author") { |a| a.text }.join(", ") \+ " (著)" desc += "<br>発売日:" \+ itmat.elements\["PublicationDate"\].text.gsub(/-/, '/') desc += "<br>" \+ itmat.elements\["ListPrice/FormattedPrice"\].text entry.description = desc entry.date = Time.now end end print "Content-Type:application/rss+xml\\r\\n\\r\\n" print rss #open("oreilly-bestseller.xml", "w") { |out| \# out.print rss #} #``` ※IE7だと application/rss だけではフィードだと認識されなかった。

2008年1月3日 · Toshimitsu Takahashi