fancygrid 1.0.0

Sign up to get free protection for your applications and to get access to all the features.
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