table_me 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +3 -0
  3. data/Rakefile +40 -0
  4. data/app/assets/images/table_me/orderable.png +0 -0
  5. data/app/assets/stylesheets/table_me/table_me.sass +171 -0
  6. data/config/routes.rb +2 -0
  7. data/lib/table_me/builder.rb +36 -0
  8. data/lib/table_me/column.rb +19 -0
  9. data/lib/table_me/engine.rb +28 -0
  10. data/lib/table_me/filter.rb +81 -0
  11. data/lib/table_me/table_for_helper/table_for_helper.rb +112 -0
  12. data/lib/table_me/table_for_presenter.rb +229 -0
  13. data/lib/table_me/table_me_helper/table_me_helper.rb +28 -0
  14. data/lib/table_me/table_me_presenter.rb +105 -0
  15. data/lib/table_me/table_pagination.rb +110 -0
  16. data/lib/table_me/table_vo.rb +48 -0
  17. data/lib/table_me/url_builder.rb +33 -0
  18. data/lib/table_me/url_parser.rb +38 -0
  19. data/lib/table_me/version.rb +3 -0
  20. data/lib/table_me.rb +15 -0
  21. data/lib/tasks/table_me_tasks.rake +4 -0
  22. data/test/dummy/README.rdoc +261 -0
  23. data/test/dummy/Rakefile +7 -0
  24. data/test/dummy/app/assets/javascripts/application.js +15 -0
  25. data/test/dummy/app/assets/stylesheets/application.css +13 -0
  26. data/test/dummy/app/controllers/application_controller.rb +3 -0
  27. data/test/dummy/app/helpers/application_helper.rb +2 -0
  28. data/test/dummy/app/models/users.rb +2 -0
  29. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  30. data/test/dummy/config/application.rb +56 -0
  31. data/test/dummy/config/boot.rb +10 -0
  32. data/test/dummy/config/database.yml +25 -0
  33. data/test/dummy/config/environment.rb +5 -0
  34. data/test/dummy/config/environments/development.rb +46 -0
  35. data/test/dummy/config/environments/production.rb +67 -0
  36. data/test/dummy/config/environments/test.rb +37 -0
  37. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  38. data/test/dummy/config/initializers/inflections.rb +15 -0
  39. data/test/dummy/config/initializers/mime_types.rb +5 -0
  40. data/test/dummy/config/initializers/secret_token.rb +7 -0
  41. data/test/dummy/config/initializers/session_store.rb +8 -0
  42. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  43. data/test/dummy/config/locales/en.yml +5 -0
  44. data/test/dummy/config/routes.rb +58 -0
  45. data/test/dummy/config.ru +4 -0
  46. data/test/dummy/db/development.sqlite3 +0 -0
  47. data/test/dummy/db/migrate/20120223225814_create_users.rb +9 -0
  48. data/test/dummy/db/schema.rb +23 -0
  49. data/test/dummy/db/test.sqlite3 +0 -0
  50. data/test/dummy/log/development.log +52 -0
  51. data/test/dummy/log/test.log +6 -0
  52. data/test/dummy/public/404.html +26 -0
  53. data/test/dummy/public/422.html +26 -0
  54. data/test/dummy/public/500.html +25 -0
  55. data/test/dummy/public/favicon.ico +0 -0
  56. data/test/dummy/script/rails +6 -0
  57. data/test/fixtures/users.yml +9 -0
  58. data/test/integration/navigation_test.rb +10 -0
  59. data/test/integration/table_for_spec.rb +97 -0
  60. data/test/table_me_test.rb +7 -0
  61. data/test/test_helper.rb +10 -0
  62. data/test/unit/table_me_presenter_test.rb +7 -0
  63. metadata +225 -0
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2012 YOURNAME
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.rdoc ADDED
@@ -0,0 +1,3 @@
1
+ = TableMe
2
+
3
+ This project rocks and uses MIT-LICENSE.
data/Rakefile ADDED
@@ -0,0 +1,40 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'TableMe'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+
27
+
28
+ Bundler::GemHelper.install_tasks
29
+
30
+ require 'rake/testtask'
31
+
32
+ Rake::TestTask.new(:test) do |t|
33
+ t.libs << 'lib'
34
+ t.libs << 'test'
35
+ t.pattern = 'test/**/*_test.rb'
36
+ t.verbose = false
37
+ end
38
+
39
+
40
+ task :default => :test
@@ -0,0 +1,171 @@
1
+ .table-me
2
+ display: inline-block
3
+ font-size: 1.2em
4
+ .table-me-table
5
+ color: #eee
6
+ display: inline-block
7
+ float: left
8
+ font-family: 'Helvetica', 'Arial', 'Sans-Serif'
9
+ a
10
+ color: #909BAC
11
+ text-decoration: none
12
+ a:visited
13
+ color: #909BAC
14
+ table
15
+ border-collapse: collapse
16
+ th
17
+ border-top: 1px solid #777
18
+ border-left: 1px solid #777
19
+ border-bottom: 1px solid #000
20
+ border-right: 1px solid #000
21
+ color: #fff
22
+ height: 25px
23
+ background: #444 /* Old browsers */
24
+ background: -moz-linear-gradient(top, #444 0%, #212121 99%) /* FF3.6+ */
25
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#444), color-stop(99%,#212121)) /* Chrome,Safari4+ */
26
+ background: -webkit-linear-gradient(top, #444 0%,#212121 99%) /* Chrome10+,Safari5.1+ */
27
+ background: -o-linear-gradient(top, #444 0%,#212121 99%) /* Opera 11.10+ */
28
+ background: -ms-linear-gradient(top, #444 0%,#212121 99%) /* IE10+ */
29
+ background: linear-gradient(top, #444 0%,#212121 99%) /* W3C */
30
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#444', endColorstr='#212121',GradientType=0 ) /* IE6-9 */
31
+ a
32
+ padding: 8px 8px 8px 16px
33
+ color: #fff
34
+ vertical-align: center
35
+ background: image-url('table_me/orderable.png') 5px 15px no-repeat
36
+ a:visited
37
+ color: #fff
38
+ a.asc
39
+ background-position: 5px -17px
40
+ a.desc
41
+ background-position: 5px -43px
42
+ td
43
+ border-left: 1px solid #666
44
+ border-right: 1px solid #666
45
+ border-collapse: collapse
46
+ padding: 8px
47
+ .red,
48
+ .orange,
49
+ .green,
50
+ padding: 3px 7px
51
+ border: 1px solid #C55
52
+ font-size: .9em
53
+ .red
54
+ background-color: #F77
55
+ color: #a33
56
+ .green
57
+ background-color: #AfA
58
+ color: #090
59
+ border-color: #2A2
60
+ .orange
61
+ background-color: #FA0
62
+ color: #840
63
+ border-color: #C70
64
+ td:last-child
65
+ border-right: none
66
+ td:first-child
67
+ border-left: none
68
+ tr
69
+ border: none
70
+ .odd
71
+ background-color: rgba(255,255,255,0.1)
72
+ .table-me-pagination-info
73
+ padding-bottom: 8px
74
+ text-align: right
75
+ h3
76
+ position: relative
77
+ top: -7px
78
+ font-size: 1.2em
79
+ color: #FFF
80
+ float: left
81
+ font-family: 'Helvetica', 'Arial', 'Sans-Serif'
82
+ margin: 0
83
+ line-height: 1.4em
84
+ p
85
+ font-size: .8em
86
+ line-height: 1.4em
87
+ margin: 0
88
+ .table-me-pagination-controls
89
+ padding-top: 8px
90
+ text-align: right
91
+
92
+ .table-filters
93
+ color: #fff
94
+ font-family: 'Helvetica', 'Arial', 'Sans-Serif'
95
+ padding: 10px
96
+ border-radius: 5px
97
+ margin: 0 25px
98
+ float: right
99
+ background-color: #333
100
+ border-left: 1px solid #aaa
101
+ border-top: 1px solid #aaa
102
+ border-right: 1px solid #000
103
+ border-bottom: 1px solid #000
104
+ h3
105
+ margin: 0 0 10px 0
106
+ padding: 0
107
+ font-size: 1em
108
+ form
109
+ margin-bottom: 10px
110
+ form:last-child
111
+ margin: 0
112
+ input
113
+ width: auto
114
+ padding: 5px
115
+ margin: 0 8px 0 0
116
+ input[type=submit]
117
+ width: auto
118
+ padding: 5px 8px
119
+ border-left: 1px solid #aaa
120
+ border-top: 1px solid #aaa
121
+ border-right: 1px solid #000
122
+ border-bottom: 1px solid #000
123
+ color: #fff
124
+ background: #444 /* Old browsers */
125
+ background: -moz-linear-gradient(top, #444 0%, #212121 99%) /* FF3.6+ */
126
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#444), color-stop(99%,#212121)) /* Chrome,Safari4+ */
127
+ background: -webkit-linear-gradient(top, #444 0%,#212121 99%) /* Chrome10+,Safari5.1+ */
128
+ background: -o-linear-gradient(top, #444 0%,#212121 99%) /* Opera 11.10+ */
129
+ background: -ms-linear-gradient(top, #444 0%,#212121 99%) /* IE10+ */
130
+ background: linear-gradient(top, #444 0%,#212121 99%) /* W3C */
131
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#444', endColorstr='#212121',GradientType=0 ) /* IE6-9 */
132
+
133
+ .table-me-table.light
134
+ color: #555
135
+ a
136
+ color: #707a8b
137
+ a:visited
138
+ color: #7079ba
139
+ table
140
+ th
141
+ border-top: 1px solid #eee
142
+ border-left: 1px solid #eee
143
+ border-bottom: 1px solid #999
144
+ border-right: 1px solid #999
145
+ color: #FFF
146
+ background: #ccc /* Old browsers */
147
+ background: -moz-linear-gradient(top, #ccc 0%, #aaa 99%) /* FF3.6+ */
148
+ background: -webkit-gradient(linear, left top, left bottom, color-stop(0%,#ccc), color-stop(99%,#aaa)) /* Chrome,Safari4+ */
149
+ background: -webkit-linear-gradient(top, #ccc 0%,#aaa 99%) /* Chrome10+,Safari5.1+ */
150
+ background: -o-linear-gradient(top, #ccc 0%,#aaa 99%) /* Opera 11.10+ */
151
+ background: -ms-linear-gradient(top, #ccc 0%,#aaa 99%) /* IE10+ */
152
+ background: linear-gradient(top, #ccc 0%,#aaa 99%) /* W3C */
153
+ filter: progid:DXImageTransform.Microsoft.gradient( startColorstr='#ccc', endColorstr='#aaa',GradientType=0 ) /* IE6-9 */
154
+ td
155
+ border-left: 1px solid #aaa
156
+ border-right: 1px solid #aaa
157
+ td:last-child
158
+ border-right: none
159
+ td:first-child
160
+ border-left: none
161
+ tr
162
+ border: none
163
+ .odd
164
+ background-color: rgba(000,000,000,0.1)
165
+ .table-me-pagination-info
166
+ h3
167
+ color: #444
168
+
169
+
170
+
171
+
data/config/routes.rb ADDED
@@ -0,0 +1,2 @@
1
+ Rails.application.routes.draw do
2
+ end
@@ -0,0 +1,36 @@
1
+ require_relative 'column'
2
+ require_relative 'filter'
3
+ module TableMe
4
+ # This class is responsible for building the various elements of the
5
+ # table through a blog passed into the table_for_presenter
6
+
7
+ class Builder
8
+ attr_accessor :options
9
+ attr_reader :columns, :names, :filters
10
+ def initialize table_options
11
+ self.options = table_options
12
+ @columns = []
13
+ @names = []
14
+ end
15
+
16
+ # Define a column
17
+ def column name,options = {}, &block
18
+ @columns << TableMe::Column.new(name,options, &block)
19
+ @names << name
20
+ end
21
+
22
+ #define a filter
23
+ def filter name
24
+ TableMe::Filter.new(options, name)
25
+ end
26
+
27
+ def filters
28
+ TableMe::Filter.filters_for options[:name]
29
+ end
30
+
31
+ def clear_filter
32
+ # TODO this seems a bit hacky to me, Will want to rework this at some point
33
+ filters.last.display_clear
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,19 @@
1
+ module TableMe
2
+ # This class is the column object that gets created
3
+ # everytime a column is used in the table_for_presenter block
4
+ # It's basically a value object which keeps track of the data
5
+ # for each individual column.
6
+ class Column
7
+ attr_accessor :name, :content, :sortable
8
+ def initialize column_name, args = {}, &block
9
+ self.name = column_name
10
+ self.content = block
11
+
12
+ if block
13
+ self.sortable = args[:sort_on]
14
+ else
15
+ self.sortable = column_name
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,28 @@
1
+ require_relative '../table_me_helper/table_me_helper'
2
+ require_relative '../table_for_helper/table_for_helper'
3
+
4
+ module TableMe
5
+ class Engine < ::Rails::Engine
6
+ isolate_namespace TableMe
7
+
8
+ # send the helper methods to actionview and action controller
9
+ initializer "table_me" do
10
+ ActionView::Base.send :include, TableMe::TableForHelper
11
+ ActionController::Base.send :include, TableMe::TableMeHelper
12
+ end
13
+
14
+ initializer "table_me.load_app_instance_data" do |app|
15
+ TableMe.setup do |config|
16
+ config.app_root = app.root
17
+ end
18
+ end
19
+
20
+ initializer "table_me.load_static_assets" do |app|
21
+ app.middleware.use ::ActionDispatch::Static, "#{root}/public"
22
+ end
23
+
24
+ config.generators do |g|
25
+ g.test_framework :rspec, view_specs: false
26
+ end
27
+ end
28
+ end
@@ -0,0 +1,81 @@
1
+ require_relative 'url_builder'
2
+ module TableMe
3
+ # This class creates the filter forms for searching a table.
4
+ # Unlike the column class, the filter class is responsible
5
+ # for creating it's own view. See the table_for_helper file
6
+ # for documentation on how to use filters
7
+ class Filter
8
+ attr_accessor :options, :column_name
9
+
10
+ @@filters = {}
11
+
12
+ def initialize options, column_name
13
+ self.options = options
14
+ self.column_name = column_name
15
+
16
+ @@filters[options[:name]] ||= []
17
+
18
+
19
+ @@filters[options[:name]].delete_if {|item| item.column_name == column_name}
20
+ @@filters[options[:name]] << self
21
+ end
22
+
23
+ # Display the filter form
24
+ def display
25
+ initial_value = options[:search] && options[:search][:column] == column_name.to_s ? options[:search][:query] : ''
26
+ <<-HTML.strip_heredoc
27
+ <form method='get' action="?">
28
+ <label for='search'>#{column_name.to_s.split('_').join(" ").titleize}</label>
29
+ <input type='text' name="tm_#{options[:name]}[search][query]" value="#{initial_value}"/>
30
+ <input type='hidden' name="tm_#{options[:name]}[search][column]" value="#{column_name}"/>
31
+ <input type='hidden' name="tm_#{options[:name]}[new_search]" value="true"/>
32
+ #{create_other_fields options}
33
+ <input id='search' type='submit' value='Search' />
34
+ </form>
35
+ HTML
36
+
37
+ end
38
+
39
+ # display a clear filters button, this clears the filters if any are active.
40
+ # This could/should be just a link styled like a button, there really isn't a
41
+ # need for it to be a form
42
+ # TODO Change this into a link instead of a form
43
+ def display_clear
44
+ <<-HTML.strip_heredoc if options[:search]
45
+ <form method='get' action="?">
46
+ #{create_other_fields options}
47
+ <input id='search' type='submit' value='Clear Filter' />
48
+ </form>
49
+ HTML
50
+ end
51
+
52
+ # getter for all the filters
53
+ def self.filters_for table_name
54
+ @@filters[table_name]
55
+ end
56
+
57
+ private
58
+
59
+ def create_other_fields options
60
+ inputs = []
61
+ TableMe::UrlBuilder.filter_options(options).each do |option|
62
+ option.each do |k,v|
63
+ if k.to_s == 'search'
64
+ # Adon't add the search fields if they are the current table
65
+ unless option[:name] == options[:name]
66
+ inputs << "<input type='hidden' name='tm_#{option[:name]}[search][query]' value='#{v[:query]}'/>"
67
+ inputs << "<input type='hidden' name='tm_#{option[:name]}[search][column]' value='#{v[:column]}'/>"
68
+ end
69
+ else
70
+ inputs << "<input type='hidden' name='tm_#{option[:name]}[#{k.to_s}]' value='#{v}'/>"
71
+ end
72
+ end
73
+ end
74
+ inputs.join("\n")
75
+ end
76
+
77
+ def url_for_tables options
78
+ TableMe::UrlBuilder.url_for options
79
+ end
80
+ end
81
+ end
@@ -0,0 +1,112 @@
1
+ require File.expand_path('../../../table_me/table_for_presenter', __FILE__)
2
+
3
+ # In your view you can create the table from the one initialized in the controller,
4
+ # the first parameter of table_for is the name set in table_me. By default the class
5
+ # name is used if a name isn't set.
6
+
7
+ # table_for :user
8
+ # Now, this will list all columns from the database in your table, which you
9
+ # may not always want to do. You can pass a block of columns to be more specific:
10
+
11
+ # table_for :user do |t|
12
+ # t.column :id
13
+ # t.column :email
14
+ # t.column :created_at
15
+ # end
16
+
17
+ # This will give you a user table with the columns id, email, and created_at.
18
+
19
+ # What if you want to customize the output of the column? Each column can also
20
+ # take a block of content:
21
+
22
+ # table_for :user do |t|
23
+ # t.column :id
24
+ # t.column :email do |c|
25
+ # "<h1>c.email</h1>"
26
+ # end
27
+ # t.column :created_at
28
+ # end
29
+
30
+ # Now, when a block is used to alter the content of a column, the sorting is lost,
31
+ # since the table can no longer assume what is in the column. You need to set a sort_on
32
+ # param to tell the column what to sort by. For example:
33
+
34
+ # table_for :user do |t|
35
+ # t.column :id
36
+ # t.column :email, sort_on: :email do |c|
37
+ # "<h1>c.email</h1>"
38
+ # end
39
+ # t.column :created_at
40
+ # end
41
+
42
+ # Filters
43
+ # You can add basic filter fields to the table by using the filter method. Right now,
44
+ # only one filter can be applied and the filters are search fields. I would like to
45
+ # eventually add different types for different types of data. I would like to eventually
46
+ # add in the ability for multiple filter types with a single search button, but the basic
47
+ # form is all I need at the moment. Ajax enabled filtering would be freaking great as well.
48
+
49
+ # Filter usage:
50
+
51
+ # table_for :user do |t|
52
+ # t.filter :email
53
+ # t.filter :name
54
+ # t.column :id
55
+ # t.column :email
56
+ # t.column :name
57
+ # end
58
+
59
+ module TableMe
60
+ module TableForHelper
61
+ def table_for(model,options = {}, &block)
62
+ table_for_presenter = TableForPresenter.new(model,options,&block)
63
+ table_for_presenter.build_table
64
+ end
65
+
66
+ # Lets say that you want to have a visual que for if a user is an admin:
67
+ # table_for :user do |t|
68
+ # t.column :id
69
+ # t.column :admin do |c|
70
+ # highlight_cell c.admin, green: true
71
+ # end
72
+ # t.column :created_at
73
+ # end
74
+
75
+ # You can put a green box around true in the column by passing an array to the color
76
+ # where the first value is the actual table value your looking for, while the second item
77
+ # is what will be used in the table field. But what if you want to change that true to the
78
+ # word 'Admin' and lets put a red box around all the non admins and make them say 'peons':
79
+
80
+ # table_for :user do |t|
81
+ # t.column :id
82
+ # t.column :admin do |c|
83
+ # highlight_cell c.admin, green: [true, 'Admin'], red: [false, 'peon']
84
+ # end
85
+ # t.column :created_at
86
+ # end
87
+
88
+ def highlight_cell value, colors
89
+ color_value = output_value = color = ''
90
+
91
+ colors.each do |k,v|
92
+ if v.kind_of? Array
93
+ color_value = v[0]
94
+ output_value = v[1]
95
+ else
96
+ output_value = color_value = v
97
+ end
98
+
99
+ if color_value == value
100
+ color = k.to_s
101
+ break
102
+ end
103
+ end
104
+
105
+ unless color.empty?
106
+ "<span class='#{color}'>#{output_value}</span>".html_safe
107
+ else
108
+ value
109
+ end
110
+ end
111
+ end
112
+ end