selectable_attr_rails 0.3.7

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ /*.gem
2
+ /coverage
3
+ /pkg
4
+ /rdoc
5
+ /selectable_attr_test.sqlite3.db
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2008 Takeshi AKIMA
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README ADDED
@@ -0,0 +1,198 @@
1
+ = SelectableAttrRails
2
+
3
+ == Introduction
4
+ selectable_attr_railsは、selectable_attrをRailsで使うときに便利なヘルパーメソッドを提供し、
5
+ エントリをDBから取得したり、I18n対応するものです。
6
+ http://github.com/akm/selectable_attr_rails/tree/master
7
+
8
+ selectable_attr は、コードが割り振られるような特定の属性について*コード*、*プログラム上での名前*、
9
+ *表示するための名前*などをまとめて管理するものです。
10
+ http://github.com/akm/selectable_attr/tree/master
11
+
12
+
13
+ == Install
14
+ === 1. Railsプロジェクトで使う場合
15
+ ==== a. plugin install
16
+ ruby script/plugin install git://github.com/akm/selectable_attr.git
17
+ ruby script/plugin install git://github.com/akm/selectable_attr_rails.git
18
+
19
+ ==== b. gem install
20
+ [sudo] gem install akimatter-selectable_attr akimatter-selectable_attr_rails -s http://gems.github .com
21
+
22
+ == チュートリアル
23
+
24
+ === selectヘルパーメソッド
25
+ 以下のようなモデルが定義してあった場合
26
+ class Person < ActiveRecord::Base
27
+ include ::SelectableAttr::Base
28
+
29
+ selectable_attr :gender do
30
+ entry '1', :male, '男性'
31
+ entry '2', :female, '女性'
32
+ entry '9', :other, 'その他'
33
+ end
34
+ end
35
+
36
+ ビューでは以下のように選択肢を表示することができます。
37
+ <% form_for(:person) do |f| %>
38
+ <%= f.select :gender %>
39
+ <% end %>
40
+
41
+ form_for、fields_forを使用しない場合でも、オブジェクト名を設定して使用可能です。
42
+ <%= select :person, :gender %>
43
+
44
+ また以下のように複数の値を取りうる場合にもこのメソドを使用することが可能です。
45
+ class RoomSearch
46
+ include ::SelectableAttr::Base
47
+
48
+ multi_selectable_attr :room_type do
49
+ entry '01', :single, 'シングル'
50
+ entry '02', :twin, 'ツイン'
51
+ entry '03', :double, 'ダブル'
52
+ entry '04', :triple, 'トリプル'
53
+ end
54
+ end
55
+
56
+ <% form_for(:room_search) do |f| %>
57
+ <%= f.select :room_type %>
58
+ <% end %>
59
+
60
+ この場合、出力されるselectタグのmultiple属性が設定されます。
61
+
62
+
63
+
64
+ === radio_button_groupヘルパーメソッド
65
+ 一つだけ値を選択するUIの場合、selectメソッドではなく<input type="radio".../>を出力することも可能です。
66
+ 上記Personモデルの場合
67
+
68
+ <% form_for(:person) do |f| %>
69
+ <%= f.radio_button_group :gender %>
70
+ <% end %>
71
+
72
+ この場合、<input type="radio" .../><label for="xxx">... という風に続けて出力されるので、改行などを出力したい場合は
73
+ 引数を一つ取るブロックを渡して以下のように記述します。
74
+
75
+ <% form_for(:person) do |f| %>
76
+ <% f.radio_button_group :gender do |b| %>
77
+ <% b.each do %>
78
+ <%= b.radio_button %>
79
+ <%= b.label %>
80
+ <br/>
81
+ <% end %>
82
+ <% end %>
83
+ <% end %>
84
+
85
+ f.radio_button_groupを呼び出しているERBのタグが、<%= %>から<% %>に変わっていることにご注意ください。
86
+
87
+
88
+ === check_box_groupヘルパーメソッド
89
+ 複数の値を選択するUIの場合、selectメソッドではなく<input type="checkbox".../>を出力することも可能です。
90
+ 上記RoomSearchクラスの場合
91
+
92
+ <% form_for(:room_search) do |f| %>
93
+ <%= f.check_box_group :room_type %>
94
+ <% end %>
95
+
96
+ この場合、<input type="checkbox" .../><label for="xxx">... という風に続けて出力されるので、改行などを出力したい場合は
97
+ 引数を一つ取るブロックを渡して以下のように記述します。
98
+
99
+ <% form_for(:person) do |f| %>
100
+ <% f.check_box_group :gender do |b| %>
101
+ <% b.each do %>
102
+ <%= b.check_box %>
103
+ <%= b.label %>
104
+ <br/>
105
+ <% end %>
106
+ <% end %>
107
+ <% end %>
108
+
109
+ f.check_box_groupを呼び出しているERBのタグが、<%= %>から<% %>に変わっていることにご注意ください。
110
+
111
+
112
+ == DBからのエントリの更新/追加
113
+ 各エントリの名称を実行時に変更したり、項目を追加することが可能です。
114
+
115
+ class RoomPlan < ActiveRecord::Base
116
+ include ::SelectableAttr::Base
117
+
118
+ selectable_attr :room_type do
119
+ update_by "select room_type, name from room_types"
120
+ entry '01', :single, 'シングル'
121
+ entry '02', :twin, 'ツイン'
122
+ entry '03', :double, 'ダブル'
123
+ entry '04', :triple, 'トリプル'
124
+ end
125
+ end
126
+
127
+ というモデルと
128
+
129
+ create_table "room_types" do |t|
130
+ t.string "room_type", :limit => 2
131
+ t.string "name", :limit => 20
132
+ end
133
+
134
+ というマイグレーションで作成されるテーブルがあったとします。
135
+
136
+ === エントリの追加
137
+ room_typeが"05"、nameが"4ベッド"というレコードがINSERTされた後、
138
+ RoomPlan#room_type_optionsなどのselectable_attrが提供するメソッドで
139
+ 各エントリへアクセスすると、update_byで指定されたSELECT文が実行され、
140
+ エントリとしては、
141
+ entry '05', :entry_05, '4ベッド'
142
+ が定義されている状態と同じようになります。
143
+
144
+ このようにコードで定義されていないエントリは、DELETEされると、エントリもなくなります。
145
+
146
+ === エントリの名称の更新
147
+ 実行時に名称を変えたい場合には、そのidに該当するレコードを追加/更新します。
148
+ 例えば、
149
+ room_typeが"04"、nameが"3ベッド"というレコードがINSERTされると、その後は
150
+ 04のエントリはの名称は"3ベッド"に変わり、また別の名称にUPDATEすると、それに
151
+ よってエントリの名称も変わります。
152
+
153
+ このようにコードによってエントリが定義されている場合は、DELETEされてもエントリは削除されず、
154
+ DELETE後は、名称が元に戻ります。
155
+
156
+
157
+ == I18n対応
158
+ エントリのロケールにおける名称をRails2.2からの機能である、I18nを内部的にしようして取得できます。
159
+
160
+ 上記RoomPlanモデルの場合、
161
+
162
+ config/locales/ja.yml
163
+ ja:
164
+ selectable_attrs:
165
+ room_types:
166
+ single: シングル
167
+ twin: ツイン
168
+ double: ダブル
169
+ triple: トリプル
170
+
171
+ config/locales/en.yml
172
+ en:
173
+ selectable_attrs:
174
+ room_types:
175
+ single: Single
176
+ twin: Twin
177
+ double: Double
178
+ triple: Triple
179
+
180
+ というYAMLを用意した上で、モデルを以下のように記述します。
181
+
182
+ class RoomPlan < ActiveRecord::Base
183
+ include ::SelectableAttr::Base
184
+
185
+ selectable_attr :room_type do
186
+ i18n_scope(:selectable_attrs, :room_types)
187
+ entry '01', :single, 'シングル'
188
+ entry '02', :twin, 'ツイン'
189
+ entry '03', :double, 'ダブル'
190
+ entry '04', :triple, 'トリプル'
191
+ end
192
+ end
193
+
194
+ これで、I18n.localeに設定されているロケールに従って各エントリの名称が変わります。
195
+
196
+
197
+ == Credit
198
+ Copyright (c) 2008 Takeshi AKIMA, released under the MIT lice nse
data/Rakefile ADDED
@@ -0,0 +1,57 @@
1
+ require 'rubygems'
2
+ gem 'rspec', '>= 1.1.4'
3
+ require 'rake'
4
+ require 'rake/rdoctask'
5
+ require 'spec/rake/spectask'
6
+ require 'spec/rake/verify_rcov'
7
+
8
+ desc 'Default: run unit tests.'
9
+ task :default => :spec
10
+
11
+ task :pre_commit => [:spec, 'coverage:verify']
12
+
13
+ desc 'Run all specs under spec/**/*_spec.rb'
14
+ Spec::Rake::SpecTask.new(:spec => 'coverage:clean') do |t|
15
+ t.spec_files = FileList['spec/**/*_spec.rb']
16
+ t.spec_opts = ["-c", "--diff"]
17
+ t.rcov = true
18
+ t.rcov_opts = ["--include-file", "lib\/*\.rb", "--exclude", "spec\/"]
19
+ end
20
+
21
+ desc 'Generate documentation for the selectable_attr_rails plugin.'
22
+ Rake::RDocTask.new(:rdoc) do |rdoc|
23
+ rdoc.rdoc_dir = 'rdoc'
24
+ rdoc.title = 'SelectableAttrRails'
25
+ rdoc.options << '--line-numbers' << '--inline-source'
26
+ rdoc.rdoc_files.include('README')
27
+ rdoc.rdoc_files.include('lib/**/*.rb')
28
+ end
29
+
30
+ namespace :coverage do
31
+ desc "Delete aggregate coverage data."
32
+ task(:clean) { rm_f "coverage" }
33
+
34
+ desc "verify coverage threshold via RCov"
35
+ RCov::VerifyTask.new(:verify => :spec) do |t|
36
+ t.threshold = 100.0 # Make sure you have rcov 0.7 or higher!
37
+ t.index_html = 'coverage/index.html'
38
+ end
39
+ end
40
+
41
+ begin
42
+ require 'jeweler'
43
+ Jeweler::Tasks.new do |s|
44
+ s.name = "selectable_attr_rails"
45
+ s.summary = "selectable_attr_rails makes possible to use selectable_attr in rails application"
46
+ s.description = "selectable_attr_rails makes possible to use selectable_attr in rails application"
47
+ s.email = "akima@gmail.com"
48
+ s.homepage = "http://github.com/akm/selectable_attr_rails/"
49
+ s.authors = ["Takeshi Akima"]
50
+ s.add_dependency("activesupport", ">= 2.0.2")
51
+ s.add_dependency("activerecord", ">= 2.0.2")
52
+ s.add_dependency("actionpack", ">= 2.0.2")
53
+ s.add_dependency("akm-selectable_attr", ">= 0.3.5")
54
+ end
55
+ rescue LoadError
56
+ puts "Jeweler not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
57
+ end
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :minor: 3
3
+ :patch: 7
4
+ :major: 0
data/init.rb ADDED
@@ -0,0 +1,5 @@
1
+ require 'selectable_attr'
2
+ require 'selectable_attr_i18n'
3
+ require 'selectable_attr_rails'
4
+
5
+ SelectableAttrRails.add_features_to_rails
data/install.rb ADDED
@@ -0,0 +1 @@
1
+ # Install hook code here
@@ -0,0 +1,38 @@
1
+ if defined?(I18n)
2
+ module SelectableAttr
3
+ class Enum
4
+ def self.i18n_export
5
+ result = {}
6
+ instances.each do |instance|
7
+ unless instance.i18n_scope
8
+ # puts "no i18n_scope of #{instance.inspect}"
9
+ next
10
+ end
11
+ paths = instance.i18n_scope.dup
12
+ current = result
13
+ paths.each do |path|
14
+ current = current[path.to_s] ||= {}
15
+ end
16
+ instance.entries.each do |entry|
17
+ current[entry.key.to_s] = entry.name
18
+ end
19
+ end
20
+ result
21
+ end
22
+
23
+ def i18n_scope(*path)
24
+ @i18n_scope = path unless path.empty?
25
+ @i18n_scope
26
+ end
27
+
28
+ class Entry
29
+ def name
30
+ I18n.locale.nil? ? @name :
31
+ @enum.i18n_scope.blank? ? @name :
32
+ I18n.translate(key, :scope => @enum.i18n_scope, :default => @name)
33
+ end
34
+ end
35
+
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,65 @@
1
+ module SelectableAttrRails
2
+ module DbLoadable
3
+ def update_by(*args, &block)
4
+ options = args.last.is_a?(Hash) ? args.pop : {}
5
+ options = {:when => :first_time}.update(options)
6
+ @sql_to_update = block_given? ? block : args.first
7
+ @update_timing = options[:when]
8
+ self.extend(InstanceMethods) unless respond_to?(:update_entries)
9
+ end
10
+
11
+ module Entry
12
+ attr_accessor :name_from_db
13
+ def name
14
+ @name_from_db || super
15
+ end
16
+ end
17
+
18
+ module InstanceMethods
19
+ def entries
20
+ update_entries if must_be_updated?
21
+ @entries
22
+ end
23
+
24
+ def must_be_updated?
25
+ return false if @update_timing == :never
26
+ return true if @update_timing == :everytime
27
+ end
28
+
29
+ def update_entries
30
+ unless @original_entries
31
+ @original_entries = @entries.dup
32
+ @original_entries.each do |entry|
33
+ entry.extend(SelectableAttrRails::DbLoadable::Entry)
34
+ end
35
+ end
36
+ records = nil
37
+ if @sql_to_update.respond_to?(:call)
38
+ records = @sql_to_update.call
39
+ else
40
+ @connection ||= ActiveRecord::Base.connection
41
+ records = @connection.select_rows(@sql_to_update)
42
+ end
43
+
44
+ new_entries = []
45
+ records.each do |r|
46
+ if entry = @original_entries.detect{|entry| entry.id == r.first}
47
+ entry.name_from_db = r.last unless r.last.blank?
48
+ new_entries << entry
49
+ else
50
+ entry = SelectableAttr::Enum::Entry.new(self, r.first, "entry_#{r.first}".to_sym, r.last)
51
+ new_entries << entry
52
+ end
53
+ end
54
+ @original_entries.each do |entry|
55
+ unless new_entries.include?(entry)
56
+ entry.name_from_db = nil
57
+ new_entries << entry if entry.defined_in_code
58
+ end
59
+ end
60
+ @entries = new_entries
61
+ end
62
+ end
63
+
64
+ end
65
+ end
@@ -0,0 +1,55 @@
1
+ module SelectableAttrRails::Helpers
2
+ class AbstractSelectionBuilder
3
+ attr_reader :entry_hash
4
+
5
+ def initialize(object, object_name, method, options, template)
6
+ @object, @object_name, @method = object, object_name, method
7
+ @base_name = @object.class.enum_base_name(method.to_s)
8
+ @template = template
9
+ @entry_hash = nil
10
+ @options = options || {}
11
+ @entry_hash_array = @options[:entry_hash_array]
12
+ end
13
+
14
+ def enum_hash_array_from_object
15
+ base_name = @object.class.enum_base_name(@method.to_s)
16
+ @object.send("#{base_name}_hash_array")
17
+ end
18
+
19
+ def enum_hash_array_from_class
20
+ base_name = @object.class.enum_base_name(@method.to_s)
21
+ @object.class.send("#{base_name}_hash_array")
22
+ end
23
+
24
+ def tag_id(tag)
25
+ result = nil
26
+ tag.scan(/ id\=\"(.*?)\"/){|s|result = s}
27
+ return result
28
+ end
29
+
30
+ def add_class_name(options, class_name)
31
+ (options ||= {}).stringify_keys!
32
+ (options['class'] ||= '') << ' ' << class_name
33
+ options
34
+ end
35
+
36
+ def camelize_keys(hash, first_letter = :lower)
37
+ result = {}
38
+ hash.each{|key, value|result[key.to_s.camelize(first_letter)] = value}
39
+ result
40
+ end
41
+
42
+ def update_options(dest, *options_array)
43
+ result = dest || {}
44
+ options_array.each do |options|
45
+ next unless options
46
+ if class_name = options.delete(:class)
47
+ add_class_name(result, class_name)
48
+ end
49
+ result.update(options)
50
+ end
51
+ result
52
+ end
53
+
54
+ end
55
+ end