Sunday, March 25, 2012

omuniauth使ったrailsサンプルサイト作成中のメモ


omuniauthを使ったサンプルサイトを作った過程の自分用メモです。まだ途中です。


demo
http://muitter.herokuapp.com/

code
https://github.com/cieux1/muitter

omniauth
https://github.com/intridea/omniauth/wiki

ほぼこちらのサイト通りにやった。感謝。
http://blog.twiwt.org/e/c3afce



# rails app作成

% rails new muitter

#Gemfile
gem 'omniauth'
gem 'omniauth-twitter'

% bundle


# config/initializer/omniauth.rb 作成
# add application info you registered at dev.twitter.com.
# *call back URL at dev.twitter can NOT be empty.

Rails.application.config.middleware.use OmniAuth::Builder do
     provider :twitter,"xxxxxxxxxxxxxxxxx","xxxxxxxxxxxxxxxxxxxxxxxxxxx"
     #provider :facebook,"App ID","App Secret"
end



% rails g controller sessions


# app/sessions_controller.rb

class SessionsController < ApplicationController
  def callback
    auth = request.env["omniauth.auth"]
    user = User.find_by_provider_and_uid(auth["provider"], auth["uid"]) || User.create_with_omniauth(auth)
    session[:user_id] = user.id
    redirect_to root_url, :notice => "Signed in!"
  end

  def destroy
    session[:user_id] = nil
    redirect_to root_url, :notice => "Signed out!"
  end
end



# routes.rb

match '/auth/:provider/callback' => 'sessions#callback'
match "/signout" => "sessions#destroy", :as => :signout



% rails g model user


# app/user.rb

class User < ActiveRecord::Base
  def self.create_with_omniauth(auth)
    create! do |user|
      user.provider = auth["provider"]
      user.uid = auth["uid"]
      user.name = auth["info"]["name"]
      user.screen_name = auth["info"]["nickname"]
    end
  end
end

# チュートリアルでは[user_info]になっているがtwitterAPIの仕様変更で[user]に変更になった?
https://github.com/intridea/omniauth/issues/249#issuecomment-3229038

# user_infoだとauth/failure のcallback routeが見つからないとでるので変更したら動いた。




# db/migrate/XXXXX_create_users.rb

class CreateUsers < ActiveRecord::Migration
  def self.up
    create_table :users do |t|
      t.string :provider, :null => false
      t.string :uid, :null => false
      t.string :screen_name, :null => false, :uniq => true
      t.string :name, :null => false

      t.timestamps
    end
    add_index :users, [:provider, :uid]
    add_index :users, [:screen_name]
  end

  def self.down
    drop_table :users
  end
end



% rake db:migrate



# app/controllers/application_controller.rb

# add helper method

class ApplicationController < ActionController::Base
  protect_from_forgery

  helper_method :current_user

  private

  def current_user
    @current_user ||= User.find(session[:user_id]) if session[:user_id]
  end
end


% rails g scaffold Mutter user_id:integer mutterbody:text


# app/views/layouts/application.html.erb
# add login/show twiiter ID part to basic template erb.

<% if current_user %>
  Welcome <%= current_user.name %>!
  <%= link_to "Sign Out", signout_path %>
<% else %>
  <%= link_to "Sign in with Twitter", "/auth/twitter" %>
<% end %>



# route.rb
# set mutter/index as root.

root :to => "mutters#index"



# model/mutter.rb
belongs_to :user


# model/user.rb
has_many :mutters


# _form.html.erb
# ログイン中のuserのidをmutterのuser_idにひもづける処理。(なかなかわからなかった)
<%= f.number_field :user_id, :type => 'hidden', :value => current_user.id %>


このへん省略というか忘れた。。。というかなんで英語で書いてたのか。。。


okinawa ruby毎週meetupでomniauthが入った時点?でUser modelに必要なtableが作成されていてあとは使うだけ、という状態になっていることを教えてもらい道が開けた感。

#rails consoleでデータ呼び出しフォーマットの確認
% rails c

Mutter.all
User.find_by_id(1)

これで例えば、userとmutterをひもづけているmutterのuser_idがnilのままじゃん、とかが確認できた。




view / erb

#ログイン時とログインしていない時の見た目の切り替え
if current_user

# 自分の書き込みかどうかの判定
<% if current_user && mutter.user.screen_name == current_user.screen_name %>
...
<% end %>

#mutters_controller.rb
# ログインしているかどうかの判定

http://guides.rubyonrails.org/action_controller_overview.html

# before_filter ログインしていないと以下の操作ができないように。
    before_filter :require_login, :only => [:new, :create, :edit, :create, :update, :destroy]
...
...
    private

    def require_login
        unless logged_in?
            #flash[:error] = "You must be logged in to create/update a mutter."
            redirect_to root_url, :notice => 'You must sign in to create/update a mutter.'
        end
    end
    def logged_in?
        !!current_user
    end
 

need to be fixed!!!!!!!!!!!!!!!!!!
ログイン時、urlのid指定したら他の人の書き込みが触れてしまう。。。
どこでやるのかな。。これ。



heroku に deploy

#ssh keyの設定

github のssh key と herokuの ssh keyは同一である必要。
2つの端末で同じアカウントからpushしていることはどーなんだ?

ssh keyは端末のID的なもの?privateとpublicのペアができるので、public keyをサーバ側に保存する。
public keyは ~/.ssh/xxx_rsa.pub



レポジトリを登録してそこにpushする。
レポジトリは複数登録できる。
git add remote ....

一覧を見るには
git remote -v



# herokuはsqlite3ではなく、PostgreSQL
# Gemfileを修正
http://railsapps.github.com/rails-heroku-tutorial.html

group :development, :test do
  gem 'sqlite3'
end
group :production do
  gem 'pg'
end


# rails serverをthinに変更。webrickは頼りない?

webrickはlocalのままに残すなら

group :production do
  gem 'thin'
end


# herokuのstackによってrailsやrubyのバージョンが違う。
最新環境は cedar (beta)で、rails3.2の場合はこれでないと動かない?
heroku createする時に指定しなければならない。

http://devcenter.heroku.com/articles/cedar
http://devcenter.heroku.com/articles/cedar-migration

$ heroku create --stack cedar --remote heroku-cedar app名
$ git push heroku-cedar HEAD:master

なぜかHEADにしないとエラー。stackを[変更]するほうのtutorialを参考にしてたからか?
error: src refspec cedar does not match any.
error: failed to push some refs to 'git@heroku.com:app名.git'

http://stackoverflow.com/questions/4181861/src-refspec-master-does-not-match-any-when-pushing-commits-in-git



#gitignoreの設定
gitignoreは設定されるとそのファイルが gitから無視される。
設定される前に上がったものは基本的には消せない。

global_gitignore

globalは端末全体に設定される。
http://d.hatena.ne.jp/passingloop/20110903/p1

~/.global_gitignoreというファイルを作って記述
git config --global core.excludesfile ~/.global_gitignore で有効化。

例多数) https://github.com/github/gitignore/tree/master/Global



# 一度pushしてしまったファイルを消すのは大変&基本やってはいけない。
http://help.github.com/remove-sensitive-data/

git filter-branch
$ git filter-branch --tree-filter 'rm -f ファイル名' HEAD

$ git filter-branch --tree-filter 'rm -f omniauth.rb' HEAD

historyが書き変わるのでcloneされていたらおかしくなる?



# db:migrate

% heroku run rake db:migrate
成功するとlocalの時と同じようなログが出る。

% heroku restart
再起動で反映

% heroku run rakd db:reset
で元に戻す。



#pageを開く
% heroku open




# 修正の流れ

修正
git add app/asset/stylesheets/application.css
git commit -m 'adjusted font size.'
git push heroku-cedar HEAD:master

の繰り返し。


# 気になること
stack変えるために新規でheroku createしたので、githubにpushする方法がよくわからなくなった模様。。。

Wednesday, September 7, 2011

Okinawa.rbの勉強会に参加してきました

記憶が風化する前に、書いておかなくては。 

技術的な話はしようにもできないのでシンプルに基本プログラマじゃないヒトからの感想。 

僕は昨年何かプログラムというものを始めようと思って、「簡単」をキーワードにいろいろ調べ続けてほんのちょっとだけperlに寄り道してからRubyにたどり着きました。「簡単」とか「カンタン」とか記事タイトルに入ってるものはそりゃあもうなんでも読んでいきました(プログラムもせずに。。。)。実際は全然簡単じゃあないんですけどね。以前に比べて簡単になったという話なだけで。

なので他の言語の歴史とかいいところや問題点がよくわかっていません。つまりRuby(というか最近の話)がすべての世代なのです。「こんなことがこんなに簡単にできるんですよ」ということも僕からすれば普通(にむつかしいこと)になっているという状況。でもこの年でニュータイプになってしまってるのがちょっと恥ずかしくも嬉しかったりしました。

エディタや環境についての話で盛り上がるのも生で見るのが初めてで、横で聞いているだけでちょっとプログラムが上手になったような気になりました。勉強会って、自分があんまわかってなくても、いいメンバーに恵まれたらそれだけでモチベーションを上げてくれるし、ほんといいもんですね。 

もちろん貰ってばかりだと悪いので、自分が今まで得てきた開発とはちょっと外れたウェブの知識を総動員してお返ししたいと思っています。まあそれもたいしたことはないんですが。。。まあでも開発というのは自分が思っている以上に限定された分野なのかもしれないという印象も受けました。デザインやインターフェイス、最適化などの部分は通常は入ってこないのが開発なのかなと思ったり。 

ということで、待望のOkinawa.rbが始まりました。そのたぶん第一回目に参加の感想でした。なんとかついていきたいと思っています。ただ発起人まわりの方々はrubyに留まらない、もっと自由な枠な集まりを心に描いているようで、その辺りは自分の身の丈にあった内容かどうかを見極めつつも積極的にがんばりたいと思います。 

興味のある方はまずはOkinawa.rb | Google グループに参加してみるといいと思います。まだオフィシャルブログとか固定URLもないかんじで流動的ですが、じきに落ち着いてくるんじゃないかと思います。ぜひ参加お待ちしています!初心者枠参加者と、それを支えてくれるやさしい先輩がたくさん集まりますように。。。

(初心者ビーム出し過ぎかもですが、これも初心者を誘い込むため!かな。。)

Wednesday, July 27, 2011

目の前のコードを楽しもう

まつもとゆきひろさんのtwitterで紹介されていたRubyマスターへの道のスライドから。

プログラミングの楽しみとは

「動いた!!」


「あんなに上手くできたら楽しいだろうな」?

幸せは現在にしかない
目の前のコードを楽しもう


調子に乗って原 悠さんの本も買おうと本屋でぱらぱらとめくってみたけど全然無理そうだったのでまたいつか。

Rubyで作る奇妙なプログラミング言語 ~Esoteric Language~
原 悠
毎日コミュニケーションズ
売り上げランキング: 282166

Saturday, April 23, 2011

ページ内のすべてのアンカータグにクリック計測用のタグを自動で追加する[Jquery][analytics]

参考にさせてもらったページ
外部リンクのクリックをGoogle Analyticsのイベントトラッキングに記録するJavaScript(jQuery使用)
個人的に使うことが多い9つのjQuery・JavaScriptスニペット

ページのURL、上の階層のid名、遷移先のURLを取得してanalyticsのイベントトラッキングで見ることができます。以前はアンカータグひとつひとつにonclickなんちゃらを入れていたので、それに比べると手品のような手軽さです。

ページ内で正しくidが使われていればこれだけで大体把握はできるはずです。inputボタンのクリックも拾うようにしています。input要素の場合は遷移先URLは空欄で上がってきます。

 jQuery(function($) {
 var _thisUrl = $(location).attr('href');
 $.each($('a,input:image,input:submit'), function() {
 var _thisId = $(this).closest("[id]").attr("id");
 $(this).click(function() {
  _gaq.push(['_trackEvent', _thisUrl, _thisId, $(this).attr('href')]);
  });
 });
});

Thursday, April 21, 2011

[ruby][SEO] ローカルに落としたサイトデータから全htmlのtitle、meta description、keyword、見出しタグを抽出してcsvに一発保存

サイト内の全htmlのtitle、meta description、keyword、見出しタグを一覧でcsvに書き出します。自分の環境では一応動いていますが、いろんなサイトのスクリプトを見よう見真似で作ったので他の環境でどのくらい使えるものなのかはわかりません。

自分が管理しているサイトではDescriptionやKeywordの大文字小文字が混在していたためエラーが出ました。rescueを初めて使ってみたところなんとか動きました。これが正しい使い方であればいいのですが。というかもっと簡潔にやる方法があれば教えてください。

以下の方法で使っています。
1.Sitesucker(OSX用)でサイトをローカルに保存。(オプションで画像やサウンドファイルなどの重いファイルを除外しておかないとサイト規模によってはすごく時間がかかります。)
2.ダウンロードしたフォルダにスクリプトを持っていってそこで実行。

require 'rubygems'
require 'hpricot'
require 'open-uri'

#改行削除メソッド
def erasebr (str)
     str.gsub!(/\n/, "")
end

#書き出しファイル名はドメイン名+csvtype+今日の日付
puts "ファイル名の先頭に入れる文字を入力してください!例)ドメイン名など半角英数"
name = gets.chomp

#書き出しメソッド
def makecsv (domain_name, csvtype)

#ファイル名は:ドメイン名+csvtype+タイムスタンプ
timestamp = Time.now.strftime("-%Y%m%d-%H%M%S")
csv_name = domain_name + "-" + csvtype.to_s + timestamp + ".csv"
puts "書き出しファイル名: " + csv_name

#ファイルタイプ(通常はhtml)
ext = "html"
text_array = Dir.glob("**/*.#{ext}")

open(csv_name, "w") do |f|
     # 全ファイル内のtitle、description、keywords、見出し要素を取得
     text_array.each do |file|
          doc = Hpricot( open(file).read )
            title = doc.at('title').inner_html
            # casesensitiveエラーを入れ子でrescue
            begin
              description = doc.at('meta[@name="Description"]')['content']
             rescue
                  begin
                   description = doc.at('meta[@name="description"]')['content']
                 rescue
                   description = "NO"
                  end
             end
            begin
              keywords = doc.at('meta[@name="Keywords"]')['content']
            rescue
                   begin
                   keywords = doc.at('meta[@name="keywords"]')['content']
                 rescue
                   keywords = "NO"
                 end
            end
            hone = doc.at('h1').inner_html
            htwo = []
            (doc/"h2").each do |d|
             htwo << d.inner_html
            end
         
          #改行を削除
          erasebr(title)
          erasebr(hone)
          erasebr(description)
          htwo.each { |c| c.gsub!(/\n/, "")}

      #要素ならべて書き込み
      if csvtype == "nokwd"
          s = file, ",title,#{title}", " ,description,#{description}", ",h1,#{hone}", ",h2,", htwo.join(","), "\n"
          else
        s = file, ",title,#{title}\n", " ,description,#{description}\n", " ,keywords,#{keywords}\n",
                 ",h1,#{hone}\n", ",h2,", htwo.join("\n,,"), "\n\n"
       end
       f.write s
     end
end
end

#keywordあり: kwd
#keywordなし : nokwd
makecsv(name, "kwd")
makecsv(name, "nokwd")


初さらしです。
動いた時は感動しました。

Friday, November 12, 2010

Perlがカラシニコフで、RubyがM-16

見ての通り「初めてのPerl」もそこそこにRubyを勉強し始めた。いろいろ読んで回った結果、ウェブサービスに向いている気がしてRubyをがんばってみようかという気になったけれど、どうも難しい。「初めてのPerl」の途中までしか知らない自分が言うのもなんだけれど、Perlのほうが簡単に感じてしまう。使うヒトに喜びをもたらすというRubyを実感するにはたぶんまだ初心者すぎるのか。

どういうところが難しいのか?

1. メソッドがたくさんあってそれを覚える必要がある。
どこのサイトや本を読んでも、そこらじゅう便利そうなメソッドだらけで読んでいる時は感心するのだけれど、覚えるまでが大変。Perlの場合は(「初めてのPerl」の場合かもしれないけど)手間はかかるけれど基本的なツールだけでなんとかなるようなシンプルさがあるような気がする。

例えば配列の要素を足す
(1..10).to_a.inject(0){|t, a| t + a}
にしてもこれが書けたらいいなとは思ってもinjectを知っていないと出てこない。これがPerlだと「よし、順番に読み込んで足していけばいい」という風にとりあえずはやり方の予測がつく(まだ全然すんなりとはできないけど)。つまり高級な言語なので高級な使い方をしたいけど、ひとつひとつのツールが最初はゴージャスすぎて戸惑う感じ。

2. オブジェクトの種類がいっぱいあってそれを覚える必要がある。
これは間違ってるかもしれないけれど、変数のカタチが自由な分だけto_aとかto_iが必要になってきて簡単にコードを書こうと思ってもつまづくポイントが多い。さっきのコードも(1..10)の後にto_aが必要なければいいのに(言っても仕方ないけど)って思う。

積み木で遊ぶ感じがPerlで、接着剤使ったプラモデル作るのがRubyな感じ。
「Perlがカラシニコフで、RubyがM-16」のような感じ。
とりあえずまだしばらくゴルゴを目指す。

とんちんかんなコト言ってるとは思いますが、後で見て笑える日が来るようがんばります。

Tuesday, August 10, 2010

learning perl exercise 6 hash

6-1:ユーザが入力した名前の姓を表示。
(氏名からすべて入力してもらうということかもしれないが、とりあえず。)

use 5.010;

my %family_name = (
"fred" => "flintstone" ,
"barney" => "rubble" ,
"wilma" => "flintstone",
);

print "Hey, type one of the names below and you know the person\'s last name.\n";

my @first_name = keys %family_name;
print "@first_name\n";

chomp ($person = <STDIN>);

print  "family name of $person is $family_name{$person}.\n";



6-2:名前のリストを読み込んで誰が何回登場したか表示。
use 5.010;

open IN, "namelist";

while ( $name = <IN> ) {
 chomp $name;
 if ( exists $namelist{$name} ) {
   $namelist{$name} = $namelist{$name} + 1;
  }
  else {
   $namelist{$name} = 1;
   }
 }
 
foreach $person (sort keys %namelist ) {
print "$person appeared $namelist{$person} time(s) in the list.\n";
}
動いた。

6-3
ENVのすべてのキーとその値を表示する。列幅は最大文字数のキーと値を取得してあわせる。
#! /usr/bin/perl
use 5.010;

my @key = sort keys %ENV;
my @value = values %ENV;

my $digit_key = &max_digit(@key);
my $digit_val = &max_digit(@value);

my $max_digit = 0;

sub max_digit {#最大値を返すサブルーチン
 foreach (@_){
  my $scales = length($_);
  if ($scales > $max_digit) {
   $max_digit = $scales;
  }
 }
 $max_digit;
}

$format = "%${digit_key}s %${digit_val}s\n";

while ( ($key, $value) = each %ENV) {#ハッシュENVのキーと値を最後の行まで代入
 printf $format, $key , $value;
}

回答例を見てあまりの短さに驚いた。

答え合わせ

6-3:
回答例はkeys %ENVの最大文字数だけを取得。

foreachで%ENVのkeyを取得すると同時にifで最大文字数を取得。
foreach my $key ( keys %ENV ) { 
 my $key_length = length( $key ) ;#$keyの文字数を代入。
 $longest = $key if $key_length > $longest;# ifのカッコなし
}


memo


ハッシュはキーと値がペアになった配列。データベース的な配列?
fredのfamily_nameはadamsの場合
$family_name{fred} = "adams";

複数同時に代入する場合(=>(fat array)を使う場合)
my %family_name (
"fred" => "adamas",
"mike" => "mann",
"alex" => "rodrigues",
"jimmy" => "carter",
);
行末がコンマ。

図書館の貸し出しカード。誰(key)が何冊(value)借りているか?

同じ名前が何回出てきたか?(名前をkey、回数をvalue)

初出の場合はundefを返すのでその分岐を利用するケース。

ハッシュの全処理にはeach %hash
while ( ($key, $value) = each %ENV) {#ハッシュENVのキーと値を最後の行まで代入
 printf $format, $key , $value; #あらかじめ決めたformatでキーと値を出力する例。
}