Ruby で Google Analytics API

平成22年7月9日(金) 11時41分18秒
区分
Google
報告者:
tahara

こんにちは!! tahara です。 Ruby で Google Analytics API をたたいてみました。

といっても Garb を使えば簡単です。 ユーザID(email)とパスワードでも認証ができるのですが、今回は OAuth を使います。

インストール

gem install garb oauth

まずは Google Analytics, OAuth and Ruby. Oh, my. | everburning を参考に OAuth します。 あらかじめ https://www.google.com/accounts/ManageDomains から CONSUMER_KEY と CONSUMER_SECRET を取得しておく必要があります。

# -*- coding: utf-8 -*-

require 'oauth'

CONSUMER_KEY = "xxxxxx"
CONSUMER_SECRET ="xxxxxxxxx"

consumer = OAuth::Consumer.new CONSUMER_KEY, CONSUMER_SECRET, {
      :signature_method   => 'HMAC-SHA1',
      :site               => 'https://www.google.com',
      :request_token_path => '/accounts/OAuthGetRequestToken',
      :authorize_path     => '/accounts/OAuthAuthorizeToken',
      :access_token_path  => '/accounts/OAuthGetAccessToken',
    }

request_token = consumer.
  get_request_token({}, :scope => "https://www.google.com/analytics/feeds/")

# 次の URL をブラウザでアクセスし、確認コードを取得する。
p request_token.authorize_url

# 取得した確認コード
ACCESS_CODE = "xxxxxxxxx"

# 確認コードからアクセストークンを取得
access_token = request_token.get_access_token(:oauth_verifier => ACCESS_CODE)

# access_token.token と access_token.secret を取得する。
p access_token.token
p access_token.secret
ACCESS_TOKEN = access_token.token
ACCESS_SECRET = access_token.secret

# 次回からは次のようにしてアクセストークンを生成する。
access_token = OAuth::AccessToken.new(consumer, ACCESS_TOKEN, ACCESS_SECRET)

CONSUMER_KEY, CONSUMER_SECRET, ACCESS_TOKEN, ACCESS_SECRET がそろったので準備完了です。 Garb を使ってみます。 次は正規表現 ^/facilities/[0-9]+$ にマッチするページのページビューを取得するコードです。

# -*- coding: utf-8 -*-
=begin
http://github.com/vigetlabs/garb
=end

require "garb"
require "oauth"

CONSUMER_KEY = "xxxxx"
CONSUMER_SECRET ="xxxxxx"
ACCESS_TOKEN = "xxxxx"
ACCESS_SECRET = "xxxxx"

consumer = OAuth::Consumer.new CONSUMER_KEY, CONSUMER_SECRET, {
      :signature_method   => 'HMAC-SHA1',
      :site               => 'https://www.google.com',
      :request_token_path => '/accounts/OAuthGetRequestToken',
      :authorize_path     => '/accounts/OAuthAuthorizeToken',
      :access_token_path  => '/accounts/OAuthGetAccessToken',
    }
access_token = OAuth::AccessToken.new(consumer, ACCESS_TOKEN, ACCESS_SECRET)

Garb::Session.access_token = access_token

# プロファイル を指定
profile = Garb::Profile.first('UA-xxxxxxx-x')

class PageView
  extend Garb::Resource

  # 横に並ぶ項目。複数指定可能
  metrics :pageviews
  # 縦に並ぶ項目。複数指定可能
  dimensions :page_path
  # 並び順。複数指定可能。降順は後に .desc をつける。
  sort :pageviews.desc

  # フィルタ
  filters do
    # 正規表現で指定可能
    # http://code.google.com/intl/ja/apis/analytics/docs/gdata/gdataReferenceDataFeed.html#filters
    # http://www.google.com/support/analytics/bin/answer.py?answer=55582
    contains(:page_path, '^/facilities/[0-9]+$')
  end
end

# OpenStruct の配列で結果を取得。最大 10000 件取得できる。:offset で取得開始位置も指定可能。
res = PageView.results(profile, :start_date => '2010-07-01'.to_date, :end_date => '2010-07-07'.to_date, :limit => 10000)
# => [#<OpenStruct page_path="/facilities/159", pageviews="1237">, #<OpenStruct page_path="/facilities/164", pageviews="1061">, ...]

Data Feed Query Explorer - Google Analytics - Google Code ではブラウザから Analytics Data Export API をたたけるようになっていますので、 このページを参考にしながら、 metrics や dimensions の設定をいろいろかえると面白いことができるかもしれません。

>View Comments          このページの上へ戻る

隙があればlispを詰め込んで行きたい (5)

平成22年7月8日(木) 16時50分29秒
区分
報告者:
chiba

隙があればlispを詰め込んで行きたい日々ですが、今回は一つのファイルを複数ファイルに分割する仕事です。
こういうのはやっぱりsed/awkの仕事だろう、と思うのですが、一つLISPでがんばりたいところ
具体的な仕事内容ですが、

  1. 複数のエントリーがまとめられた、複数の長いテキストファイルを入力とする
  2. エントリーの区切り文字は^_
  3. エントリーごとに1ファイルにして出力
というところです。
書き捨てな感じですが、
(dolist (file (directory "/var/tmp/foo/bar/big-*.txt"))
  (dolist (mail (cl-utilities:partition-if 
                 (mycl-util:curry #'string= "^_")
                 (kmrcl:read-file-to-strings file)))
    (alexandria:with-output-to-file 
        (out (format nil "/var/tmp/result/~A" (gensym "foo-")))
      (print out)
      (dolist (line mail)
        (write-line line out)))))
のように書いてみました。
軽く解説すると、
  1. ファイル読み込み1行を要素とするリストにする
  2. PARTITION-IFでリストの中身をデリミタで1ファイル1リストとして分割する
  3. 分割した要素1リストを1ファイルとして出力。名前が被らないようにGENSYMで名前をつける
という風にしてみました。
想像していたより短く書けたので満足です。

>View Comments          このページの上へ戻る

隙があればlispを詰め込んで行きたい (4)

平成22年6月29日(火) 11時09分06秒
区分
lisp
報告者:
chiba

隙があればlispを詰め込んで行きたい日々ですが、今回はWordPressのデータをいじる仕事です。
仕事の内容ですが、WordPressにはカスタムフィールドという便利機能があり、ここに入力したデータが便利にページに表示されます。
このカスタムフィールド内のHTMLを大量に変更することになりました。
具体的には、

select * from wp_postmeta where meta_key = 'こんにちは画像';
で目的の内容が取得できるので、このテキストの内容を置換して同じ場所に戻してやる、ということになります。
SQL文を書いて色々すれば良いのですが、CLSQLを使ってみることにしました。
(asdf:oos 'asdf:load-op :clsql)
(in-package :clsql-user)

(connect '("localhost" "db" "wp" "")
         :database-type :mysql)
  
(execute-command "set character_set_client='utf8'")
(execute-command "set character_set_connection='utf8'")
(execute-command "set character_set_results='utf8'"))

;; DBから読み出してSQLを出力
;; <a href=...>を<a target="_blank" href=...>に変更

(let ((ahref (ppcre:create-scanner "(<a)(\\s+)(href=.*)"))
      (window.open (ppcre:create-scanner "window.open")))
  (alexandria:with-output-to-file (out "/tmp/foo.txt")
    (do-query ((meta_id post_id meta_key meta_value)
               "select * from wp_postmeta where meta_key = 'こんにちは画像';")
      (cond ((ppcre:scan "window.open" meta_value)
             ;; 既にwindow.openで開くような指定があるばあいはスルー
             :nop)
            ('T (let ((new-val
                       (ppcre:regex-replace 
                        ahref
                        meta_value
                        (lambda (match &rest registers)
                          (declare (ignore match))
                          (destructuring-bind (a b c) registers
                            (format nil
                                    "~a target=\"_blank\"~a~a"
                                    a
                                    b
                                    c)))
                        :simple-calls 'T)))
                  (format out
                          "update wp_postmeta set meta_value = '~A' where meta_key = 'こんにちは画像' and post_id = ~A;~%" 
                          new-val
                          post_id))))))) 
内容としては、
  • CLSQLを準備
  • MySQLに接続
  • 文字コードをUTF-8に設定(任意)
  • select文を発行して結果をリストで受けとる
  • リストの内容から、目的の文を取り出しPPCRE(Common LispのPerl正規表現互換パッケージ)で処理
  • ファイルにSQL文を書き出し
今回は目的のSQLサーバーに直接接続するのが面倒だったので、ファイルにSQL文を書き出しましたが、CLSQLで接続して変更ということも勿論できます。
また、"select〜"みたいなことになっていますが、(select [...])のようにも書けるようです。
色々適当ですが、とりあえず目的は達成できました

>View Comments          このページの上へ戻る

隙があればlispを詰め込んで行きたい (3)

平成22年6月22日(火) 10時13分38秒
区分
LISP
報告者:
chiba

隙があればlispを詰め込んで行きたい日々ですが、今回はウェブ上のHTMLの内容確認の仕事です。
具体的には、h1、title、meta keywords、meta descriptionが記述されたCSV形式のファイルがあり、これの内容どおりになっているかをチェックする、という内容。
とりあえず、HTMLのタグの抜き出しには、Drakmaと、closure-htmlを使って、欲しいタグの内容を抜き出す関数を作成してみました。
|((:h1 ...) (:keywords ...) (:description ...) (:title ...))|という変な名前ですが、title keywords description h1を抜き出して返す関数名が思い付かなかったので返す結果の形をそのまま名前にしています。Common Lispは名前に記号も使えるので思考が停止したときに便利ですね。

(defun |((:h1 ...) (:keywords ...) (:description ...) (:title ...))| (url)
  (let* ((page (drakma:http-request url))
         (doc (chtml:parse page (cxml-stp:make-builder)))
         (ans () ))
    (stp:do-recursively (a doc)
      (when (and (typep a 'stp:element)
                 (or (string-equal (stp:local-name a) "title")
                     (string-equal (stp:local-name a) "meta")
                     (string-equal (stp:local-name a) "h1")))
        (let* ((kwd-or-dsc (cond ((string-equal (stp:attribute-value a "name")
                                                "description")
                                  `(:description
                                    ,(stp:attribute-value a "content")))
                                 ((string-equal (stp:attribute-value a "name")
                                                "keywords")
                                  `(:keywords
                                    ,(stp:attribute-value a "content")))))
               (tag (intern (string-upcase (stp:local-name a)) :keyword))
               (svalue (stp:string-value a))
               (tem () ))
          (cond ((and (string= "" svalue)
                      (eq :meta tag))
                 :nop)
                ('T (push svalue tem)
                    (push tag tem)))
          (when kwd-or-dsc
            (push (cadr kwd-or-dsc) tem)
            (push (car kwd-or-dsc) tem))
          (and tem (push tem ans))
          )))
    ans))
この|((:h1 ...) (:keywords ...) (:description ...) (:title ...))|を使って
(progn
  (print '////////////////////////////////////////////////////////////////)
  (dolist (x (fare-csv:read-csv-file "foo.csv"))
    (destructuring-bind (url title kwd dsc h1) x
      (let* ((url (ppcre:regex-replace "://www.example.com" ;CSVファイルのURL
                                       url
                                       "://www.example.net")) ;実際のサーバーのURL
             (q (|((:h1 ...) (:keywords ...) (:description ...) (:title ...))| url)))
        (flet ((*check (var key)
                 (string= var (second (assoc key q)))))
          (cond ((and (*check h1 :h1)
                      (*check kwd :keywords)
                      (*check dsc :description)
                      (*check title :title))
                 :nop)
                ('t
                 (flet ((frob (var key)
                          (list key
                                (if (*check var key)
                                    :ok
                                    (list :ng var (second (assoc key q)))))))
                   (print '*******************************************)
                   (print url)
                   (print (frob h1 :h1))
                   (print (frob kwd :keywords))
                   (print (frob dsc :description))
                   (print (frob title :title))))))))))
のように殴り書きしてみました。キーワードが一致していないと
////////////////////////////////////////////////////////////////
*******************************************
"http://www.example.com/foo/bar" 
(:H1 (:NG "こんにちは" "Routing Error")) 
(:KEYWORDS (:NG "なるほど" NIL)) 
(:DESCRIPTION (:NG "なんのことですか?" NIL)) 
(:TITLE (:NG "mjd!" "Action Controller: Exception caught")) 
...
のような結果がREPLに出てきます。
なんだか長いですけど、Common Lispで書いても実行を確認しながら書けるので、そんなに大変でもありません。

>View Comments          このページの上へ戻る

Google Data APIs Objective-C Client LibraryをiOS SDKで使用するための準備

平成22年6月18日(金) 16時56分26秒
区分
objective-c
報告者:
masuda

こんにちは。masudaです。

今回はGoogle Data APIs Objective-C Client LibraryをiOS SDKで使用するための準備について書きたいと思います。

Google Data APIs Objective-C Client LibraryはGoogleの様々なサービスをObjective-Cから使うことができるライブラリです。
Google Data APIs Objective-C Client Library

あらかじめGData.xcodeprojを、ライブラリを使用したいプロジェクトの「ファイルとグループ」欄に追加しておきましょう。

ライブラリをコンパイルする

コンパイルオプションを指定してコンパイルします。
今回はPicasaサービスとそれに付随してOAuthを使用したので、以下のオプションを指定しました。

-DGDATA_INCLUDE_PHOTOS_SERVICE=1
-DGDATA_INCLUDE_OAUTH=1

iPhone Simulator版とiPhone OS版をReleaseでコンパイルします。

lipoでFat Binaryにする

cd build
lipo -create Release-iphoneos/libGDataTouchStaticLib.a Release-iphonesimulator/libGDataTouchStaticLib.a -output libGDataTouchStaticLib.a

コンパイルしたライブラリをXcodeに追加する

  • Xcodeの「ターゲット」でアプリを指定し右クリックし「情報を見る」を選択。
  • 「一般」 – 「リンク済みライブラリ」の下の「+」をクリック。
  • 「その他を追加…」をクリックし、さきほどコンパイルしたlibGDataTouchStaticLib.aを選択。

コンパイルするための設定を追加する

  • プロジェクトを右クリックし、「情報を見る」を選択。
  • 「ビルド」を選ぶ。
  • 「ヘッダ検索パス」に/usr/include/libxml2を追加。
  • 「ユーザヘッダ検索パス」にライブラリのフォルダを追加(このとき「再帰的」にチェックを入れる)。
  • 「常にユーザパスを検索」にチェックを入れる。
  • 「他のリンカフラグ」に -all_load -ObjC -lxml2 を指定。

参考文献

>View Comments          このページの上へ戻る

隙があればlispを詰め込んで行きたい (2)

平成22年6月12日(土) 00時08分23秒
区分
LISP
報告者:
chiba

こんにちは、chibaです。
今回もネタがないのでLISPネタです。
日常の作業では、テキストファイルを加工することが割とあったりすると思いますが、そういう時には、sedや、awkって便利ですよね。
ちょっとした一行野郎が大活躍、ということは結構あると思います。
自分もそういう一行野郎が好きではあるのですが、いやしかし、隙があればlispを詰め込んで行きたい。
最近もsedを使いたくなるようなHTMLの編集作業に遭遇しました。
「指定したディレクトリ以下に含まれるすべてのHTMLファイルの<div class="foo"から、</div>の間を、用意したテキストファイルの内容と置き換える。」
という仕事です。
sedで一発だろうと思いましたが、とりあえず、Common Lispで書いてみました。

(LOOP :FOR FILE :IN
   (DIRECTORY "/tmp/foo/**/*.html")
   :DO
   (WITH-OPEN-FILE (IN FILE)
     (WITH-OPEN-FILE (OUT (FORMAT NIL "~A.new.html" (PATHNAME FILE))
                          :DIRECTION :OUTPUT
                          :IF-EXISTS :SUPERSEDE
                          :IF-DOES-NOT-EXIST :CREATE)
       (LOOP :WITH OPEN 
          :FOR LINE := (READ-LINE IN NIL NIL) :WHILE LINE
          :DO (PROGN
                (WHEN (SEARCH "
" LINE) (SETQ OPEN 'T)) (COND ((AND OPEN (SEARCH "
" LINE)) (SETQ OPEN NIL) (WITH-OPEN-FILE (IN2 "g000001/foo.html") (LOOP :FOR LINE := (READ-LINE IN2 NIL NIL) :WHILE LINE :DO (WRITE-LINE LINE OUT)))) ((NOT OPEN) (WRITE-LINE LINE OUT))) )))))
気分は一行野郎なのでコードもいきあたりばったりで汚ないです。
これで仕事は片付いたのですが、率直な感想として、sedに比べると書くのがかなりめんどくさいです。(一切ライブラリを使ってないということもありますが…)
こんなにめんどうでは、やっぱりCommon Lispよりsedを使ってしまいます。
いやしかし、こういう道具を沢山書き溜めておいて、必要なときにさっと出せるようになれば、そのうちsedやawkではなく、自然にCommon Lispを使うようになるかもしれません。
ということで、若干無理はあるものの、コードを纒めて次の機会に備えておくことにしました。
(DEFUN SED (START-PAT END-PAT NEW 
            &KEY (IN *STANDARD-INPUT*) (OUT *STANDARD-OUTPUT*))
  (LOOP :WITH OPEN 
        :FOR LINE := (READ-LINE IN NIL NIL) :WHILE LINE
        :DO (PROGN
              (WHEN (SEARCH START-PAT LINE)
                (SETQ OPEN 'T))
              (COND ((AND OPEN (SEARCH END-PAT LINE))
                     (SETQ OPEN NIL)
                     (WRITE-LINE NEW OUT))
                    ((NOT OPEN)
                     (WRITE-LINE LINE OUT))))))

(DEFUN MAP-FILE-INTO (FILES FUNCTION)
  (LET ((TEMPNAME-SUFFIX (GENSYM "TEMP-FILE-")))
    (DOLIST (FILE FILES)
      (LET ((TEMPFILE-NAME (FORMAT NIL "~A_~A" FILE TEMPNAME-SUFFIX)))
        (WITH-OPEN-FILE (IN FILE)
          (WITH-OPEN-FILE (OUT TEMPFILE-NAME
                           :DIRECTION :OUTPUT
                           :IF-EXISTS :SUPERSEDE
                           :IF-DOES-NOT-EXIST :CREATE)
            (FUNCALL FUNCTION IN OUT)))
        (RENAME-FILE TEMPFILE-NAME FILE)))))
この2つを使えば、今回の仕事は、
(MAP-FILE-INTO (DIRECTORY "**/*.html")
               (LAMBDA (IN OUT)
                 (SED "<div class='foo'>"
                      "</div>"
                      (KL:READ-FILE-TO-STRING "foo.txt")
                      :IN IN
                      :OUT OUT)))
のように書けます。
いまのところぱっとしませんが、そのうちCommon Lispだけでテキスト仕事は片付けられるようになることを目指します!

>View Comments          このページの上へ戻る

Lisp on Rails 第9回 〜 ビュー

平成22年6月5日(土) 17時15分45秒
区分
Lisp on Rails
報告者:
tahara

こんにちは!! tahara です。 Objective-C づけになり、すっかりこぶさたしておりましたが Lisp on Rails 第9回です!

今回はビューです。 Common Lisp で ERB 相当を実装します。 Common Lisp で実装するからにはリードテーブルを使い、 HTML ファイルを関数にコンパイルしたいと思います。

HTML ファイルを関数にコンパイルソースは http://github.com/quek/lisp-on-rails/blob/master/action-pack/ecl.lisp です。

ところどころ説明させていただきます。 html-defun-readtable ではビューファイルの最初の1文字をマクロキャラクタにして、 先頭に (in-package :xxxx) を追加し、全体を (defun xxxx () ...) でくるむようにしています。 動的にリーダをカスタマイズしているのです。 これでビューファイルを1つの関数として読み込むことができるようになります。

(defun html-defun-readtable (fname pathspec)
  (let ((*readtable* (basic-readtable)))
    (set-macro-character
     (first-char pathspec)
     (let ((in-package t))
       (lambda (stream char)
         (unread-char char stream)
         (print
          (if in-package
              (progn
                (setf in-package nil)
                `(in-package ,(package-name action-controller:*app-package*)))
              `(defun ,fname ()
                 ,(body-code stream char)))))))
    *readtable*))

Rails ではコントローラからビューへの値の受け渡しは @foo のようなインスタンス変数が使われます。 それに対応するためビューファイルの中に @ で始まるシンボルがあれば、 コントローラのスロット値へのアクセスに変換するシンボルマクロを定義します。

(defun body-code (stream char)
  (walk-body-code (read-body-code stream char)))

(defun read-body-code (stream char)
  (let ((*readtable* (make-html-readtable char)))
    (loop for x = (read stream nil stream t)
          until (eq x stream)
          collect x)))

(defun walk-body-code (code)
  `(symbol-macrolet
       ,(series:collect
            (series:mapping
             ((x (series:choose-if (q:^ q:symbol-head-p _ "@")
                                   (series:scan-lists-of-lists-fringe code))))
             `(,x (slot-value action-controller:*controller*
                              ',(intern (subseq (symbol-name x) 1)
                                        action-controller:*app-package*)))))
     ,@code))

そんなこんなで、なんとかモデル、コントローラ、ビューが繋がりました。

モデル

(in-package :blog)

(def-record post
  (:has-many comments))

(def-record comment
  (:belongs-to post))

コントローラ。生の defclass です。

(in-package :blog)

(defclass top-controller (application-controller)
  ((message)
   (posts)))

(defmethod index ((self top-controller))
  (with-slots (message posts) self
    (setf message "まみむめも♪"
          posts (all post))))

ビュー。HTML タグと loop が混在するのもまた一興ですね。

<!doctype html>
<html>
  <head>
    <meta charset="UTF-8">
    <title>ブログ</title>
  </head>
  <body>
    <h1><%= @message %></h1>
    <h2>投稿を loop で表示する</h2>
    <ul><% (loop for post in @posts for comments = (comments-of post) do %>
      <li><%= (content-of post) %> -- <%= (name-of post) %></li>
      <% if comments do %>
      <ul><% (loop for comment in comments do %>
        <li><%= (body-of comment) %> -- <%= (commenter-of comment) %></li><% ) %>
      </ul><% ) %>
    </ul>
  </body>
</html>

コントローラは次のように書けるようにすると、それっぽい気もしますが、いまはまだ書けません。

(def-controller top (application)
  (def-action index
      (setf @message "まみむめも♪"
            @posts (all post)))
  (def-action foo
      (setf @essage "foo")))

以上、まとめますと 「リーダをいじれる言語は素敵ですね!」 でした。

ソースはこちらから http://github.com/quek/lisp-on-rails

第10回につづきます。

>View Comments          このページの上へ戻る

macroexpandでHTMLを書く

平成22年6月2日(水) 16時22分57秒
区分
Emacs
報告者:
chiba

こんにちは、chibaです。
今回もネタがないのでEmacsネタです。
Dylanのマクロについての文献:
http://people.csail.mit.edu/jrb/Projects/dexprs.pdf を読んでいて、XMLや、LISPは、Skeleton Syntax Tree Representationsに分類されるというのを読んで、そうか、HTMLも簡単にマクロ書けたりするのかもと思ったのでEmacs Lispでちょっと試してみました。

(defun split-id-or-class (string)
  (let ((elts (split-string string "[\\.#]")))
    (if (cdr elts)
        elts
        string)))

(defmacro defhtmltag (tag)
  (let* ((tag-str (symbol-name tag))
         (tags (split-string tag-str "[\\.#]"))
         (tag-name (car tags))
         (attr (cadr tags)))
    `(defmacro ,tag (&rest body)
       `(list ,(concat
                "<"
                ,tag-name 
                (apply #'concat 
                       (cond ((find ?. ,tag-str)
                              (list " class=\"" ,attr "\""))
                             ((find ?# ,tag-str)
                              (list " id=\"" ,attr "\""))
                             ('T (list ""))))
                ">")
              ,@body
              ,(concat "</" ,tag-name ">")))))

(defun flatten (lis)
  (cond ((atom lis) lis)
        ((listp (car lis))
         (append (flatten (car lis)) (flatten (cdr lis))))
        (t (append (list (car lis)) (flatten (cdr lis))))))

(defmacro with-html-output-to-string (&rest body)
  `(reduce (lambda (x y) (format "%s%s" x y))
           (flatten (list ,@body))))
使い方としては、
(defhtmltag li.foo)
のようにすると、
(list "<li>" "こんにちは" "</li>")
のようなものにマクロが展開されるので、リストの入れ子を平坦にして一つの文字列に繋げれば完成です。
ちょっと込み入ったところだと
(defhtmltag table)
(defhtmltag tbody)
(defhtmltag td)
(defhtmltag tr)

(defun list-to-table (list)
  (table 
   (tbody
    (mapcar (lambda (x)
              (tr
               (mapcar (lambda (y) (td y))
                       x)))
            list))))

(insert
 (with-html-output-to-string
  (list-to-table '((foo bar baz quux)
                   (1 2 3 4)
                   (z z z z)))))
;=> <li class="foo"><li class="foo"><li class="foo"><li class="foo">foo</li><li class="foo">bar</li><li class="foo">baz</li><li class="foo">quux</li></li><li class="foo"><li class="foo">1</li><li class="foo">2</li><li class="foo">3</li><li class="foo">4</li></li><li class="foo"><li class="foo">z</li><li class="foo">z</li><li class="foo">z</li><li class="foo">z</li></li></li></li>
ということもできます。
ちなみに、マクロ展開でHTML書くって新しいかも!と思いましたが、別にマクロにしなくても関数でも書けることに今気付きました…
とりあえず、もし、HTMLにマクロがあったとしたらLISPと似た感じになるのかなあという印象は持ちました。

>View Comments          このページの上へ戻る

隙があればlispを詰め込んで行きたい

平成22年5月25日(火) 16時09分08秒
区分
Emacs
報告者:
chiba

こんにちは、chibaです。
今回もネタがないのでEmacsネタです。 日常のちょっとしたテキストの編集作業なのですが、

app/views/top/foo.html.erb
app/views/shared/_foo.html.erb
app/views/shared/foo.html.erb
app/views/top/_foo.html.erb
app/views/shared/foo.html.erb
app/views/layouts/application.html.erb
config/links/foo.html.erb
public/images/shared/foo.jpg
のようなテキストをスペース区切りの一行にまとめる必要に迫られました。
ここはEmacsで、改行をスペースに置換してやれば良いんじゃないかと思いますが、いや、ここは踏ん張ってlispを書いてゆきたいと思い、
(mapconcat (lambda (x) (format "%s" x))
'(
app/views/top/foo.html.erb
app/views/shared/_foo.html.erb
app/views/shared/foo.html.erb
app/views/top/_foo.html.erb
app/views/shared/foo.html.erb
app/views/layouts/application.html.erb
config/links/foo.html.erb
public/images/shared/foo.jpg
)
" ") 
のようなものを書いて、式のにカーソルを持っていって、C-u C-x C-eです。
そうすると、式の後ろに
=> "app/views/top/foo.html.erb app/views/shared/_foo.html.erb app/views/shared/foo.html.erb app/views/top/_foo.html.erb app/views/shared/foo.html.erb app/views/layouts/application.html.erb config/links/foo.html.erb public/images/shared/foo.jpg"
のようなものが挿入されます。lambdaって書けて良かったですよね。
次に、
A    app/views/top/foo.html.erb
M    app/views/shared/_foo.html.erb
M    app/views/shared/foo.html.erb
M    app/views/top/_foo.html.erb
?    app/views/shared/foo.html.erb
M    app/views/layouts/application.html.erb
A    config/links/foo.html.erb
M    public/images/shared/foo.jpg
のようなテキストでファイル名の部分だけ、一行にスペース区切りでつなげる必要に迫られました。
ここは、Emacsで、kill-rectangleでしょうか。いや、ここは踏ん張ってlispを書いてゆきたいと思い、
(apply #'concat
(loop for (x y) on 
'(
A    app/views/top/foo.html.erb
M    app/views/shared/_foo.html.erb
M    app/views/shared/foo.html.erb
M    app/views/top/_foo.html.erb
?    app/views/shared/foo.html.erb
M    app/views/layouts/application.html.erb
A    config/links/foo.html.erb
M    public/images/shared/foo.jpg
)
by #'cddr
collect (format "%s " y))
)
のようなものを書き、上と同じく、eval-last-sexp すると
"app/views/top/foo.html.erb app/views/shared/_foo.html.erb app/views/shared/foo.html.erb app/views/top/_foo.html.erb app/views/shared/foo.html.erb app/views/layouts/application.html.erb config/links/foo.html.erb public/images/shared/foo.jpg "
という文字列が得られます。
なるほど、これは、shell-command-on-regionで、awk '{if($1=="M"){printf("%s ", $2);}}'とかしている場合ではないですね。
Emacs便利です。

>View Comments          このページの上へ戻る

EmacsでGoogle Chart

平成22年5月20日(木) 11時19分38秒
区分
Emacs
報告者:
chiba

こんにちは、chibaです。
今回もネタがないのでEmacsで遊んでいたネタを。
Googleは、便利にグラフを書けるAPIであるGoogle Chart APIというのを提供しています。
URLを指定するだけで、グラフが書けたり、QRコードが書けたり色々便利なものですが、数値のリストからスタックされた棒グラフを書いてみようと思ったので、ちょっと遊んでみました。

(defun* gchart-stack-bar-chart-url (data &key (style :horizontal))
  (let ((len (length data)))
    (concat
     "http://chart.apis.google.com/chart?"
     "cht=b" (case style
               (:horizontal "hs")
               (otherwise "vs")) 
     "&chd=t:"
     (let* ((totals (mapcar (lambda (x)
                              (apply #'+ x))
                            data))
            (max (apply #'max totals))
            (to-string (lambda (x) (format "%s" x))))
       (mapconcat 
        to-string
        (mapcar 
         (lambda (x)
           (mapconcat
            to-string x ","))
         (apply #'mapcar*
                #'list
                (mapcar
                 (lambda (x) 
                   (mapcar
                    (lambda (y)
                      (format "%.1f"
                              (* (/ 100 max) y)))
                    x))
                 data)))
        "|"))
     "&chco=ff0000,00ff00,0000ff"
     "&chs=350x"
     (int-to-string (* len 28)))))
(let ((data '((10 20 30)
              (3 50 1)
              (1 2 3)
              (33 33 33)
              (5 6 7)
              (33 33 33)
              (3 3 9))))
  (browse-url (gchart-stack-bar-chart-url data)))
これで、

のようなグラフが書けます。
タイトルも付けられるようですが、飽きてしまいました
Emacs便利です。

>View Comments          このページの上へ戻る

技師部隊からの
お知らせ

インフルエンザに気をつけて頑張っています

本頁の来客数
五万三千七百七十名

メンバー一覧

アクトインディ技師部隊員名簿

アクトインディへ

投稿する

カテゴリー

アクトインディ

aaaa