tabulatr2 0.6.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (112) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +1 -0
  4. data/.travis.yml +4 -0
  5. data/Changelog.textile +124 -0
  6. data/Gemfile +21 -0
  7. data/LICENSE +23 -0
  8. data/README.md +242 -0
  9. data/Rakefile +11 -0
  10. data/app/assets/images/tabulatr/buttons_lite_background.png +0 -0
  11. data/app/assets/images/tabulatr/pager_arrow_left.gif +0 -0
  12. data/app/assets/images/tabulatr/pager_arrow_left_off.gif +0 -0
  13. data/app/assets/images/tabulatr/pager_arrow_right.gif +0 -0
  14. data/app/assets/images/tabulatr/pager_arrow_right_off.gif +0 -0
  15. data/app/assets/images/tabulatr/sort_arrow_down.gif +0 -0
  16. data/app/assets/images/tabulatr/sort_arrow_down_off.gif +0 -0
  17. data/app/assets/images/tabulatr/sort_arrow_up.gif +0 -0
  18. data/app/assets/images/tabulatr/sort_arrow_up_off.gif +0 -0
  19. data/app/assets/javascripts/tabulatr/application.js +452 -0
  20. data/app/assets/javascripts/tabulatr/jquery.inview.min.js +3 -0
  21. data/app/assets/javascripts/tabulatr.js +1 -0
  22. data/app/assets/stylesheets/tabulatr/application.css +40 -0
  23. data/app/assets/stylesheets/tabulatr.css +4 -0
  24. data/init.rb +1 -0
  25. data/lib/generators/tabulatr/install_generator.rb +44 -0
  26. data/lib/generators/tabulatr/templates/tabulatr.rb +5 -0
  27. data/lib/generators/tabulatr/templates/tabulatr.yml +14 -0
  28. data/lib/initializers/action_controller.rb +13 -0
  29. data/lib/initializers/action_view.rb +31 -0
  30. data/lib/initializers/active_record.rb +48 -0
  31. data/lib/initializers/mark_as_localizable.rb +43 -0
  32. data/lib/tabulatr/engine.rb +3 -0
  33. data/lib/tabulatr/tabulatr/adapter/active_record.rb +84 -0
  34. data/lib/tabulatr/tabulatr/adapter.rb +55 -0
  35. data/lib/tabulatr/tabulatr/batch_actions.rb +51 -0
  36. data/lib/tabulatr/tabulatr/data_cell.rb +132 -0
  37. data/lib/tabulatr/tabulatr/dummy_record.rb +40 -0
  38. data/lib/tabulatr/tabulatr/empty_cell.rb +44 -0
  39. data/lib/tabulatr/tabulatr/filter_cell.rb +145 -0
  40. data/lib/tabulatr/tabulatr/filter_icon.rb +6 -0
  41. data/lib/tabulatr/tabulatr/finder/find_for_table.rb +187 -0
  42. data/lib/tabulatr/tabulatr/finder.rb +64 -0
  43. data/lib/tabulatr/tabulatr/formattr.rb +55 -0
  44. data/lib/tabulatr/tabulatr/header_cell.rb +146 -0
  45. data/lib/tabulatr/tabulatr/json_builder.rb +57 -0
  46. data/lib/tabulatr/tabulatr/paginator.rb +76 -0
  47. data/lib/tabulatr/tabulatr/row_builder.rb +128 -0
  48. data/lib/tabulatr/tabulatr/security.rb +21 -0
  49. data/lib/tabulatr/tabulatr/settings.rb +158 -0
  50. data/lib/tabulatr/tabulatr.rb +343 -0
  51. data/lib/tabulatr/version.rb +3 -0
  52. data/lib/tabulatr.rb +34 -0
  53. data/spec/dummy/.gitignore +18 -0
  54. data/spec/dummy/README.rdoc +28 -0
  55. data/spec/dummy/Rakefile +6 -0
  56. data/spec/dummy/app/assets/images/.keep +0 -0
  57. data/spec/dummy/app/assets/javascripts/application.js +16 -0
  58. data/spec/dummy/app/assets/stylesheets/application.css.scss +15 -0
  59. data/spec/dummy/app/controllers/application_controller.rb +5 -0
  60. data/spec/dummy/app/controllers/concerns/.keep +0 -0
  61. data/spec/dummy/app/controllers/products_controller.rb +24 -0
  62. data/spec/dummy/app/controllers/tags_controller.rb +5 -0
  63. data/spec/dummy/app/controllers/vendors_controller.rb +5 -0
  64. data/spec/dummy/app/helpers/application_helper.rb +9 -0
  65. data/spec/dummy/app/mailers/.keep +0 -0
  66. data/spec/dummy/app/models/.keep +0 -0
  67. data/spec/dummy/app/models/concerns/.keep +0 -0
  68. data/spec/dummy/app/models/product.rb +5 -0
  69. data/spec/dummy/app/models/tag.rb +3 -0
  70. data/spec/dummy/app/models/vendor.rb +2 -0
  71. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  72. data/spec/dummy/app/views/products/count_tags.html.erb +9 -0
  73. data/spec/dummy/app/views/products/one_item_per_page.html.erb +9 -0
  74. data/spec/dummy/app/views/products/simple_index.html.erb +9 -0
  75. data/spec/dummy/app/views/products/stupid_array.html.erb +20 -0
  76. data/spec/dummy/bin/bundle +3 -0
  77. data/spec/dummy/bin/rails +4 -0
  78. data/spec/dummy/bin/rake +4 -0
  79. data/spec/dummy/config/application.rb +23 -0
  80. data/spec/dummy/config/boot.rb +5 -0
  81. data/spec/dummy/config/database.yml +25 -0
  82. data/spec/dummy/config/environment.rb +5 -0
  83. data/spec/dummy/config/environments/development.rb +29 -0
  84. data/spec/dummy/config/environments/production.rb +80 -0
  85. data/spec/dummy/config/environments/test.rb +36 -0
  86. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  87. data/spec/dummy/config/initializers/filter_parameter_logging.rb +4 -0
  88. data/spec/dummy/config/initializers/inflections.rb +16 -0
  89. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  90. data/spec/dummy/config/initializers/secret_token.rb +12 -0
  91. data/spec/dummy/config/initializers/session_store.rb +3 -0
  92. data/spec/dummy/config/initializers/tabulatr.rb +5 -0
  93. data/spec/dummy/config/initializers/wrap_parameters.rb +14 -0
  94. data/spec/dummy/config/locales/en.yml +23 -0
  95. data/spec/dummy/config/locales/tabulatr.yml +14 -0
  96. data/spec/dummy/config/routes.rb +13 -0
  97. data/spec/dummy/config.ru +4 -0
  98. data/spec/dummy/db/migrate/20130730132101_create_vendors.rb +12 -0
  99. data/spec/dummy/db/migrate/20130730132321_create_products.rb +12 -0
  100. data/spec/dummy/db/migrate/20130730132348_create_tags.rb +14 -0
  101. data/spec/dummy/db/schema.rb +47 -0
  102. data/spec/dummy/lib/assets/.keep +0 -0
  103. data/spec/dummy/log/.keep +0 -0
  104. data/spec/dummy/public/404.html +58 -0
  105. data/spec/dummy/public/422.html +58 -0
  106. data/spec/dummy/public/500.html +57 -0
  107. data/spec/dummy/public/favicon.ico +0 -0
  108. data/spec/features/tabulatrs_spec.rb +227 -0
  109. data/spec/lib/tabulatr/tabulatr/finder/find_for_table_spec.rb +187 -0
  110. data/spec/spec_helper.rb +45 -0
  111. data/tabulatr.gemspec +29 -0
  112. metadata +258 -0
@@ -0,0 +1,128 @@
1
+ #--
2
+ # Copyright (c) 2010-2011 Peter Horn, Provideal GmbH
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
+ # These are extensions for use as a row builder
25
+ # In a seperate class call only for clearity
26
+
27
+ class Tabulatr
28
+
29
+ # called inside the build_table block, branches into data, header,
30
+ # or filter building methods depending on the current mode
31
+ def column(name, opts={}, &block)
32
+ #puts "column: '#{name}'"
33
+ case @row_mode
34
+ when :data then data_column(name, opts, &block)
35
+ when :header then
36
+ @attributes << name.to_s
37
+ header_column(name, opts, &block)
38
+ when :filter then filter_column(name, opts, &block)
39
+ when :empty then empty_column(name, opts, &block)
40
+ else raise "Wrong row mode '#{@row_mode}'"
41
+ end # case
42
+ end
43
+
44
+ # called inside the build_table block, branches into data, header,
45
+ # or filter building methods depending on the current mode
46
+ def association(relation, name, opts={}, &block)
47
+ #puts "assoc: '#{relation}.#{name}'"
48
+ case @row_mode
49
+ when :data then data_association(relation, name, opts, &block)
50
+ when :header then
51
+ @attributes << "#{relation}:#{name}"
52
+ header_association(relation, name, opts, &block)
53
+ when :filter then filter_association(relation, name, opts, &block)
54
+ when :empty then empty_column(name, opts, &block)
55
+ else raise "Wrong row mode '#{@row_mode}'"
56
+ end # case
57
+ end
58
+
59
+ # called inside the build_table block, branches into data, header,
60
+ # or filter building methods depending on the current mode
61
+ def checkbox(opts={}, &block)
62
+ #puts "column: '#{name}'"
63
+ case @row_mode
64
+ when :data then data_checkbox(opts, &block)
65
+ when :header then header_checkbox(opts, &block)
66
+ when :filter then filter_checkbox(opts, &block)
67
+ when :empty then nil
68
+ else raise "Wrong row mode '#{@row_mode}'"
69
+ end # case
70
+ end
71
+
72
+ def action(opts={}, &block)
73
+ #puts "column: '#{name}'"
74
+ case @row_mode
75
+ when :data then data_action(opts, &block)
76
+ when :header then header_action(opts, &block)
77
+ when :filter then filter_action(opts, &block)
78
+ when :empty then nil
79
+ else raise "Wrong row mode '#{@row_mode}'"
80
+ end # case
81
+ end
82
+
83
+ private
84
+
85
+ # returns self, sets record to nil and row_mode as required for a
86
+ # header row
87
+ def header_row_builder
88
+ @record = nil
89
+ @row_mode = :header
90
+ self
91
+ end
92
+
93
+ def filter_form_builder
94
+ @record = nil
95
+ @row_mode = :filter
96
+ self
97
+ end
98
+
99
+ def empty_row_builder
100
+ @record = nil
101
+ @row_mode = :empty
102
+ self
103
+ end
104
+
105
+ def data_row_builder(record)
106
+ @record = record
107
+ @row_mode = :data
108
+ self
109
+ end
110
+
111
+
112
+ # some preprocessing of the options
113
+ def normalize_column_options(name, opts)
114
+ preset = column_preset_for(opts[:preset] || name)
115
+ opts = Tabulatr::COLUMN_OPTIONS.merge(preset).merge(opts)
116
+ {:width => 'width', :align => 'text-align', :valign => 'vertical-align'}.each do |key,css|
117
+ if opts[key]
118
+ [:th_html, :filter_html].each do |set|
119
+ opts[set] ||= {}
120
+ opts[set][:style] = (opts[set][:style] ? opts[set][:style] << "; " : "") << "#{css}: #{opts[key]}"
121
+ end # each
122
+ end # if
123
+ end # each
124
+ # more to come!
125
+ opts
126
+ end
127
+ end
128
+
@@ -0,0 +1,21 @@
1
+ require 'securerandom'
2
+
3
+ class Tabulatr
4
+ class Security
5
+ def self.sign(arglist, salt=nil)
6
+ salt ||= SecureRandom.base64
7
+ str = "#{Tabulatr.secret_tokens.first}-#{salt}-#{arglist}-#{Rails.application.config.secret_token}-#{Tabulatr.secret_tokens.last}"
8
+ hash = Digest::SHA1.hexdigest(str)
9
+ "#{arglist}-#{salt}-#{hash[5..40]}"
10
+ end
11
+
12
+ def self.validate(str)
13
+ arglist, salt, hash = str.split('-')
14
+ str == sign(arglist, salt)
15
+ end
16
+
17
+ def self.validate!(str)
18
+ validate(str) or raise "SECURITY!"
19
+ end
20
+ end
21
+ end
@@ -0,0 +1,158 @@
1
+ #--
2
+ # Copyright (c) 2010-2011 Peter Horn, Provideal GmbH
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 'whiny_hash'
25
+
26
+ class Tabulatr
27
+
28
+ # Hash keeping the defaults for the table options, may be overriden in the
29
+ # table_for call
30
+ TABLE_OPTIONS = WhinyHash.new({ # WhinyHash.new({
31
+ :table_class => 'tabulatr_table', # class for the actual data table
32
+ :control_div_class_before => 'table-controls', # class of the upper div containing the paging and batch action controls
33
+ :control_div_class_after => 'table-controls', # class of the lower div containing the paging and batch action controls
34
+ :paginator_div_class => 'pagination', # class of the div containing the paging controls
35
+
36
+ # which controls to be rendered above and below the tabel and in which order
37
+ :before_table_controls => [:filter, :paginator],
38
+ :after_table_controls => [],
39
+
40
+ :table_html => false, # a hash with html attributes for the table
41
+ :row_html => false, # a hash with html attributes for the normal trs
42
+ :header_html => false, # a hash with html attributes for the header trs
43
+ :filter_html => false, # a hash with html attributes for the filter trs
44
+ :filter => true, # false for no filter row at all
45
+ :paginate => false, # true to show paginator
46
+ :default_pagesize => 10, # default pagesize
47
+ :sortable => true, # true to allow sorting (can be specified for every sortable column)
48
+ :batch_actions => false, # :name => value hash of batch action stuff
49
+ :footer_content => false, # if given, add a <%= content_for <footer_content> %> before the </table>
50
+ :path => '#' # where to send the AJAX-requests to
51
+ })
52
+
53
+ # these settings are considered constant for the whole application, can not be overridden
54
+ # on a per-table basis.
55
+ # That's necessary to allow find_for_table to work properly
56
+ TABLE_FORM_OPTIONS = WhinyHash.new({
57
+ :filter_postfix => '_filter', # postfix for name of the filter in the params :hash => xxx_filter
58
+ :sort_postfix => '_sort', # postfix for name of the filter in the params :hash => xxx_filter
59
+ :associations_filter => '__association', # name of the associations in the filter hash
60
+ :batch_postfix => '_batch', # postfix for name of the batch action select
61
+ :checked_separator => ',' # symbol to separate the checked ids
62
+ })
63
+
64
+ # these settings are considered constant for the whole application, can not be overridden
65
+ # on a per-table basis.
66
+ # That's necessary to allow find_for_table to work properly
67
+ PAGINATE_OPTIONS = ActiveSupport::HashWithIndifferentAccess.new({
68
+ :page => 1,
69
+ :pagesize => 10,
70
+ :pagesizes => [10, 20, 50]
71
+ })
72
+
73
+ # Hash keeping the defaults for the column options
74
+ COLUMN_OPTIONS = ActiveSupport::HashWithIndifferentAccess.new({
75
+ :header => false, # a string to write into the header cell
76
+ :width => false, # the width of the cell
77
+ :align => false, # horizontal alignment
78
+ :valign => false, # vertical alignment
79
+ :wrap => true, # wraps
80
+ :type => :string, # :integer, :date
81
+ :th_html => false, # a hash with html attributes for the header cell
82
+ :filter_html => false, # a hash with html attributes for the filter cell
83
+ :filter => true, # false for no filter field,
84
+ # container for options_for_select
85
+ # String from options_from_collection_for_select or the like
86
+ # :range for range spec
87
+ # :checkbox for a 0/1 valued checkbox
88
+ :checkbox_value => '1', # value if checkbox is checked
89
+ :checkbox_label => '', # text behind the checkbox
90
+ :filter_width => '97%', # width of the filter <input>
91
+ :range_filter_symbol => '&ndash;', # put between the <inputs> of the range filter
92
+ :sortable => true, # if set, sorting-stuff is added to the header cell
93
+ :format_methods => [] # javascript methods to execute on this column
94
+ })
95
+
96
+ # defaults for the find_for_table
97
+ FINDER_OPTIONS = WhinyHash.new({
98
+ :default_order => false,
99
+ :precondition => false,
100
+ :store_data => false,
101
+ :name_mapping => nil,
102
+ :action => nil
103
+ })
104
+
105
+ # Stupid hack
106
+ SQL_OPTIONS = WhinyHash.new({
107
+ :like => nil
108
+ })
109
+
110
+ def self.finder_options(n=nil)
111
+ FINDER_OPTIONS.merge!(n) if n
112
+ FINDER_OPTIONS
113
+ end
114
+
115
+ def self.column_options(n=nil)
116
+ COLUMN_OPTIONS.merge!(n) if n
117
+ COLUMN_OPTIONS
118
+ end
119
+
120
+ def self.table_options(n=nil)
121
+ TABLE_OPTIONS.merge!(n) if n
122
+ TABLE_OPTIONS
123
+ end
124
+
125
+ def self.paginate_options(n=nil)
126
+ PAGINATE_OPTIONS.merge!(n) if n
127
+ PAGINATE_OPTIONS
128
+ end
129
+
130
+ def self.table_form_options(n=nil)
131
+ TABLE_FORM_OPTIONS.merge!(n) if n
132
+ TABLE_FORM_OPTIONS
133
+ end
134
+
135
+ def self.table_design_options(n=nil)
136
+ raise("table_design_options stopped existing. Use table_options instead.")
137
+ end
138
+ def table_design_options(n=nil) self.class.table_design_options(n) end
139
+
140
+ def self.sql_options(n=nil)
141
+ SQL_OPTIONS.merge!(n) if n
142
+ SQL_OPTIONS
143
+ end
144
+ def sql_options(n=nil) self.class.sql_options(n) end
145
+
146
+ COLUMN_PRESETS = {}
147
+ def self.column_presets(n=nil)
148
+ COLUMN_PRESETS.merge!(n) if n
149
+ COLUMN_PRESETS
150
+ end
151
+ def column_presets(n=nil) self.class.column_presets(n) end
152
+ def column_preset_for(name)
153
+ h = COLUMN_PRESETS[name.to_sym]
154
+ return {} unless h
155
+ return h if h.is_a? Hash
156
+ COLUMN_PRESETS[h] || {}
157
+ end
158
+ end
@@ -0,0 +1,343 @@
1
+ #--
2
+ # Copyright (c) 2010-2011 Peter Horn, Provideal GmbH
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
+ # Tabulatr is a class to allow easy creation of data tables as you
25
+ # frequently find them on 'index' pages in rails. The 'table convention'
26
+ # here is that we consider every table to consist of three parts:
27
+ # * a header containing the names of the attribute of the column
28
+ # * a filter which is an input element to allow for searching the
29
+ # particular attribute
30
+ # * the rows with the actual data.
31
+ #
32
+ # Additionally, we expect that people want to 'select' rows and perform
33
+ # batch actions on these rows.
34
+ #
35
+ # Author:: Peter Horn, (mailto:peter.horn@provideal.net)
36
+ # Copyright:: Copyright (c) 2010-2011 by Provideal GmbH (http://www.provideal.net)
37
+ # License:: MIT Licence
38
+ class Tabulatr
39
+
40
+ include ActionView::Helpers::TagHelper
41
+ include ActionView::Helpers::FormTagHelper
42
+ include ActionView::Helpers::FormOptionsHelper
43
+ include ActionView::Helpers::TranslationHelper
44
+ include ActionView::Helpers::RecordTagHelper
45
+ # include ActionView::Helpers::AssetTagHelper
46
+ # include Rails::Application::Configurable
47
+
48
+ # Constructor of Tabulatr
49
+ #
50
+ # Parameters:
51
+ # <tt>klass</tt>:: the klass of the data for the table
52
+ # <tt>view</tt>:: the current instance of ActionView
53
+ # <tt>opts</tt>:: a hash of options specific for this table
54
+ def initialize(klass_or_record, view=nil, toptions={})
55
+ if klass_or_record.is_a?(Class) && klass_or_record < ActiveRecord::Base
56
+ @klass = klass_or_record
57
+ @records = nil
58
+ elsif klass_or_record.respond_to?(:each)
59
+ @records = klass_or_record
60
+ @klass = @records.first.try(:class)
61
+ toptions = toptions.merge! \
62
+ :filter => false,
63
+ :paginate => false,
64
+ :sortable => false
65
+ else
66
+ raise "Give a model-class or an collection to `table_for'"
67
+ end
68
+ @view = view
69
+ @table_options = TABLE_OPTIONS.merge(toptions)
70
+ @table_form_options = TABLE_FORM_OPTIONS
71
+ @val = []
72
+ @record = nil
73
+ @row_mode = false
74
+ @classname = @klass.to_s.downcase.gsub("/","_")
75
+ @attributes = []
76
+ end
77
+
78
+ cattr_accessor :bootstrap_paginator, instance_accessor: false do
79
+ 'create_ul_paginator'
80
+ end
81
+
82
+ def self.config &block
83
+ yield self
84
+ end
85
+
86
+ def self.secret_tokens=(secret_tokens)
87
+ @@secret_tokens = secret_tokens
88
+ end
89
+
90
+ def self.secret_tokens
91
+ @@secret_tokens ||= []
92
+ end
93
+
94
+ # the actual table definition method. It takes an Array of records, a hash of
95
+ # options and a block with the actual <tt>column</tt> calls.
96
+ #
97
+ # The following options are evaluated here:
98
+ # <tt>:table_html</tt>:: a hash with html-attributes added to the <table> created
99
+ # <tt>:header_html</tt>:: a hash with html-attributes added to the <tr> created
100
+ # for the header row
101
+ # <tt>:filter_html</tt>:: a hash with html-attributes added to the <tr> created
102
+ # for the filter row
103
+ # <tt>:row_html</tt>:: a hash with html-attributes added to the <tr>s created
104
+ # for the data rows
105
+ # <tt>:filter</tt>:: if set to false, no filter row is output
106
+ def build_table(&block)
107
+ return nil if @records && @records.blank?
108
+ @val = []
109
+ # TODO: make_tag(:input, :type => 'submit', :style => 'display:inline; width:1px; height:1px', :value => '__submit')
110
+ unless @records
111
+ render_table_controls(:control_div_class_before, :before_table_controls)
112
+ end
113
+
114
+ render_element(:table, &block)
115
+
116
+ unless @records
117
+ render_table_controls(:control_div_class_after, :after_table_controls)
118
+ make_tag(:div, class: "tabulatr_count",
119
+ :'data-table' => "#{@klass.to_s.downcase}_table",
120
+ :'data-format-string' => I18n.t('tabulatr.count')){}
121
+
122
+ render_filter_dialog &block
123
+ sec_hash = Tabulatr::Security.sign(@attributes.join(','))
124
+ make_tag(:span, :id => "tabulatr_security_#{@klass.to_s.downcase}",
125
+ :'data-salt' => sec_hash.split('-')[1],
126
+ :'data-hash' => sec_hash.split('-')[2]){}
127
+ end
128
+ @val.join("").html_safe
129
+ end
130
+
131
+ def render_element(element, &block)
132
+ case element
133
+ when :filter then render_filter_icon
134
+ when :paginator then render_paginator
135
+ when :table then render_table &block
136
+ else
137
+ if element.is_a?(String)
138
+ concat(element)
139
+ else
140
+ raise "unknown element '#{element}'"
141
+ end
142
+ end
143
+ end
144
+
145
+
146
+ def render_table(&block)
147
+ to = @table_options[:table_html]
148
+ to = (to || {}).merge(:class => "#{@table_options[:table_class]} table",
149
+ :'data-path' => @table_options[:path], :id => "#{@klass.to_s.downcase}_table")
150
+ make_tag(:table, to) do
151
+ make_tag(:thead) do
152
+ render_table_header(&block)
153
+ end # </thead>
154
+ if @records
155
+ make_tag(:tbody) do
156
+ render_table_rows(&block)
157
+ end # </tbody>
158
+ else
159
+ make_tag(:tbody) do
160
+ render_empty_start_row(&block)
161
+ end # </tbody>
162
+ end
163
+ content_for(@table_options[:footer_content]) if @table_options[:footer_content]
164
+ end # </table>
165
+ end
166
+
167
+ private
168
+
169
+ def readable_name_for(name, relation=nil)
170
+ if relation
171
+ "#{@klass.human_attribute_name(relation).titlecase}
172
+ #{@klass.reflect_on_association(relation).klass.
173
+ human_attribute_name(name).titlecase}"
174
+ else
175
+ @klass.human_attribute_name(name).titlecase
176
+ end
177
+ end
178
+
179
+ # either append to the internal string buffer or use
180
+ # ActionView#concat to output if an instance is available.
181
+ def concat(s, html_escape=false)
182
+ #@view.concat(s) if (Rails.version.to_f < 3.0 && @view)
183
+ #puts "\##{Rails.version.to_f} '#{s}'"
184
+ if s.present? then @val << (html_escape ? h(s) : s) end
185
+ end
186
+
187
+ def h(s)
188
+ ERB::Util.h(s)
189
+ end
190
+
191
+ def t(s)
192
+ return '' unless s.present?
193
+ begin
194
+ if s.respond_to?(:should_localize?) and s.should_localize?
195
+ translate(s)
196
+ else
197
+ case @should_translate
198
+ when :translate then translate(s)
199
+ when true then translate(s)
200
+ when :localize then localize(s)
201
+ else
202
+ if !@should_translate
203
+ s
204
+ elsif @should_translate.respond_to?(:call)
205
+ @should_translate.call(s)
206
+ else
207
+ raise "Wrong value '#{@should_translate}' for table option ':translate', should be false, true, :translate, :localize or a proc."
208
+ end
209
+ end
210
+ end
211
+ rescue
212
+ raise "Translating '#{s}' failed!"
213
+ end
214
+ end
215
+
216
+ # render the header row
217
+ def render_table_header(&block)
218
+ make_tag(:tr, @table_options[:header_html]) do
219
+ yield(header_row_builder)
220
+ end # </tr>"
221
+ end
222
+
223
+
224
+ def render_filter_options(&block)
225
+ yield(filter_form_builder)
226
+ make_tag(:input, :type => 'hidden', :name => 'sort_by')
227
+ make_tag(:input, :type => 'hidden', :name => 'orientation')
228
+ end
229
+
230
+ def render_empty_start_row(&block)
231
+ row_html = @table_options[:row_html] || {}
232
+ row_html[:class] = 'empty_row'
233
+ make_tag(:tr, row_html) do
234
+ yield empty_row_builder
235
+ end
236
+ end
237
+
238
+ def render_table_controls div_class, before_or_after
239
+ make_tag(:div, :class => @table_options[div_class]) do
240
+ @table_options[before_or_after].each do |element|
241
+ render_element(element)
242
+ end
243
+ end if @table_options[before_or_after].present? # </div>
244
+ end
245
+
246
+ def render_filter_dialog &block
247
+ make_tag(:div, class: 'modal fade', id: "tabulatr_filter_dialog_#{@klass.to_s.downcase}", style: "display:none ;") do
248
+ make_tag(:div, class: 'modal-dialog') do
249
+ make_tag(:div, class: 'modal-content') do
250
+ make_tag(:div, class: 'modal-header') do
251
+ make_tag(:button, class: :close, :'data-dismiss' => :modal,
252
+ :'aria-hidden' => true) do
253
+ concat "&times"
254
+ end
255
+ make_tag(:h3, class: 'modal-title') do
256
+ concat I18n.t('tabulatr.filter')
257
+ end
258
+ end
259
+ make_tag(:form, :'data-table' => "#{@klass.to_s.downcase}_table",
260
+ class: 'form-horizontal tabulatr_filter_form', :'data-remote' => true) do
261
+ make_tag(:div, class: 'modal-body') do
262
+ render_filter_options &block
263
+ end
264
+ make_tag(:div, class: 'modal-footer') do
265
+ make_tag(:input, :type => 'submit',
266
+ :class => 'submit-table btn btn-primary',
267
+ :value => I18n.t('tabulatr.apply_filters'))
268
+ end
269
+ end
270
+ end # modal-content
271
+ end # modal-dialog
272
+ end # modal fade
273
+ end
274
+
275
+ # render the table rows, only used if records are passed
276
+ def render_table_rows(&block)
277
+ # row_classes = @table_options[:row_classes] || []
278
+ # row_html = @table_options[:row_html] || {}
279
+ # row_class = row_html[:class] || ""
280
+ @records.each_with_index do |record, i|
281
+ #concat("<!-- Row #{i} -->")
282
+ # if row_classes.present?
283
+ # rc = row_class.present? ? row_class + " " : ''
284
+ # rc += row_classes[i % row_classes.length]
285
+ # else
286
+ # rc = nil
287
+ # end
288
+ rc = nil
289
+ make_tag(:tr, :class => rc, :id => dom_id(record)) do
290
+ yield(data_row_builder(record))
291
+ end # </tr>
292
+ end
293
+ end
294
+
295
+ def render_table_row(&block)
296
+ row_html = @table_options[:row_html] || {}
297
+ row_html[:class] = 'empty_row'
298
+ make_tag(:tr, row_html) do
299
+ yield empty_row_builder
300
+ end
301
+ end
302
+
303
+
304
+ # stringly produce a tag w/ some options
305
+ def make_tag(name, hash={}, &block)
306
+ attrs = hash ? tag_options(hash) : ''
307
+ if block_given?
308
+ if name
309
+ concat("<#{name}#{attrs}>")
310
+ yield
311
+ concat("</#{name}>")
312
+ else
313
+ yield
314
+ end
315
+ else
316
+ concat("<#{name}#{attrs} />")
317
+ end
318
+ nil
319
+ end
320
+
321
+ def make_image_button(options)
322
+ inactive = options.delete(:inactive)
323
+ if(options['data-sort'] == 'desc')
324
+ icon_class = 'icon-arrow-down'
325
+ else
326
+ icon_class = 'icon-arrow-up'
327
+ end
328
+ if !inactive
329
+ make_tag(:i,
330
+ options.merge(
331
+ :class => "tabulatr-sort #{icon_class}"
332
+ )
333
+ )
334
+ else
335
+ make_tag(:i, :class => "tabulatr-sort #{icon_class}")
336
+ end
337
+ end
338
+
339
+ end
340
+
341
+ Dir[File.join(File.dirname(__FILE__), "tabulatr", "*.rb")].each do |file|
342
+ require file
343
+ end
@@ -0,0 +1,3 @@
1
+ class Tabulatr
2
+ VERSION = "0.6.0"
3
+ end
data/lib/tabulatr.rb ADDED
@@ -0,0 +1,34 @@
1
+ #--
2
+ # Copyright (c) 2010-2011 Peter Horn, Provideal GmbH
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 'tabulatr/tabulatr'
25
+ require 'tabulatr/engine'
26
+ require 'whiny_hash'
27
+
28
+ #--
29
+ # Mainly Monkey Patching...
30
+ #--
31
+ Dir[File.join(File.dirname(__FILE__), "initializers", "*.rb")].each do |file|
32
+ require file
33
+ end
34
+