Archive for the ‘ruby’ Category

    はてなブックマーク - rails と jquery treeview でディレクトリツリーをらくらく実装する
    このエントリーをはてなブックマークに追加

    ども、Fusic 河野です。
    だいぶ久しぶりですね。

    今度の rubykaigi2011 に参加させて頂くこととなりました!
    会場で見かけたら、ぜひお気軽にお声をかけてくださいませ。

    [rubykaigi2011]
    rubykaigi.org/2011/

    それと、もう一つ告知です。
    来る7月28日(木)、福岡で活動している Androidの会福岡支部九州GTUG
    HTML5+α福岡iDevQ、そして、AppleKnight の4つのコミュニティが
    福岡県Ruby・コンテンツ産業振興センターに集結し、同時に勉強会、懇親会を行います。

    ご興味のある方は、ぜひともご参加ください!!
    参加登録は下記のリンク先より facebook にて参加表明をお願いいたします。

    [futuresync]
    futuresync.jp/

    さて、わたくし、前回の記事でも書きましたが、主に ruby on rails を
    やっており、ZENPRE の iPhone アプリを担当させていただいてます。

    今回とあるお客様の rails 案件にて、ツリービューを作る必要がありました。
    一から作ろうかと思っていたところ、jquery ui の treeview を見つけ、
    使ってみたので、簡単なサンプルとともにご紹介をします。

    いやー、それにしてもコレ凄く便利です!!

    まずは、バージョン確認です。

    $ ruby -v
    ruby 1.9.2p0 (2010-08-18 revision 29036) [i686-linux]
     
    $ rails -v
    Rails 3.0.9

    rails のプロジェクトを作成します。

    $ rails new treeview_test
    $ cd treeview_test/
    $ rm public/index.html

    prototype.js を削除ります。
    サヨナラ〜、お元気で〜♪

    $ vim Gemfile
     
    gem 'jquery-rails'
     
    $ rails g jquery:install

    ツリービューを表示するためのコントローラを作成します。

    $ rails g controller Treeviews index
    $ vim routes.rb
     
      root :to => "treeviews#index"

    そして、本日の主役のご登場!
    jquery treeview を下記よりダウンロードします。
    bassistance.de/jquery-plugins/jquery-plugin-treeview/

    js や css、jpg などを rails の各ディレクトリにコピーします。
    画像は、images に “treeview” というディレクトリを
    作成し、そこにコピーしています。
    そして、jquery.treeview.css に記載されている画像のパスを
    ちょっと変更します。

     images/xxxx → ../images/treeview/

    treeview を取り込みます。

    $ vim app/views/layouts/application.html.erb
     
    <!DOCTYPE html>
    <html>
    <head>
      <title>TreeviewTest</title>
      <%= stylesheet_link_tag :all %>
      <%= javascript_include_tag :defaults %>
      <%= javascript_include_tag 'jquery.treeview' %>
      <%= javascript_include_tag 'jquery.treeview.edit' %>
      <%= javascript_include_tag 'jquery.treeview.async' %>
      <%= csrf_meta_tag %>
    </head>
    <body>
     
    <%= yield %>
     
    </body>
    </html>

    ツリービューを表示するため view を書き換えます。

    $ vim app/views/treeviews/index.html.erb
     
    <script type="text/javascript">
    $(document).ready(function(){
      $("#example").treeview({});
    });
    </script>
     
    <ul id="example" class="filetree">
    	<li><span class="folder">Folder 1</span>
    		<ul>
    			<li><span class="file">Item 1.1</span></li>
    		</ul>
    	</li>
    	<li><span class="folder">Folder 2</span>
    		<ul>
    			<li><span class="folder">Subfolder 2.1</span>
    				<ul>
    					<li><span class="file">File 2.1.1</span></li>
    					<li><span class="file">File 2.1.2</span></li>
    				</ul>
    			</li>
    			<li><span class="file">File 2.2</span></li>
    		</ul>
    	</li>
    	<li class="closed"><span class="folder">Folder 3 (closed at start)</span>
    		<ul>
    			<li><span class="file">File 3.1</span></li>
    		</ul>
    	</li>
    	<li><span class="file">File 4</span></li>
    </ul>

    完成です!!
    いやー、ほんとめちゃくちゃ簡単ですね!!
    まー、これだけだと寂しいのとせっかくの rails も意味がないので
    rails にディレクトリ一覧を返す関数を作成し、ajax 使って
    ディレクトリ階層を動的に表示できるように変更します。
    おー!rails っぽいですね!

    routes にパスを追加します。

    $ vim config/routes</code>
     
      match '/treeviews/get_dir_list' => 'treeviews#get_dir_list'

    さっきの view を書き換えます。

    $ vim app/views/treeviews/index.html.erb
     
    <script type="text/javascript">
    $(document).ready(function(){
      $("#example").treeview({});
      $("#example2").treeview({
    		url: "<%= treeviews_get_dir_list_path %>"
      });
    });
    </script>
     
    <h4>通常のツリービュー表示</h4>
     
    <ul id="example" class="filetree">
    	<li><span class="folder">Folder 1</span>
    		<ul>
    			<li><span class="file">Item 1.1</span></li>
    		</ul>
    	</li>
    	<li><span class="folder">Folder 2</span>
    		<ul>
    			<li><span class="folder">Subfolder 2.1</span>
    				<ul>
    					<li><span class="file">File 2.1.1</span></li>
    					<li><span class="file">File 2.1.2</span></li>
    				</ul>
    			</li>
    			<li><span class="file">File 2.2</span></li>
    		</ul>
    	</li>
    	<li class="closed"><span class="folder">Folder 3 (closed at start)</span>
    		<ul>
    			<li><span class="file">File 3.1</span></li>
    		</ul>
    	</li>
    	<li><span class="file">File 4</span></li>
    </ul>
     
     
    <h4>sync させたツリービュー表示</h4>
    <ul id="example2" class="filetree"></ul>

    treeview の url に先ほど routes に追加したパスを記載します。

    controller もゴニョゴニョと追加します。

    $ vim app/controllers/treeviews_controller.rb
    class TreeviewsController < ApplicationController
     
      def index
      end
     
      def get_dir_list
        root_flg = (params["root"] == "source") ? true : false
        path = "./"
        path = params["root"] unless root_flg
        arry = []
        d = dir_list(path, arry)
        render :json => arry || [], :layout => false
      end
     
    protected
      def dir_list(path, arry=[], root_flg=false)
        Dir.glob("#{path}/*").map do |d|
          classes = "file"
          classes = "folder" if FileTest.directory?(d)
          hasChildren = (FileTest.directory?(d)) ? true : false
        	h = {"id" => d, "text" => File.basename(d), "path" => File.dirname(d), "expanded" => false, "classes" => classes, "hasChildren" => hasChildren, "children" => []}
          arry << h
        end
        arry
      end
     
    end

    dir_list() の関数でディレクトリ情報の hash を作成し、view 側に json で返します。
    返された json を treeview 側が解析してツリービューを表示します。
    返す hash の id のところがミソでして、ここにパスを記載していると
    treeview にてディレクトリを選択された場合、params["root"] に選択された
    ところのパスが渡されます。
    一番上のルートの場合には、params["root"] には、”source” という文字が渡ってきます。

    今回もいつもの如くソースは github にあげてます。
    また、サンプルをここに置いてますので実際の動作を確認できます。
    今回始めて dotcloud を使ってみました。
    これまためちゃくちゃ便利ですねー!
    dotcloud で rails を動かす方法とかは、また次回にでも、デモ。

    それでは、良い rails ライフを〜。

    [ソース]
    github.com/sora0513/TreeViewTest

    [デモ]
    8befe980.dotcloud.com/

      はてなブックマーク - Ustream Data APIの使い方
      このエントリーをはてなブックマークに追加

      ダーツの、Dartsliveのレーティングは9、PHOENIXのレーティングは12、の山本です。
      親にはゴルフをしろと言われてますが、休日は打ちっぱなしより投げっぱなしです。
      お父さん、すんません。

      さて、タイトルが、前の杉本によるエントリーと似ていてすみません。
      が、似て非なるもののご紹介です。

      Ustream Data APIは、Flash Client APIとは異なり、動画に関する情報やユーザーに関する情報がXML形式やJSONなど、計4種類の形式で受け取れますので、どんな言語にも組み込むことができます。PHP形式もありますので、PHPで組み込む場合はすごく親和性が高いのではないのでしょうか。

      API key の発行

      developer.ustream.tvから、開発者用のアカウントを作成する必要があります。
      登録するとAPI Keyが発行されます。

      ユーザー情報を取ってみる

      基本中の基本といえば、ユーザーの情報を取ってくることでしょうか。
      そのアドレスは以下のようになります。

      http://api.ustream.tv/xml/user/ya_ma23/getInfo?key=APIKEY

      ROOTのURL直下にある「xml」は、xml形式で返して表示することを示します。ここを、「json」「php」「html」のいずれかに変えることができます。

      「ya_ma23」に当たるところがユーザー名を指定します。
      そして、APIKEYの部分に、取得したAPIキーを入力します。

      これは私個人の情報になりますが、XML形式だとこんなふうに返ります。

      <xml>
        <results>
        <id>1134298</id>
        <userName>ya_ma23</userName>
        <registeredAt>2009-04-07</registeredAt>
        <url>http://www.ustream.tv/user/ya_ma23</url>
        <gender>male</gender>
        <website></website>
        <about></about>
        <imageUrl></imageUrl>
        <rating>0.000</rating>
        <numberOf>
          <comments>0</comments>
          <friends>1</friends>
        </numberOf>
        </results>
        <msg></msg>
        <error></error>
        <processTime>TRUE</processTime>
        <version>mashery-r10</version>
      </xml>

      テスト用アカウントのため、何とも寂しい情報しかありませんが、プロフィール情報等が取得できます。

      番組に関する情報を取得する

      番組に関する情報も取得できます。チャンネル自体の情報や、そのチャンネルの埋め込みタグや、投稿されたコメントなどを取得できます。
      ごく基本的な情報の取得のために「getInfo」メソッドがあります。

      http://api.ustream.tv/xml/channel/ya_ma/getInfo?key=APIKEY

      「ya_ma」の部分にチャンネル名を指定します。
      これで取得できる情報をここに書くとすんごく長くなってしまうので割愛しますが、
      このチャンネルを作ったユーザーや、ブラウザで見るためのURL、いつ作られたか、いつ配信されたか、視聴者数、埋め込みタグ、といった様々な情報が得られます。

      録画された全てのチャンネルを取得する

      Ustreamはライブ配信した映像を録画することができます。
      で、その録画したチャンネルのリストというものも、APIで取得できます。
      そのメソッドが「listAllVideos」です。

      http://api.ustream.tv/xml/channel/ya_ma/listAllVideos?key=APIKEY

      ZENPREでは、一度配信したプレゼンをあとでいつでも見れるようにする、同期録画機能を備えてますが、
      その設定画面にて、このAPIを使用しています。
      録画番組の一覧をAPIから取得し、ユーザーはただ選択するだけで済むように負担を軽減しています。

      おわり

      Ustreamは、Flash Client APIと合わせても、結構豊富にAPIを提供しています。
      ただ、そこまで普及していないのかな、という印象です。
      OAuthに対応して、Ustreamサイト以外からでも気軽に視聴や配信ができればなと思います。

      イベント、勉強会、セミナー、討論会、個人的に行なっている活動、など、多くの人に見てもらいたい活動を行うときに、
      テレビやラジオ、新聞等で宣伝するだけでなく、こういったネットでの配信も活用してほしいなあと、いち技術者は思う次第です。

        はてなブックマーク - [rails] config.cache_classes = trueでも特定のクラスだけアクセスの度にloadする
        このエントリーをはてなブックマークに追加

        Ruby技術者認定試験に合格してほっとして倒れそうな山本ですこんにちは。

        (Rails2.xの話になります)
        さて、Railsでは起動時に環境の指定ができますが、productionモードの時はある程度のクラスがキャッシュ化されて読み込みが早くなります。
        通常であれば、config/environments/production.rbに以下の記述があると思います。

        config.cache_classes = true

        これにより、クラスは起動時の一回のみの読み込みとなり高速化が図られてます。

        話はちょっと変わりますが、modelクラスでバリデーションを定義するとき、メッセージを個別に指定したい場合は以下のように書くと思います。

        validates_format_of :hoge, :with => /moge/,  :message => "mogeと書いてください。"

        この時、以下のように

        validates_format_of :hoge, :with => /moge/,  :message => "(#{Time.now}に発生)mogeと書いてください。"

        にして、クラスが読まれたときの時間を表示するといった場合、
        developmentモードでは、その時の時間が表示されるのですが、productionモードでは初回の起動時の時間が表示されたまま、変わることはありません。
        これはproductionモードだとmodelクラスが一度しか読まれないからです。

        そもそも、productionモードでは何が起こっているのか

        ひとえにproductionモードと言っても、ウラでは何が起きているのか。
        その情報を見つけることができなかったため、ソースを追ってみました。

        config.cache_classesは、どこで何らかの処理をされているのか。
        config/environments~以下の各クラスで定義されている、
        config.cache_classesは、どこで判定がされ処理されているのでしょうか、ってことで探してみると以下の場所にありました。

        # Rails2.3.5 rails-2.3.5/lib/initializer.rb
        def initialize_dependency_mechanism
          ActiveSupport::Dependencies.mechanism = configuration.cache_classes ? :require : :load
        end

        ここで、クラスを読み込むために、requireを使うかloadを使うかのか判定を行っています。
        requireは一度しか読み込みませんが、loadは何回でも読みます。

        特定のクラスだけ読み込む術はないものか

        “指定したクラスだけは、load読み込みにする” なんてのができればいいのでしょうが、一括で起動時にrequireしてしまっているので、どうしようもないのかなとか思いつつ、いろいろやってみました。
        ActiveSupportのDependenciesクラスが色々と鍵を握ってるみたいです。

        まず、controllerにbefore_filterを仕込みその中で、以下のように書いてみました。

        ActiveSupport::Dependencies.load_file("Modelクラスへのパス")

        いかにもなメソッドです。loadし直してくれるのではと、やってみましたが表示時間は変わらず結果は×。

        次に、

        ActiveSupport::Dependencies.load_with_new_constant_marking("Modelクラスへのパス")

        これもいかにもな名前です。が、あえなく×。

        で、

        modelクラスに "unloadable" と宣言する

        ついに見つけました! これを書けば起動時にこのクラスが読まれることはない! はず!

        と意気揚々と試しましたが、×。

        こうなったらと、手当たり次第作戦。
        すると、以下のメソッドをbefore_filterに仕込むと、アクセスの度に、表示する時間が変わりました。

        ActiveSupport::Dependencies.remove_unloadable_constants!

        ついにやったと思いつつ、ソースを見てみます。

        def remove_unloadable_constants!
          autoloaded_constants.each { |const| remove_constant const }
          autoloaded_constants.clear
          explicitly_unloadable_constants.each { |const| remove_constant const }
        end

        どうやら、起動時に読み込んだクラスをリムーブしてるみたいですね。
        これ自体を読んだのであれば、全てが消えてしまう感じでこわいので、ここで使われているremove_constantメソッドだけを使って、該当のクラスを読み込み直しました。

        ActiveSupport::Dependencies.remove_constant "Modelのクラス名"
        load "Modelクラスへのパス"

        すると、晴れてアクセスする度に表示する時間が変わりました。
        #:nodoc:となっていますのでこのメソッドに解説がないのですが、このメソッドは以下のとおり。

        def remove_constant(const)
          return false unless qualified_const_defined? const
         
          const = $1 if /\A::(.*)\Z/ =~ const.to_s
          names = const.to_s.split('::')
          if names.size == 1 # It's under Object
            parent = Object
          else
            parent = (names[0..-2] * '::').constantize
          end
         
          log "removing constant #{const}"
          parent.instance_eval { remove_const names.last }
          return true
        end

        引数で渡したクラス名をチェックしたのち、
        remove_constという記述があり、クラスの定数を削除しているようです。

        一応、他のページ表示や処理にも問題ないようです。

        最適解か

        バリデーションのメッセージを動的に変えたい、という動機からここまでの調査に至りましたが、もっと簡単な方法があるのかもしれません。
        そもそも、どうしてもと言うならcontroller側でエラーメッセージを定義してもいいでしょうし。

        いかんともしがたい理由でmodelをアクセスの度にloadしたい方はご参考に~。