google custom search engine(CSE)を使って、検索結果をjsonで取得する

google検索結果を取得しようとした時に、すこし苦労したので手順を整理しておく。


参照したのはここ
Custom Search — Google Developers

API keyの取得

https://cloud.google.com/console/project

まず、上のURLにアクセス

f:id:ryufloat:20140118163048p:plain

次に規約に同意するので Accept をクリック

f:id:ryufloat:20140118163944p:plain
①、②、③の順にクリックして


f:id:ryufloat:20140118163951p:plain
必要な種類のkeyを選択。
今回はServer Keyを作る。


f:id:ryufloat:20140118163956p:plain
IPアドレスで制限をかけたい場合は、枠の中に書く。
今回は制限を書けないので空のままCreateをクリック


f:id:ryufloat:20140118164007p:plain
これでAPI keyの作成が完了。


Custom search engine IDの作成

Custom Search Engine
上のURLにアクセス

f:id:ryufloat:20140118165433p:plain
Addをクリックして、Custom search engineの登録をする


f:id:ryufloat:20140118165440p:plain
Custom search engineは、本来自分のサイト内検索用のAPIなため、最低ひとつのURLを入れないと作成出来ない。
ここでは適当に入れておき、あとでコントロールパネルから削除を行う。


f:id:ryufloat:20140118165448p:plain
作成後、表示されるページでコントロールパネルへ


f:id:ryufloat:20140118165453p:plain
フィルタ等はこのページで指定出来る。
google 全体を検索したい場合、

  • ウェブ全体 を指定
  • 検索したいサイト を空欄に

するとOK。

Custom search engine IDは、このコントロールパネル内で確認出来る。

検索を行いJSONを取得する

https://www.googleapis.com/customsearch/v1?key={API_KEY}&cx={CUSTOM SEARCH ENGINE ID}&q={SEARCH_WORDS}

このURLを使う。
これは検索を行いJSONデータを返却するURLになっている。
なので、任意の言語でjsonからdecodeをして、使用すればおk。

パラメータを少しだけ解説

  • key: API key
  • cx: Custom search engine ID
  • p: 検索用クエリ

とりあえず、上の3つがあれば検索出来るはず。

JSONからサイトの情報を取得する

得られたJSONデータにおいて、itemsにサイトの情報が入っている。

後は実際にデータを眺めてもらえば良いと思うが、一応PHPの簡単なサンプルをおいておく。

このサンプルでは、得られたデータから、1番目に表示されるサイトの情報を取得するところまで行っている。

$cse_url = https://www.googleapis.com/customsearch/v1?key={API_KEY}&cx={CUSTOM SEARCH ENGINE ID}&q={SEARCH_WORDS}';
$search_result = file_get_contents($cse_url, true);
$search_result = json_decode($search_result);
$result_first = $search_result->items[0];

この先で取得出来るサイトの情報は、次の項に書いたパラメータが記載されているURLを参照してもらうと良いかと。

パラメータ

以下にパラメータが載っているので、何かやりたいと思ったら参照してみると良い。
CSE: list - Custom Search — Google Developers


注意点

検索用URLはSSLを使用”しなければならない”。

次のようにhttp://...とすると、

http://www.googleapis.com/customsearch/v1?key={API_KEY}&cx={CUSTOM SEARCH ENGINE ID}&q={SEARCH_WORD}

以下のエラーを吐く

{"error":{"errors":[{"domain":"global","reason":"sslRequired","message":"SSL is required to perform this operation."}],"code":403,"message":"SSL is required to perform this operation."}}

これに関しては
error in getting userinfo from google drive api - Stack Overflow
ここにある通り。

httpではなくhttpsを使えばOK

https://www.googleapis.com/customsearch/v1?key={API_KEY}&cx={CUSTOM SEARCH ENGINE ID}&q={SEARCH_WORD}

Rails 4.0 で多対多関連

この記事内では、"timeline"モデルと,"user"モデルをどのようにして接続する事が出来るかを見ていきます。

timeline.user
user.timeline

等でアクセス出来るように作っています(つもりです)。

Rubyや、Railsの経験が浅いため、もっと良い方法を知っている方、間違えを見つけた方はコメントを頂けるととてもうれしいです。

またポリモーフィズムを使用した関連付けに関しては触れていません。

あくまで導入部分だと思って参照して頂ければとおもいます。

HABTM

has_and_belongs_to_many を使った例。

モデルをいくつかの種類に分けて参照させたいときなどに便利です。

#/app/models/user.rb
class User < ActiveRecord::Base
  has_and_belongs_to_many :timelines_member, :class_name => 'Timeline',
                          :foreign_key => :member_timeline_id, :association_foreign_key => :member_id,
                          :join_table => :timelines_members

  has_and_belongs_to_many :timelines_observer, :class_name => 'Timeline',
                          :foreign_key => :observer_timeine_id, :association_foreign_key => :observer_id,
                          :join_table => :timelines_observers
end
#/app/models/timeline.rb
class Timeline < ActiveRecord::Base
  has_and_belongs_to_many :members, :class_name => 'User',
                          :foreign_key => :member_id, :association_foreign_key => :member_timeline_id,
                          :join_table => :timelines_members

  has_and_belongs_to_many :observers, :class_name => 'User', 
                          :foreign_key => :observer_id, :association_foreign_key => :observer_timeine_id,
                          :join_table => :timelines_observers
end
#/db/migrate/[TIMESTAMP]_timelines_members.rb
class TimelinesMembers < ActiveRecord::Migration
  def change
    create_table :timelines_members, id: false do |t|
      t.integer :member_id, null: false
      t.integer :member_timeline_id, null: false
    end
    add_index(:timelines_members, [:member_id, :member_timeline_id], :unique => true)
  end
end
#/db/migrate/[TIMESTAMP]_timelines_obserbers.rb
class TimelinesObservers < ActiveRecord::Migration
  def change
    create_table :timelines_observers, id: false do |t|
      t.integer :observer_id, null: false
      t.integer :observer_timeline_id, null: false
    end
    add_index(:timelines_observers, [:observer_id, :observer_timeline_id], :unique => true)
  end
end

HM with relationship table

has_manyとbelongs_toで関連を作るのですが、2つのモデルの間にrelationshipテーブルを挟む方法です。

2つのモデルの間に”状態”等のステータスを持たせたい場合に有効です。

#/app/models/user.rb
  has_many :timeline_user_relationships
  has_many :timelines, :through => :timeline_user_relationships
end
#/app/models/timeline.rb
class Timeline < ActiveRecord::Base
  has_many :timeline_user_relationships
  has_many :users, :through => :timeline_user_relationships
end
#/app/models/timeline_user_relationship.rb
class TimelineUserRelationship < ActiveRecord::Base
  belongs_to :user
  belongs_to :timeline
end
#/db/migrate/[TIMESTAMP]_create_timeline_user_relationships.rb
class CreateTimelineUserRelationships < ActiveRecord::Migration
  def change
    create_table :timeline_user_relationships do |t|
      t.references :user, index: true
      t.references :timeline, index: true
      t.integer :role

      t.timestamps
    end
    add_index(:timeline_user_relationships, [:user_id, :timeline_id], :unique => true)
  end
end

create_join_table

Rails 4.0から追加されたものです。
Railsドキュメントにも記載されています。
http://railsdoc.com/migration#テーブルを結合して作成(create_join_table)

手軽に作成出来るので、とりあえず関連をつけたいという時に使うと良いかと思います。

#/app/models/user.rb
class User < ActiveRecord::Base
  has_and_belongs_to_many :timelines
end
#/app/models/timeline.rb
class Timeline < ActiveRecord::Base
  has_and_belongs_to_many :users
end
#/db/migrate/[TIMESTAMP]_create_timelines_users.rb
class CreateUsersGroups < ActiveRecord::Migration
  def change
    create_join_table :timelines, :users
    add_index(:groups_users, [:group_id, :user_id], :unique => true)
  end
end


ざっと3種類紹介しましたが、option等は必要になった際に使ってみて追加していこうと思います。

FrozenBear v1.4.0

f:id:ryufloat:20131219015205j:plain

先日、FrozenBear最後(?)の主要バージョンとなる、バージョン1.4.0をリリースしました。

昨年冬に最初のリリースを行い、季節の巡りとともに、FrozenBearのアップデートを重ねて参りましたが、ここで一区切りとなります。


二度目の冬は、明るい陽の光がきれいです。


末永くお使いいただける事を願っております。

rspec-rails (2.14.0) + devise (3.2.1, 3.2.0) + capybara (2.2.0) でsign in判定

deviseを使っていて、rspec内でサインインしているかどうかの判定をしたいときのやり方。

バージョンごとに微妙に変化があるようで、実際に動作確認したのはタイトルのもののみです。
他のバージョンに関しては、この記事では対応出来ない可能性があるため、注意してください。


1つずつ注意点を確認して行きます。
最終的な結論は記事の下の方にコードをおいておくので、やり方だけ分かれば良いという人は下までスキップしてください。

devise

deviseを使う際に問題になる事の1つに、rspecでのsign in、sign outの検証が、deviseが提供する’sign_in’と’sign_out'メソッドでは出来ないという事があります。

そこで、以下のページで解決策が提示されています。

https://github.com/plataformatec/devise/wiki/How-To:-Test-with-Capybara

このページではまず、Capybaraによって提供されるメソッドを使って、Sign inフォームを直接埋めることでsign inする事を推奨しています。しかし推奨した後に、この方法ではテストの数が増えた時に時間がかかってしまうとも言っています。

そこで、warden test helpersというヘルパーを用いてこの問題を解決する方法が書かれています。

そして、なぜ’sign_in’と’sign_out’メソッドを使わないかというと、以下のような理由らしいです。

If you're wondering why we can't just use Devise's built in sign_in and sign_out methods, it's because these require direct access to the request object which is not available while using Capybara. To bundle the functionality of both methods together you can create a helper method.

翻訳(適当)すると
『なぜDeviseによって提供されている’sign_in’と’sign_out’メソッドを使わないのか疑問に思うだろう。その理由は、Capybaraを使っている時には使う事のできない”request object”に直接アクセスする必要があるから。なので、それら2つのメソッドを持っているヘルパーメソッドを自分で作ってやる必要がある。』
らしいです。

という事で、deviseの問題はwarden test helpersで解決する事が出来ます。

capybara

rails_tutorial(http://railstutorial.jp/?version=4.0#top)をやっていると、capybaraを使用しているspecが spec/requests というフォルダ内にある事に気がつくかもしれません。
そうして、capybara v2.2.0でも同じようにするとうまくいかなくてはまります。
2.2.0では仕様変更があったらしく、capybaraが動作するのは <b>spec/features</b> 内のファイルに対してとなっています。
ここを間違えていると、いくらdeviseの問題を解決してもテストが通りません。

また、Capybaraを使用するためには、rspecに Capybara::DSL をincludeする必要もあります。

結論

以上2つの問題を解決するよう定義したspec_helperが以下になります。

#spec/spec_helper.rb

# This file is copied to spec/ when you run 'rails generate rspec:install'
ENV["RAILS_ENV"] ||= 'test'
require File.expand_path("../../config/environment", __FILE__)
require 'rspec/rails'
require 'rspec/autorun'

# Requires supporting ruby files with custom matchers and macros, etc,
# in spec/support/ and its subdirectories.
Dir[Rails.root.join("spec/support/**/*.rb")].each { |f| require f }

# Checks for pending migrations before tests are run.
# If you are not using ActiveRecord, you can remove this line.
ActiveRecord::Migration.check_pending! if defined?(ActiveRecord::Migration)

RSpec.configure do |config|
  # ## Mock Framework
  #
  # If you prefer to use mocha, flexmock or RR, uncomment the appropriate line:
  #
  # config.mock_with :mocha
  # config.mock_with :flexmock
  # config.mock_with :rr

  # Remove this line if you're not using ActiveRecord or ActiveRecord fixtures
  config.fixture_path = "#{::Rails.root}/spec/fixtures"

  # If you're not using ActiveRecord, or you'd prefer not to run each of your
  # examples within a transaction, remove the following line or assign false
  # instead of true.
  config.use_transactional_fixtures = true

  # If true, the base class of anonymous controllers will be inferred
  # automatically. This will be the default behavior in future versions of
  # rspec-rails.
  config.infer_base_class_for_anonymous_controllers = false

  # Run specs in random order to surface order dependencies. If you find an
  # order dependency and want to debug it, you can fix the order by providing
  # the seed, which is printed after each run.
  #     --seed 1234
  config.order = "random"

  # for Capybara : Capybara用のDSLをinclude
  config.include Capybara::DSL

  # for Devise : Deviseを使用している時にsign in/outを取るためのヘルパーをinclude
  include Warden::Test::Helpers
end 

さらに、capybaraは必ず spec/features フォルダー内で使用してください。
そうしないとvisitやpageが定義されていません、というようなエラーが出て、テストが赤くなります。


最後に、実際にログイン、ログアウトの処理の仕方を確認します。

login_as(user, :scope => :user)     #ユーザーをadmin falseでログイン
logout(:user)     #ログインしているユーザーをログアウトさせる

上記2つのメソッドで、ログイン、ログアウトの処理を実行します。
下にサンプルのspecファイルをおいておいたので、照らし合わせて確認してみてください。

#spec/features/application_controller_spec.rb  !!features 直下であることに注意
require 'spec_helper'
 
describe 'ApplicationController' do
 
  let(:user) { FactoryGirl.create(:user) }
 
  describe 'header' do
    
    describe 'has menus' do
 
      before { visit root_path }
 
      specify 'whenever a user is signed in or not' do
        expect(page).to have_link('Home', root_path)
      end
 
      specify 'when a user is signed in' do
        login_as(user, :scope => :user)
        visit root_path
 
        expect(page).to have_link('Sign out', destroy_user_session_path)
        expect(page).not_to have_link('Sign up', new_user_registration_path)
        expect(page).not_to have_link('Sign in', new_user_session_path)
      end
 
      specify 'when a user is signed in' do
        logout(:user)  
      
        expect(page).to have_link('Sign up', new_user_registration_path)
        expect(page).to have_link('Sign in', new_user_session_path)
        expect(page).not_to have_link('Sign out', destroy_user_session_path)
     end
    end
  end
end

rspec-rails (2.14.0) + devise (3.2.1, 3.2.0) + cap ...

Ruby 2.0, Rails 4.0, devise, mysql, rails_admin の初期設定メモ

ruby2.0 rails4.0の環境が整っている事を前提とします。

プロジェクト作成

$ rails new sample —database=mysql

データベースの設定(developmentのみ表記、他は割愛)

まず、以下の記述があるか確認。無ければ各自付け足してください。

# Gemfile


# Use mysql as the database for Active Record
gem 'mysql2' 


念のためターミナルから以下を実行して、mysql2をインストールする。

$ gem install mysql2

設定ファイルを編集。
mysqlのユーザーは各自作成しましょう。(追記するかも)
ここで注意点。
パスワードはクォートで囲う必要がある様です。ruby初心者のため、細かい原因が分からないのですが、多分パスワードが数字だったのが悪いのかと思います。試していませんが(試せ)、もしかしたらユーザー名も、数字の時はクォートで囲うべきなのかもしれません。
これに関してはstackoverflowに回答がありましたが、どこにあったか分からなくなりました(探せ)。

# config/database.yml

development:
  adapter: mysql2
  encoding: utf8
  database: sample_db
  pool: 5
  username: sample_mysql_user  
  password: ‘user_password’  
  host: localhost
  socket: /tmp/mysql.sock

その後ターミナルに戻り、以下を実行。
db:setupはdb:createの処理もやってくれるので、db:createは冗長なのですが、下に書くエラーが出るのがdb:createのタイミングなので、一応。

$ rake db:create
$ rake db:migrate

これでデータベースとテーブルの作成が完了。

config/database.ymlにてパスワードを囲わなかった場合のエラーは以下のようになる。

$ rake db:create —trace

と叩くと

** Invoke db:create (first_time)
** Invoke db:load_config (first_time)
** Execute db:load_config
** Execute db:create
no implicit conversion of Fixnum into String

……….

Couldn’t create database for {“adapter”=>”mysql2”, “encoding”=>“utf8”, “database”=>”sample_db”, “pool”=>5, “username”=>”sample_mysql_user”, “password”=>user_password”, “host”=>”localhost”, “socket”=>”/tmp/mysql.sock”}

と表示され、rake db:migrateを叩いても同じようなエラーとなります。
一応メモとして。

deviseとrails_adminの設定

まずGemfileに以下を記述

# Gemfile
     .
     .
     .
gem ‘devise’
gem ‘rails_admin'

その後、コンソールにて

$ bundle install

と叩く。

devise

$ rails g devise:install


===============================================================================
Some setup you must do manually if you haven't yet:
  1. Ensure you have defined default url options in your environments files. Here
     is an example of default_url_options appropriate for a development environment
     in config/environments/development.rb:
       config.action_mailer.default_url_options = { :host => 'localhost:3000' }
     In production, :host should be set to the actual host of your application.
  2. Ensure you have defined root_url to *something* in your config/routes.rb.
     For example:
       root :to => "home#index"
  3. Ensure you have flash messages in app/views/layouts/application.html.erb.
     For example:
       <p class="notice"><%= notice %></p>
       <p class="alert"><%= alert %></p>
  4. If you are deploying on Heroku with Rails 3.2 only, you may want to set:
       config.assets.initialize_on_precompile = false
     On config/application.rb forcing your application to not access the DB
     or load models when precompiling your assets.
  5. You can copy Devise views (for customization) to your app by running:
       rails g devise:views
===============================================================================


上の指示通りに設定を行う。

1
# config/environments/development.rb 
     .
     .
     .
     # for devise
     config.action_mailer.default_url_options = { :host => 'localhost:3000' } 
end
2

まだcontrollerを作成していないので、後回し

3
# app/views/layouts/application.html.erb
     .
     .
     <%= yield %>

       <p class="notice"><%= notice %></p>
       <p class="alert"><%= alert %></p>
     .
     .
4

今はrails4.0以上を使う事前提なので、この設定は必要なし。

5

viewを作成する際に行う。


これで、devise側から指示された設定はひとまず完了。

次にUserモデルを作成する。
Userには管理者であるかないかという属性を持たせたい。
今回は、deviseのhowtoにあるoption 2を選択する(以下URLを参照)。
https://github.com/plataformatec/devise/wiki/How-To:-Add-an-Admin-role#option-2---adding-an-admin-attribute

そこで、次のコマンドを叩く

$ rails g devise User
$ rails generate migration add_admin_to_users admin:boolean

そして、生成されたmigrationファイルを以下のように編集。

# db/migrate/${DATE_CREATED}_add_admin_to_users.rb


class AddAdminToUsers &lt; ActiveRecord::Migration
  def self.up
    add_column :users, :admin, :boolean, :default => false
  end

  def self.down
    remove_column :users, :admin
  end
end

マイグレーションを行う前に、まずrails_adminの設定をする。

rails_admin

$ rake g rails_admin:install

2つ聞かれるが、今回はadminをuserに組み込む事にしたので、両方、空のenterを押してしまえばよい。

これでrails_adminの設定は完了。簡単。


deviseとrails_adminの設定が終わったので

$ rake db:migrate

する。


あとはcontrollerを作成して、routeを追加して、viewを作成する。


テスト出来るように、手順を残しておく。
今回はroot home#indexを使う。deviseが例を書いてくれているrouteです。

$ rails g controller home index
# app/controllers/home_controller.rb

class HomeController < ApplicationController

  before_filter :authenticate_user! # 追加

  def index
  end
end 

こうして、railsサーバーを立ち上げる。

$ rails s

http://localhost:3000/

を見てみると、deviseにより作られた画面が表示されるので、sign upボタンを押して、登録すると、rails_adminの画面を見る事が出来る。

以上。
随時追記or変更して行く予定。