table_helper 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
data/CHANGELOG ADDED
@@ -0,0 +1,19 @@
1
+ *SVN*
2
+
3
+ *0.0.3* (June 1st, 2008)
4
+
5
+ * Remove dependency on set_or_append
6
+
7
+ *0.0.2* (May 5th, 2008)
8
+
9
+ * Updated documentation
10
+
11
+ *0.0.1* (August 18th, 2007)
12
+
13
+ * Add README documentation
14
+
15
+ * Add gem dependency on set_or_append
16
+
17
+ * Refactor test method names
18
+
19
+ * Convert dos newlines to unix newlines
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2006-2008 Aaron Pfeifer
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,161 @@
1
+ = table_helper
2
+
3
+ +table_helper+ adds a helper method for generating HTML tables from collections.
4
+
5
+ == Resources
6
+
7
+ Wiki
8
+
9
+ * http://wiki.pluginaweek.org/Table_helper
10
+
11
+ API
12
+
13
+ * http://api.pluginaweek.org/table_helper
14
+
15
+ Development
16
+
17
+ * http://dev.pluginaweek.org/browser/trunk/table_helper
18
+
19
+ Source
20
+
21
+ * http://svn.pluginaweek.org/trunk/table_helper
22
+
23
+ == Description
24
+
25
+ Tables of summary data for ActiveRecord models are often formatted in the same
26
+ way by creating a header indicating the attribute and a body containing the
27
+ data from each record in separate rows. table_helper makes it easier to create
28
+ these types of tables by DRYing much of the html being generated.
29
+
30
+ == Usage
31
+
32
+ === Basic Example
33
+
34
+ <%= collection_table Person.find(:all) %>
35
+
36
+ ...is compiled to (formatted here for the sake of sanity):
37
+
38
+ <table cellpadding="0" cellspacing="0">
39
+ <thead>
40
+ <tr>
41
+ <th class="first_name" scope="col">First Name</th>
42
+ <th class="last_name" scope="col">Last Name</th>
43
+ <th class="company_id" scope="col">Company</th>
44
+ <th class="role" scope="col">Role</th>
45
+ </tr>
46
+ </thead>
47
+ <tbody>
48
+ <tr class="row">
49
+ <td class="first_name">John</td>
50
+ <td class="last_name">Doe</td>
51
+ <td class="company_id">1</td>
52
+ <td class="role">President</td>
53
+ </tr>
54
+ <tr class="row">
55
+ <td class="first_name">Jane</td>
56
+ <td class="last_name">Doe</td>
57
+ <td class="company_id">1</td>
58
+ <td class="role">Vice-President</td>
59
+ </tr>
60
+ </tbody>
61
+ <table>
62
+
63
+ === Advanced Example
64
+
65
+ <%=
66
+ collection_table(@posts, {}, :id => 'posts', :class => 'summary') do |header, body|
67
+ header.column :title
68
+ header.column :category
69
+ header.column :author
70
+ header.column :publish_date, 'Date<br \>Published'
71
+ header.column :num_comments, '# Comments'
72
+ header.column :num_trackbacks, '# Trackbacks'
73
+
74
+ body.alternate = true
75
+ body.build do |row, post, index|
76
+ row.category post.category.name
77
+ row.author post.author.name
78
+ row.publish_date time_ago_in_words(post.published_on)
79
+ row.num_comments post.comments.empty? ? '-' : post.comments.size
80
+ row.num_trackbacks post.trackbacks.empty? ? '-' : post.trackbacks.size
81
+ end
82
+ end
83
+ %>
84
+
85
+ ...is compiled to (formatted here for the sake of sanity):
86
+
87
+ <table cellpadding="0" cellspacing="0" class="summary" id="posts">
88
+ <thead>
89
+ <tr>
90
+ <th class="title" scope="col">Title</th>
91
+ <th class="category" scope="col">Category</th>
92
+ <th class="author" scope="col">Author</th>
93
+ <th class="publish_date" scope="col">Date<br \>Published</th>
94
+ <th class="num_comments" scope="col"># Comments</th>
95
+ <th class="num_trackbacks" scope="col"># Trackbacks</th>
96
+ </tr>
97
+ </thead>
98
+ <tbody class="alternate">
99
+ <tr class="row">
100
+ <td class="title">Open-source projects: The good, the bad, and the ugly</td>
101
+ <td class="category">General</td>
102
+ <td class="author">John Doe</td>
103
+ <td class="publish_date">23 days</td>
104
+ <td class="num_comments">-</td>
105
+ <td class="num_trackbacks">-</td>
106
+ </tr>
107
+ <tr class="row alternate">
108
+ <td class="title">5 reasons you should care about Rails</td>
109
+ <td class="category">Rails</td><td class="author">John Q. Public</td>
110
+ <td class="publish_date">21 days</td>
111
+ <td class="num_comments">-</td>
112
+ <td class="num_trackbacks">-</td>
113
+ </tr>
114
+ <tr class="row">
115
+ <td class="title">Deprecation: Stop digging yourself a hole</td>
116
+ <td class="category">Rails</td>
117
+ <td class="author">Jane Doe</td>
118
+ <td class="publish_date">17 days</td>
119
+ <td class="num_comments">-</td>
120
+ <td class="num_trackbacks">-</td>
121
+ </tr>
122
+ <tr class="row alternate">
123
+ <td class="title">Jumpstart your Rails career at RailsConf 2007</td>
124
+ <td class="category">Conferences</td>
125
+ <td class="author">Jane Doe</td>
126
+ <td class="publish_date">4 days</td>
127
+ <td class="num_comments">-</td>
128
+ <td class="num_trackbacks">-</td>
129
+ </tr>
130
+ <tr class="row">
131
+ <td class="title">Getting some REST</td>
132
+ <td class="category">Rails</td>
133
+ <td class="author">John Doe</td>
134
+ <td class="publish_date">about 18 hours</td>
135
+ <td class="num_comments">-</td>
136
+ <td class="num_trackbacks">-</td>
137
+ </tr>
138
+ </tbody>
139
+ </table>
140
+
141
+ === Caveat Emptor
142
+
143
+ See the API for more information on syntax and options. You should only use
144
+ table_helper if it fits the needs of your application. Remember one of the key
145
+ principles of Rails, KISS (Keep It Simple Stupid). table_helper works really
146
+ well when you need to quickly output several of these types of summary tables.
147
+ If this is not the case, you may want to stick to using actual html.
148
+
149
+ == Testing
150
+
151
+ Before you can run any tests, the following gem must be installed:
152
+ * plugin_test_helper[http://wiki.pluginaweek.org/Plugin_test_helper]
153
+
154
+ To run against a specific version of Rails:
155
+
156
+ rake test RAILS_FRAMEWORK_ROOT=/path/to/rails
157
+
158
+ == Dependencies
159
+
160
+ * Rails 2.0 or later
161
+ * set_or_append[http://wiki.pluginaweek.org/Set_or_append]
data/Rakefile ADDED
@@ -0,0 +1,80 @@
1
+ require 'rake/testtask'
2
+ require 'rake/rdoctask'
3
+ require 'rake/gempackagetask'
4
+ require 'rake/contrib/sshpublisher'
5
+
6
+ PKG_NAME = 'table_helper'
7
+ PKG_VERSION = '0.0.3'
8
+ PKG_FILE_NAME = "#{PKG_NAME}-#{PKG_VERSION}"
9
+ RUBY_FORGE_PROJECT = 'pluginaweek'
10
+
11
+ desc 'Default: run unit tests.'
12
+ task :default => :test
13
+
14
+ desc 'Test the table_helper plugin.'
15
+ Rake::TestTask.new(:test) do |t|
16
+ t.libs << 'lib'
17
+ t.pattern = 'test/**/*_test.rb'
18
+ t.verbose = true
19
+ end
20
+
21
+ desc 'Generate documentation for the table_helper plugin.'
22
+ Rake::RDocTask.new(:rdoc) do |rdoc|
23
+ rdoc.rdoc_dir = 'rdoc'
24
+ rdoc.title = 'TableHelper'
25
+ rdoc.template = '../rdoc_template.rb'
26
+ rdoc.options << '--line-numbers' << '--inline-source'
27
+ rdoc.rdoc_files.include('README')
28
+ rdoc.rdoc_files.include('lib/**/*.rb')
29
+ end
30
+
31
+ spec = Gem::Specification.new do |s|
32
+ s.name = PKG_NAME
33
+ s.version = PKG_VERSION
34
+ s.platform = Gem::Platform::RUBY
35
+ s.summary = 'Adds a helper method for generating HTML tables from collections'
36
+
37
+ s.files = FileList['{lib,test}/**/*'].to_a + %w(CHANGELOG init.rb MIT-LICENSE Rakefile README)
38
+ s.require_path = 'lib'
39
+ s.autorequire = 'table_helper'
40
+ s.has_rdoc = true
41
+ s.test_files = Dir['test/**/*_test.rb']
42
+
43
+ s.author = 'Aaron Pfeifer'
44
+ s.email = 'aaron@pluginaweek.org'
45
+ s.homepage = 'http://www.pluginaweek.org'
46
+ end
47
+
48
+ Rake::GemPackageTask.new(spec) do |p|
49
+ p.gem_spec = spec
50
+ p.need_tar = true
51
+ p.need_zip = true
52
+ end
53
+
54
+ desc 'Publish the beta gem'
55
+ task :pgem => [:package] do
56
+ Rake::SshFilePublisher.new('aaron@pluginaweek.org', '/home/aaron/gems.pluginaweek.org/public/gems', 'pkg', "#{PKG_FILE_NAME}.gem").upload
57
+ end
58
+
59
+ desc 'Publish the API documentation'
60
+ task :pdoc => [:rdoc] do
61
+ Rake::SshDirPublisher.new('aaron@pluginaweek.org', "/home/aaron/api.pluginaweek.org/public/#{PKG_NAME}", 'rdoc').upload
62
+ end
63
+
64
+ desc 'Publish the API docs and gem'
65
+ task :publish => [:pgem, :pdoc, :release]
66
+
67
+ desc 'Publish the release files to RubyForge.'
68
+ task :release => [:gem, :package] do
69
+ require 'rubyforge'
70
+
71
+ ruby_forge = RubyForge.new.configure
72
+ ruby_forge.login
73
+
74
+ %w( gem tgz zip ).each do |ext|
75
+ file = "pkg/#{PKG_FILE_NAME}.#{ext}"
76
+ puts "Releasing #{File.basename(file)}..."
77
+
78
+ ruby_forge.add_release(RUBY_FORGE_PROJECT, PKG_NAME, PKG_VERSION, file)
79
+ end
80
+ end
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'table_helper'
@@ -0,0 +1,119 @@
1
+ require 'table_helper/body_row'
2
+
3
+ module PluginAWeek #:nodoc:
4
+ module TableHelper
5
+ # Represents the body of the table. In HTML, you can think of this as
6
+ # the <tbody> tag of the table.
7
+ class Body < HtmlElement
8
+ # If set to :odd or :even, every odd or even-numbered row will have the
9
+ # class 'alternate' appended to its html attributes, respectively.
10
+ # Default is nil.
11
+ attr_accessor :alternate_rows
12
+
13
+ # The caption to display in the collection is empty
14
+ attr_accessor :empty_caption
15
+
16
+ def initialize(collection, header) #:nodoc:
17
+ super()
18
+
19
+ @collection, @header = collection, header
20
+ @empty_caption = 'No matches found.'
21
+ end
22
+
23
+ def alternate_rows=(value) #:nodoc:
24
+ raise ArgumentError, 'alternate_rows must be set to :odd or :even' if value && ![:odd, :even].include?(value)
25
+ @alternate_rows = value
26
+ end
27
+
28
+ # Builds the body of the table. This includes the actual data that is
29
+ # generated for each object in the collection.
30
+ #
31
+ # build expects a block that defines the data in each cell. Each
32
+ # iteration of the block will provide the object being rendered, the row
33
+ # within the table that will be built and the index of the object. For
34
+ # example,
35
+ #
36
+ # body.build do |row, post, index|
37
+ # row.title "<div class=\"wrapped\">#{post.title}</div>"
38
+ # row.category post.category.name
39
+ # end
40
+ #
41
+ # In addition, to specifying the data, you can also modify the html
42
+ # options of the row. For more information on doing this, see the
43
+ # BodyRow class.
44
+ #
45
+ # If the collection is empty and +empty_caption+ is set on the Body,
46
+ # then the actual body will be replaced by a single row containing the
47
+ # html that was stored in +empty_caption+.
48
+ #
49
+ # == Default Values
50
+ #
51
+ # Whenever possible, the default value of a cell will be set to the
52
+ # object's attribute with the same name as the cell. For example,
53
+ # if a Post consists of the attribute +title+, then the cell for the
54
+ # title will be prepopulated with that attribute's value:
55
+ #
56
+ # body.build do |row, post index|
57
+ # row.category post.category.name
58
+ # end
59
+ #
60
+ # +row.title+ is already set to post.category so there's no need to
61
+ # manually set the value of that cell. However, it is always possible
62
+ # to override the default value like so:
63
+ #
64
+ # body.build do |row, post, index|
65
+ # row.title link_to(post.title, post_url(post))
66
+ # row.category post.category.name
67
+ # end
68
+ def build(&block)
69
+ @content = ''
70
+
71
+ # Display nothing if there are no objects to display
72
+ if @collection.empty? && @empty_caption
73
+ row = Row.new
74
+ row[:class] = 'no_content'
75
+
76
+ html_options = {}
77
+ html_options[:colspan] = @header.column_names.size if @header.column_names.size > 1
78
+ row.cell nil, @empty_caption, html_options
79
+
80
+ @content << row.html
81
+ else
82
+ @collection.each_with_index do |object, i|
83
+ @content << build_row(object, i, &block)
84
+ end
85
+ end
86
+
87
+ @content
88
+ end
89
+
90
+ # Builds a row for an object in the table.
91
+ #
92
+ # The provided block should set the values for each cell in the row.
93
+ def build_row(object, index = @collection.index(object), &block)
94
+ row = BodyRow.new(object, @header)
95
+ row.alternate = alternate_rows ? index.send("#{@alternate_rows}?") : false
96
+
97
+ yield row, object, index if block_given?
98
+
99
+ row.html
100
+ end
101
+
102
+ def html #:nodoc:
103
+ html_options = @html_options.dup
104
+ html_options[:class] = (html_options[:class].to_s + ' alternate').strip if alternate_rows
105
+
106
+ content_tag(tag_name, content, html_options)
107
+ end
108
+
109
+ private
110
+ def tag_name
111
+ 'tbody'
112
+ end
113
+
114
+ def content
115
+ @content
116
+ end
117
+ end
118
+ end
119
+ end
@@ -0,0 +1,86 @@
1
+ require 'table_helper/row'
2
+
3
+ module PluginAWeek #:nodoc:
4
+ module TableHelper
5
+ # Represents a single row within the body of a table. The row can consist
6
+ # of either data cells or header cells.
7
+ #
8
+ # == Borders
9
+ #
10
+ # Each row has an optional special border row that can be generated either
11
+ # immediately before or immediately after this row. A separate border row
12
+ # is usually used when you cannot express borders in the css of the row
13
+ # containing the data (e.g. dotted borders in Internet Explorer).
14
+ #
15
+ # To modify the properties of the border, you can access +row.border+ like
16
+ # so:
17
+ #
18
+ # r = BodyRow.new
19
+ # r.border_type = :before
20
+ # r.border[:style] = 'color: #ff0000;'
21
+ #
22
+ # == Alternating rows
23
+ #
24
+ # Alternating rows can be automated by setting the +alternate+ property.
25
+ # For example,
26
+ #
27
+ # r = BodyRow.new
28
+ # r.alternate = true
29
+ class BodyRow < Row
30
+ # True if this is an alternating row, otherwise false. Default is false.
31
+ attr_accessor :alternate
32
+
33
+ def initialize(object, header) #:nodoc:
34
+ super()
35
+
36
+ @header = header
37
+ @alternate = false
38
+ @html_options[:class] = ('row ' + @html_options[:class].to_s).strip
39
+
40
+ # For each column defined in the table, see if we can prepopulate the
41
+ # cell based on the data in the object. If not, we can at least
42
+ # provide shortcut accessors to the cell
43
+ @header.column_names.each do |column|
44
+ if object.respond_to?(column)
45
+ cell(column, object.send(column))
46
+ else
47
+ define_cell_accessor(column)
48
+ end
49
+ end
50
+ end
51
+
52
+ # Generates the html for this row in additional to the border row
53
+ # (if specified)
54
+ def html
55
+ original_options = @html_options.dup
56
+ @html_options[:class] = (@html_options[:class].to_s + ' alternate').strip if alternate
57
+ html = super
58
+ @html_options = original_options
59
+ html
60
+ end
61
+
62
+ private
63
+ # Builds the row's cells based on the order of the columns in the
64
+ # header. If a cell cannot be found for a specific column, then a blank
65
+ # cell is rendered.
66
+ def content
67
+ number_to_skip = 0 # Keeps track of the # of columns to skip
68
+
69
+ html = ''
70
+ @header.column_names.each do |column|
71
+ number_to_skip -= 1 and next if number_to_skip > 0
72
+
73
+ if cell = @cells[column]
74
+ number_to_skip = (cell[:colspan] || 1) - 1
75
+ else
76
+ cell = Cell.new(column, '', :class => 'empty')
77
+ end
78
+
79
+ html << cell.html
80
+ end
81
+
82
+ html
83
+ end
84
+ end
85
+ end
86
+ end