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.
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