From: shimada Date: Sat, 15 May 2010 16:27:47 +0000 (+0900) Subject: today's work. songs new, create, edit X-Git-Url: http://git.osdn.net/view?p=praisedb%2Fpraisedb.git;a=commitdiff_plain;h=fcf7b1e1c42700ccc3dd1d7baf36c1d94367b775 today's work. songs new, create, edit --- diff --git a/app/controllers/songs_controller.rb b/app/controllers/songs_controller.rb index 5246677..dfaba3e 100644 --- a/app/controllers/songs_controller.rb +++ b/app/controllers/songs_controller.rb @@ -1,9 +1,29 @@ class SongsController < BaseController -def index -end + def index + end -def new - @song = Song.new -end + def new + @song = Song.new + @song.font_size = Song::DEFAULT_FONT_SIZE + end + + def create + render :action => :new unless params[:song] + @song = Song.new(params[:song]) + unless @song.valid? + p @song.errors + p @song.valid? + flash[:error] = "error desu" + return render :action => :new + end + unless @song.save + flash[:error] = "保存できませんでした" + end + render :action => :edit + end + def edit + @song = Song.find_by_id(params[:id]) + redirect_to songs_path if @song.blank? + end end diff --git a/app/models/song.rb b/app/models/song.rb index 091746f..33a5e76 100644 --- a/app/models/song.rb +++ b/app/models/song.rb @@ -1,2 +1,20 @@ class Song < ActiveRecord::Base + include ::SelectableAttr::Base + + DEFAULT_FONT_SIZE = 2 + CODES = %w(A Ab Bb C Cm D E Eb Em F F#m Fm G) + + selectable_attr :font_size do + entry "0", :xsmall, 'XSmall' + entry "1", :small, 'Small' + entry "2", :middle, 'Middle' + entry "3", :large, 'Large' + entry "4", :xlarge, 'XLarge' + end + +# validates_presence_of :titile, :words, :font_size +# validates_numericality_of :font_size + def kana + self.words + end end diff --git a/app/views/songs/_form.html.erb b/app/views/songs/_form.html.erb new file mode 100644 index 0000000..566ae27 --- /dev/null +++ b/app/views/songs/_form.html.erb @@ -0,0 +1,26 @@ +<%= f.error_messages %> + + + + + + + + + + + + + + + + + + + + + + + + +
コード(CODE)<%= f.select :code, Song::CODES, :include_blank => true %>
題名(TITLE)<%= f.text_field :title %>
歌詞(WORDS)<%= f.text_area :words %>
出典(SOURCE)<%= f.text_field :copyright %>
表示サイズ(FONT SIZE)<%= f.select :font_size, Song.font_size_options %>
<%= f.submit "保存する(SAVE)" %>
diff --git a/app/views/songs/edit.html.erb b/app/views/songs/edit.html.erb new file mode 100644 index 0000000..0c45310 --- /dev/null +++ b/app/views/songs/edit.html.erb @@ -0,0 +1,22 @@ +<% content_for :head do -%> + Praise DATABASE + <%= javascript_include_tag "prototype" %> + <%= javascript_include_tag "roman" %> +<% end -%> +

歌詞の編集 (edit this song)

+ +<% form_for @song, song_path(@song), :method => :put do |f| -%> + <%= render :partial => 'form', :locals => {:f => f} %> + <%= f.text_area :kana, :style => "display:none" %> +<% end -%> + +<% form_for @song, song_path, :method => :delete do |f| -%> +

このデータを<%= f.submit "削除する" %>

+<% end -%> +[<%= link_to "表示", song_path(@song) %>] +[<%= link_to "印刷用画面", print_song_path(@song) %>] +[<%= link_to "トップに戻る", song_path %>] +[<%= link_to_function "ローマ字", "toRoman()" %>] +[<%= link_to_function "リセット", "reset()" %>] +
+ diff --git a/app/views/songs/new.html.erb b/app/views/songs/new.html.erb new file mode 100644 index 0000000..1df576e --- /dev/null +++ b/app/views/songs/new.html.erb @@ -0,0 +1,11 @@ +

歌詞の作成 (create new song)

+

歌詞その他を入力してください。ローマ字の登録は次の画面でローマ字ボタン押してください。

+ +

+ <%= flash[:notice] %> + <%= @error_messages %> +

+ +<% form_for @song, songs_path do |f| -%> + <%= render :partial => 'form', :locals => {:f => f} %> +<% end -%> diff --git a/config/environment.rb b/config/environment.rb index 029a58b..39545f1 100644 --- a/config/environment.rb +++ b/config/environment.rb @@ -38,4 +38,4 @@ Rails::Initializer.run do |config| # The default locale is :en and all translations from config/locales/*.rb,yml are auto loaded. # config.i18n.load_path += Dir[Rails.root.join('my', 'locales', '*.{rb,yml}')] # config.i18n.default_locale = :de -end \ No newline at end of file +end diff --git a/config/routes.rb b/config/routes.rb index 1ba78b2..5e12c7e 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -1,6 +1,8 @@ ActionController::Routing::Routes.draw do |map| # The priority is based upon order of creation: first created -> highest priority. - map.resources :songs, :collection => { :search => [:get], :list => [:get] } + map.resources :songs, + :collection => { :search => [:get], :list => [:get] }, + :member => { :print => [:get] } # Sample of regular route: # map.connect 'products/:id', :controller => 'catalog', :action => 'view' diff --git a/db/migrate/20100424171005_create_songs.rb b/db/migrate/20100424171005_create_songs.rb index 7634101..aecb4d5 100644 --- a/db/migrate/20100424171005_create_songs.rb +++ b/db/migrate/20100424171005_create_songs.rb @@ -6,7 +6,7 @@ class CreateSongs < ActiveRecord::Migration t.text :words, :null => false, :default => "", :comment => "歌詞本体" t.text :words_for_search, :null => false, :default => "", :comment => "歌詞検索用のデータ" t.string :copyright, :null => false, :default => "", :comment => "楽曲の出典" - t.int :font_size, :null => false, :default => 4, :comment => "表示フォントサイズ" + t.integer :font_size, :null => false, :default => 2, :comment => "表示フォントサイズ" t.datetime :deleted_at, :comment => "削除タイムスタンプ" t.timestamps diff --git a/public/images/TLCblue.png b/public/images/TLCblue.png new file mode 100644 index 0000000..0e69d41 Binary files /dev/null and b/public/images/TLCblue.png differ diff --git a/public/javascripts/roman.js b/public/javascripts/roman.js new file mode 100644 index 0000000..fc81d98 --- /dev/null +++ b/public/javascripts/roman.js @@ -0,0 +1,410 @@ + +var src; +var kana; +var box; + +function init() { + box = $('song_words'); + src = box.value; + kana = $('song_kana').value; +} + +function reset() { + box.value = src; +} + +function toRoman() { + var result = ""; + var roman = new Roman(); + var srcLines = src.split("\n"); + var kanaLines = kana.split("\n"); + for (var i = 0; i < srcLines.length; i++) { + result += srcLines[i] + "\n"; + if (srcLines[i].match(/^\s*$/)) continue; + result += roman.conv(kanaLines[i]) + "\n"; + } + box.value = result; +} + +function Roman(convTable) { + var kanaTable = []; + var romaTable = []; + for (var i = 0; i < tbl.length; i+=2) { + kanaTable.push(new RegExp(tbl[i], "g")); + romaTable.push(tbl[i+1]); + } + function convert(str) { + var result = str; + for (var i = 0; i < kanaTable.length; i++) { + result = result.replace(kanaTable[i], romaTable[i]); + } + result = result.replace(/[ ]+/g, " "); + result = result.replace(/^ /mg, ""); + return result; + } + + return {"conv": convert}; +} + +var tbl = +[ + + // 特殊表記(固有名詞) かなの長さ順に並べる + + "インマヌエル","EMMANUEL", + "ハレルーヤー","HALLELUJAH", + + "クリスマス","CHRISTMAS", + "グローリー","GLORY", + "リバイバル","REVIVAL", + "ファミリー","FAMILY", + + "アーメン","AMEN", + "アルファ","ALPHA", + "グローリ","GLORY", + "ジーザス","JESUS", + "しょうり","SHORI", + "ハレルヤ","HALLELUJAH", + "メサイヤ","MESSIAH", + "キリスト","KIRISUTO", + + "メシヤ","MESSIAH", + "オメガ","OMEGA", + "ホサナ","HOSANNA", + "ホザナ","HOSANNA", + "ロード","LORD", + "イエス","IESU", + + + // 促音の特例:っち、っちゃ、っちゅ、っちょ + + "っちゃ","TCHA", + "っちゅ","TCHU", + "っちょ","TCHO", + "っち","TCHI", + + "ッチャ","TCHA", + "ッチュ","TCHU", + "ッチョ","TCHO", + "ッチ","TCHI", + + + // 拗音のうしろの「あ」「う」は消える + // 例:しょうり → ○SHORI ×SHOURI + + "([きしちにひみりぎじぢ]ゃ)あ","$1", + "([きしちにひみりぎじぢ][ゅょ])う","$1", + + "([キシチニヒミリギジヂ]ャ)ア","$1", + "([キシチニヒミリギジヂ][ュョ])ウ","$1", + + + // 拗音 + + "きゃ","KYA", + "きゅ","KYU", + "きょ","KYO", + + "しゃ","SHA", + "しゅ","SHU", + "しょ","SHO", + + "ちゃ","CHA", + "ちゅ","CHU", + "ちょ","CHO", + + "にゃ","NYA", + "にゅ","NYU", + "にょ","NYO", + + "ひゃ","HYA", + "ひゅ","HYU", + "ひょ","HYO", + + "みゃ","MYA", + "みゅ","MYU", + "みょ","MYO", + + "りゃ","RYA", + "りゅ","RYU", + "りょ","RYO", + + "ぎゃ","GYA", + "ぎゅ","GYU", + "ぎょ","GYO", + + "じゃ","JYA", + "じゅ","JYU", + "じょ","JYO", + + "キャ","KYA", + "キュ","KYU", + "キョ","KYO", + + "シャ","SHA", + "シュ","SHU", + "シュ","SHU", + "ショ","SHO", + + "チャ","CHA", + "チュ","CHU", + "チョ","CHO", + + "ニャ","NYA", + "ニュ","NYU", + "ニョ","NYO", + + "ヒャ","HYA", + "ヒュ","HYU", + "ヒョ","HYO", + + "ミャ","MYA", + "ミュ","MYU", + "ミョ","MYO", + + "リャ","RYA", + "リュ","RYU", + "リョ","RYO", + + "ギャ","GYA", + "ギュ","GYU", + "ギョ","GYO", + "ジャ","JYA", + "ジュ","JYU", + "ジョ","JYO", + + + //「おう」はHで伸ばす + // 例:「王」→「おう」→「OH」 + + "おう","OH", + + + // それ以外の「おの段+う」は「う」を無視する + // 例:「栄光」→「えいこう」→「EIKO」 + + "([こそとのほもよろを])う","$1", + + + // アの段、オの段の長音はHで伸ばす + // 例:"サー" → "サH" → "SAH" + // "コー" → "コH" → "KOH" + + "([アカサタナハマヤラワ])ー","$1H", + "([ガザダバ])ー","$1H", + + "([オコソトノホモヨロヲ])ー","$1H", + "([ゴゾドボ])ー","$1H", + + // それ以外の長音は無視する + + "(.)ー","$1", + + + //「ん」の直後の母音はハイフンで分ける + + "んあ","N-A", + "んい","N-I", + "んう","N-U", + "んえ","N-E", + "んお","N-O", + + "ンア","N-A", + "ンイ","N-I", + "ンウ","N-U", + "ンエ","N-E", + "ンオ","N-O", + + + // 通常のかな + + "あ","A", + "い","I", + "う","U", + "え","E", + "お","O", + + "か","KA", + "き","KI", + "く","KU", + "け","KE", + "こ","KO", + + "が","GA", + "ぎ","GI", + "ぐ","GU", + "げ","GE", + "ご","GO", + + "さ","SA", + "し","SHI", + "す","SU", + "せ","SE", + "そ","SO", + + "ざ","ZA", + "じ","JI", + "ず","ZU", + "ぜ","ZE", + "ぞ","ZO", + + "た","TA", + "ち","CHI", + "つ","TSU", + "て","TE", + "と","TO", + + "だ","DA", + "ぢ","JI", + "づ","ZU", + "で","DE", + "ど","DO", + + "な","NA", + "に","NI", + "ぬ","NU", + "ね","NE", + "の","NO", + + "は","HA", + "ひ","HI", + "ふ","FU", + "へ","HE", + "ほ","HO", + + "ば","BA", + "び","BI", + "ぶ","BU", + "べ","BE", + "ぼ","BO", + + "ぱ","PA", + "ぴ","PI", + "ぷ","PU", + "ぺ","PE", + "ぽ","PO", + + "ま","MA", + "み","MI", + "む","MU", + "め","ME", + "も","MO", + + "や","YA", + "ゆ","YU", + "よ","YO", + + "ら","RA", + "り","RI", + "る","RU", + "れ","RE", + "ろ","RO", + + "わ","WA", + "を","O", + + "ア","A", + "イ","I", + "ウ","U", + "エ","E", + "オ","O", + + "カ","KA", + "キ","KI", + "ク","KU", + "ケ","KE", + "コ","KO", + + "ガ","GA", + "ギ","GI", + "グ","GU", + "ゲ","GE", + "ゴ","GO", + + "サ","SA", + "シ","SHI", + "ス","SU", + "セ","SE", + "ソ","SO", + + "ザ","ZA", + "ジ","JI", + "ズ","ZU", + "ゼ","ZE", + "ゾ","ZO", + + "タ","TA", + "チ","CHI", + "ツ","TSU", + "テ","TE", + "ト","TO", + + "ダ","DA", + "ヂ","JI", + "ヅ","ZU", + "デ","DE", + "ド","DO", + + "ナ","NA", + "ニ","NI", + "ヌ","NU", + "ネ","NE", + "ノ","NO", + + "ハ","HA", + "ヒ","HI", + "フ","FU", + "ヘ","HE", + "ホ","HO", + + "バ","BA", + "ビ","BI", + "ブ","BU", + "ベ","BE", + "ボ","BO", + + "パ","PA", + "ピ","PI", + "プ","PU", + "ペ","PE", + "ポ","PO", + + "マ","MA", + "ミ","MI", + "ム","MU", + "メ","ME", + "モ","MO", + + "ヤ","YA", + "ユ","YU", + "ヨ","YO", + + "ラ","RA", + "リ","RI", + "ル","RU", + "レ","RE", + "ロ","RO", + + "ワ","WA", + "ヲ","O", + + // 撥音の特例:B,M,Pの前にはNの代わりにMをおく + // それ以外はNとする + + "[んン]([BMP])","M$1", + "[んン]","N", + + + // 促音は次の子音を重ねる(特例は定義済み) + // ※ここまでの変換でその後ろのかなは + //  ローマ字に変換されているという前提 + + "[っッ](.)","$1$1", + + + // 全角スペースを半角スペースに + + " "," " +]; + +Event.observe(window, "load", init); diff --git a/public/stylesheets/base.css b/public/stylesheets/base.css new file mode 100644 index 0000000..510ef59 --- /dev/null +++ b/public/stylesheets/base.css @@ -0,0 +1,25 @@ +* { + scrollbar-base-color: #223355; +} +body { + background-color: #005; + background-image: url(/images/TLCblue.png); + background-attachment: fixed; + color: white; +} +a:link { + color: white; + text-decoration: none; +} +a:visited { + color: white; + text-decoration: none; +} +a:hover { + background-color: red; + text-decoration: underline; +} +a:active { + color: red; + text-decoration: none; +} diff --git a/vendor/plugins/selectable_attr/MIT-LICENSE b/vendor/plugins/selectable_attr/MIT-LICENSE new file mode 100644 index 0000000..a6a8191 --- /dev/null +++ b/vendor/plugins/selectable_attr/MIT-LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2008 Takeshi AKIMA + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/plugins/selectable_attr/README b/vendor/plugins/selectable_attr/README new file mode 100644 index 0000000..504a8fc --- /dev/null +++ b/vendor/plugins/selectable_attr/README @@ -0,0 +1,334 @@ += SelectableAttr +== Introduction +selectable_attr は、コードが割り振られるような特定の属性について*コード*、*プログラム上での名前*、 +*表示するための名前*などをまとめて管理するものです。 +http://github.com/akm/selectable_attr/tree/master + +Railsで使用する場合、selectable_attr_railsと一緒に使うことをオススメします。 +http://github.com/akm/selectable_attr_rails/tree/master + + +== Install +=== 1. Railsプロジェクトで使う場合 +==== a. plugin install + ruby script/plugin install git://github.com/akm/selectable_attr.git + ruby script/plugin install git://github.com/akm/selectable_attr_rails.git + +==== b. gem install + [sudo] gem install akimatter-selectable_attr akimatter-selectable_attr_rails -s http://gems.github .com + +=== 2. 非Railsで使う場合 +==== a. gem install + [sudo] gem install akimatter-selectable_attr -s http://gems.github .com + + + +== Tutorial + +=== シンプルなパターン + require 'rubygems' + require 'selectable_attr' + + class Person + include ::SelectableAttr::Base + + selectable_attr :gender do + entry '1', :male, '男性' + entry '2', :female, '女性' + entry '9', :other, 'その他' + end + end + +上記のようなクラスがあった場合、以下のようなクラスメソッドを使うことが可能です。 + + irb(main):020:0> Person.gender_ids + => ["1", "2", "9"] + irb(main):021:0> Person.gender_keys + => [:male, :female, :other] + irb(main):022:0> Person.gender_names + => ["男性", "女性", "その他"] + irb(main):023:0> Person.gender_options + => [["男性", "1"], ["女性", "2"], ["その他", "9"]] # railsでoptions_for_selectメソッドなどに使えます。 + irb(main):024:0> Person.gender_key_by_id("1") + => :male + irb(main):025:0> Person.gender_name_by_id("1") + => "男性" + irb(main):026:0> Person.gender_id_by_key(:male) # 特定のキーから対応するidを取得できます。 + => "1" + irb(main):027:0> Person.gender_name_by_key(:male) + => "男性" + +また使用可能なインスタンスメソッドには以下のようなものがあります。 + + irb> person = Person.new + => # + irb> person.gender_key + => nil + irb> person.gender_name + => nil + irb> person.gender = "2" + => "2" + irb> person.gender_key + => :female + irb> person.gender_name + => "女性" + irb> person.gender_key = :other + => :other + irb> person.gender + => "9" + irb> person.gender_name + => "その他" + +genderが代入可能なことはもちろん、gender_keyも代入可能です。 +# ただし、gender_nameには代入できません。 + + +=== 複数の値を取りうるパターン + require 'rubygems' + require 'selectable_attr' + + class RoomSearch + include ::SelectableAttr::Base + + multi_selectable_attr :room_type do + entry '01', :single, 'シングル' + entry '02', :twin, 'ツイン' + entry '03', :double, 'ダブル' + entry '04', :triple, 'トリプル' + end + end + +multi_selectable_attrを使った場合に使用できるクラスメソッドは、selectable_attrの場合と同じです。 + + irb> room_search = RoomSearch.new + => # + irb> room_search.room_type_ids + => [] + irb> room_search.room_type_keys + => [] + irb> room_search.room_type_names + => [] + irb> room_search.room_type_selection + => [false, false, false, false] + irb> room_search.room_type_keys = [:twin, :double] + => [:twin, :double] + irb> room_search.room_type + => ["02", "03"] + irb> room_search.room_type_names + => ["ツイン", "ダブル"] + irb> room_search.room_type_ids + => ["02", "03"] + irb> room_search.room_type = ["01", "04"] + => ["01", "04"] + irb> room_search.room_type_keys + => [:single, :triple] + irb> room_search.room_type_names + => ["シングル", "トリプル"] + irb> room_search.room_type_selection + => [true, false, false, true] + irb> room_search.room_type_hash_array + => [{:select=>true, :key=>:single, :name=>"シングル", :id=>"01"}, {:select=>false, :key=>:twin, :name=>"ツイン", :id=>"02"}, {:select=>false, :key=>:double, :name=>"ダブル", :id=>"03"}, {:select=>true, :key=>:triple, :name=>"トリプル", :id=>"04"}] + irb> room_search.room_type_hash_array_selected + => [{:select=>true, :key=>:single, :name=>"シングル", :id=>"01"}, {:select=>true, :key=>:triple, :name=>"トリプル", :id=>"04"}] + + + +=== Entry +==== エントリの取得 +エントリは様々な拡張が可能です。例えば以下のようにid、key、name以外の属性を設定することも可能です。 + + require 'rubygems' + require 'selectable_attr' + + class Site + include ::SelectableAttr::Base + + selectable_attr :protocol do + entry '01', :http , 'HTTP' , :port => 80 + entry '02', :https, 'HTTPS' , :port => 443 + entry '03', :ssh , 'SSH' , :port => 22 + entry '04', :svn , 'Subversion', :port => 3690 + end + end + +クラスメソッドで各エントリを取得することが可能です。 + entry = Site.protocol_entry_by_key(:https) + entry = Site.protocol_entry_by_id('02') + +インスタンスメソッドでは以下のように取得できます。 + site = Site.new + site.protocol_key = :https + entry = site.protocol_entry + +==== エントリの属性 +id, key, nameもそのままメソッドとして用意されています。 + irb> entry.id + => "02" + irb> entry.key + => :https + irb> entry.name + => "HTTPS" + +またオプションの属性もHashのようにアクセス可能です。 + irb> entry[:port] + => 443 + +to_hashメソッドで、id, key, nameを含むHashを作成します。 + irb> entry.to_hash + => {:key=>:https, :port=>443, :name=>"HTTPS", :id=>"02"} + +matchメソッドでid,key,nameを除くオプションの属性群と一致しているかどうかを判断可能です。 + + irb> entry.match?(:port => 22) + => false + irb> entry.match?(:port => 443) + => true + + # ここではオプションの属性として:portしか設定していないので、matchに渡すHashのキーと値の組み合わせも一つだけですが、 + # 複数ある場合にmatch?がtrueとなるためには、完全に一致している必要があります。 + + +==== クラスメソッドでのエントリの扱い +クラスメソッドでエントリを取得する方法として、xxx_entry_by_id, xxx_entry_by_keyを紹介しましたが、 +全てのエントリで構成される配列を取得するメソッドが xxx_entriesです。 + + irb> entries = Site.protocol_entries + irb> entries.length + => 4 + +また、各エントリをto_hashでHashに変換した配列を xxx_hash_arrayメソッドで取得することも可能です。 + irb> Site.protocol_hash_array + => [ + {:key=>:http, :port=>80, :name=>"HTTP", :id=>"01"}, + {:key=>:https, :port=>443, :name=>"HTTPS", :id=>"02"}, + {:key=>:ssh, :port=>22, :name=>"SSH", :id=>"03"}, + {:key=>:svn, :port=>3690, :name=>"Subversion", :id=>"04"} + ] + + +=== Enum +あまり表にでてきませんが、エントリをまとめる役割のオブジェクトがEnumです。 +これはクラスメソッドxxx_enumで取得することができます。 + + irb> enum = Site.protocol_enum + +Enumには以下のようなメソッドが用意されています。 + irb> enum.entries + irb> enum.entries.map{|entry| entry[:port]} + => [80, 443, 22, 3690] + +EnumはEnumerableをincludeしているため、以下のように記述することも可能です。 + irb> enum.map{|entry| entry[:port]} + => [80, 443, 22, 3690] + + irb> enum.entry_by_id("03") + => #22} + irb> enum.entry_by_key(:ssh) + => #22} + irb> enum.entry_by_id_or_key(:ssh) + => #22} + irb> enum.entry_by_id_or_key('03') + => #22} + irb> enum.entry_by_hash(:port => 22) + => #22} + +また、これらのメソッドが面倒と感じるようであれば、以下のような簡単なアクセスも可能です。 + irb> enum['03'] + => #22} + irb> enum[:ssh] + => #22} + irb> enum[:port => 22] + => #22} + +またクラスメソッドで紹介したようなxxx_ids, xxx_keys, xxx_namesや、xxx_key_by_idなどのメソッドも用意されています。 + irb> enum.ids + => ["01", "02", "03", "04"] + irb> enum.keys + => [:http, :https, :ssh, :svn] + irb> enum.names + => ["HTTP", "HTTPS", "SSH", "Subversion"] + irb> enum.options + => [["HTTP", "01"], ["HTTPS", "02"], ["SSH", "03"], ["Subversion", "04"]] + irb> enum.key_by_id('04') + => :svn + irb> enum.id_by_key(:svn) + => "04" + irb> enum.name_by_id('04') + => "Subversion" + irb> enum.name_by_key(:svn) + => "Subversion" + irb> enum.to_hash_array + => [ + {:key=>:http, :port=>80, :name=>"HTTP", :id=>"01"}, + {:key=>:https, :port=>443, :name=>"HTTPS", :id=>"02"}, + {:key=>:ssh, :port=>22, :name=>"SSH", :id=>"03"}, + {:key=>:svn, :port=>3690, :name=>"Subversion", :id=>"04"} + ] + +id, key以外でエントリを特定したい場合はfindメソッドが使えます。 + irb> enum.find(:port => 22) + => #22} + +findメソッドにはブロックを渡すこともできます。 + irb> enum.find{|entry| entry[:port] > 1024} + => #3690} + + +=== Entryへのメソッド定義 +entryメソッドにブロックを渡すとエントリのオブジェクトにメソッドを定義することが可能です。 + + require 'rubygems' + require 'selectable_attr' + + class Site + include ::SelectableAttr::Base + + selectable_attr :protocol do + entry '01', :http , 'HTTP', :port => 80 do + def accept?(model) + # httpで指定された場合はhttpsも可、という仕様 + model.url =~ /^http[s]{0,1}\:\/\// + end + end + + entry '02', :https, 'HTTPS', :port => 443 do + def accept?(model) + model.url =~ /^https\:\/\// + end + end + + entry '03', :ssh , 'SSH', :port => 22 do + def accept?(model) + false + end + end + + entry '04', :svn , 'Subversion', :port => 3690 do + def accept?(model) + model.url =~ /^svn\:\/\/|^svn+ssh\:\/\// + end + end + + end + end + + enum = Site.protocol_enum + + class Project + attr_accessor :url + end + project = Project.new + project.url = "http://github.com/akm/selectable_attr/tree/master" + + irb> enum[:http].accept?(project) + => 0 + irb> enum[:https].accept?(project) + => nil + + というようにentryメソッドに渡したブロックは、生成されるエントリオブジェクトのコンテキストでinstance_evalされるので、そのメソッドを定義することが可能です。 + + + + +== Credit +Copyright (c) 2008 Takeshi AKIMA, released under the MIT lice nse diff --git a/vendor/plugins/selectable_attr/Rakefile b/vendor/plugins/selectable_attr/Rakefile new file mode 100644 index 0000000..6316356 --- /dev/null +++ b/vendor/plugins/selectable_attr/Rakefile @@ -0,0 +1,54 @@ +require 'rubygems' +gem 'rspec', '>= 1.1.4' +require 'rake' +require 'rake/rdoctask' +require 'spec/rake/spectask' +require 'spec/rake/verify_rcov' + +desc 'Default: run unit tests.' +task :default => :spec + +task :pre_commit => [:spec, 'coverage:verify'] + +desc 'Run all specs under spec/**/*_spec.rb' +Spec::Rake::SpecTask.new(:spec => 'coverage:clean') do |t| + t.spec_files = FileList['spec/**/*_spec.rb'] + t.spec_opts = ["-c", "--diff"] + t.rcov = true + t.rcov_opts = ["--include-file", "lib\/*\.rb", "--exclude", "spec\/"] +end + +desc 'Generate documentation for the selectable_attr plugin.' +Rake::RDocTask.new(:rdoc) do |rdoc| + rdoc.rdoc_dir = 'rdoc' + rdoc.title = 'SelectableAttr' + rdoc.options << '--line-numbers' << '--inline-source' << '-c UTF-8' + rdoc.rdoc_files.include('README*') + rdoc.rdoc_files.include('lib/**/*.rb') +end + +namespace :coverage do + desc "Delete aggregate coverage data." + task(:clean) { rm_f "coverage" } + + desc "verify coverage threshold via RCov" + RCov::VerifyTask.new(:verify => :spec) do |t| + t.threshold = 100.0 # Make sure you have rcov 0.7 or higher! + t.index_html = 'coverage/index.html' + end +end + +begin + require 'jeweler' + Jeweler::Tasks.new do |s| + s.name = "selectable_attr" + s.summary = "selectable_attr generates extra methods dynamically" + s.description = "selectable_attr generates extra methods dynamically for attribute which has options" + s.email = "akima@gmail.com" + s.homepage = "http://github.com/akm/selectable_attr/" + s.authors = ["Takeshi Akima"] + end +rescue LoadError + puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com" +end + diff --git a/vendor/plugins/selectable_attr/VERSION.yml b/vendor/plugins/selectable_attr/VERSION.yml new file mode 100644 index 0000000..f235338 --- /dev/null +++ b/vendor/plugins/selectable_attr/VERSION.yml @@ -0,0 +1,4 @@ +--- +:patch: 11 +:major: 0 +:minor: 3 diff --git a/vendor/plugins/selectable_attr/init.rb b/vendor/plugins/selectable_attr/init.rb new file mode 100644 index 0000000..a742965 --- /dev/null +++ b/vendor/plugins/selectable_attr/init.rb @@ -0,0 +1 @@ +require 'selectable_attr' diff --git a/vendor/plugins/selectable_attr/install.rb b/vendor/plugins/selectable_attr/install.rb new file mode 100644 index 0000000..f7732d3 --- /dev/null +++ b/vendor/plugins/selectable_attr/install.rb @@ -0,0 +1 @@ +# Install hook code here diff --git a/vendor/plugins/selectable_attr/lib/selectable_attr.rb b/vendor/plugins/selectable_attr/lib/selectable_attr.rb new file mode 100644 index 0000000..457aa6b --- /dev/null +++ b/vendor/plugins/selectable_attr/lib/selectable_attr.rb @@ -0,0 +1,17 @@ +# -*- coding: utf-8 -*- +require 'logger' + +module SelectableAttr + autoload :VERSION, 'selectable_attr/version' + autoload :Enum, 'selectable_attr/enum' + autoload :Base, 'selectable_attr/base' + + class << self + def logger + @logger ||= Logger.new(STDERR) + end + def logger=(value) + @logger = value + end + end +end diff --git a/vendor/plugins/selectable_attr/lib/selectable_attr/base.rb b/vendor/plugins/selectable_attr/lib/selectable_attr/base.rb new file mode 100644 index 0000000..e614327 --- /dev/null +++ b/vendor/plugins/selectable_attr/lib/selectable_attr/base.rb @@ -0,0 +1,304 @@ +# -*- coding: utf-8 -*- +module SelectableAttr + module Base + def self.included(base) + base.extend(ClassMethods) + end + + ENUM_ARRAY_METHODS = { + :none => { + :to_hash_array => Proc.new do |enum, attr_value| + value = (attr_value || []).map(&:to_s) + enum.to_hash_array do |hash| + hash[:select] = value.include?(hash[:id].to_s) + end + end, + + :to_attr_value => Proc.new do |enum, hash_array| + hash_array.select{|hash| hash[:select]}.map{|hash| hash[:id]} + end + }, + + :comma_string => { + :to_hash_array => Proc.new do |enum, attr_value| + values = attr_value.is_a?(Array) ? attr_value.map{|v|v.to_s} : + (attr_value || '').split(',') + enum.to_hash_array do |hash| + hash[:select] = values.include?(hash[:id].to_s) + end + end, + + :to_attr_value => Proc.new do |enum, hash_array| + hash_array.select{|hash| hash[:select]}.map{|hash| hash[:id]}.join(',') + end + }, + + + :binary_string => { + :to_hash_array => Proc.new do |enum, attr_value| + value = attr_value || '' + idx = 0 + enum.to_hash_array do |hash| + hash[:select] = (value[idx, 1] == '1') + idx += 1 + end + end, + + :to_attr_value => Proc.new do |enum, hash_array| + result = '' + hash_map = hash_array.inject({}){|dest, hash| dest[hash[:id]] = hash; dest} + enum.each do |entry| + hash = hash_map[entry.id] + result << (hash[:select] ? '1' : '0') + end + result + end + } + } + + module ClassMethods + def single_selectable_attrs + @single_selectable_attrs_hash ||= {}; + @single_selectable_attrs_hash[self] ||= [] + end + + def multi_selectable_attrs + @multi_selectable_attrs_hash ||= {}; + @multi_selectable_attrs_hash[self] ||= [] + end + + def selectable_attr_type_for(attr) + single_selectable_attrs.include?(attr.to_s) ? :single : + multi_selectable_attrs.include?(attr.to_s) ? :multi : nil + end + + def enum(*args, &block) + process_definition(block, *args) do |enum, context| + self.single_selectable_attrs << context[:attr].to_s + define_enum_class_methods(context) + define_enum_instance_methods(context) + end + end + alias_method :single_selectable_attr, :enum + alias_method :selectable_attr, :enum + + + def enum_array(*args, &block) + base_options = args.last.is_a?(Hash) ? args.pop : {} + args << base_options # .update({:attr_accessor => false}) + process_definition(block, *args) do |enum, context| + self.multi_selectable_attrs << context[:attr].to_s + define_enum_class_methods(context) + define_enum_array_instance_methods(context) + end + end + alias_method :multi_selectable_attr, :enum_array + + def process_definition(block, *args) + base_options = args.last.is_a?(Hash) ? args.pop : {} + enum = base_options[:enum] || create_enum(&block) + args.each do |attr| + context = { + :enum => enum, + :attr_accessor => !has_attr(attr), + :attr => attr, + :base_name => enum_base_name(attr) + }.update(base_options) + define_enum(context) + define_accessor(context) + yield(enum, context) + unless enum.i18n_scope + paths = [:selectable_attrs] + self.name.to_s.split('::').map{|s| s.to_sym} + paths << attr.to_sym + enum.i18n_scope(*paths) + end + end + enum + end + + def has_attr(attr) + return true if self.method_defined?(attr) + return false unless self.respond_to?(:columns) + if self.respond_to?(:connection) and self.respond_to?(:connected?) + begin + self.connection unless self.connected? + rescue Exception + return nil if !self.connected? + end + end + (respond_to?(:table_exists?) && self.table_exists?) ? + (self.columns || []).any?{|col|col.name.to_s == attr.to_s} : false + end + + def attr_enumeable_base(*args, &block) + @base_name_processor = block + end + + def enum_base_name(attr) + if @base_name_processor + @base_name_processor.call(attr).to_s + else + attr.to_s.gsub(selectable_attr_name_pattern, '') + end + end + + DEFAULT_SELECTABLE_ATTR_NAME_PATTERN = /(_cd$|_code$|_cds$|_codes$)/ + + def selectable_attr_name_pattern + @selectable_attr_name_pattern ||= DEFAULT_SELECTABLE_ATTR_NAME_PATTERN + end + alias_method :enum_name_pattern, :selectable_attr_name_pattern + + def selectable_attr_name_pattern=(value) + @selectable_attr_name_pattern = value + end + alias_method :enum_name_pattern=, :selectable_attr_name_pattern= + + def create_enum(&block) + result = Enum.new + result.instance_eval(&block) + result + end + + def define_enum(context) + base_name = context[:base_name] + const_name = "#{base_name.upcase}_ENUM" + const_set(const_name, context[:enum]) unless const_defined?(const_name) + end + + def enum_for(attr) + base_name = enum_base_name(attr) + name = "#{base_name.upcase}_ENUM" + const_defined?(name) ? const_get(name) : nil + end + + def define_accessor(context) + attr = context[:attr] + return unless (instance_methods & [attr, "#{attr}="]).empty? + if context[:attr_accessor] + if context[:default] + if respond_to?(:attr_accessor_with_default) + attr_accessor_with_default(attr, context[:default]) + else + instance_var_name = "@#{attr}" + attr_writer(attr) + define_method(attr) do + value = instance_variable_get(instance_var_name) + instance_variable_set(instance_var_name, value = context[:default]) unless value + value + end + end + else + attr_accessor(attr) + end + else + if context[:default] + SelectableAttr.logger.warn(":default option ignored for #{attr}") + end + end + end + + def define_enum_class_methods(context) + base_name = context[:base_name] + enum = context[:enum] + mod = Module.new + mod.module_eval do + define_method("#{base_name}_enum"){enum} + define_method("#{base_name}_hash_array"){enum.to_hash_array} + define_method("#{base_name}_entries"){enum.entries} + define_method("#{base_name}_options"){|*ids_or_keys|enum.options(*ids_or_keys)} + define_method("#{base_name}_ids"){|*ids_or_keys| enum.ids(*ids_or_keys)} + define_method("#{base_name}_keys"){|*ids_or_keys|enum.keys(*ids_or_keys)} + define_method("#{base_name}_names"){|*ids_or_keys|enum.names(*ids_or_keys)} + define_method("#{base_name}_key_by_id"){|id|enum.key_by_id(id)} + define_method("#{base_name}_id_by_key"){|key|enum.id_by_key(key)} + define_method("#{base_name}_name_by_id"){|id|enum.name_by_id(id)} + define_method("#{base_name}_name_by_key"){|key|enum.name_by_key(key)} + define_method("#{base_name}_entry_by_id"){|id|enum.entry_by_id(id)} + define_method("#{base_name}_entry_by_key"){|key|enum.entry_by_key(key)} + end + if convertors = ENUM_ARRAY_METHODS[context[:convert_with] || :none] + mod.module_eval do + define_method("#{base_name}_to_hash_array", convertors[:to_hash_array]) + define_method("hash_array_to_#{base_name}", convertors[:to_attr_value]) + end + end + self.extend(mod) + end + + def define_enum_instance_methods(context) + attr = context[:attr] + base_name = context[:base_name] + instance_methods = <<-EOS + def #{base_name}_key + self.class.#{base_name}_key_by_id(#{attr}) + end + def #{base_name}_key=(key) + self.#{attr} = self.class.#{base_name}_id_by_key(key) + end + def #{base_name}_name + self.class.#{base_name}_name_by_id(#{attr}) + end + def #{base_name}_entry + self.class.#{base_name}_entry_by_id(#{attr}) + end + def #{base_name}_entry + self.class.#{base_name}_entry_by_id(#{attr}) + end + EOS + self.module_eval(instance_methods) + end + + def define_enum_array_instance_methods(context) + attr = context[:attr] + base_name = context[:base_name] + # ActiveRecord::Baseから継承している場合は、基本カラムに対応するメソッドはない + self.module_eval(<<-"EOS") + def #{base_name}_ids + #{base_name}_hash_array_selected.map{|hash|hash[:id]} + end + def #{base_name}_ids=(ids) + ids = ids.split(',') if ids.is_a?(String) + ids = ids ? ids.map(&:to_s) : [] + update_#{base_name}_hash_array{|hash|ids.include?(hash[:id].to_s)} + end + def #{base_name}_hash_array + self.class.#{base_name}_to_hash_array(self.class.#{base_name}_enum, #{attr}) + end + def #{base_name}_hash_array=(hash_array) + self.#{attr} = self.class.hash_array_to_#{base_name}(self.class.#{base_name}_enum, hash_array) + end + def #{base_name}_hash_array_selected + #{base_name}_hash_array.select{|hash|!!hash[:select]} + end + def update_#{base_name}_hash_array(&block) + hash_array = #{base_name}_hash_array.map do |hash| + hash.merge(:select => yield(hash)) + end + self.#{base_name}_hash_array = hash_array + end + def #{base_name}_keys + #{base_name}_hash_array_selected.map{|hash|hash[:key]} + end + def #{base_name}_keys=(keys) + update_#{base_name}_hash_array{|hash|keys.include?(hash[:key])} + end + def #{base_name}_selection + #{base_name}_hash_array.map{|hash|!!hash[:select]} + end + def #{base_name}_selection=(selection) + idx = -1 + update_#{base_name}_hash_array{|hash| idx += 1; !!selection[idx]} + end + def #{base_name}_names + #{base_name}_hash_array_selected.map{|hash|hash[:name]} + end + def #{base_name}_entries + ids = #{base_name}_ids + self.class.#{base_name}_enum.select{|entry|ids.include?(entry.id)} + end + EOS + end + end + end +end diff --git a/vendor/plugins/selectable_attr/lib/selectable_attr/enum.rb b/vendor/plugins/selectable_attr/lib/selectable_attr/enum.rb new file mode 100644 index 0000000..66b751a --- /dev/null +++ b/vendor/plugins/selectable_attr/lib/selectable_attr/enum.rb @@ -0,0 +1,168 @@ +# -*- coding: utf-8 -*- +module SelectableAttr + + class Enum + include Enumerable + + class << self + def instances + @@instances ||= [] + end + end + + def initialize(&block) + @entries = [] + instance_eval(&block) if block_given? + SelectableAttr::Enum.instances << self + end + + def entries + @entries + end + + def each(&block) + entries.each(&block) + end + + def define(id, key, name, options = nil, &block) + entry = Entry.new(self, id, key, name, options, &block) + entry.instance_variable_set(:@defined_in_code, true) + @entries << entry + end + alias_method :entry, :define + + def i18n_scope(*path) + @i18n_scope = path unless path.empty? + @i18n_scope + end + + def match_entry(entry, value, *attrs) + attrs.any?{|attr| entry[attr].to_s == value.to_s} + end + + def entry_by(value, *attrs) + entries.detect{|entry| match_entry(entry, value, *attrs)} || Entry::NULL + end + + def entry_by_id(id) + entry_by(id, :id) + end + + def entry_by_key(key) + entry_by(key, :key) + end + + def entry_by_id_or_key(id_or_key) + entry_by(id_or_key, :id, :key) + end + + def entry_by_hash(attrs) + entries.detect{|entry| attrs.all?{|(attr, value)| entry[attr].to_s == value.to_s }} || Entry::NULL + end + + def [](arg) + arg.is_a?(Hash) ? entry_by_hash(arg) : entry_by_id_or_key(arg) + end + + def values(*args) + args = args.empty? ? [:name, :id] : args + result = entries.collect{|entry| args.collect{|arg| entry.send(arg) }} + (args.length == 1) ? result.flatten : result + end + + def map_attrs(attrs, *ids_or_keys) + if attrs.is_a?(Array) + ids_or_keys.empty? ? + entries.map{|entry| attrs.map{|attr|entry.send(attr)}} : + ids_or_keys.map do |id_or_key| + entry = entry_by_id_or_key(id_or_key) + attrs.map{|attr|entry.send(attr)} + end + else + attr = attrs + ids_or_keys.empty? ? + entries.map(&attr.to_sym) : + ids_or_keys.map{|id_or_key|entry_by_id_or_key(id_or_key).send(attr)} + end + end + + def ids(*ids_or_keys); map_attrs(:id, *ids_or_keys); end + def keys(*ids_or_keys); map_attrs(:key, *ids_or_keys); end + def names(*ids_or_keys); map_attrs(:name, *ids_or_keys); end + def options(*ids_or_keys); map_attrs([:name, :id], *ids_or_keys); end + + def key_by_id(id); entry_by_id(id).key; end + def id_by_key(key); entry_by_key(key).id; end + def name_by_id(id); entry_by_id(id).name; end + def name_by_key(key); entry_by_key(key).name; end + + def find(options = nil, &block) + entries.detect{|entry| + block_given? ? yield(entry) : entry.match?(options) + } || Entry::NULL + end + + def to_hash_array + entries.map do |entry| + result = entry.to_hash + yield(result) if defined? yield + result + end + end + + def length + entries.length + end + alias_method :size, :length + + class Entry + BASE_ATTRS = [:id, :key, :name] + attr_reader :id, :key + attr_reader :defined_in_code + def initialize(enum, id, key, name, options = nil, &block) + @enum = enum + @id = id + @key = key + @name = name + @options = options + self.instance_eval(&block) if block + end + + attr_reader :name + + def [](option_key) + BASE_ATTRS.include?(option_key) ? send(option_key) : + @options ? @options[option_key] : nil + end + + def match?(options) + @options === options + end + + def null? + false + end + + def null_object? + self.null? + end + + def to_hash + (@options || {}).merge(:id => @id, :key => @key, :name => name) + end + + def inspect + # object_idを2倍にしているのは通常のinspectと合わせるためです。 + '#<%s:%x @id=%s, @key=%s, @name=%s, @options=%s>' % [ + self.class.name, object_id * 2, id.inspect, key.inspect, name.inspect, @options.inspect] + end + + NULL = new(nil, nil, nil, nil) do + def null?; true; end + def name; nil; end + end + end + + end + +end diff --git a/vendor/plugins/selectable_attr/lib/selectable_attr/version.rb b/vendor/plugins/selectable_attr/lib/selectable_attr/version.rb new file mode 100644 index 0000000..102a9d4 --- /dev/null +++ b/vendor/plugins/selectable_attr/lib/selectable_attr/version.rb @@ -0,0 +1,3 @@ +module SelectableAttr + VERSION = '0.0.3' +end diff --git a/vendor/plugins/selectable_attr/selectable_attr.gemspec b/vendor/plugins/selectable_attr/selectable_attr.gemspec new file mode 100644 index 0000000..8b1165c --- /dev/null +++ b/vendor/plugins/selectable_attr/selectable_attr.gemspec @@ -0,0 +1,54 @@ +# -*- encoding: utf-8 -*- + +Gem::Specification.new do |s| + s.name = %q{selectable_attr} + s.version = "0.3.7" + + s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version= + s.authors = ["Takeshi Akima"] + s.date = %q{2009-08-17} + s.description = %q{selectable_attr generates extra methods dynamically for attribute which has options} + s.email = %q{akima@gmail.com} + s.extra_rdoc_files = [ + "README" + ] + s.files = [ + ".gitignore", + "MIT-LICENSE", + "README", + "Rakefile", + "VERSION.yml", + "init.rb", + "install.rb", + "lib/selectable_attr.rb", + "lib/selectable_attr/base.rb", + "lib/selectable_attr/enum.rb", + "lib/selectable_attr/version.rb", + "selectable_attr.gemspec", + "spec/selectable_attr_base_alias_spec.rb", + "spec/selectable_attr_enum_spec.rb", + "spec/spec_helper.rb", + "tasks/selectable_attr_tasks.rake", + "uninstall.rb" + ] + s.homepage = %q{http://github.com/akm/selectable_attr/} + s.rdoc_options = ["--charset=UTF-8"] + s.require_paths = ["lib"] + s.rubygems_version = %q{1.3.4} + s.summary = %q{selectable_attr generates extra methods dynamically} + s.test_files = [ + "spec/selectable_attr_base_alias_spec.rb", + "spec/selectable_attr_enum_spec.rb", + "spec/spec_helper.rb" + ] + + if s.respond_to? :specification_version then + current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION + s.specification_version = 3 + + if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then + else + end + else + end +end diff --git a/vendor/plugins/selectable_attr/spec/selectable_attr_base_alias_spec.rb b/vendor/plugins/selectable_attr/spec/selectable_attr_base_alias_spec.rb new file mode 100644 index 0000000..3346147 --- /dev/null +++ b/vendor/plugins/selectable_attr/spec/selectable_attr_base_alias_spec.rb @@ -0,0 +1,439 @@ +# -*- coding: utf-8 -*- +require File.join(File.dirname(__FILE__), 'spec_helper') +require 'stringio' + +describe SelectableAttr do + + def assert_enum_class_methods(klass, attr = :enum1) + klass.send("#{attr}_enum").length.should == 3 + expected_hash_array = [ + {:id => 1, :key => :entry1, :name => "エントリ1"}, + {:id => 2, :key => :entry2, :name => "エントリ2"}, + {:id => 3, :key => :entry3, :name => "エントリ3"} + ] + klass.send("#{attr}_hash_array").should == expected_hash_array + klass.send("#{attr}_enum").to_hash_array.should == expected_hash_array + klass.send("#{attr}_entries"). + map{|entry| {:id => entry.id, :key => entry.key, :name => entry.name} }. + should == expected_hash_array + + klass.send("#{attr}_ids").should == [1,2,3] + klass.send("#{attr}_ids", :entry2, :entry3).should == [2,3] + klass.send("#{attr}_keys").should == [:entry1, :entry2, :entry3] + klass.send("#{attr}_keys", 1,3).should == [:entry1, :entry3] + klass.send("#{attr}_names").should == ["エントリ1", "エントリ2", "エントリ3"] + klass.send("#{attr}_names", 1,2).should == ["エントリ1", "エントリ2"] + klass.send("#{attr}_names", :entry1, :entry2).should == ["エントリ1", "エントリ2"] + klass.send("#{attr}_options").should == [['エントリ1', 1], ['エントリ2', 2], ['エントリ3', 3]] + klass.send("#{attr}_options", :entry2, :entry3).should == [['エントリ2', 2], ['エントリ3', 3]] + klass.send("#{attr}_options", 1,2).should == [['エントリ1', 1], ['エントリ2', 2]] + + klass.send("#{attr}_id_by_key", nil).should be_nil + klass.send("#{attr}_id_by_key", :entry1).should == 1 + klass.send("#{attr}_id_by_key", :entry2).should == 2 + klass.send("#{attr}_id_by_key", :entry3).should == 3 + klass.send("#{attr}_name_by_key", nil).should be_nil + klass.send("#{attr}_name_by_key", :entry1).should == "エントリ1" + klass.send("#{attr}_name_by_key", :entry2).should == "エントリ2" + klass.send("#{attr}_name_by_key", :entry3).should == "エントリ3" + + klass.send("#{attr}_key_by_id", nil).should be_nil + klass.send("#{attr}_key_by_id", 1).should == :entry1 + klass.send("#{attr}_key_by_id", 2).should == :entry2 + klass.send("#{attr}_key_by_id", 3).should == :entry3 + klass.send("#{attr}_name_by_id", nil).should be_nil + klass.send("#{attr}_name_by_id", 1).should == "エントリ1" + klass.send("#{attr}_name_by_id", 2).should == "エントリ2" + klass.send("#{attr}_name_by_id", 3).should == "エントリ3" + end + + def assert_single_enum_instance_methods(obj, attr = :enum1) + obj.send("#{attr}=", 1) + obj.send(attr).should == 1 + obj.enum1_key.should == :entry1 + obj.enum1_name.should == "エントリ1" + obj.enum1_entry.to_hash.should == {:id => 1, :key => :entry1, :name => "エントリ1"} + + obj.enum1_key = :entry2 + obj.send(attr).should == 2 + obj.enum1_key.should == :entry2 + obj.enum1_name.should == "エントリ2" + obj.enum1_entry.to_hash.should == {:id => 2, :key => :entry2, :name => "エントリ2"} + + obj.send("#{attr}=", 3) + obj.send(attr).should == 3 + obj.enum1_key.should == :entry3 + obj.enum1_name.should == "エントリ3" + obj.enum1_entry.to_hash.should == {:id => 3, :key => :entry3, :name => "エントリ3"} + end + + class EnumBase + include ::SelectableAttr::Base + end + + describe "selectable_attr with default" do + class EnumMock1 < EnumBase + selectable_attr :enum1, :default => 2 do + entry 1, :entry1, "エントリ1" + entry 2, :entry2, "エントリ2" + entry 3, :entry3, "エントリ3" + end + end + + class EnumMock1WithEnum < EnumBase + selectable_attr :enum1, :default => 2 do + entry 1, :entry1, "エントリ1" + entry 2, :entry2, "エントリ2" + entry 3, :entry3, "エントリ3" + end + end + + it "test_selectable_attr1" do + assert_enum_class_methods(EnumMock1) + mock1 = EnumMock1.new + mock1.enum1.should == 2 + assert_single_enum_instance_methods(mock1) + + assert_enum_class_methods(EnumMock1WithEnum) + mock1 = EnumMock1WithEnum.new + mock1.enum1.should == 2 + assert_single_enum_instance_methods(mock1) + + EnumMock1.selectable_attr_type_for(:enum1).should == :single + EnumMock1WithEnum.selectable_attr_type_for(:enum1).should == :single + end + end + + + describe "attr_enumeable_base" do + class EnumMock2 < EnumBase + attr_enumeable_base do |attr| + attr.to_s.gsub(/(.*)_code(.*)$/){"#{$1}#{$2}"} + end + + selectable_attr :enum_code1 do + entry 1, :entry1, "エントリ1" + entry 2, :entry2, "エントリ2" + entry 3, :entry3, "エントリ3" + end + end + + class EnumMock2WithEnum < EnumBase + attr_enumeable_base do |attr| + attr.to_s.gsub(/(.*)_code(.*)$/){"#{$1}#{$2}"} + end + + enum :enum_code1 do + entry 1, :entry1, "エントリ1" + entry 2, :entry2, "エントリ2" + entry 3, :entry3, "エントリ3" + end + end + + it "test_selectable_attr2" do + assert_enum_class_methods(EnumMock2) + assert_single_enum_instance_methods(EnumMock2.new, :enum_code1) + assert_enum_class_methods(EnumMock2WithEnum) + assert_single_enum_instance_methods(EnumMock2WithEnum.new, :enum_code1) + end + end + + + def assert_multi_enum_instance_methods(obj, patterns) + obj.enum_array1_hash_array.should == [ + {:id => 1, :key => :entry1, :name => "エントリ1", :select => false}, + {:id => 2, :key => :entry2, :name => "エントリ2", :select => false}, + {:id => 3, :key => :entry3, :name => "エントリ3", :select => false} + ] + obj.enum_array1_selection.should == [false, false, false] + obj.enum_array1.should be_nil + obj.enum_array1_entries.should == [] + obj.enum_array1_keys.should == [] + obj.enum_array1_names.should == [] + + obj.enum_array1 = patterns[0] + obj.enum_array1.should == patterns[0] + obj.enum_array1_hash_array.should == [ + {:id => 1, :key => :entry1, :name => "エントリ1", :select => false}, + {:id => 2, :key => :entry2, :name => "エントリ2", :select => false}, + {:id => 3, :key => :entry3, :name => "エントリ3", :select => false} + ] + obj.enum_array1_selection.should == [false, false, false] + obj.enum_array1_entries.should == [] + obj.enum_array1_keys.should == [] + obj.enum_array1_names.should == [] + + obj.enum_array1 = patterns[1] + obj.enum_array1.should == patterns[1] + obj.enum_array1_hash_array.should == [ + {:id => 1, :key => :entry1, :name => "エントリ1", :select => false}, + {:id => 2, :key => :entry2, :name => "エントリ2", :select => false}, + {:id => 3, :key => :entry3, :name => "エントリ3", :select => true} + ] + obj.enum_array1_selection.should == [false, false, true] + obj.enum_array1_entries.map(&:id).should == [3] + obj.enum_array1_keys.should == [:entry3] + obj.enum_array1_names.should == ['エントリ3'] + + obj.enum_array1 = patterns[3] + obj.enum_array1.should == patterns[3] + obj.enum_array1_hash_array.should == [ + {:id => 1, :key => :entry1, :name => "エントリ1", :select => false}, + {:id => 2, :key => :entry2, :name => "エントリ2", :select => true}, + {:id => 3, :key => :entry3, :name => "エントリ3", :select => true} + ] + obj.enum_array1_selection.should == [false, true, true] + obj.enum_array1_entries.map(&:id).should == [2, 3] + obj.enum_array1_keys.should == [:entry2, :entry3] + obj.enum_array1_names.should == ['エントリ2', 'エントリ3'] + + obj.enum_array1 = patterns[7] + obj.enum_array1.should == patterns[7] + obj.enum_array1_hash_array.should == [ + {:id => 1, :key => :entry1, :name => "エントリ1", :select => true}, + {:id => 2, :key => :entry2, :name => "エントリ2", :select => true}, + {:id => 3, :key => :entry3, :name => "エントリ3", :select => true} + ] + obj.enum_array1_selection.should == [true, true, true] + obj.enum_array1_ids.should == [1, 2, 3] + obj.enum_array1_entries.map(&:id).should == [1, 2, 3] + obj.enum_array1_keys.should == [:entry1, :entry2, :entry3] + obj.enum_array1_names.should == ['エントリ1', 'エントリ2', 'エントリ3'] + + obj.enum_array1_ids = [1,3]; obj.enum_array1.should == patterns[5] + obj.enum_array1_ids = [1,2]; obj.enum_array1.should == patterns[6] + obj.enum_array1_ids = [2]; obj.enum_array1.should == patterns[2] + + obj.enum_array1_keys = [:entry1,:entry3]; obj.enum_array1.should == patterns[5] + obj.enum_array1_keys = [:entry1,:entry2]; obj.enum_array1.should == patterns[6] + obj.enum_array1_keys = [:entry2]; obj.enum_array1.should == patterns[2] + + obj.enum_array1_selection = [true, false, true]; obj.enum_array1.should == patterns[5] + obj.enum_array1_selection = [true, true, false]; obj.enum_array1.should == patterns[6] + obj.enum_array1_selection = [false, true, false]; obj.enum_array1.should == patterns[2] + + obj.enum_array1_ids = "1,3"; obj.enum_array1.should == patterns[5] + obj.enum_array1_ids = "1,2"; obj.enum_array1.should == patterns[6] + obj.enum_array1_ids = "2"; obj.enum_array1.should == patterns[2] + end + + describe ":convert_with => :binary_string" do + class EnumMock3 < EnumBase + multi_selectable_attr :enum_array1, :convert_with => :binary_string do + entry 1, :entry1, "エントリ1" + entry 2, :entry2, "エントリ2" + entry 3, :entry3, "エントリ3" + end + end + + class EnumMock3WithEnumArray < EnumBase + enum_array :enum_array1, :convert_with => :binary_string do + entry 1, :entry1, "エントリ1" + entry 2, :entry2, "エントリ2" + entry 3, :entry3, "エントリ3" + end + end + + it "test_multi_selectable_attr_with_binary_string" do + expected = (0..7).map{|i| '%-03b' % i} # ["000", "001", "010", "011", "100", "101", "110", "111"] + assert_enum_class_methods(EnumMock3, :enum_array1) + assert_multi_enum_instance_methods(EnumMock3.new, expected) + assert_enum_class_methods(EnumMock3WithEnumArray, :enum_array1) + assert_multi_enum_instance_methods(EnumMock3WithEnumArray.new, expected) + EnumMock3.selectable_attr_type_for(:enum_array1).should == :multi + end + end + + describe "multi_selectable_attr" do + class EnumMock4 < EnumBase + multi_selectable_attr :enum_array1 do + entry 1, :entry1, "エントリ1" + entry 2, :entry2, "エントリ2" + entry 3, :entry3, "エントリ3" + end + end + + class EnumMock4WithEnumArray < EnumBase + enum_array :enum_array1 do + entry 1, :entry1, "エントリ1" + entry 2, :entry2, "エントリ2" + entry 3, :entry3, "エントリ3" + end + end + + it "test_multi_selectable_attr2" do + # [[], [3], [2], [2, 3], [1], [1, 3], [1, 2], [1, 2, 3]] + expected = + (0..7).map do |i| + s = '%03b' % i + a = s.split('').map{|v| v.to_i} + ret = [] + a.each_with_index{|val, pos| ret << pos + 1 if val == 1} + ret + end + assert_enum_class_methods(EnumMock4, :enum_array1) + assert_multi_enum_instance_methods(EnumMock4.new, expected) + assert_enum_class_methods(EnumMock4WithEnumArray, :enum_array1) + assert_multi_enum_instance_methods(EnumMock4WithEnumArray.new, expected) + end + end + + describe "convert_with" do + class EnumMock5 < EnumBase + multi_selectable_attr :enum_array1, :convert_with => :comma_string do + entry 1, :entry1, "エントリ1" + entry 2, :entry2, "エントリ2" + entry 3, :entry3, "エントリ3" + end + end + + class EnumMock5WithEnumArray < EnumBase + enum_array :enum_array1, :convert_with => :comma_string do + entry 1, :entry1, "エントリ1" + entry 2, :entry2, "エントリ2" + entry 3, :entry3, "エントリ3" + end + end + + it "test_multi_selectable_attr_with_comma_string" do + # ["", "3", "2", "2,3", "1", "1,3", "1,2", "1,2,3"] + expected = + (0..7).map do |i| + s = '%03b' % i + a = s.split('').map{|v| v.to_i} + ret = [] + a.each_with_index{|val, pos| ret << pos + 1 if val == 1} + ret.join(',') + end + assert_enum_class_methods(EnumMock5, :enum_array1) + assert_multi_enum_instance_methods(EnumMock5.new, expected) + assert_enum_class_methods(EnumMock5WithEnumArray, :enum_array1) + assert_multi_enum_instance_methods(EnumMock5WithEnumArray.new, expected) + end + end + + describe "selectable_attr_name_pattern" do + class EnumMock6 < EnumBase + # self.selectable_attr_name_pattern = /(_cd$|_code$|_cds$|_codes$)/ + selectable_attr :category_id do + entry "01", :category1, "カテゴリ1" + entry "02", :category2, "カテゴリ2" + end + end + + class EnumMock7 < EnumBase + self.selectable_attr_name_pattern = /(_cd$|_id$|_cds$|_ids$)/ + selectable_attr :category_id do + entry "01", :category1, "カテゴリ1" + entry "02", :category2, "カテゴリ2" + end + end + + it "test_selectable_attr_name_pattern" do + EnumMock6.selectable_attr_name_pattern.should == /(_cd$|_code$|_cds$|_codes$)/ + EnumMock6.respond_to?(:category_enum).should == false + EnumMock6.respond_to?(:category_id_enum).should == true + EnumMock6.new.respond_to?(:category_key).should == false + EnumMock6.new.respond_to?(:category_id_key).should == true + + EnumMock7.selectable_attr_name_pattern.should == /(_cd$|_id$|_cds$|_ids$)/ + EnumMock7.respond_to?(:category_enum).should == true + EnumMock7.respond_to?(:category_id_enum).should == false + EnumMock7.new.respond_to?(:category_key).should == true + EnumMock7.new.respond_to?(:category_id_key).should == false + end + end + + describe "has_attr" do + class ConnectableMock1 < EnumBase + class << self + def columns; end + def connection; end + def connectec?; end + def table_exists?; end + end + end + + it "should return false if column does exist" do + ConnectableMock1.should_receive(:connected?).and_return(true) + ConnectableMock1.should_receive(:table_exists?).and_return(true) + ConnectableMock1.should_receive(:columns).and_return([mock(:column1, :name => :column1)]) + ConnectableMock1.has_attr(:column1).should == true + end + + it "should return false if column doesn't exist" do + ConnectableMock1.should_receive(:connected?).and_return(true) + ConnectableMock1.should_receive(:table_exists?).and_return(true) + ConnectableMock1.should_receive(:columns).and_return([mock(:column1, :name => :column1)]) + ConnectableMock1.has_attr(:unknown_column).should == false + end + + it "should return nil unless connected" do + ConnectableMock1.should_receive(:connected?).and_return(false) + ConnectableMock1.should_receive(:table_exists?).and_return(false) + ConnectableMock1.has_attr(:unknown_column).should == false + end + + it "should return nil if can't connection cause of Exception" do + ConnectableMock1.should_receive(:connected?).twice.and_return(false) + ConnectableMock1.should_receive(:connection).and_raise(IOError.new("can't connect to DB")) + ConnectableMock1.has_attr(:unknown_column).should == nil + end + end + + describe "enum_for" do + class EnumMock10 < EnumBase + selectable_attr :enum1, :default => 2 do + entry 1, :entry1, "エントリ1" + entry 2, :entry2, "エントリ2" + entry 3, :entry3, "エントリ3" + end + end + + it "return constant by Symbol access" do + enum1 = EnumMock10.enum_for(:enum1) + enum1.class.should == SelectableAttr::Enum + end + + it "return constant by String access" do + enum1 = EnumMock10.enum_for('enum1') + enum1.class.should == SelectableAttr::Enum + end + + it "return nil for unexist attr" do + enum1 = EnumMock10.enum_for('unexist_attr') + enum1.should == nil + end + + end + + describe "define_accessor" do + class DefiningMock1 < EnumBase + class << self + def attr_accessor_with_default(*args); end + end + end + + it "should call attr_accessor_with_default when both of attr_accessor and default are given" do + DefiningMock1.should_receive(:attr_accessor_with_default).with(:enum1, 1) + DefiningMock1.define_accessor(:attr => :enum1, :attr_accessor => true, :default => 1) + end + + it "should call attr_accessor_with_default when default are given but attr_accessor is not TRUE" do + SelectableAttr.logger.should_receive(:warn).with(":default option ignored for enum1") + DefiningMock1.define_accessor(:attr => :enum1, :attr_accessor => false, :default => 1) + end + + it "warning message to logger" do + io = StringIO.new + SelectableAttr.logger = Logger.new(io) + DefiningMock1.define_accessor(:attr => :enum1, :attr_accessor => false, :default => 1) + io.rewind + io.read.should =~ /WARN -- : :default option ignored for enum1$/ + end + + + end + + +end diff --git a/vendor/plugins/selectable_attr/spec/selectable_attr_enum_spec.rb b/vendor/plugins/selectable_attr/spec/selectable_attr_enum_spec.rb new file mode 100644 index 0000000..18b22f4 --- /dev/null +++ b/vendor/plugins/selectable_attr/spec/selectable_attr_enum_spec.rb @@ -0,0 +1,139 @@ +# -*- coding: utf-8 -*- +require File.join(File.dirname(__FILE__), 'spec_helper') + +describe SelectableAttr::Enum do + + Enum1 = SelectableAttr::Enum.new do + entry 1, :book, '書籍' + entry 2, :dvd, 'DVD' + entry 3, :cd, 'CD' + entry 4, :vhs, 'VHS' + end + + it "test_define" do + Enum1[1].id.should == 1 + Enum1[2].id.should == 2 + Enum1[3].id.should == 3 + Enum1[4].id.should == 4 + Enum1[1].key.should == :book + Enum1[2].key.should == :dvd + Enum1[3].key.should == :cd + Enum1[4].key.should == :vhs + Enum1[1].name.should == '書籍' + Enum1[2].name.should == 'DVD' + Enum1[3].name.should == 'CD' + Enum1[4].name.should == 'VHS' + + Enum1[:book].id.should == 1 + Enum1[:dvd ].id.should == 2 + Enum1[:cd ].id.should == 3 + Enum1[:vhs ].id.should == 4 + Enum1[:book].key.should == :book + Enum1[:dvd ].key.should == :dvd + Enum1[:cd ].key.should == :cd + Enum1[:vhs ].key.should == :vhs + Enum1[:book].name.should == '書籍' + Enum1[:dvd].name.should == 'DVD' + Enum1[:cd].name.should == 'CD' + Enum1[:vhs].name.should == 'VHS' + + Enum1.values.should == [['書籍', 1], ['DVD', 2], ['CD', 3], ['VHS', 4]] + Enum1.values(:name, :id).should == [['書籍', 1], ['DVD', 2], ['CD', 3], ['VHS', 4]] + Enum1.values(:name, :key).should == [['書籍', :book], ['DVD', :dvd], ['CD', :cd], ['VHS', :vhs]] + end + + InetAccess = SelectableAttr::Enum.new do + entry 1, :email, 'Eメール', :protocol => 'mailto:' + entry 2, :website, 'ウェブサイト', :protocol => 'http://' + entry 3, :ftp, 'FTP', :protocol => 'ftp://' + end + + it "test_define_with_options" do + InetAccess[1].id.should == 1 + InetAccess[2].id.should == 2 + InetAccess[3].id.should == 3 + InetAccess[1].key.should == :email + InetAccess[2].key.should == :website + InetAccess[3].key.should == :ftp + + + InetAccess[1].name.should == 'Eメール' + InetAccess[2].name.should == 'ウェブサイト' + InetAccess[3].name.should == 'FTP' + InetAccess[1][:protocol].should == 'mailto:' + InetAccess[2][:protocol].should == 'http://' + InetAccess[3][:protocol].should == 'ftp://' + + InetAccess[9].id.should be_nil + InetAccess[9].key.should be_nil + InetAccess[9].name.should be_nil + InetAccess[9][:protocol].should be_nil + InetAccess[9][:xxxx].should be_nil + end + + it "test_get_by_option" do + InetAccess[:protocol => 'mailto:'].should == InetAccess[1] + InetAccess[:protocol => 'http://'].should == InetAccess[2] + InetAccess[:protocol => 'ftp://'].should == InetAccess[3] + end + + it "test_null?" do + InetAccess[1].null?.should == false + InetAccess[2].null?.should == false + InetAccess[3].null?.should == false + InetAccess[9].null?.should == true + InetAccess[:protocol => 'mailto:'].null?.should == false + InetAccess[:protocol => 'http://'].null?.should == false + InetAccess[:protocol => 'ftp://'].null?.should == false + InetAccess[:protocol => 'svn://'].null?.should == true + end + + it "test_null_object?" do + InetAccess[1].null_object?.should == false + InetAccess[2].null_object?.should == false + InetAccess[3].null_object?.should == false + InetAccess[9].null_object?.should == true + InetAccess[:protocol => 'mailto:'].null_object?.should == false + InetAccess[:protocol => 'http://'].null_object?.should == false + InetAccess[:protocol => 'ftp://'].null_object?.should == false + InetAccess[:protocol => 'svn://'].null_object?.should == true + end + + it "test_to_hash_array" do + Enum1.to_hash_array.should == [ + {:id => 1, :key => :book, :name => '書籍'}, + {:id => 2, :key => :dvd, :name => 'DVD'}, + {:id => 3, :key => :cd, :name => 'CD'}, + {:id => 4, :key => :vhs, :name => 'VHS'} + ] + + InetAccess.to_hash_array.should == [ + {:id => 1, :key => :email, :name => 'Eメール', :protocol => 'mailto:'}, + {:id => 2, :key => :website, :name => 'ウェブサイト', :protocol => 'http://'}, + {:id => 3, :key => :ftp, :name => 'FTP', :protocol => 'ftp://'} + ] + end + + describe "find" do + it "with options" do + InetAccess.find(:protocol => 'http://').should == InetAccess[2] + InetAccess.find(:protocol => 'svn+ssh://').should == SelectableAttr::Enum::Entry::NULL + end + + it "with block" do + InetAccess.find{|entry| entry.key.to_s =~ /tp/}.should == InetAccess[3] + InetAccess.find{|entry| entry.key.to_s =~ /XXXXXX/}.should == SelectableAttr::Enum::Entry::NULL + end + end + + describe :inspect do + it "NULL" do + SelectableAttr::Enum::Entry::NULL.inspect.should =~ /\#/ + end + + it "valid" do + InetAccess[1].inspect.should =~ /\#"mailto:"\}>/ + end + end + +end diff --git a/vendor/plugins/selectable_attr/spec/spec_helper.rb b/vendor/plugins/selectable_attr/spec/spec_helper.rb new file mode 100644 index 0000000..e761d9b --- /dev/null +++ b/vendor/plugins/selectable_attr/spec/spec_helper.rb @@ -0,0 +1,11 @@ +$KCODE='u' + +$LOAD_PATH << File.join(File.dirname(__FILE__), '..', 'lib') +require File.join(File.dirname(__FILE__), '..', 'init') + +def assert_hash(expected, actual) + keys = (expected.keys + actual.keys).uniq + keys.each do |key| + assert_equal expected[key], actual[key], "unmatch value for #{key.inspect}" + end +end diff --git a/vendor/plugins/selectable_attr/tasks/selectable_attr_tasks.rake b/vendor/plugins/selectable_attr/tasks/selectable_attr_tasks.rake new file mode 100644 index 0000000..5252188 --- /dev/null +++ b/vendor/plugins/selectable_attr/tasks/selectable_attr_tasks.rake @@ -0,0 +1,18 @@ +require 'yaml' +require 'yaml_waml' + +namespace :i18n do + namespace :selectable_attr do + task :load_all_models => :environment do + Dir.glob(File.join(RAILS_ROOT, 'app', 'models', '**', '*.rb')) do |file_name| + require file_name + end + end + + desc "Export i18n resources for selectable_attr entries" + task :export => :"i18n:selectable_attr:load_all_models" do + obj = {I18n.locale => SelectableAttr::Enum.i18n_export} + puts YAML.dump(obj) + end + end +end diff --git a/vendor/plugins/selectable_attr/uninstall.rb b/vendor/plugins/selectable_attr/uninstall.rb new file mode 100644 index 0000000..9738333 --- /dev/null +++ b/vendor/plugins/selectable_attr/uninstall.rb @@ -0,0 +1 @@ +# Uninstall hook code here diff --git a/vendor/plugins/selectable_attr_rails/MIT-LICENSE b/vendor/plugins/selectable_attr_rails/MIT-LICENSE new file mode 100644 index 0000000..a6a8191 --- /dev/null +++ b/vendor/plugins/selectable_attr_rails/MIT-LICENSE @@ -0,0 +1,20 @@ +Copyright (c) 2008 Takeshi AKIMA + +Permission is hereby granted, free of charge, to any person obtaining +a copy of this software and associated documentation files (the +"Software"), to deal in the Software without restriction, including +without limitation the rights to use, copy, modify, merge, publish, +distribute, sublicense, and/or sell copies of the Software, and to +permit persons to whom the Software is furnished to do so, subject to +the following conditions: + +The above copyright notice and this permission notice shall be +included in all copies or substantial portions of the Software. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND +NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE +LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION +OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION +WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. diff --git a/vendor/plugins/selectable_attr_rails/README b/vendor/plugins/selectable_attr_rails/README new file mode 100644 index 0000000..a27b482 --- /dev/null +++ b/vendor/plugins/selectable_attr_rails/README @@ -0,0 +1,204 @@ += SelectableAttrRails + +== Introduction +selectable_attr_railsは、selectable_attrをRailsで使うときに便利なヘルパーメソッドを提供し、 +エントリをDBから取得したり、I18n対応するものです。 +http://github.com/akm/selectable_attr_rails/tree/master + +selectable_attr は、コードが割り振られるような特定の属性について*コード*、*プログラム上での名前*、 +*表示するための名前*などをまとめて管理するものです。 +http://github.com/akm/selectable_attr/tree/master + + +== Install +=== a. plugin install + ruby script/plugin install git://github.com/akm/selectable_attr.git + ruby script/plugin install git://github.com/akm/selectable_attr_rails.git + +=== b. gem install + [sudo] gem install selectable_attr_rails + +config/initializers/selectable_attr.rb + require 'selectable_attr' + require 'selectable_attr_i18n' + require 'selectable_attr_rails' + SelectableAttrRails.setup + + +== チュートリアル + +=== selectヘルパーメソッド +以下のようなモデルが定義してあった場合 + class Person < ActiveRecord::Base + include ::SelectableAttr::Base + + selectable_attr :gender do + entry '1', :male, '男性' + entry '2', :female, '女性' + entry '9', :other, 'その他' + end + end + +ビューでは以下のように選択肢を表示することができます。 + <% form_for(:person) do |f| %> + <%= f.select :gender %> + <% end %> + +form_for、fields_forを使用しない場合でも、オブジェクト名を設定して使用可能です。 + <%= select :person, :gender %> + +また以下のように複数の値を取りうる場合にもこのメソドを使用することが可能です。 + class RoomSearch + include ::SelectableAttr::Base + + multi_selectable_attr :room_type do + entry '01', :single, 'シングル' + entry '02', :twin, 'ツイン' + entry '03', :double, 'ダブル' + entry '04', :triple, 'トリプル' + end + end + + <% form_for(:room_search) do |f| %> + <%= f.select :room_type %> + <% end %> + +この場合、出力されるselectタグのmultiple属性が設定されます。 + + + +=== radio_button_groupヘルパーメソッド + 一つだけ値を選択するUIの場合、selectメソッドではなくを出力することも可能です。 + 上記Personモデルの場合 + + <% form_for(:person) do |f| %> + <%= f.radio_button_group :gender %> + <% end %> + + この場合、