maiha-active_record_view 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (42) hide show
  1. data/README +25 -0
  2. data/Rakefile +22 -0
  3. data/arv/properties/boolean +4 -0
  4. data/arv/properties/date +5 -0
  5. data/arv/properties/datetime +5 -0
  6. data/arv/properties/default +3 -0
  7. data/arv/properties/integer +6 -0
  8. data/arv/properties/string +4 -0
  9. data/arv/properties/text +7 -0
  10. data/arv/properties/time +5 -0
  11. data/arv/properties/timestamp +5 -0
  12. data/arv/property.erb +19 -0
  13. data/init.rb +0 -0
  14. data/install.rb +1 -0
  15. data/lib/active_record_view.rb +2 -0
  16. data/lib/active_record_view/controller.rb +9 -0
  17. data/lib/active_record_view/default_actions.rb +29 -0
  18. data/lib/active_record_view/engines.rb +68 -0
  19. data/lib/active_record_view/helper.rb +152 -0
  20. data/lib/active_record_view/record_identifier.rb +42 -0
  21. data/lib/active_record_view/render_arv.rb +51 -0
  22. data/lib/active_record_view/write_exception.rb +11 -0
  23. data/lib/localized/model.rb +103 -0
  24. data/lib/localized/view_property.rb +337 -0
  25. data/spec/fixtures/card.rb +12 -0
  26. data/spec/fixtures/card/equip.rb +13 -0
  27. data/spec/fixtures/card/magic.rb +13 -0
  28. data/spec/fixtures/card/member.rb +13 -0
  29. data/spec/fixtures/card/unit.rb +13 -0
  30. data/spec/fixtures/deck.rb +2 -0
  31. data/spec/localized/decks.yml +39 -0
  32. data/spec/migrate/001_create_cards.rb +16 -0
  33. data/spec/migrate/002_create_decks.rb +5 -0
  34. data/spec/models/active_record_view_helper_spec.rb +393 -0
  35. data/spec/models/render_spec.rb +220 -0
  36. data/spec/schema.rb +30 -0
  37. data/spec/spec.opts +6 -0
  38. data/spec/spec_helper.rb +27 -0
  39. data/tasks/active_record_view_tasks.rake +37 -0
  40. data/test/active_record_view_test.rb +8 -0
  41. data/uninstall.rb +1 -0
  42. 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
@@ -0,0 +1,4 @@
1
+ :options :
2
+ :masters :
3
+ - true : "Yes"
4
+ - false : "No"
@@ -0,0 +1,5 @@
1
+ :time_format : '%Y-%m-%d'
2
+ :options :
3
+ :include_blank : true
4
+ :use_month_numbers : true
5
+ # :start_year : 2000
@@ -0,0 +1,5 @@
1
+ :time_format : '%Y-%m-%d'
2
+ :options :
3
+ :include_blank : true
4
+ :use_month_numbers : true
5
+ # :start_year : 2000
@@ -0,0 +1,3 @@
1
+ :options :
2
+ :masters :
3
+ # - "value" : "Label"
@@ -0,0 +1,6 @@
1
+ # :format : "%s points"
2
+ :options :
3
+ :size : 10
4
+ :masters :
5
+ # - 1 : "item1"
6
+ # - 2 : "item2"
@@ -0,0 +1,4 @@
1
+ :options :
2
+ :size : 60
3
+ :masters :
4
+ # :format : "%s"
@@ -0,0 +1,7 @@
1
+ :options :
2
+ :rows : 5
3
+ :cols : 60
4
+ # :column_type : :string
5
+ :ipe_options :
6
+ :rows : 3
7
+ :cols : 30
@@ -0,0 +1,5 @@
1
+ :time_format : '%Y-%m-%d'
2
+ :options :
3
+ :include_blank : true
4
+ :use_month_numbers : true
5
+ # :start_year : 2000
@@ -0,0 +1,5 @@
1
+ :time_format : '%Y-%m-%d'
2
+ :options :
3
+ :include_blank : true
4
+ :use_month_numbers : true
5
+ # :start_year : 2000
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,2 @@
1
+ module ActiveRecordView
2
+ end
@@ -0,0 +1,9 @@
1
+ module ActiveRecordView::Controller
2
+ include ActiveRecordView::RenderArv
3
+
4
+ def render_arv(arv, options = {})
5
+ add_variables_to_assigns
6
+ response.body = @template.render_arv(arv, options)
7
+ @performed_render = true
8
+ end
9
+ end
@@ -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