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,187 @@
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 from ActionController instances
25
+ # In a seperate class call only for clearity
26
+
27
+ require 'activerecord_outer_joins'
28
+
29
+ module Tabulatr::Finder
30
+
31
+ # -------------------------------------------------------------------
32
+ # Called if SomeActveRecordSubclass::find_for_table(params) is called
33
+ #
34
+ def self.find_for_table(klaz, params, options={}, &block)
35
+ adapter = if klaz.respond_to?(:descends_from_active_record?) then ::Tabulatr::Adapter::ActiveRecordAdapter.new(klaz)
36
+ else raise("Don't know how to deal with class '#{klaz}'")
37
+ end
38
+
39
+ Tabulatr::Security.validate!("#{params[:arguments]}-#{params[:salt]}-#{params[:hash]}")
40
+
41
+ form_options = Tabulatr.table_form_options
42
+ opts = Tabulatr.finder_options.merge(options)
43
+ params ||= {} # just to be sure
44
+ cname = adapter.class_to_param
45
+ sort_name = "#{cname}#{form_options[:sort_postfix]}"
46
+ filter_name = "#{cname}#{form_options[:filter_postfix]}"
47
+ batch_name = "#{cname}#{form_options[:batch_postfix]}"
48
+ check_name = "tabulatr_checked"
49
+ append = params[:append].present? ? params[:append] : false
50
+
51
+
52
+ append = string_to_boolean append
53
+ # before we do anything else, we find whether there's something to do for batch actions
54
+ checked_param = ActiveSupport::HashWithIndifferentAccess.new({:checked_ids => '', :current_page => []}).
55
+ merge(params[check_name] || {})
56
+
57
+ id = adapter.primary_key
58
+
59
+ serializer = options[:serializer].presence
60
+
61
+ # checkboxes
62
+ checked_ids = checked_param[:checked_ids]
63
+ selected_ids = checked_ids.split(',')
64
+
65
+ execute_batch_actions(params[batch_name], selected_ids, &block)
66
+
67
+ # at this point, we've retrieved the filter settings, the sorting setting, the pagination settings and
68
+ # the selected_ids.
69
+ filter_param = (params[filter_name] || {})
70
+ sortparam = params[sort_name]
71
+
72
+ includes = []
73
+ maps = klaz.tabulatr_name_mappings.merge(opts[:name_mapping] || {})
74
+
75
+ build_conditions(filter_param, form_options, includes, adapter, maps)
76
+ order = build_order(params[:sort_by], params[:orientation], params[:default_order], maps, adapter, klaz)
77
+
78
+ c = adapter.includes(includes).references(includes).count
79
+ # Group statments return a hash
80
+ c = c.count unless c.class == Fixnum
81
+ pagesize = params[:pagesize]
82
+ pagination_data = build_offset(params[:page], pagesize, c, opts)
83
+
84
+ total = adapter.preconditions_scope(opts).count
85
+ # here too
86
+ total = total.count unless total.class == Fixnum
87
+
88
+ # Now, actually find the stuff
89
+ opts[:name_mapping] ||= {}
90
+ find_on = (klaz.tabulatr_select_attributes(opts[:name_mapping]).try do |s| adapter.select(s) end) || adapter
91
+ found = find_on.outer_joins(includes)
92
+ .limit(pagination_data[:pagesize]).offset(pagination_data[:offset])
93
+ .order(order).to_a
94
+
95
+ found.define_singleton_method(:__pagination) do
96
+ { :page => pagination_data[:page],
97
+ :pagesize => pagination_data[:pagesize],
98
+ :count => c,
99
+ :pages => pagination_data[:pages],
100
+ :pagesizes => {},
101
+ :total => total,
102
+ :append => append,
103
+ :table_id => params[:table_id] }
104
+ end
105
+
106
+ found.define_singleton_method(:to_tabulatr_json) do |klass=nil|
107
+ Tabulatr::JsonBuilder.build found, klass, params[:arguments], id
108
+ end
109
+
110
+ found
111
+ end
112
+
113
+ private
114
+
115
+ def self.execute_batch_actions batch_param, selected_ids, &block
116
+ if batch_param.present? && block_given?
117
+ batch_param = batch_param.keys.first.to_sym if batch_param.is_a?(Hash)
118
+ yield(Invoker.new(batch_param, selected_ids))
119
+ end
120
+ end
121
+
122
+ def self.build_conditions filter_param, form_options, includes, adapter, maps
123
+ filter_param.each do |filter|
124
+ name, value = filter
125
+ next unless value.present?
126
+ if (name != form_options[:associations_filter])
127
+ table_name = adapter.table_name
128
+ nn = extract_column_name(table_name, name, maps)
129
+ adapter.add_conditions_from(nn, value)
130
+ else
131
+ value.each do |assoc_filter|
132
+ name,value = assoc_filter
133
+ assoc, att = name.split(".").map(&:to_sym)
134
+ includes << assoc
135
+ table_name = adapter.table_name_for_association(assoc)
136
+ nn = extract_column_name(table_name, name, maps, att)
137
+ adapter.add_conditions_from(nn, value)
138
+ end
139
+ end
140
+ end
141
+ end
142
+
143
+ def self.extract_column_name(table_name, n, maps, att=nil)
144
+ if maps[n.to_sym]
145
+ maps[n.to_sym]
146
+ else
147
+ if att
148
+ t = "#{table_name}.#{att}"
149
+ else
150
+ t = "#{table_name}.#{n}"
151
+ end
152
+ raise "SECURITY violation, field name is '#{t}'" unless /^[\d\w]+(\.[\d\w]+)?$/.match t
153
+ t
154
+ end
155
+ end
156
+
157
+ def self.build_order sort_by, orientation, default_order, maps, adapter, klaz
158
+ if sort_by.present?
159
+ s_by = maps[sort_by] ? maps[sort_by] : sort_by
160
+ adapter.order_for_query_new s_by, orientation
161
+ else
162
+ default_order || "#{klaz.table_name}.#{klaz.primary_key} asc"
163
+ end
164
+ end
165
+
166
+ def self.build_offset page, pagesize=10, count, opts
167
+ page ||= 1
168
+ pagesize, page = pagesize.to_i, page.to_i
169
+ pagesize = 10 if pagesize == 0
170
+
171
+ pages = (count/pagesize.to_f).ceil
172
+
173
+ {offset: ((page-1)*pagesize).to_i, pagesize: pagesize.to_i, pages: pages,
174
+ page: page
175
+ }
176
+ end
177
+
178
+ def self.string_to_boolean str
179
+ if str == 'true'
180
+ str = true
181
+ elsif str == 'false'
182
+ str = false
183
+ end
184
+ str
185
+ end
186
+
187
+ end
@@ -0,0 +1,64 @@
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 from ActionController instances
25
+ module Tabulatr::Finder
26
+
27
+ require File.join(File.dirname(__FILE__), 'finder', 'find_for_table')
28
+
29
+ # compress the list of ids as good as I could imagine ;)
30
+ # uses fancy base twisting
31
+ def self.compress_id_list(list)
32
+ if list.length == 0
33
+ ""
34
+ else
35
+ "GzB" + Base64.encode64(
36
+ Zlib::Deflate.deflate(
37
+ list.join(Tabulatr.table_form_options[:checked_separator])))
38
+ end
39
+ end
40
+
41
+ # inverse of compress_id_list
42
+ def self.uncompress_id_list(str)
43
+ if !str.present?
44
+ []
45
+ elsif str.starts_with?("GzB")
46
+ Zlib::Inflate.inflate(Base64.decode64(str[3..-1])).split(
47
+ Tabulatr.table_form_options[:checked_separator])
48
+ end
49
+ end
50
+
51
+ class Invoker
52
+ def initialize(batch_action, ids)
53
+ @batch_action = batch_action.to_sym
54
+ @ids = ids
55
+ end
56
+
57
+ def method_missing(name, *args, &block)
58
+ if @batch_action == name
59
+ yield(@ids)
60
+ end
61
+ end
62
+ end
63
+
64
+ end
@@ -0,0 +1,55 @@
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
+ module Tabulatr::Formattr
25
+ ALLOWED_METHODS = [:euro, :dollar, :percent, :lamp]
26
+ #include ActionView::TagHelpers
27
+
28
+ def self.format(nam, val)
29
+ nam = nam.to_sym
30
+ if ALLOWED_METHODS.member?(nam)
31
+ self.send nam, val
32
+ else
33
+ "[Requested unautorized format '#{nam}' for '#{val}'.]"
34
+ end
35
+ end
36
+
37
+ def self.euro(x)
38
+ ("%.2f&thinsp;&euro;" % x).gsub(".", ",")
39
+ end
40
+
41
+ def self.dollar(x)
42
+ "$&thinsp;%.2f" % x
43
+ end
44
+
45
+ def self.percent(x)
46
+ ("%.2f&thinspace;%%" % 100.0*x).gsub(".", ",")
47
+ end
48
+
49
+ def self.lamp(x, mapping)
50
+ s = mapping[x].to_s
51
+ return "?" unless %w{g y r n}.member?(s)
52
+ image_tag("tabulatr/#{s}state.gif").html_safe
53
+ end
54
+
55
+ end
@@ -0,0 +1,146 @@
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
+ class Tabulatr
25
+
26
+
27
+ # the method used to actually define the headers of the columns,
28
+ # taking the name of the attribute and a hash of options.
29
+ #
30
+ # The following options are evaluated here:
31
+ # <tt>:th_html</tt>:: a hash with html-attributes added to the <th>s created
32
+ # <tt>:header</tt>:: if present, the value will be output in the header cell,
33
+ # otherwise, the capitalized name is used
34
+ def header_column(name, opts={}, &block)
35
+ raise "Not in header mode!" if @row_mode != :header
36
+ sortparam = "#{@classname}#{@table_form_options[:sort_postfix]}"
37
+ filter_name = "#{@classname}#{@table_form_options[:filter_postfix]}[#{name}]"
38
+ bid = "#{@classname}#{@table_form_options[:sort_postfix]}"
39
+
40
+ create_header_tag(name, opts, sortparam, filter_name, name,
41
+ nil, bid
42
+ )
43
+ # opts = normalize_column_options(name, opts)
44
+ # opts = normalize_header_column_options opts
45
+ # opts[:th_html]['data-tabulatr-column-name'] = name
46
+ # opts[:th_html]['data-tabulatr-form-name'] = filter_name
47
+ # opts[:th_html]['data-tabulatr-sorting-name'] = "#{@klass.table_name}.#{name}"
48
+ # make_tag(:th, opts[:th_html]) do
49
+ # concat(t(opts[:header] || readable_name_for(name)), :escape_html)
50
+ # create_sorting_elements opts, sortparam, name, bid
51
+ # end # </th>
52
+ end
53
+
54
+ # the method used to actually define the headers of the columns,
55
+ # taking the name of the attribute and a hash of options.
56
+ #
57
+ # The following options are evaluated here:
58
+ # <tt>:th_html</tt>:: a hash with html-attributes added to the <th>s created
59
+ # <tt>:header</tt>:: if present, the value will be output in the header cell,
60
+ # otherwise, the capitalized name is used
61
+ def header_association(relation, name, opts={}, &block)
62
+ raise "Not in header mode!" if @row_mode != :header
63
+ create_header_tag(name, opts,
64
+ "#{@classname}#{@table_form_options[:sort_postfix]}[#{relation.to_s}.#{name.to_s}]",
65
+ "#{@classname}#{@table_form_options[:filter_postfix]}[#{@table_form_options[:associations_filter]}][#{relation.to_s}.#{name.to_s}]",
66
+ "#{relation}:#{name}",
67
+ relation
68
+ )
69
+ end
70
+
71
+ def header_checkbox(opts={}, &block)
72
+ raise "Whatever that's for!" if block_given?
73
+ opts = normalize_column_options(:checkbox_column, opts)
74
+ opts = normalize_header_column_options opts, :checkbox
75
+ make_tag(:th, opts[:th_html]) do
76
+ make_tag(:input, type: 'checkbox', :'data-table' => "#{@klass.to_s.downcase}_table",
77
+ class: "tabulatr_mark_all"){}
78
+ render_batch_actions if @table_options[:batch_actions]
79
+ end
80
+ end
81
+
82
+ def header_action(opts={}, &block)
83
+ raise "Please specify a block" unless block_given?
84
+ opts = normalize_column_options(:action_column, opts)
85
+ opts = normalize_header_column_options opts, :action
86
+ dummy = DummyRecord.for(@klass)
87
+ cont = yield(dummy)
88
+ cont = cont.join(' ') if cont.is_a? Array
89
+ opts[:th_html]['data-tabulatr-action'] = cont.gsub('"', "'")
90
+ @attributes = (@attributes + dummy.requested_methods).flatten
91
+ names = dummy.requested_methods.join(',')
92
+
93
+ make_tag(:th, opts[:th_html].merge('data-tabulatr-column-name' => names)) do
94
+ concat(t(opts[:header] || ""), :escape_html)
95
+ end
96
+ end
97
+
98
+ private
99
+
100
+ def normalize_header_column_options opts, type=nil
101
+ opts[:th_html] ||= {}
102
+ opts[:th_html]['data-tabulatr-column-type'] = type if type
103
+ if opts[:format_methods]
104
+ opts[:th_html]['data-tabulatr-methods'] = opts[:format_methods].join(',')
105
+ end
106
+ opts
107
+ end
108
+
109
+ def create_sorting_elements opts, sortparam, name, bid=""
110
+ if opts[:sortable] and @table_options[:sortable]
111
+ if @sorting and @sorting[:by].to_s == name.to_s
112
+ pname = "#{sortparam}[_resort][#{name}]"
113
+ bid = "#{bid}_#{name}"
114
+ sort_dir = @sorting[:direction] == 'asc' ? 'desc' : 'asc'
115
+ make_tag(:input, :type => :hidden,
116
+ :name => "#{sortparam}[#{name}][#{@sorting[:direction]}]",
117
+ :value => "#{@sorting[:direction]}")
118
+ else
119
+ pname = "#{sortparam}[_resort][#{name}]"
120
+ bid = "#{bid}_#{name}"
121
+ sort_dir = 'desc'
122
+ end
123
+ make_image_button(:id => bid, :name => pname, :'data-sort' => sort_dir)
124
+ end
125
+ end
126
+
127
+
128
+ def create_header_tag name, opts, sort_param, filter_name, column_name, relation=nil, bid=nil
129
+ opts = normalize_column_options(name, opts)
130
+ opts = normalize_header_column_options(opts)
131
+ opts[:th_html]['data-tabulatr-column-name'] = column_name
132
+ opts[:th_html]['data-tabulatr-form-name'] = filter_name
133
+ opts[:th_html]['data-tabulatr-sorting-name'] = sorting_name(name, relation)
134
+ make_tag(:th, opts[:th_html]) do
135
+ concat(t(opts[:header] || readable_name_for(name, relation)), :escape_html)
136
+ create_sorting_elements(opts, sort_param, name, bid) unless relation && name.to_sym == :count
137
+ end # </th>
138
+ end
139
+
140
+ def sorting_name name, relation=nil
141
+ return "#{@klass.reflect_on_association(relation).table_name}.#{name}" if relation && @klass.reflect_on_association(relation).klass.column_names.include?(name.to_s)
142
+ return "#{@klass.table_name}.#{name}" if @klass.column_names.include?(name.to_s)
143
+ name
144
+ end
145
+
146
+ end
@@ -0,0 +1,57 @@
1
+ module Tabulatr::JsonBuilder
2
+ def self.build(data, klass, requested_attributes, id)
3
+ if klass && ActiveModel.const_defined?(:ArraySerializer)
4
+ ActiveModel::ArraySerializer.new(data,
5
+ { root: "data", meta: data.__pagination,
6
+ each_serializer: klass
7
+ }).as_json
8
+ else
9
+ id_included = false
10
+ attrs = build_hash_from requested_attributes, id
11
+ result = []
12
+ data.each do |f|
13
+ tmp = {}
14
+ attrs.each do |at|
15
+ insert_attribute_in_hash(at, f, tmp)
16
+ end
17
+ result << tmp
18
+ end
19
+ { data: result, meta: data.__pagination }
20
+ end
21
+ end
22
+
23
+ private
24
+
25
+ def self.build_hash_from requested_attributes, id
26
+ attrs = []
27
+ id_included = false
28
+ requested_attributes.split(',').each do |par|
29
+ if par.include? ':'
30
+ relation, action = par.split(':')
31
+ attrs << {action: action, relation: relation}
32
+ else
33
+ id_included = true if par == id
34
+ attrs << {action: par}
35
+ end
36
+ end
37
+ attrs << {action: id} unless id_included
38
+ attrs
39
+ end
40
+
41
+ def self.insert_attribute_in_hash at, f, r={}
42
+ if at.has_key? :relation
43
+ if f.class.reflect_on_association(at[:relation].to_sym).collection?
44
+ if at[:action].to_sym == :count
45
+ r["#{at[:relation]}:#{at[:action]}"] = f.try(at[:relation]).count
46
+ else
47
+ r["#{at[:relation]}:#{at[:action]}"] = f.try(at[:relation]).map(&at[:action].to_sym).join(', ')
48
+ end
49
+ else
50
+ r["#{at[:relation]}:#{at[:action]}"] = f.try(at[:relation]).try(at[:action])
51
+ end
52
+ else
53
+ r[at[:action]] = f.send at[:action]
54
+ end
55
+ r
56
+ end
57
+ end
@@ -0,0 +1,76 @@
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
+ class Tabulatr
25
+
26
+ #render the paginator controls, inputs etc.
27
+ def render_paginator
28
+ # get the current pagination state
29
+ if (@table_options[:paginate].is_a?(Fixnum)) && @klass.count > @table_options[:paginate] ||
30
+ @table_options[:paginate] === true
31
+ send(Tabulatr.bootstrap_paginator)
32
+ end
33
+ make_tag(:div, :class => 'btn-group tabulatr-per-page', :'data-table' => "#{@klass.to_s.downcase}_table") do
34
+ make_tag(:button, :class => 'btn') do
35
+ concat(I18n.t('tabulatr.rows_per_page'))
36
+ end
37
+ make_tag(:button, :class => 'btn dropdown-toggle', :'data-toggle' => 'dropdown') do
38
+ make_tag(:span, :class => 'caret'){}
39
+ end
40
+ make_tag(:ul, :class => 'dropdown-menu') do
41
+ [10, 25, 50, 100].push(@table_options[:default_pagesize]).uniq.sort.each do |n|
42
+ create_pagination_select(n, n == @table_options[:default_pagesize])
43
+ end
44
+ end
45
+ end
46
+ end
47
+
48
+ def create_pagination_select n, default=false
49
+ make_tag(:li) do
50
+ params = { :href => "javascript: void(0);",
51
+ :'data-items-per-page' => n }
52
+ params[:class] = 'active' if default
53
+ make_tag(:a, params) do
54
+ concat(n)
55
+ end
56
+ end
57
+ end
58
+
59
+ private
60
+
61
+ # bootstrap 3
62
+ def create_ul_paginator
63
+ make_tag(:ul, :class => @table_options[:paginator_div_class],
64
+ :'data-table' => "#{@klass.to_s.downcase}_table") do
65
+ end
66
+ end
67
+
68
+ # bootstrap 2
69
+ def create_div_paginator
70
+ make_tag(:div, :class => @table_options[:paginator_div_class],
71
+ :'data-table' => "#{@klass.to_s.downcase}_table") do
72
+ make_tag(:ul){}
73
+ end
74
+ end
75
+
76
+ end