SinatraおよびDataMapperに基づくホームファイルホスティング。 パート2-高度な機能。

最初の記事では、 SinatraとDataMapperを使用して簡単なWebアプリケーションを作成する方法について説明しました。 今回は、多くの新機能を追加し、コード全体を改善します。



事前準備



このセクションでは、今後のすべての研究のステージを設定します。



そのため、前回、次のファイル構造を持つアプリケーションを取得しました。



 /
   myapp.rb
  ビュー/
     list.erb
  ファイル/




この場合、すべてのコードは単一のファイルmyapp.rbにあります。 私の意見では、これは非常に不便でandいため、今回はフォルダとファイルを作成することから始めます。フォルダとファイルのいくつかは、ストーリーの過程で役立つでしょう。 私は次の構造を得ました:



  /
  ファイル/
   lib /
    タスク/
  マニュアル/
  公開/
  テスト/
  ビュー/




filesフォルダーには、外部からアクセスできるファイルがあります。 libフォルダーにはライブラリーがあります。 tasksフォルダーにはrake-tasksが含まれます。 手動フォルダーに、Webインターフェースをバイパスしてファイルを配置します。 パブリック-ブラウザに直接与えられたファイルのリポジトリ。 テストでは、奇妙なことにテストを入れました。 最後に、ビューはすでによく知られています-HTMLページを生成するためのテンプレートがあります(これまでのところ1つしかありません)。



次に、Sinatraをローカルに(アプリケーションフォルダーに直接)インストールします。これにより、Sinatra gemのインストールについて心配する必要がなくなります。 これを行うには、gitリポジトリからSinatraコードをダウンロードします。 これを行うには、 gitをインストールする必要があります。



  cd lib
 git clone git://github.com/bmizerany/sinatra.git
 rm -rfシナトラ/ .git
 rmシナトラ/ .gitignore
 cd ..




アプリケーションのメインコードは、先頭を次のように変更した後、init.rbファイルに配置します。



  「rubygems」が必要
 require File.expand_path(File.dirname(__ FILE__)+ '/ lib / sinatra / lib / sinatra')
 require File.expand_path(File.dirname(__ FILE__)+ '/ lib / config')
 「ftools」が必要
 「dm-core」が必要
 「dm-validations」が必要
 「dm-timestamps」が必要




このコードは、Sinatraとconfig.rb構成ファイルをlibフォルダーから接続します。これを作成し、次のコードを記述します(これまでのところ何もしません)。



 設定する
   #Sinatraの構成オプションがあります
終わり 




次に、rakefileが必要です。これはrakeユーティリティ用の特別なファイルで、タスクを取得する場所を彼女に伝えます。 ルートディレクトリにあるRakefileというファイル(拡張子なし)には、次のものが含まれている必要があります。



  「レーキ」が必要
 'rake / testtask'が必要
 'rake / rdoctask'が必要
 
 Dir ["#{File.dirname(__ FILE __)} / lib / tasks / ** / *。Rake"]。Sort.each {| ext |  extをロード} 




その中で、単にlib / tasksディレクトリとそのサブディレクトリからすべてのrakeファイルをロードします(これには正規表現/**/*.rakeが使用されます)。



コードを共有する



1つのファイルにあるアプリケーションコードは不便であるだけでなく、間違っていることもあります:)したがって、まずはStoredFileクラスを別のファイルに配置することから始めます。 libフォルダーに、次の内容のファイルstored_file.rbが表示されます。



  「dm-core」が必要
 「dm-validations」が必要
 「dm-timestamps」が必要

 DataMapper.setup(:default、 "sqlite3://#{Dir.pwd} /files.sqlite3")

 StoredFileクラス
   DataMapperを含める::リソース

  プロパティ:id、Integer ,: serial => true#主シリアルキー
  プロパティ:ファイル名、文字列、nullable => false#nullにはできません
  プロパティ:created_at、DateTime
  
   default_scope(:default).update(:order => [:created_at.desc])
終わり

 DataMapper.auto_upgrade!




そして、init.rbの無数のrequireの終わりに、次の行を追加します



  'lib / stored_file'が必要 




アプリケーションの機能は1つのiotaを変更していませんが、今ではもう少しまともに見えます。



ビバ・ラ・ハムル!



アプリケーションの最初のバージョンでは、Erb言語(Embedded RuBy)を使用してHTMLページを生成しました。これは、Rubyコードフラグメントを含むHTMLコードです。 このアプローチはRailsでは伝統的ですが、私たちはわずかな決定に取り組んでいます:)この点に関して、テンプレートの言語としてHamlを使用することにしました。



Hamlは、テンプレートの主な神として、美しさを説くマークアップ言語です。 確かに、私の意見では、Hamlコードはきれいに見えます(そして、彼らはそれがより速く動くと言います)。 Hamlのもう1つの重要なプロパティは、タグを閉じないことを許可しないことです(明示的な形式でHTMLタグをまったく使用しないため)



インストールは簡単です:



  sudo gem install haml 




ビューディレクトリにlist.hamlファイルを作成して、記入を始めましょう。 Hamlは、ブロック区切り記号としてコード内のパディングを使用するという点でPythonに似ています(HTMLでは終了タグが使用され、Erbではendキーワードが使用されます)。 HTMLは



  <div>
 <span>一部のテキスト</ span>
 </ div>
 <ul>
 <li>アイテム1 </ li>
 <li>アイテム2 </ li>
 </ ul> 




Hamlでは、次のようになります。



  %div
   %span一部のテキスト
 %ul
   %liアイテム1
   %liアイテム2 




ご覧のとおり、はるかに短いです。



便利な構文について少し説明します。



  %TAG 


タグ<TAG />または<TAG> </ TAG>を作成します-タグに応じて(hrでは最初のオプションが選択され、aでは2番目のオプションが選択されます)



 タグコンテンツの割合 


<TAG> CONTENT </ TAG>タグを作成します。



 %TAG
  内容


<tag> CONTENT <tag>に変わります。 同時に、CONTENTには%演算子と他の演算子も含まれる場合があります。



使用される属性構文は、Rubyハッシュに使用されるものと同じ構文です。



  %span {:class => "header" ,: id => "news_135" ,: style => "float:left"}ムースは3キログラムのドッグフードを食べてbarえました! 


<span class = "header" id = "news_135" style = "float:left">ムースは3キログラムのドッグフードを食べてandえました!</ span>



実際、クラスとIDを指定するためのより単純な構文(CSSを非常に連想させる)があります。

  %span.header#news_135別の黄色の見出し 




目的のクラスでdivを作成するには、さらに簡単です:

  .content =%div.content =%div {:class => "content"} = <div class = "content"> 




ここでRubyを取得しましょう。 コード行(Erbの<%%>に類似)を実行するには、その前に「-」記号を付けます。

  -str = @ my_object.get_some_string 




結果を文字列に変換するには、「=」演算子を使用します。

  -現在= Time.now
 =今 


現在の時刻を表示します。



もちろん、タグ演算子で= completeを使用できます。

  %span#current_time = Time.now 




そして、Hamlテンプレートを記述するために知っておく必要がある最後のことは、演算子「!!!」です。 デフォルトでは、DOCTYPE Transitionalを作成します。 そして、私たちは彼からそれ以上を必要としません。



テンプレートを書く



ファイルのリストと新しいファイルのアップロードフォームを表示する次のテンプレートを入手しました。

 !!!
 %html {:xmlns => "http://www.w3.org/1999/xhtml"}
   %頭
     %title File Wash Name Me
     %meta {: "http-equiv" => "content-type" ,: content => "text / html; charset = UTF-8"}
   %体
     .main
		 -if @ files.empty?
		   %h1ファイルがありません。
		 -その他
		   .list
		     %h1ファイルリスト
		     %table {:cellspacing => 0}
		       %tr
		         %thファイル
		         %thがアップロードされました
		         %th削除
		       -@ files.each do | file |
		         %tr
		           %td.filename
		             %a {:href => "/#{file.id}" ,:タイトル=> file.filename} = file.filename
		           %td.created_at = file.created_at.strftime( "%d%b")
		           %td.delete
		             %a {:href => "/#{file.idasket/delete" ,: title => "削除"}削除
		 .upload
		   %h1追加
		   %form {:name => "new_file" ,: enctype => "multipart / form-data" ,: method => "post" ,: action => "/"}
		     %input {:name => "file" ,: type => "file"}
		     %br
		     %input {:type => "submit" ,: value => "Download"}




結果のテンプレートは非常にエレガントであることに注意するしかありません(特に、Haml構文を強調したテキストエディターを使用している場合)。



レイアウトをテンプレートから分離する



アプリケーションに他のビューも必要になる可能性があることを想像してみましょう(そして、それらが必要になります)-「!!!」、「%html」などでそれぞれに共通のヘッダーを書き込みません。 この問題を解決するために、SinatraはRailsと同様にレイアウトメカニズムを備えています。 レイアウトはトップレベルのテンプレートと考えることができます-テンプレートの内容はレイアウトに挿入され、一緒にユーザーに送られます。



レイアウトを使用するには、layout.hamlファイルをビューフォルダーに配置するだけで、アプリケーションが自動的に使用を開始します。 ところで、同様に、Erbテンプレートに使用されるlayout.erbレイアウトを作成できます。



次のコードがテンプレートからレイアウトに転送されます。



  !!!
 %html {:xmlns => "http://www.w3.org/1999/xhtml"}
   %頭
     %title File Wash Name Me
     %meta {: "http-equiv" => "content-type" ,: content => "text / html; charset = UTF-8"}
   %体
     .main
       =収量 


ここで最後にある行「= yield」のみが新規です。 これも、Erbレイアウトの場合と同様に、その呼び出しの場所(つまり、クラスmainを持つdiv内)でHamlテンプレートの処理を呼び出します。 もちろん、layout.hamlに配置したコードは、list.hamlから削除する必要があります。



それだけです! 今私たちは堅実な人々であり、Hamlをテンプレート言語として使用しています。 残っているのは、list.erbは表示せず、list.hamlであることをSinatraに指摘することです。 これを行うには、init.rbに移動し、「erb:list」の行を「haml:list」に置き換えます。 これで終わりです-私たちはHamlを使用して生活を楽しんでいます。



ただし、成功しなかったという事実に少しお金をかける準備ができているので、Sinatraはテンプレートまたはレイアウトファイルのスイープに関連するエラーメッセージを提供しました。 ここに問題があります:Hamlは、インデントとして2つのスペースを使用するテンプレートとレイアウトでのみ機能します。たとえば、タブではなくスペースだけです。 そして正確に2。それを修正すると、おそらくエラーは消えます。 特に、ページからコードを直接コピーした場合、スペースではなくタブが必ずあります。



pr索好きな目からリンクを隠す



ダウンロードしているファイルのIDを知る機会をユーザーに与えるべきではない理由については既に説明したので、これについては詳しく説明しません。 次はこの問題を解決します。 ダウンロード用のアドレスとして、ファイルの代わりに40文字のSHA-1ダイジェストを識別子として使用します。 これを行うには、次を実行します。

stored_file.rbにダイジェストを保存するための新しいフィールドを追加します。

 プロパティ:sha、String 


ファイルの最後に「DataMapper.auto_upgrade!」と書きました。これは、データベースがDataMapperによって自動的に更新されることを意味します。



init.rbファイルを開き、最初のブロック(「/」を取得)を除くすべてのブロックを変更します。

  「/」を投稿
   tempfile = params ['file'] [:tempfile]
   filename = params ['file'] [:filename] digest = Digest :: SHA1.hexdigest(filename)#ダイジェストを計算する
   @file = StoredFile.create:filename => filename ,: sha => digest#ファイル名とダイジェストをデータベースに書き込みます
   File.copy(tempfile.path、「./ files/#{@file.idasket.upload」)
  リダイレクト '/'
終わり

 #ファイルをダウンロード 
   get '/:sha' do#現在、idではなくshaの値でファイルを検索しています
   @file = StoredFile.first:sha => params [:sha]
   send_file "./files/#{@file.idasket.upload" ,: filename => @ file.filename ,: type => 'Application / octet-stream'
終わり

 #ファイルを削除
   get '/:sha / delete'#を実行し、idではなくshaによっても削除します
   @file = StoredFile.first:sha => params [:sha]
   File.delete( "./ files/#{@file.idasket.upload")
   @ file.destroy
  リダイレクト '/'
終わり 




テンプレートを変更する必要があります:

 %td.filename
   %a {:href => "/#{<strong> file.sha </ strong>}" ,: title => file.filename} = file.filename
 %td.created_at = file.created_at.strftime( "%d%b")
 %td.delete
   %a {:href => "/#{<strong> file.sha </ strong>} / delete" ,: title => "削除"}削除




できた! リンクは次のようになります。

 ローカルホスト:4567 / 1c62e8aa8072c8a3cd5df1ba49bb4513bc1d8a88 




ダウンロードカウンターとファイルサイズの表示を追加する



これは非常にシンプルなポイントです。 ダウンロード数とファイルサイズをデータベースに保存します。つまり、StoredFileクラスの説明を拡張する必要があります。

  プロパティ:ダウンロード、整数、デフォルト=> 0
  プロパティ:filesize、Integer 、: nullable => false




ファイルのアップロードとダウンロードは次のようになります。



 「/」を投稿
   tempfile = params ['file'] [:tempfile]
   filename = params ['file'] [:filename]
  ダイジェスト=ダイジェスト:: SHA1.hexdigest(ファイル名)
   @file = StoredFile.create:filename => filename ,: sha => digest ,: filesize => File.size(tempfile.path)

   File.copy(tempfile.path、「./ files/#{@file.idasket.upload」)
  リダイレクト '/'
終わり

 「/:sha」を取得
   @file = StoredFile.first:sha => params [:sha]
   @ file.downloads + = 1
   @ file.save

   send_file "./files/#{@file.idasket.upload" ,: filename => @ file.filename ,: type => 'Application / octet-stream'
終わり




新しいデータを次のように表示します。

       %tr
         %thファイル
         %thがアップロードされました
         %thダウンロード済み

         %th削除
       -@ files.each do | file |
         %tr
           %td.filename
             %a {:href => "/#{file.sha}" ,: title => file.filename} = file.filename
             = "(#{file.filesize / 1024} Kb)"
          
           %td.created_at = file.created_at.strftime( "%d%b")
           %td.downloads = file.downloads
          
           %td.delete
             %a {:href => "/#{file.shaasket/delete" ,: title => "削除"}削除 




少しのJavaScript



このセクションは、基本技術の研究に直接関係するものではありませんが、新しい「価値システム」にもう少し慣れることができます。 確認なしでファイルを削除することは、家庭で使用する場合でも最善の解決策ではないことに同意します。誤ってクリックする可能性があります。 したがって、ファイルを削除しようとするときに確認を追加します。



パブリックフォルダーに、次の基本コンテンツを含むscript.jsファイルを作成します。

 関数ORLY(){
	 return confirm( 'Are you sure');
 }




次に、このJavaScriptファイルをレイアウトに接続します。

     %meta {: "http-equiv" => "content-type" ,: content => "text / html; charset = UTF-8"}
     %script {:type => "text / javascript" ,: src => "/script.js"}

   %体 




そして、テンプレートで小さな編集を行います。

 %td.delete
   %a {:href => "/#{file.shaasket/delete" ,: title => "Delete" ,: onclick => "return ORLY();"}削除




したがって、「削除」ボタンをクリックすると、彼らが私たちに確信があるかどうか尋ねられ、これは私たちの心を変える余分な機会です。



新しいファイルを自動的に検索して追加する



問題の元のステートメントに戻りましょう。ホームサーバーで動作する便利なインターフェイスを備えたファイルホスティングサービスです。 自宅にいる間は、ある種の「サイト」に移動してファイルをアップロードするのはそれほど便利ではありません。共有フォルダにドロップしてリストに追加する方がはるかに簡単です。



幸いなことに、Make for RubyのアナログであるRakeを使用すれば簡単です。 このユーティリティを使用すると、rakeコマンドTASKNAMEによって実行されるいわゆるタスク(タスク)を作成できます。 起動すると、rakeは現在のディレクトリでRakefileを探し、そこに指定されたコマンドを実行し、指定された名前のタスクを検索して実行します。



レーキタスクとは何ですか? これは、特別に細工されたRubyコードに他なりません。 この場合、rakeタスクはすべてのファイルを手動ディレクトリから取得し、通常の方法でロードをシミュレートします(つまり、それらをファイルフォルダーにコピーし、データベースに必要なエントリを作成します)。



Rakeが突然ない場合(これはありそうにありません)、インストールはRubyの従来の方法で実行されます。

  sudo gem install rake 




そのため、最初に作成したRakefileは、Rakeにlib / tasksディレクトリから.rake拡張子を持つすべてのファイルをダウンロードする必要があることを伝えます。 これは、manual_monitor.rakeを呼び出すファイルを作成することを意味します。 次の内容を書き込みます。



  「ftools」が必要
 'lib / datamapper / stored_file'が必要です

 desc「ディレクトリ「manual」をチェックし、そこからすべてのファイルをアップロードします」
タスク:manual_monitor do
   Dir ["manual / *"]。各do |パス|
     file = File.new(パス)
     filename = File.basename(file.path)
    ダイジェスト=ダイジェスト:: SHA1.hexdigest(ファイル名)
     stored_file = StoredFile.create:filename => filename ,: sha => digest ,: filesize => File.size(file.path)
     File.move(パス、「./ files /#{stored_file.idasket.upload」)
     「ファイル#{path}が正常にアップロードされました。ID=#{stored_file.id}」
  終わり
終わり




最初に、ftools(ファイルユーティリティ)とstored_file.rb(StoredFileクラスを記述します-データベースを操作するために必要です。次はcheck_filesタスクの説明です。

 タスク:TASK_NAME do
  コード
終わり


タスクの前のオプションのdescコマンドを使用すると、タスクの任意のテキスト説明を指定できます(rake --tasksコマンドを使用して表示できます)



タスク自体の内部では、ブラウザーを介してファイルをダウンロードするコードとほぼ同じことを行いますが、いくつかの違いがあります。

  1. 手動ディレクトリのすべてのファイルを処理します
  2. ファイルを手動からファイルに移動します(再度処理しないように)




手動フォルダーにファイルを配置してコマンドを呼び出すことで、タスクの動作を確認できます

  rake manual_monitor 


Rakeは、ファイルが正常にコピーされたことを報告する必要があります。 Webインターフェイスを介して、それが正しいことを確認できます。



ここで、チームに自動的に電話をかける方法を学ぶ必要があります。 crontabとRubyデーモンの2つのオプションを検討しました。 もちろん、私たちは変態であり、辺境の人ですが、この場合、私は自転車を発明し始めず(そして単にシステムリソースを浪費しません)、crontabを利用しました。 しかし、誰かがRubyデーモンを起動するオプションに興味があるなら、私は彼について話すことができます。



私のcrontabはこのように見えます

 * / 1 * * * * cd / path / to / my / app /;  rake manual_monitor


つまり、コマンドは毎分実行されます。



わあ!



最初は1つの記事に収めることを計画していましたが、実際には2つでも少ないことがわかりました。 第3部では、次のことについて話す予定です。





今私はリストを書いており、3つの部分が私にとって十分であることをすでに疑っています...しかし、あなたが何か他のものを知りたいなら-コメントに書いて、私たちはそれを理解します:)



もう一度、ご清聴ありがとうございました!




All Articles