fancygrid 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.
Files changed (97) hide show
  1. data/.bundle/config +2 -0
  2. data/.rspec +1 -0
  3. data/CHANGELOG +34 -0
  4. data/Gemfile +15 -0
  5. data/Gemfile.lock +125 -0
  6. data/LICENSE +20 -0
  7. data/README.rdoc +299 -0
  8. data/ROADMAP +1 -0
  9. data/Rakefile +45 -0
  10. data/VERSION +1 -0
  11. data/app/views/fancygrid/_cells.html.haml +13 -0
  12. data/app/views/fancygrid/base/controls.html.haml +40 -0
  13. data/app/views/fancygrid/base/list_frame.html.haml +37 -0
  14. data/app/views/fancygrid/base/search.html.haml +33 -0
  15. data/app/views/fancygrid/base/sort.html.haml +20 -0
  16. data/app/views/fancygrid/base/table_frame.html.haml +45 -0
  17. data/config/initializers/fancygrid.rb +67 -0
  18. data/config/locales/fancygrid.de.yml +41 -0
  19. data/config/locales/fancygrid.en.yml +42 -0
  20. data/fancygrid.gemspec +162 -0
  21. data/init.rb +1 -0
  22. data/lib/fancygrid.rb +73 -0
  23. data/lib/fancygrid/grid.rb +387 -0
  24. data/lib/fancygrid/helper.rb +129 -0
  25. data/lib/fancygrid/node.rb +533 -0
  26. data/lib/fancygrid/query_generator.rb +338 -0
  27. data/lib/fancygrid/view.rb +148 -0
  28. data/lib/generators/install_generator.rb +61 -0
  29. data/lib/generators/views_generator.rb +25 -0
  30. data/lib/version.rb +0 -0
  31. data/public/images/fancygrid/add.png +0 -0
  32. data/public/images/fancygrid/clear.png +0 -0
  33. data/public/images/fancygrid/ddn.png +0 -0
  34. data/public/images/fancygrid/dn.png +0 -0
  35. data/public/images/fancygrid/dots.png +0 -0
  36. data/public/images/fancygrid/loading.gif +0 -0
  37. data/public/images/fancygrid/magnifier.png +0 -0
  38. data/public/images/fancygrid/next.png +0 -0
  39. data/public/images/fancygrid/order.png +0 -0
  40. data/public/images/fancygrid/prev.png +0 -0
  41. data/public/images/fancygrid/reload.png +0 -0
  42. data/public/images/fancygrid/remove.png +0 -0
  43. data/public/images/fancygrid/spacer.gif +0 -0
  44. data/public/images/fancygrid/submit.png +0 -0
  45. data/public/images/fancygrid/th_bg.png +0 -0
  46. data/public/images/fancygrid/up.png +0 -0
  47. data/public/images/fancygrid/uup.png +0 -0
  48. data/public/javascripts/fancygrid.js +477 -0
  49. data/public/javascripts/fancygrid.min.js +17 -0
  50. data/public/stylesheets/fancygrid.css +289 -0
  51. data/public/stylesheets/fancygrid.scss +302 -0
  52. data/spec/dummy/Rakefile +7 -0
  53. data/spec/dummy/app/controllers/application_controller.rb +3 -0
  54. data/spec/dummy/app/helpers/application_helper.rb +2 -0
  55. data/spec/dummy/app/models/project.rb +3 -0
  56. data/spec/dummy/app/models/ticket.rb +3 -0
  57. data/spec/dummy/app/views/layouts/application.html.erb +14 -0
  58. data/spec/dummy/config.ru +4 -0
  59. data/spec/dummy/config/application.rb +45 -0
  60. data/spec/dummy/config/boot.rb +10 -0
  61. data/spec/dummy/config/database.yml +22 -0
  62. data/spec/dummy/config/environment.rb +5 -0
  63. data/spec/dummy/config/environments/development.rb +26 -0
  64. data/spec/dummy/config/environments/production.rb +49 -0
  65. data/spec/dummy/config/environments/test.rb +35 -0
  66. data/spec/dummy/config/initializers/backtrace_silencers.rb +7 -0
  67. data/spec/dummy/config/initializers/inflections.rb +10 -0
  68. data/spec/dummy/config/initializers/mime_types.rb +5 -0
  69. data/spec/dummy/config/initializers/secret_token.rb +7 -0
  70. data/spec/dummy/config/initializers/session_store.rb +8 -0
  71. data/spec/dummy/config/locales/en.yml +5 -0
  72. data/spec/dummy/config/routes.rb +58 -0
  73. data/spec/dummy/db/migrate/20110112183948_create_projects.rb +11 -0
  74. data/spec/dummy/db/migrate/20110112183956_create_tickets.rb +14 -0
  75. data/spec/dummy/db/test.sqlite3 +0 -0
  76. data/spec/dummy/log/development.log +0 -0
  77. data/spec/dummy/log/production.log +0 -0
  78. data/spec/dummy/log/server.log +0 -0
  79. data/spec/dummy/log/test.log +1026 -0
  80. data/spec/dummy/public/404.html +26 -0
  81. data/spec/dummy/public/422.html +26 -0
  82. data/spec/dummy/public/500.html +26 -0
  83. data/spec/dummy/public/favicon.ico +0 -0
  84. data/spec/dummy/public/javascripts/application.js +2 -0
  85. data/spec/dummy/public/javascripts/controls.js +965 -0
  86. data/spec/dummy/public/javascripts/dragdrop.js +974 -0
  87. data/spec/dummy/public/javascripts/effects.js +1123 -0
  88. data/spec/dummy/public/javascripts/prototype.js +6001 -0
  89. data/spec/dummy/public/javascripts/rails.js +175 -0
  90. data/spec/dummy/public/stylesheets/.gitkeep +0 -0
  91. data/spec/dummy/script/rails +6 -0
  92. data/spec/grid_spec.rb +15 -0
  93. data/spec/integration/navigation_spec.rb +9 -0
  94. data/spec/node_spec.rb +326 -0
  95. data/spec/query_generator_spec.rb +358 -0
  96. data/spec/spec_helper.rb +53 -0
  97. metadata +214 -0
data/init.rb ADDED
@@ -0,0 +1 @@
1
+ require 'fancygrid'
data/lib/fancygrid.rb ADDED
@@ -0,0 +1,73 @@
1
+ require "fancygrid"
2
+ require "version"
3
+ require "fancygrid/helper"
4
+ require "fancygrid/node"
5
+ require "fancygrid/grid"
6
+ require "fancygrid/query_generator"
7
+ require "fancygrid/view"
8
+
9
+ module Fancygrid
10
+
11
+ mattr_accessor :table_template
12
+ @@table_template = "fancygrid/base/table_frame"
13
+
14
+ mattr_accessor :list_template
15
+ @@list_template = "fancygrid/base/list_frame"
16
+
17
+ mattr_accessor :controls_template
18
+ @@controls_template = "fancygrid/base/controls"
19
+
20
+ mattr_accessor :sort_template
21
+ @@sort_template = "fancygrid/base/sort"
22
+
23
+ mattr_accessor :search_template
24
+ @@search_template = "fancygrid/base/search"
25
+
26
+ mattr_accessor :cells_template_directory
27
+ @@cells_template_directory = "fancygrid/"
28
+
29
+ mattr_accessor :cells_template
30
+ @@cells_template = "_cells"
31
+
32
+ mattr_accessor :i18n_scope
33
+ @@i18n_scope = "fancygrid"
34
+
35
+ mattr_accessor :use_grid_name_as_cells_template
36
+ @@use_grid_name_as_cells_template = false
37
+
38
+ mattr_accessor :search_visible
39
+ @@search_visible = false
40
+
41
+ mattr_accessor :default_search_type
42
+ @@default_search_type = :simple
43
+
44
+ mattr_accessor :default_grid_type
45
+ @@default_grid_type = :table
46
+
47
+ mattr_accessor :default_per_page_options
48
+ @@default_per_page_options = [5, 10, 15, 20, 25, 30, 40, 50]
49
+
50
+ mattr_accessor :default_per_page_selection
51
+ @@default_per_page_selection = 20
52
+
53
+ mattr_accessor :search_operators
54
+ @@search_operators = Fancygrid::QueryGenerator::OPERATOR_NAMES
55
+
56
+ def self.setup
57
+ yield self
58
+ end
59
+
60
+ class Engine < Rails::Engine#:nodoc:
61
+
62
+ generators do
63
+ require File.join(File.dirname(__FILE__), "generators", "install_generator")
64
+ require File.join(File.dirname(__FILE__), "generators", "views_generator")
65
+ end
66
+
67
+ initializer "fancygrid.initialize" do |app|
68
+ ActionController::Base.send :include, Fancygrid::Helper
69
+ ActionView::Base.send :include, Fancygrid::Helper
70
+ end
71
+ end
72
+ end
73
+
@@ -0,0 +1,387 @@
1
+ module Fancygrid#:nodoc:
2
+
3
+ class Grid < Fancygrid::Node
4
+
5
+ # Collection of all fancygrid leafs. These are instances of Fancygrid::Node
6
+ # and define the columns of a table. They may refer to an attribute or a
7
+ # method or a renderable cell of a model
8
+ attr_accessor :leafs
9
+
10
+ # The database query that is sent to the database.
11
+ attr_accessor :query
12
+
13
+ # The current POST or GET parameters.
14
+ attr_accessor :request_params
15
+
16
+ # The result of the database query. This is the data that is going to be rendered.
17
+ attr_accessor :dataset
18
+
19
+ # Number of possible matching results.
20
+ attr_accessor :resultcount
21
+
22
+ # Order and visibility definition for each column
23
+ attr_accessor :view
24
+
25
+
26
+
27
+ # Url for the ajax callback.
28
+ attr_accessor :url
29
+
30
+ # the request type for the ajax callback
31
+ attr_accessor :ajax_type
32
+
33
+ # enables or disables the sort window
34
+ attr_accessor :enable_sort_window
35
+
36
+ # The template name that is used to render this grid.
37
+ attr_accessor :template
38
+
39
+ # Enables or disables the input fields for simple search.
40
+ attr_accessor :search_visible
41
+
42
+ # Specifies the type of the search. Must be one of "simple" or "complex"
43
+ attr_accessor :search_type
44
+
45
+ # Specifies a set of enabled search operators
46
+ attr_accessor :search_operators
47
+
48
+ # Enables or disables the rendering of the top control bar.
49
+ attr_accessor :hide_top_control
50
+
51
+ # Enables or disables the rendering of the bottom control bar.
52
+ attr_accessor :hide_bottom_control
53
+
54
+ # Specifies the rendering strategy. May be one of 'table' or 'list'
55
+ attr_accessor :grid_type
56
+
57
+ # Spcified the select options for per page drop down
58
+ attr_accessor :per_page_options
59
+
60
+ # Spcified theselected value in the per page drop down
61
+ attr_accessor :per_page_selection
62
+
63
+ # Specifies the search options for search input fields
64
+ attr_accessor :search_formats
65
+
66
+
67
+
68
+
69
+ # Initializes the root node of the fancygrid tree.
70
+ def initialize(name, klass = nil, table_name = nil, params = nil)
71
+ super(self, nil, name)
72
+ initialize_node(name, klass, table_name)
73
+
74
+ self.url = nil
75
+ self.ajax_type = :get
76
+ self.enable_sort_window = false
77
+ self.leafs = []
78
+ self.dataset = nil
79
+ self.resultcount = 0
80
+
81
+ self.query = {}
82
+ self.request_params = (params || {})
83
+
84
+ self.grid_type = Fancygrid.default_grid_type
85
+ self.search_visible = Fancygrid.search_visible
86
+ self.search_type = Fancygrid.default_search_type
87
+ self.search_operators = Fancygrid.search_operators
88
+ self.search_formats = {}
89
+
90
+ if Fancygrid.use_grid_name_as_cells_template
91
+ self.template = File.join(Fancygrid.cells_template_directory.to_s, name.to_s)
92
+ else
93
+ self.template = File.join(Fancygrid.cells_template_directory.to_s, Fancygrid.cells_template.to_s)
94
+ end
95
+
96
+ self.per_page_options = Fancygrid.default_per_page_options
97
+ self.per_page_selection = Fancygrid.default_per_page_selection
98
+
99
+ view_opts = self.request_params[:fancygrid] || {}
100
+ view_opts = view_opts[self.name]
101
+
102
+ self.load_view(view_opts || {})
103
+ end
104
+
105
+ # Inserts a given node into the leafs collection.
106
+ #
107
+ def insert_node(node)
108
+ raise "Node must be a leaf" unless node.is_leaf?
109
+ if (self.view)
110
+ node.position = self.view.get_node_position(node)
111
+ node.visible = self.view.get_node_visibility(node)
112
+ node.search_value = self.view.get_node_search_value(node)
113
+ end
114
+ leafs << node
115
+ end
116
+
117
+ # Sorts the leafs by position attribute
118
+ #
119
+ def sort_leafs!
120
+ leafs.sort! { |a, b| a.position.to_i <=> b.position.to_i }
121
+ end
122
+
123
+ # Takes the given view hash and aligns the leafs using the passed view definitions
124
+ #
125
+ def load_view options
126
+ options ||= {}
127
+ options = options[:fancygrid] || options
128
+ options = options[self.name] || options
129
+ self.view = Fancygrid::View.new(options)
130
+
131
+ # reorder current leafs
132
+ new_leafs = self.leafs
133
+ self.leafs = []
134
+ new_leafs.each do |leaf|
135
+ insert_node(leaf)
136
+ end
137
+ end
138
+
139
+ # Yields a query generator which should be used to build a find query
140
+ #
141
+ # == Options
142
+ # The options are the same as in active record finder method
143
+ # * <tt>:conditions</tt> - An SQL fragment like “administrator = 1”, ["user_name = ?", username], or ["user_name = :user_name", { :user_name => user_name }]
144
+ # * <tt>:order</tt> - An SQL fragment like “created_at DESC, name”.
145
+ # * <tt>:group</tt> - An attribute name by which the result should be grouped. Uses the GROUP BY SQL-clause.
146
+ # * <tt>:having</tt> - Combined with :group this can be used to filter the records that a GROUP BY returns. Uses the HAVING SQL-clause.
147
+ # * <tt>:limit</tt> - An integer determining the limit on the number of rows that should be returned.
148
+ # * <tt>:offset</tt> - An integer determining the offset from where the rows should be fetched. So at 5, it would skip rows 0 through 4.
149
+ # * <tt>:joins</tt> - Either an SQL fragment for additional joins like “LEFT JOIN comments ON comments.post_id = id” (rarely needed), named associations in the same form used for the :include option, which will perform an INNER JOIN on the associated table(s), or an array containing a mixture of both strings and named associations. If the value is a string, then the records will be returned read-only since they will have attributes that do not correspond to the table’s columns. Pass :readonly => false to override.
150
+ # * <tt>:include</tt> - Names associations that should be loaded alongside. The symbols named refer to already defined associations. See eager loading under Associations.
151
+ # * <tt>:select</tt> - By default, this is “*” as in “SELECT * FROM”, but can be changed if you, for example, want to do a join but not include the joined columns. Takes a string with the SELECT SQL fragment (e.g. “id, name”).
152
+ # * <tt>:from</tt> - By default, this is the table name of the class, but can be changed to an alternate table name (or even the name of a database view).
153
+ # * <tt>:readonly</tt> - Mark the returned records read-only so they cannot be saved or updated.
154
+ # * <tt>:lock</tt> - An SQL fragment like “FOR UPDATE” or “LOCK IN SHARE MODE”. :lock => true gives connection’s default exclusive lock, usually “FOR UPDATE”.
155
+ def find(options={})#:yields: generator
156
+ raise "calling 'find' twice or after 'data=' is not allowed" unless dataset.nil?
157
+
158
+ # don not process same or equal leafs twice
159
+ leafs.compact!
160
+
161
+ # get the parameters for this grid instance, they are mapped like this { :fancygrid => { :gird_name => ..options.. }}
162
+ params = request_params[:fancygrid] || {}
163
+ params = params[self.name] || {}
164
+
165
+ # build default query hash
166
+ url_options = {
167
+ :select => self.leafs.map{ |leaf| leaf.select_name }.compact,
168
+ :conditions => params[:conditions],
169
+ :pagination => params[:pagination],
170
+ :operator => params[:operator]
171
+ }
172
+
173
+ generator = Fancygrid::QueryGenerator.new(url_options)
174
+ generator.parse_options(options)
175
+ yield(generator) if block_given?
176
+
177
+ generator.order(self.view.get_sort_order)
178
+
179
+ self.query = generator.query
180
+ end
181
+
182
+ # Sets a custom dataset that should be rendered.Blanks out the
183
+ # callback <tt>url</tt> so no ajax request will be made.
184
+ #
185
+ def data= data
186
+ leafs.compact!
187
+
188
+ self.dataset = data.to_a
189
+ self.url = nil
190
+ end
191
+
192
+ # Runs the current query and caches the resulting data
193
+ #
194
+ def query_for_data
195
+ if self.record_klass < ActiveRecord::Base
196
+ self.dataset = self.record_klass.find(:all, self.query)
197
+
198
+ count_query = self.query.reject do |k, v|
199
+ [:limit, :offset, :order].include?(k.to_sym )
200
+ end
201
+ self.resultcount = self.record_klass.count(:all, count_query)
202
+
203
+ elsif self.record_klass < ActiveResource::Base
204
+ self.dataset = self.record_klass.find(:all, :params => self.query)
205
+ self.resultcount = self.dataset.delete_at(self.dataset.length - 1).total
206
+ else
207
+ raise "Unable to query for data. Supported base classes are 'ActiveRecord::Base' and 'ActiveResource::Base' but '#{self.record_klass}' was given"
208
+ end
209
+
210
+ self.resultcount = self.resultcount.length if self.resultcount.respond_to?(:length)
211
+ end
212
+
213
+
214
+
215
+
216
+
217
+ # Returns true if the callback url is blank, that is when no ajax
218
+ # functionality is wanted.
219
+ def is_static?
220
+ self.url.blank?
221
+ end
222
+
223
+ # Determines whether the grid instance is initialized with simle search
224
+ #
225
+ def has_simple_search?
226
+ self.search_type.to_s == "simple" && !self.is_static?
227
+ end
228
+
229
+ # Determines whether the grid instance is initialized with complex search
230
+ #
231
+ def has_complex_search?
232
+ self.search_type.to_s == "complex" && !self.is_static?
233
+ end
234
+
235
+ # Determines whether the grid instance displays the top control bar
236
+ #
237
+ def has_top_control?
238
+ !self.hide_top_control && !self.is_static?
239
+ end
240
+
241
+ # Determines whether the grid instance displays the bottom control bar
242
+ #
243
+ def has_bottom_control?
244
+ !self.hide_bottom_control && !self.is_static?
245
+ end
246
+
247
+ # Determines whether the grid instance is able to sort columns
248
+ #
249
+ def has_sort_window?
250
+ !self.is_static? && self.enable_sort_window
251
+ end
252
+
253
+ def enable_state_caching!
254
+ load_view_proc do |instance|
255
+ opts = session[:fancygrid] || {}
256
+ opts[instance.name.to_s] || {}
257
+ end
258
+ load_view_proc do |instance, dump|
259
+ session[:fancygrid] ||= {}
260
+ session[:fancygrid][instance.name.to_s] = dump
261
+ end
262
+ end
263
+
264
+ # If a block is given a new Proc is created for later css evaluation
265
+ #
266
+ def css_proc
267
+ @css_proc = Proc.new if block_given?
268
+ @css_proc
269
+ end
270
+
271
+ # Evaluates the css class for a table row by using the passed record and the ccs_proc of this grid
272
+ #
273
+ def css_proc_evaluate(record)
274
+ @css_proc and @css_proc.call(record) or ""
275
+ end
276
+
277
+ # Gets and sets a proc for loading a dumped view from session, database or whatever place
278
+ #
279
+ # == Example
280
+ #
281
+ # fancygrid_for :companies do |g|
282
+ # g.load_view_proc do |instance|
283
+ # # load a hash from session, fancygrid will use that to initiate its view
284
+ # session["fancygrid_#{instance.name.to_s}"] || {}
285
+ # end
286
+ # end
287
+ def load_view_proc
288
+ @load_view_proc = Proc.new if block_given?
289
+ @load_view_proc
290
+ end
291
+
292
+ # Evaluates the <tt>load_view_proc</tt> if available
293
+ #
294
+ def load_view_proc_evaluate
295
+ return load_view_proc.call(self) if load_view_proc.is_a?(Proc)
296
+ end
297
+
298
+ # Gets and sets a proc for storing a dumped view to session, database or whatever place
299
+ #
300
+ # == Example
301
+ #
302
+ # fancygrid_for :companies do |g|
303
+ # g.store_view_proc do |instance, dump|
304
+ # # store the dump to. The dump comes from the fancygrid view
305
+ # session["fancygrid_#{instance.name.to_s}"] = dump
306
+ # end
307
+ # end
308
+ def store_view_proc
309
+ @store_view_proc = Proc.new if block_given?
310
+ @store_view_proc
311
+ end
312
+
313
+ # Evaluates the <tt>store_view_proc</tt> if available
314
+ #
315
+ def store_view_proc_evaluate
316
+ store_view_proc.call(self, self.view.dump) if store_view_proc.is_a?(Proc)
317
+ end
318
+
319
+
320
+ # Yields each leaf that is visible
321
+ #
322
+ def each_leaf#:yields: leaf
323
+ leafs.compact!
324
+
325
+ leafs.each do |leaf|
326
+ yield leaf
327
+ end
328
+ end
329
+
330
+ # Yields each leaf that is visible
331
+ #
332
+ def each_visible_leaf#:yields: leaf
333
+ leafs.compact!
334
+
335
+ leafs.each do |leaf|
336
+ yield leaf if leaf.visible
337
+ end
338
+ end
339
+
340
+ # Yields each leaf that is not visible
341
+ #
342
+ def each_hidden_leaf#:yields: leaf
343
+ leafs.compact!
344
+
345
+ leafs.each do |leaf|
346
+ yield leaf unless leaf.visible
347
+ end
348
+ end
349
+
350
+ # Gets all leafs that are visible and searchable
351
+ def serachable_leafs
352
+ leafs.select { |leaf| leaf && leaf.searchable && leaf.visible }.compact
353
+ end
354
+
355
+ # Yields each fetched record if available
356
+ #
357
+ def each_record#:yields: record
358
+ return unless self.dataset
359
+ self.dataset.each do |record|
360
+ yield record
361
+ end
362
+ end
363
+
364
+
365
+
366
+ # Builds the javascript options for the javascript part of fancygrid
367
+ def js_options
368
+ {
369
+ :url => self.url,
370
+ :ajaxType => self.ajax_type,
371
+ :name => self.name,
372
+ :isStatic => self.is_static?,
373
+ :gridType => self.grid_type,
374
+ :searchVisible => (self.view.search_visible or self.search_visible),
375
+ :searchType => self.search_type,
376
+ :hideTopControl => self.hide_top_control,
377
+ :hideBottomControl => self.hide_bottom_control,
378
+ :paginationPage => self.view.get_pagination_page,
379
+ :paginationPerPage => self.view.get_pagination_per_page,
380
+ }.to_json
381
+ end
382
+
383
+ def log(message)#:nodoc:
384
+ #Rails.logger.debug("[FANCYGRID] #{message.to_s}")
385
+ end
386
+ end
387
+ end