cheveret 1.0.0

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/.document ADDED
@@ -0,0 +1,5 @@
1
+ README.rdoc
2
+ lib/**/*.rb
3
+ bin/*
4
+ features/**/*.feature
5
+ LICENSE
data/.gitignore ADDED
@@ -0,0 +1,21 @@
1
+ ## MAC OS
2
+ .DS_Store
3
+
4
+ ## TEXTMATE
5
+ *.tmproj
6
+ tmtags
7
+
8
+ ## EMACS
9
+ *~
10
+ \#*
11
+ .\#*
12
+
13
+ ## VIM
14
+ *.swp
15
+
16
+ ## PROJECT::GENERAL
17
+ coverage
18
+ rdoc
19
+ pkg
20
+
21
+ ## PROJECT::SPECIFIC
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 RateCity
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.md ADDED
@@ -0,0 +1,112 @@
1
+ Cheveret
2
+ ========
3
+
4
+ A Rails library for generating flexible width HTML tables.
5
+
6
+ _**Cheveret**: (noun) a small English table of the 18th century, having an oblong top, one or two rows of drawers, and slender legs joined near the bottom by a shelf._
7
+
8
+ Install
9
+ -------
10
+
11
+ Cheveret was developed for Rails 2.3.8 and might work with more recent versions. There are no other dependencies aside from Rails itself.
12
+
13
+ To install using Bundler simply add the following to your `Gemfile` and run a `bundle install`:
14
+
15
+ gem 'cheveret', :git => 'git@github.com:ratecity/cheveret.git'
16
+
17
+ Usage
18
+ -----
19
+
20
+ Generating HTML tables of data in the views of your Rails application is not very **DRY** even for the simpler of cases. Cheveret allows you to more clearly separate logic and templating and reduce the amount of code in your views.
21
+
22
+ There are several ways to define the structure of a table using Cheveret, the simplest being via the `tabulatable` method in your `ActiveRecord` models:
23
+
24
+ class Book < ActiveRecord::Base
25
+ tabulatable do |t|
26
+ t.flexible :title
27
+ t.column :author :width => 200, :flexible => true
28
+ t.column :release_date :width => 100, :label=> false
29
+ t.sortable :amount,
30
+ t.fixed :add_to_cart, :width => 120, :display => false
31
+ end
32
+
33
+ ...
34
+
35
+ end
36
+
37
+ An instance of `Cheveret::Table` will then be configured on your model, accessible via `.cheveret`.
38
+
39
+ ### Rendering
40
+
41
+ You can now render a table in your view by using the `cheveret_table` helper:
42
+
43
+ = cheveret_table_for @books
44
+
45
+ If your view should only display a subset of the defined columns in the table (e.g. not display the _add to cart_ button for non-authenticated users), supply either `:only` or `:exclude`:
46
+
47
+ = cheveret_table_for @books, :exclude => :add_to_cart
48
+
49
+ ### Header
50
+
51
+ By default, Cheveret will attempt to use i18n to look up a sensible label for your column headers using the `cheveret.#{object.human_name}.#{column_name}.label` scope, where `object` is the object in which your columns are defined. If a translation is found at `.desc` in the same scope, it will be used as the HTML `title` attribute of the label element.
52
+
53
+ The default behaviour can be overridden by way of the `:label` option when defining your columns. Prevent the label from being rendered altogether by specifying `:label => false`.
54
+
55
+ Lastly, you can completely override the markup for each header cell by using the `cheveret_headers_for` helper and supplying a block:
56
+
57
+ = cheveret_headers_for @books do |name, column|
58
+ - case name
59
+ - when :add_to_cart
60
+ = image_tag("add_to_cart.png")
61
+
62
+ In the above example, the block will be called once for each column in the table and render the results. There are two exceptions to this behaviour:
63
+
64
+ 1. if the return value is `nil`, Cheveret will render standard markup using the columns `:label` setting
65
+
66
+ 2. otherwise, if the return value is `false`, no markup will be rendered
67
+
68
+ ### Body
69
+
70
+ When rendering the table body, Cheveret assumes it's dealing with `ActiveRecord` model objects (or similar) and that you have _named your columns to match your data_. To get a value for each table cell it will call `object.send(column_name)` to each of your objects.
71
+
72
+ This is sufficient for a list of simple `ActiveRecord` instances, but what if you need to format values, or you want to list something else? Enter the `cheveret_body_for` helper:
73
+
74
+ = cheveret_body_for @books do |name, column, book|
75
+ - case name
76
+ - when :amount
77
+ = number_to_currency(book.amount)
78
+
79
+ This behaves in the same way as `cheveret_header`, except for the additional 3rd argument passed to the block, which gives access to the data object being rendered.
80
+
81
+ You could, for example, create a column that is an aggregate of more than one attribute:
82
+
83
+ = cheveret_body_for @books do |name, column, book|
84
+ - case name
85
+ - when :title
86
+ = "#{book.title} (#{book.edition})"
87
+
88
+ Or, if you're using _Sunspot_, list stored search results without hitting the database to get model objects:
89
+
90
+ = cheveret_body_for @books do |name, column, book|
91
+ = book.stored(name)
92
+
93
+ Note on Patches/Pull Requests
94
+ -----------------------------
95
+
96
+ * Fork the project.
97
+ * Make your feature addition or bug fix.
98
+ * Add tests for it. This is important so I don't break it in a
99
+ future version unintentionally.
100
+ * Commit, do not mess with rakefile, version, or history.
101
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
102
+ * Send me a pull request. Bonus points for topic branches.
103
+
104
+ Maintainers & Contributors
105
+ --------------------------
106
+
107
+ * Ben Caldwell - http://github.com/lankz
108
+
109
+ Copyright
110
+ ---------
111
+
112
+ Copyright (c) 2010 RateCity. See LICENSE for details.
data/Rakefile ADDED
@@ -0,0 +1,47 @@
1
+ require 'rubygems'
2
+ require 'rake'
3
+
4
+ begin
5
+ require 'jeweler'
6
+ Jeweler::Tasks.new do |gem|
7
+ gem.name = "cheveret"
8
+ gem.summary = %Q{Rails library for generating flexible width HTML tables}
9
+ gem.description = %Q{Generating HTML tables of data in the views of your Rails
10
+ application is not very DRY even for the simpler of cases. Cheveret allows you to more
11
+ clearly separate logic and templating and reduce the amount of code in your views.}
12
+ gem.email = "aulankz@gmail.com"
13
+ gem.homepage = "http://github.com/lankz/cheveret"
14
+ gem.authors = ["Ben Caldwell"]
15
+ gem.add_development_dependency "rspec", ">= 1.2.9"
16
+ # gem is a Gem::Specification... see http://www.rubygems.org/read/chapter/20 for additional settings
17
+ end
18
+ Jeweler::GemcutterTasks.new
19
+ rescue LoadError
20
+ puts "Jeweler (or a dependency) not available. Install it with: gem install jeweler"
21
+ end
22
+
23
+ require 'spec/rake/spectask'
24
+ Spec::Rake::SpecTask.new(:spec) do |spec|
25
+ spec.libs << 'lib' << 'spec'
26
+ spec.spec_files = FileList['spec/**/*_spec.rb']
27
+ end
28
+
29
+ Spec::Rake::SpecTask.new(:rcov) do |spec|
30
+ spec.libs << 'lib' << 'spec'
31
+ spec.pattern = 'spec/**/*_spec.rb'
32
+ spec.rcov = true
33
+ end
34
+
35
+ task :spec => :check_dependencies
36
+
37
+ task :default => :spec
38
+
39
+ require 'rake/rdoctask'
40
+ Rake::RDocTask.new do |rdoc|
41
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
42
+
43
+ rdoc.rdoc_dir = 'rdoc'
44
+ rdoc.title = "cheveret #{version}"
45
+ rdoc.rdoc_files.include('README*')
46
+ rdoc.rdoc_files.include('lib/**/*.rb')
47
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 1.0.0
data/cheveret.gemspec ADDED
@@ -0,0 +1,59 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{cheveret}
8
+ s.version = "1.0.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Ben Caldwell"]
12
+ s.date = %q{2010-09-17}
13
+ s.description = %q{Generating HTML tables of data in the views of your Rails
14
+ application is not very DRY even for the simpler of cases. Cheveret allows you to more
15
+ clearly separate logic and templating and reduce the amount of code in your views.}
16
+ s.email = %q{aulankz@gmail.com}
17
+ s.extra_rdoc_files = [
18
+ "LICENSE",
19
+ "README.md"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ ".gitignore",
24
+ "LICENSE",
25
+ "README.md",
26
+ "Rakefile",
27
+ "VERSION",
28
+ "cheveret.gemspec",
29
+ "init.rb",
30
+ "lib/cheveret.rb",
31
+ "rails/init.rb",
32
+ "spec/cheveret_spec.rb",
33
+ "spec/spec.opts",
34
+ "spec/spec_helper.rb"
35
+ ]
36
+ s.homepage = %q{http://github.com/lankz/cheveret}
37
+ s.rdoc_options = ["--charset=UTF-8"]
38
+ s.require_paths = ["lib"]
39
+ s.rubygems_version = %q{1.3.7}
40
+ s.summary = %q{Rails library for generating flexible width HTML tables}
41
+ s.test_files = [
42
+ "spec/cheveret_spec.rb",
43
+ "spec/spec_helper.rb"
44
+ ]
45
+
46
+ if s.respond_to? :specification_version then
47
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
48
+ s.specification_version = 3
49
+
50
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
51
+ s.add_development_dependency(%q<rspec>, [">= 1.2.9"])
52
+ else
53
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
54
+ end
55
+ else
56
+ s.add_dependency(%q<rspec>, [">= 1.2.9"])
57
+ end
58
+ end
59
+
data/init.rb ADDED
@@ -0,0 +1,2 @@
1
+ require 'cheveret'
2
+ ActionView::Base.send :include, Cheveret::Helpers
data/lib/cheveret.rb ADDED
@@ -0,0 +1,235 @@
1
+ #--
2
+ # Copyright (c) 2010 RateCity
3
+ #
4
+ # Permission is hereby granted, free of charge, to any person obtaining
5
+ # a copy of this software and associated documentation files (the
6
+ # "Software"), to deal in the Software without restriction, including
7
+ # without limitation the rights to use, copy, modify, merge, publish,
8
+ # distribute, sublicense, and/or sell copies of the Software, and to
9
+ # permit persons to whom the Software is furnished to do so, subject to
10
+ # the following conditions:
11
+ #
12
+ # The above copyright notice and this permission notice shall be
13
+ # included in all copies or substantial portions of the Software.
14
+ #
15
+ # THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
16
+ # EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
17
+ # MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
18
+ # NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
19
+ # LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
20
+ # OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
21
+ # WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
22
+ #++
23
+
24
+ require 'forwardable'
25
+
26
+ module Cheveret
27
+ class Table
28
+ attr_accessor :columns
29
+
30
+ def initialize()
31
+ @columns = ::ActiveSupport::OrderedHash.new
32
+ end
33
+
34
+ def columns(*args, &block)
35
+ return @columns.values unless block_given?
36
+ instance_eval(&block)
37
+ # todo: support populating columns from an array
38
+ # todo: attempt to automatically resolve columns from activerecord etc.
39
+ end
40
+
41
+ def add_column(column_name, config={})
42
+ @columns[column_name] = Column.new(column_name, config)
43
+ end
44
+
45
+ alias_method :column, :add_column
46
+
47
+ def fixed(column_name, config={})
48
+ add_column(column_name, config.merge({ :flexible => false }))
49
+ end
50
+
51
+ def flexible(column_name, config={})
52
+ add_column(column_name, config.merge({ :flexible => true }))
53
+ end
54
+
55
+ def hidden(column_name, config={})
56
+ add_column(column_name, config.merge({ :visible => false }))
57
+ end
58
+
59
+ def remove_column(column_name)
60
+ # todo: allow columns to be removed, not sure why you'd want to do this. maybe
61
+ # just set :visible => false instead?
62
+ raise NotImplementedError
63
+ end
64
+
65
+ def resize!(new_width)
66
+ return true if new_width == @width
67
+ @width = new_width
68
+
69
+ columns_width = 0
70
+ flexibles = []
71
+
72
+ self.columns.each do |column|
73
+ if column.visible?
74
+ columns_width += column.width
75
+ flexibles << column.name if column.flexible?
76
+ end
77
+ end
78
+
79
+ # todo: handle too-many/too-wide columns
80
+ raise "uh-oh spaghettio-s" if columns_width > new_width
81
+
82
+ # todo: fix rounding in with calculation
83
+ # todo: trim last column that fits into table width if necessary
84
+ if columns_width < new_width && !flexibles.empty?
85
+ padding = (new_width - columns_width) / flexibles.length
86
+ flexibles.each { |name| @columns[name].width += padding }
87
+ end
88
+ end
89
+ end
90
+
91
+ class Column
92
+ extend ::Forwardable
93
+
94
+ [ :width, :visible, :flexible, :label, :sortable ].each do |attr|
95
+ class_eval <<-RUBY_EVAL, __FILE__, __LINE__ + 1
96
+ def_delegator :@config, :#{attr}, :#{attr}
97
+ def_delegator :@config, :#{attr}=, :#{attr}=
98
+ RUBY_EVAL
99
+ end
100
+
101
+ attr_accessor :name
102
+
103
+ def initialize(column_name, config={})
104
+ @name = column_name
105
+
106
+ @config = ::ActiveSupport::OrderedOptions.new
107
+ config.each { |k, v| send("#{k}=", v) if respond_to?(k) }
108
+ end
109
+
110
+ # sets the column to be visible. columns are visible by default. use this when
111
+ # you've defined hidden columns that should only be displayed on some condition
112
+ def show
113
+ self.visible = true
114
+ end
115
+
116
+ # hides the column, prevents it from being rendered in the table
117
+ # helper for setting :visible => false
118
+ def hide
119
+ self.visible = false
120
+ end
121
+
122
+ # returns true for columns that have :visible => true or #show called on them
123
+ # columns are visible by default
124
+ def visible?
125
+ self.visible != false
126
+ end
127
+
128
+ def flexible?
129
+ self.flexible != false
130
+ end
131
+
132
+ def label
133
+ case @config.label
134
+ when nil then @name.to_str.humanize # todo: support i18n for column labels
135
+ when false then nil
136
+ else @config.label
137
+ end
138
+ end
139
+
140
+ def data(object)
141
+ object.send(self.name) if object.respond_to?(self.name)
142
+ end
143
+
144
+ def width
145
+ @config.width || 0
146
+ end
147
+ end
148
+
149
+ module Helpers
150
+ # renders a table for a collection of objects
151
+ def table_for(collection, options={}, &block)
152
+ builder = options.delete(:builder) || ActionView::Base.default_table_builder
153
+
154
+ options.merge!({ :collection => collection })
155
+ builder.render(Table.new, self, options, &block)
156
+ end
157
+
158
+ class TableBuilder
159
+ extend ::Forwardable
160
+ def_delegators :@table, :columns
161
+
162
+ def self.render(table, template, options={}, &block)
163
+ # todo: merge container element with :html passed into table_for
164
+ attrs = { :class => "table",
165
+ :style => "width: #{options[:width]}px;" }
166
+
167
+ template.content_tag(:div, attrs) do
168
+ template.capture(self.new(table, template, options), &block)
169
+ end
170
+ end
171
+
172
+ def initialize(table, template, options={})
173
+ @table, @template, = table, template
174
+
175
+ @width = options.delete(:width)
176
+ @collection = options.delete(:collection)
177
+ end
178
+
179
+ def header(*args, &block)
180
+ @table.resize!(@width)
181
+
182
+ row = @template.content_tag(:div, :class => "tr") do
183
+ map_columns(:th) do |column|
184
+ # todo: prevent output of empty <a> tag for header label
185
+ output = nil_capture(column, &block) if block_given?
186
+ output ||= @template.content_tag(:a, column.label)
187
+ end
188
+ end
189
+
190
+ @template.content_tag(:div, row, :class => "thead")
191
+ end
192
+
193
+ def body(&block)
194
+ @table.resize!(@width)
195
+ alt = false
196
+
197
+ rows = @collection.map do |object|
198
+ object_name = object.class.to_s.split('::').last.underscore || ''
199
+ klass = [ 'tr', object_name, (alt = !alt) ? nil : 'alt' ].compact
200
+ @template.content_tag(:div, :class => klass.join(' ')) do
201
+ map_columns(:td) do |column|
202
+ output = nil_capture(column, object, &block) if block_given?
203
+ output ||= @template.content_tag(:span, column.data(object))
204
+ end
205
+ end
206
+ end
207
+
208
+ @template.content_tag(:div, rows.join, :class => "tbody")
209
+ end
210
+
211
+ private
212
+
213
+ def map_columns(type, &block) #:nodoc:
214
+ @table.columns.map do |column|
215
+ attrs = { :class => [type, column.name].join(' '),
216
+ :style => "width: #{column.width}px;" }
217
+
218
+ @template.content_tag(:div, attrs) do
219
+ yield(column)
220
+ end if column.visible?
221
+ end
222
+ end
223
+
224
+ def nil_capture(*args, &block) #:nodoc:
225
+ custom = @template.capture(*args, &block).strip
226
+ output = custom.empty? ? nil : custom
227
+ end
228
+ end
229
+ end
230
+
231
+ ActionView::Base.class_eval do
232
+ cattr_accessor :default_table_builder
233
+ self.default_table_builder = ::Cheveret::Helpers::TableBuilder
234
+ end
235
+ end
data/rails/init.rb ADDED
@@ -0,0 +1 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'init'))
@@ -0,0 +1,7 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Cheveret" do
4
+ it "fails" do
5
+ fail "hey buddy, you should probably rename this file and start specing for real"
6
+ end
7
+ end
data/spec/spec.opts ADDED
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,9 @@
1
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
2
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
3
+ require 'cheveret'
4
+ require 'spec'
5
+ require 'spec/autorun'
6
+
7
+ Spec::Runner.configure do |config|
8
+
9
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: cheveret
3
+ version: !ruby/object:Gem::Version
4
+ hash: 23
5
+ prerelease: false
6
+ segments:
7
+ - 1
8
+ - 0
9
+ - 0
10
+ version: 1.0.0
11
+ platform: ruby
12
+ authors:
13
+ - Ben Caldwell
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-09-17 00:00:00 +10:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ name: rspec
23
+ prerelease: false
24
+ requirement: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 13
30
+ segments:
31
+ - 1
32
+ - 2
33
+ - 9
34
+ version: 1.2.9
35
+ type: :development
36
+ version_requirements: *id001
37
+ description: |-
38
+ Generating HTML tables of data in the views of your Rails
39
+ application is not very DRY even for the simpler of cases. Cheveret allows you to more
40
+ clearly separate logic and templating and reduce the amount of code in your views.
41
+ email: aulankz@gmail.com
42
+ executables: []
43
+
44
+ extensions: []
45
+
46
+ extra_rdoc_files:
47
+ - LICENSE
48
+ - README.md
49
+ files:
50
+ - .document
51
+ - .gitignore
52
+ - LICENSE
53
+ - README.md
54
+ - Rakefile
55
+ - VERSION
56
+ - cheveret.gemspec
57
+ - init.rb
58
+ - lib/cheveret.rb
59
+ - rails/init.rb
60
+ - spec/cheveret_spec.rb
61
+ - spec/spec.opts
62
+ - spec/spec_helper.rb
63
+ has_rdoc: true
64
+ homepage: http://github.com/lankz/cheveret
65
+ licenses: []
66
+
67
+ post_install_message:
68
+ rdoc_options:
69
+ - --charset=UTF-8
70
+ require_paths:
71
+ - lib
72
+ required_ruby_version: !ruby/object:Gem::Requirement
73
+ none: false
74
+ requirements:
75
+ - - ">="
76
+ - !ruby/object:Gem::Version
77
+ hash: 3
78
+ segments:
79
+ - 0
80
+ version: "0"
81
+ required_rubygems_version: !ruby/object:Gem::Requirement
82
+ none: false
83
+ requirements:
84
+ - - ">="
85
+ - !ruby/object:Gem::Version
86
+ hash: 3
87
+ segments:
88
+ - 0
89
+ version: "0"
90
+ requirements: []
91
+
92
+ rubyforge_project:
93
+ rubygems_version: 1.3.7
94
+ signing_key:
95
+ specification_version: 3
96
+ summary: Rails library for generating flexible width HTML tables
97
+ test_files:
98
+ - spec/cheveret_spec.rb
99
+ - spec/spec_helper.rb