maiha-active_record_view 0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README +25 -0
- data/Rakefile +22 -0
- data/arv/properties/boolean +4 -0
- data/arv/properties/date +5 -0
- data/arv/properties/datetime +5 -0
- data/arv/properties/default +3 -0
- data/arv/properties/integer +6 -0
- data/arv/properties/string +4 -0
- data/arv/properties/text +7 -0
- data/arv/properties/time +5 -0
- data/arv/properties/timestamp +5 -0
- data/arv/property.erb +19 -0
- data/init.rb +0 -0
- data/install.rb +1 -0
- data/lib/active_record_view.rb +2 -0
- data/lib/active_record_view/controller.rb +9 -0
- data/lib/active_record_view/default_actions.rb +29 -0
- data/lib/active_record_view/engines.rb +68 -0
- data/lib/active_record_view/helper.rb +152 -0
- data/lib/active_record_view/record_identifier.rb +42 -0
- data/lib/active_record_view/render_arv.rb +51 -0
- data/lib/active_record_view/write_exception.rb +11 -0
- data/lib/localized/model.rb +103 -0
- data/lib/localized/view_property.rb +337 -0
- data/spec/fixtures/card.rb +12 -0
- data/spec/fixtures/card/equip.rb +13 -0
- data/spec/fixtures/card/magic.rb +13 -0
- data/spec/fixtures/card/member.rb +13 -0
- data/spec/fixtures/card/unit.rb +13 -0
- data/spec/fixtures/deck.rb +2 -0
- data/spec/localized/decks.yml +39 -0
- data/spec/migrate/001_create_cards.rb +16 -0
- data/spec/migrate/002_create_decks.rb +5 -0
- data/spec/models/active_record_view_helper_spec.rb +393 -0
- data/spec/models/render_spec.rb +220 -0
- data/spec/schema.rb +30 -0
- data/spec/spec.opts +6 -0
- data/spec/spec_helper.rb +27 -0
- data/tasks/active_record_view_tasks.rake +37 -0
- data/test/active_record_view_test.rb +8 -0
- data/uninstall.rb +1 -0
- metadata +93 -0
data/README
ADDED
|
@@ -0,0 +1,25 @@
|
|
|
1
|
+
ActiveRecordView
|
|
2
|
+
================
|
|
3
|
+
|
|
4
|
+
# use :arv in controllers
|
|
5
|
+
class ApplicationController < ActionController::Base
|
|
6
|
+
include ActiveRecordView::Controller
|
|
7
|
+
end
|
|
8
|
+
|
|
9
|
+
# use :arv in views
|
|
10
|
+
module ApplicationHelper
|
|
11
|
+
include ActiveRecordView::Helper
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
|
|
15
|
+
Requirements
|
|
16
|
+
============
|
|
17
|
+
|
|
18
|
+
http://github.com/maiha/named_options
|
|
19
|
+
|
|
20
|
+
|
|
21
|
+
Author
|
|
22
|
+
======
|
|
23
|
+
maiha@wota.jp
|
|
24
|
+
|
|
25
|
+
|
data/Rakefile
ADDED
|
@@ -0,0 +1,22 @@
|
|
|
1
|
+
require 'rake'
|
|
2
|
+
require 'rake/testtask'
|
|
3
|
+
require 'rake/rdoctask'
|
|
4
|
+
|
|
5
|
+
desc 'Default: run unit tests.'
|
|
6
|
+
task :default => :test
|
|
7
|
+
|
|
8
|
+
desc 'Test the active_record_view plugin.'
|
|
9
|
+
Rake::TestTask.new(:test) do |t|
|
|
10
|
+
t.libs << 'lib'
|
|
11
|
+
t.pattern = 'test/**/*_test.rb'
|
|
12
|
+
t.verbose = true
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
desc 'Generate documentation for the active_record_view plugin.'
|
|
16
|
+
Rake::RDocTask.new(:rdoc) do |rdoc|
|
|
17
|
+
rdoc.rdoc_dir = 'rdoc'
|
|
18
|
+
rdoc.title = 'ActiveRecordView'
|
|
19
|
+
rdoc.options << '--line-numbers' << '--inline-source'
|
|
20
|
+
rdoc.rdoc_files.include('README')
|
|
21
|
+
rdoc.rdoc_files.include('lib/**/*.rb')
|
|
22
|
+
end
|
data/arv/properties/date
ADDED
data/arv/properties/text
ADDED
data/arv/properties/time
ADDED
data/arv/property.erb
ADDED
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
names:
|
|
2
|
+
:class : "<%= klass.name %>"
|
|
3
|
+
|
|
4
|
+
<%- maxlength = klass.columns.map{|column| column.name.length}.max -%>
|
|
5
|
+
column_names:
|
|
6
|
+
<%- for column in klass.columns -%>
|
|
7
|
+
<%= %Q|%-#{maxlength}s : "%s"| % [column.name, column.human_name] %>
|
|
8
|
+
<%- end -%>
|
|
9
|
+
|
|
10
|
+
<%- for column in klass.columns %>
|
|
11
|
+
property_<%= column.name %>:
|
|
12
|
+
<%- begin -%>
|
|
13
|
+
<%= arv_file("properties/#{column.type}") -%>
|
|
14
|
+
<%- rescue -%>
|
|
15
|
+
<%= arv_file("properties/default") -%>
|
|
16
|
+
<%- end -%>
|
|
17
|
+
|
|
18
|
+
<%- end -%>
|
|
19
|
+
|
data/init.rb
ADDED
|
File without changes
|
data/install.rb
ADDED
|
@@ -0,0 +1 @@
|
|
|
1
|
+
# Install hook code here
|
|
@@ -0,0 +1,29 @@
|
|
|
1
|
+
module ActiveRecordView::DefaultActions
|
|
2
|
+
def link_to_new(record = nil)
|
|
3
|
+
link_to "追加", :action=>"new"
|
|
4
|
+
end
|
|
5
|
+
|
|
6
|
+
def link_to_list(record = nil)
|
|
7
|
+
link_to "一覧", :action=>"list"
|
|
8
|
+
end
|
|
9
|
+
|
|
10
|
+
def link_to_show(record)
|
|
11
|
+
link_to "参照", :action=>"show", :id=>record
|
|
12
|
+
end
|
|
13
|
+
|
|
14
|
+
def link_to_edit(record)
|
|
15
|
+
link_to "修正", :action=>"edit", :id=>record
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
def link_to_destroy(record)
|
|
19
|
+
link_to "削除", {:action=>"destroy", :id=>record}, :confirm=>"本当に削除しますか?", :method => "post"
|
|
20
|
+
end
|
|
21
|
+
|
|
22
|
+
def link_to_save(record)
|
|
23
|
+
if record.new_record?
|
|
24
|
+
link_to "作成", :action=>"create", :id=>record
|
|
25
|
+
else
|
|
26
|
+
link_to "更新", :action=>"update", :id=>record
|
|
27
|
+
end
|
|
28
|
+
end
|
|
29
|
+
end
|
|
@@ -0,0 +1,68 @@
|
|
|
1
|
+
module ActiveRecordView
|
|
2
|
+
module Engines
|
|
3
|
+
extend RecordIdentifier
|
|
4
|
+
|
|
5
|
+
######################################################################
|
|
6
|
+
### Abstract Engine
|
|
7
|
+
|
|
8
|
+
class Base
|
|
9
|
+
include RecordIdentifier
|
|
10
|
+
|
|
11
|
+
def initialize(*args)
|
|
12
|
+
@method, @klass, @column, @view = args
|
|
13
|
+
end
|
|
14
|
+
|
|
15
|
+
def value(record, column = @column, view = @view)
|
|
16
|
+
record.__send__(column)
|
|
17
|
+
end
|
|
18
|
+
|
|
19
|
+
def show(record, column = @column, view = @view)
|
|
20
|
+
value(record, column, view)
|
|
21
|
+
end
|
|
22
|
+
|
|
23
|
+
def edit(record, column = @column, view = @view, record_name = nil)
|
|
24
|
+
record_name ||= singular_class_name(record.class)
|
|
25
|
+
tag_id = "#{record_name}_#{column}"
|
|
26
|
+
tag_name = "#{record_name}[#{column}]"
|
|
27
|
+
view.text_field_tag tag_name, value(record, column, view), :name=>tag_name
|
|
28
|
+
end
|
|
29
|
+
end
|
|
30
|
+
|
|
31
|
+
######################################################################
|
|
32
|
+
### Concreate Engines
|
|
33
|
+
|
|
34
|
+
# helper methods should have first priority
|
|
35
|
+
class Helper < Base
|
|
36
|
+
def show(record, column = @column, view = @view)
|
|
37
|
+
view.__send__(@method, record)
|
|
38
|
+
end
|
|
39
|
+
|
|
40
|
+
def edit(record, column = @column, view = @view, record_name = nil)
|
|
41
|
+
view.__send__(@method, record)
|
|
42
|
+
end
|
|
43
|
+
end
|
|
44
|
+
|
|
45
|
+
# then we search view properties
|
|
46
|
+
class Property < Base
|
|
47
|
+
def show(record, column = @column, view = @view)
|
|
48
|
+
@method.human_value(super, view)
|
|
49
|
+
end
|
|
50
|
+
|
|
51
|
+
def edit(record, column = @column, view = @view, record_name = nil)
|
|
52
|
+
record_name ||= singular_class_name(record.class)
|
|
53
|
+
@method.human_edit(record_name, view, :record=>record)
|
|
54
|
+
end
|
|
55
|
+
end
|
|
56
|
+
|
|
57
|
+
# and then we try ducktyping by respond_to?
|
|
58
|
+
class DuckType < Base
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
# when no engines are found, we try to call record[key] as a last-effort
|
|
62
|
+
class Missing < Base
|
|
63
|
+
def value(record, column = @column, view = @view)
|
|
64
|
+
record[column]
|
|
65
|
+
end
|
|
66
|
+
end
|
|
67
|
+
end
|
|
68
|
+
end
|
|
@@ -0,0 +1,152 @@
|
|
|
1
|
+
module ActiveRecordView::Helper
|
|
2
|
+
include ActiveRecordView::RecordIdentifier
|
|
3
|
+
include ActiveRecordView::WriteException
|
|
4
|
+
include ActiveRecordView::DefaultActions
|
|
5
|
+
include ActiveRecordView::RenderArv
|
|
6
|
+
|
|
7
|
+
def collection_tr_for(*args)
|
|
8
|
+
options = args.optionize :records, :fields
|
|
9
|
+
return nil if options[:records].blank?
|
|
10
|
+
|
|
11
|
+
if options[:fields].blank?
|
|
12
|
+
record = options[:records].first
|
|
13
|
+
record.is_a?(ActiveRecord::Base) or
|
|
14
|
+
raise TypeError, "expect ActiveRecord, but got #{record.class}"
|
|
15
|
+
options[:fields] = record.class.content_columns.map(&:name)
|
|
16
|
+
end
|
|
17
|
+
|
|
18
|
+
options[:records].map do |record|
|
|
19
|
+
style = list_row_class(record) if respond_to? :list_row_class
|
|
20
|
+
content_tag :tr,
|
|
21
|
+
options[:fields].map{|name|
|
|
22
|
+
html = active_record_value(record, name, :singular_name => options[:singular_name])
|
|
23
|
+
content_tag :td, html, :class=>name
|
|
24
|
+
}.join,
|
|
25
|
+
:class => [style, dom_class(record), options[:class] || options[:singular_name]].compact.join(' '),
|
|
26
|
+
:id => dom_id(record, options[:prefix])
|
|
27
|
+
end.join
|
|
28
|
+
end
|
|
29
|
+
|
|
30
|
+
def collection_tbody_for(*args)
|
|
31
|
+
html = collection_tr_for(*args)
|
|
32
|
+
if html.blank?
|
|
33
|
+
return nil
|
|
34
|
+
else
|
|
35
|
+
content_tag(:tbody, html)
|
|
36
|
+
end
|
|
37
|
+
end
|
|
38
|
+
|
|
39
|
+
######################################################################
|
|
40
|
+
### ActiveRecord value
|
|
41
|
+
def active_record_value(record_or_name, format, options = {})
|
|
42
|
+
record = active_record_for(record_or_name)
|
|
43
|
+
engine = find_active_record_view_engine(record.class, format)
|
|
44
|
+
engine.show(record, format, self)
|
|
45
|
+
rescue NameError => err
|
|
46
|
+
write_exception(err)
|
|
47
|
+
record[format]
|
|
48
|
+
rescue Exception => err
|
|
49
|
+
active_record_error(err, record_or_name, format, options)
|
|
50
|
+
end
|
|
51
|
+
|
|
52
|
+
def active_record_form(record_or_name, format, options = {})
|
|
53
|
+
record_name = active_record_name_for(record_or_name)
|
|
54
|
+
record = options[:record] || active_record_for(record_or_name)
|
|
55
|
+
engine = find_active_record_view_engine(record.class, format, "edit_")
|
|
56
|
+
engine.edit(record, format, self, record_name)
|
|
57
|
+
rescue Exception => err
|
|
58
|
+
active_record_error(err, record_or_name, format, options)
|
|
59
|
+
end
|
|
60
|
+
|
|
61
|
+
def find_active_record_view_engine(klass, column, prefix = nil)
|
|
62
|
+
caching_key = [klass, column, prefix]
|
|
63
|
+
@cached_arv_engines ||= {}
|
|
64
|
+
@cached_arv_engines[caching_key] ||= active_record_view_engine_for(klass, column, prefix)
|
|
65
|
+
end
|
|
66
|
+
|
|
67
|
+
def active_record_error(err, record_or_name, format, options)
|
|
68
|
+
write_exception(err)
|
|
69
|
+
'???'
|
|
70
|
+
end
|
|
71
|
+
|
|
72
|
+
private
|
|
73
|
+
def active_record_for(record_or_name, strict = false)
|
|
74
|
+
record =
|
|
75
|
+
case record_or_name
|
|
76
|
+
when String, Symbol
|
|
77
|
+
instance_variable_get("@#{record_or_name}")
|
|
78
|
+
else
|
|
79
|
+
record_or_name
|
|
80
|
+
end
|
|
81
|
+
if strict and !record.is_a?(ActiveRecord::Base)
|
|
82
|
+
raise TypeError, "expect ActiveRecord, but got #{record.class} by #{record_or_name.inspect}"
|
|
83
|
+
end
|
|
84
|
+
return record
|
|
85
|
+
end
|
|
86
|
+
|
|
87
|
+
######################################################################
|
|
88
|
+
### Engine mapper
|
|
89
|
+
|
|
90
|
+
def active_record_view_engine_for(klass, column, prefix = nil)
|
|
91
|
+
ActiveRecord::Base.logger.debug "active_record_view: searching renderer for (%s, %s)" % [klass, column]
|
|
92
|
+
column = column.to_s.intern
|
|
93
|
+
active_record_view_engine_for_active_record(klass, column, self, prefix) or
|
|
94
|
+
active_record_view_engine_for_classed_helper(klass, column, self) or
|
|
95
|
+
active_record_view_engine_for_helper(klass, column, self) or
|
|
96
|
+
active_record_view_engine_for_ducktype(klass, column, self) or
|
|
97
|
+
active_record_view_engine_for_missing(klass, column, self) or
|
|
98
|
+
raise TypeError, "no engines found for #{klass}"
|
|
99
|
+
end
|
|
100
|
+
|
|
101
|
+
def active_record_view_engine_for_classed_helper(klass, column, view)
|
|
102
|
+
method = "%s_%s" % [active_record_name_for(klass), column]
|
|
103
|
+
view.respond_to?(method) and
|
|
104
|
+
ActiveRecordView::Engines::Helper.new(method, klass, column, view)
|
|
105
|
+
end
|
|
106
|
+
|
|
107
|
+
def active_record_view_engine_for_helper(klass, column, view)
|
|
108
|
+
view.respond_to?(column) and
|
|
109
|
+
ActiveRecordView::Engines::Helper.new(column, klass, column, view)
|
|
110
|
+
end
|
|
111
|
+
|
|
112
|
+
def active_record_view_engine_for_ducktype(klass, column, view)
|
|
113
|
+
klass.new.respond_to?(column) and
|
|
114
|
+
ActiveRecordView::Engines::DuckType.new(column, klass, column, view)
|
|
115
|
+
rescue ArgumentError
|
|
116
|
+
klass.instance_methods.include?(column.to_s) and
|
|
117
|
+
ActiveRecordView::Engines::DuckType.new(column, klass, column, view)
|
|
118
|
+
end
|
|
119
|
+
|
|
120
|
+
def active_record_view_engine_for_missing(klass, column, view)
|
|
121
|
+
klass.instance_methods.include?("[]") and
|
|
122
|
+
ActiveRecordView::Engines::Missing.new(column, klass, column, view)
|
|
123
|
+
end
|
|
124
|
+
|
|
125
|
+
def active_record_view_engine_for_active_record(klass, column, view, prefix)
|
|
126
|
+
index = klass.ancestors.index(ActiveRecord::Base) or return nil
|
|
127
|
+
ancestors = [klass] + klass.ancestors[0...index]
|
|
128
|
+
checked_methods = {}
|
|
129
|
+
ancestors.each do |k|
|
|
130
|
+
helper_method = ("%s%s_%s" % [prefix, active_record_name_for(k), column]).intern
|
|
131
|
+
next if checked_methods[helper_method]
|
|
132
|
+
checked_methods[helper_method] = true
|
|
133
|
+
|
|
134
|
+
# first, check helper methods
|
|
135
|
+
exist = respond_to?(helper_method)
|
|
136
|
+
ActiveRecord::Base.logger.debug " helper method '#{helper_method}' ... %s" % (exist ? "yes" : "no")
|
|
137
|
+
return ActiveRecordView::Engines::Helper.new(helper_method, klass, column, view) if exist
|
|
138
|
+
|
|
139
|
+
# second, check view property if not STI
|
|
140
|
+
next unless k.is_a?(Class)
|
|
141
|
+
next unless k.descends_from_active_record?
|
|
142
|
+
|
|
143
|
+
property = Localized::ViewProperty[k, column]
|
|
144
|
+
if property and !property.blank?
|
|
145
|
+
ActiveRecord::Base.logger.debug " ViewProperty[%s, %s] ... yes" % [k, column]
|
|
146
|
+
return ActiveRecordView::Engines::Property.new(property, klass, column, view)
|
|
147
|
+
end
|
|
148
|
+
end
|
|
149
|
+
return nil
|
|
150
|
+
end
|
|
151
|
+
|
|
152
|
+
end
|
|
@@ -0,0 +1,42 @@
|
|
|
1
|
+
module ActiveRecordView::RecordIdentifier
|
|
2
|
+
# derived from SimplyHelpful::RecordIdentifier
|
|
3
|
+
def partial_path(record_or_class)
|
|
4
|
+
klass = class_from_record_or_class(record_or_class)
|
|
5
|
+
"#{klass.name.tableize}/#{klass.name.demodulize.underscore}"
|
|
6
|
+
end
|
|
7
|
+
|
|
8
|
+
def dom_class(record_or_class, prefix = nil)
|
|
9
|
+
[ prefix, singular_class_name(record_or_class) ].compact * '_'
|
|
10
|
+
end
|
|
11
|
+
|
|
12
|
+
def dom_id(record, prefix = nil)
|
|
13
|
+
prefix ||= 'new' unless record.id
|
|
14
|
+
[ prefix, singular_class_name(record), record.id ].compact * '_'
|
|
15
|
+
end
|
|
16
|
+
|
|
17
|
+
def plural_class_name(record_or_class)
|
|
18
|
+
singular_class_name(record_or_class).pluralize
|
|
19
|
+
end
|
|
20
|
+
|
|
21
|
+
def singular_class_name(record_or_class)
|
|
22
|
+
class_from_record_or_class(record_or_class).name.underscore.tr('/', '_')
|
|
23
|
+
end
|
|
24
|
+
|
|
25
|
+
def demodulized_class_name(record_or_class)
|
|
26
|
+
class_from_record_or_class(record_or_class).name.demodulize.underscore.tr('/', '_')
|
|
27
|
+
end
|
|
28
|
+
|
|
29
|
+
def active_record_name_for(name)
|
|
30
|
+
case name
|
|
31
|
+
when String, Symbol
|
|
32
|
+
name.to_s
|
|
33
|
+
else
|
|
34
|
+
demodulized_class_name(name)
|
|
35
|
+
end
|
|
36
|
+
end
|
|
37
|
+
|
|
38
|
+
private
|
|
39
|
+
def class_from_record_or_class(record_or_class)
|
|
40
|
+
record_or_class.is_a?(Class) ? record_or_class : record_or_class.class
|
|
41
|
+
end
|
|
42
|
+
end
|