magic_grid 0.9.2

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 (95) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +19 -0
  3. data/Rakefile +44 -0
  4. data/lib/assets/javascripts/magic_grid.js +154 -0
  5. data/lib/locales/en.yml +7 -0
  6. data/lib/magic_grid.rb +7 -0
  7. data/lib/magic_grid/definition.rb +212 -0
  8. data/lib/magic_grid/engine.rb +14 -0
  9. data/lib/magic_grid/helpers.rb +243 -0
  10. data/lib/magic_grid/railtie.rb +9 -0
  11. data/lib/magic_grid/version.rb +3 -0
  12. data/lib/tasks/magic_grid_tasks.rake +4 -0
  13. data/test/dummy/Rakefile +7 -0
  14. data/test/dummy/app/assets/javascripts/application.js +9 -0
  15. data/test/dummy/app/assets/javascripts/posts.js +8 -0
  16. data/test/dummy/app/assets/javascripts/users.js +2 -0
  17. data/test/dummy/app/assets/stylesheets/application.css +7 -0
  18. data/test/dummy/app/assets/stylesheets/posts.css +4 -0
  19. data/test/dummy/app/assets/stylesheets/scaffold.css +56 -0
  20. data/test/dummy/app/assets/stylesheets/users.css +4 -0
  21. data/test/dummy/app/controllers/application_controller.rb +3 -0
  22. data/test/dummy/app/controllers/posts_controller.rb +104 -0
  23. data/test/dummy/app/controllers/users_controller.rb +84 -0
  24. data/test/dummy/app/helpers/application_helper.rb +2 -0
  25. data/test/dummy/app/helpers/posts_helper.rb +2 -0
  26. data/test/dummy/app/helpers/users_helper.rb +2 -0
  27. data/test/dummy/app/models/post.rb +6 -0
  28. data/test/dummy/app/models/user.rb +4 -0
  29. data/test/dummy/app/views/layouts/application.html.erb +19 -0
  30. data/test/dummy/app/views/posts/_form.html.erb +33 -0
  31. data/test/dummy/app/views/posts/by_user.html.erb +18 -0
  32. data/test/dummy/app/views/posts/edit.html.erb +6 -0
  33. data/test/dummy/app/views/posts/index.html.erb +76 -0
  34. data/test/dummy/app/views/posts/new.html.erb +5 -0
  35. data/test/dummy/app/views/posts/show.html.erb +25 -0
  36. data/test/dummy/app/views/users/_form.html.erb +25 -0
  37. data/test/dummy/app/views/users/edit.html.erb +6 -0
  38. data/test/dummy/app/views/users/index.html.erb +42 -0
  39. data/test/dummy/app/views/users/new.html.erb +5 -0
  40. data/test/dummy/app/views/users/show.html.erb +15 -0
  41. data/test/dummy/config.ru +4 -0
  42. data/test/dummy/config/application.rb +59 -0
  43. data/test/dummy/config/boot.rb +10 -0
  44. data/test/dummy/config/database.yml +25 -0
  45. data/test/dummy/config/environment.rb +5 -0
  46. data/test/dummy/config/environments/development.rb +40 -0
  47. data/test/dummy/config/environments/production.rb +67 -0
  48. data/test/dummy/config/environments/test.rb +37 -0
  49. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  50. data/test/dummy/config/initializers/inflections.rb +15 -0
  51. data/test/dummy/config/initializers/mime_types.rb +5 -0
  52. data/test/dummy/config/initializers/secret_token.rb +7 -0
  53. data/test/dummy/config/initializers/session_store.rb +8 -0
  54. data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
  55. data/test/dummy/config/locales/en.yml +5 -0
  56. data/test/dummy/config/routes.rb +68 -0
  57. data/test/dummy/db/development.sqlite3 +0 -0
  58. data/test/dummy/db/migrate/20111025191809_create_users.rb +10 -0
  59. data/test/dummy/db/migrate/20111025195229_create_posts.rb +13 -0
  60. data/test/dummy/db/schema.rb +34 -0
  61. data/test/dummy/db/test.sqlite3 +0 -0
  62. data/test/dummy/log/development.log +49565 -0
  63. data/test/dummy/log/passenger.3000.log +362 -0
  64. data/test/dummy/log/test.log +77 -0
  65. data/test/dummy/public/404.html +26 -0
  66. data/test/dummy/public/422.html +26 -0
  67. data/test/dummy/public/500.html +26 -0
  68. data/test/dummy/public/css/smoothness/images/ui-bg_flat_0_aaaaaa_40x100.png +0 -0
  69. data/test/dummy/public/css/smoothness/images/ui-bg_flat_75_ffffff_40x100.png +0 -0
  70. data/test/dummy/public/css/smoothness/images/ui-bg_glass_55_fbf9ee_1x400.png +0 -0
  71. data/test/dummy/public/css/smoothness/images/ui-bg_glass_65_ffffff_1x400.png +0 -0
  72. data/test/dummy/public/css/smoothness/images/ui-bg_glass_75_dadada_1x400.png +0 -0
  73. data/test/dummy/public/css/smoothness/images/ui-bg_glass_75_e6e6e6_1x400.png +0 -0
  74. data/test/dummy/public/css/smoothness/images/ui-bg_glass_95_fef1ec_1x400.png +0 -0
  75. data/test/dummy/public/css/smoothness/images/ui-bg_highlight-soft_75_cccccc_1x100.png +0 -0
  76. data/test/dummy/public/css/smoothness/images/ui-icons_222222_256x240.png +0 -0
  77. data/test/dummy/public/css/smoothness/images/ui-icons_2e83ff_256x240.png +0 -0
  78. data/test/dummy/public/css/smoothness/images/ui-icons_454545_256x240.png +0 -0
  79. data/test/dummy/public/css/smoothness/images/ui-icons_888888_256x240.png +0 -0
  80. data/test/dummy/public/css/smoothness/images/ui-icons_cd0a0a_256x240.png +0 -0
  81. data/test/dummy/public/css/smoothness/jquery-ui-1.8.18.custom.css +565 -0
  82. data/test/dummy/public/favicon.ico +0 -0
  83. data/test/dummy/script/rails +6 -0
  84. data/test/dummy/test/fixtures/posts.yml +23 -0
  85. data/test/dummy/test/fixtures/users.yml +15 -0
  86. data/test/dummy/test/functional/posts_controller_test.rb +49 -0
  87. data/test/dummy/test/functional/users_controller_test.rb +50 -0
  88. data/test/dummy/test/integration/spider_test.rb +27 -0
  89. data/test/dummy/test/unit/helpers/posts_helper_test.rb +4 -0
  90. data/test/dummy/test/unit/helpers/users_helper_test.rb +4 -0
  91. data/test/dummy/test/unit/post_test.rb +7 -0
  92. data/test/dummy/test/unit/user_test.rb +7 -0
  93. data/test/magic_grid_test.rb +11 -0
  94. data/test/test_helper.rb +17 -0
  95. metadata +333 -0
@@ -0,0 +1,14 @@
1
+ module MagicGrid
2
+ class Engine < ::Rails::Engine
3
+ # Once in production, on every page view in development
4
+ config.to_prepare do |app|
5
+ # no caching based on path like require does
6
+ load 'magic_grid/helpers.rb'
7
+ end
8
+
9
+ initializer "magic_grid" do |app|
10
+ # Provide some fallback translations that users can override
11
+ app.config.i18n.load_path += Dir.glob(File.expand_path('../../locales/*.{rb,yml}', __FILE__))
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,243 @@
1
+ require 'magic_grid/definition'
2
+ require 'will_paginate/array'
3
+
4
+ module MagicGrid
5
+ module Helpers
6
+ def normalize_magic(collection, columns = [], options = {})
7
+ if collection.is_a? MagicGrid::Definition
8
+ collection
9
+ elsif columns.is_a? MagicGrid::Definition
10
+ columns
11
+ elsif options.is_a? MagicGrid::Definition
12
+ options
13
+ else
14
+ MagicGrid::Definition.new(columns, collection, controller, options)
15
+ end
16
+ end
17
+
18
+ def magic_collection(collection, cols, opts = {})
19
+ normalize_magic(collection, cols, opts).collection
20
+ end
21
+
22
+ def magic_grid(collection = nil, cols = nil, opts = {}, &block)
23
+ grid = normalize_magic(collection, cols, opts)
24
+ base_params = grid.base_params
25
+ data = {
26
+ :searcher => grid.options[:searcher],
27
+ :current => url_for,
28
+ :live_search => grid.options[:live_search],
29
+ :listeners => (grid.options[:listeners] unless grid.options[:listeners].empty?),
30
+ :remote => grid.options[:remote],
31
+ :default_ajax_handler => grid.options[:default_ajax_handler],
32
+ :params => base_params,
33
+ }
34
+ classes = ['magic_grid'] << grid.options[:class]
35
+ content_tag('table',
36
+ :class => classes.join(' '),
37
+ :id => grid.magic_id,
38
+ :data => data.select {|_,v| v }
39
+ ) do
40
+ table = content_tag('thead', :data => {:params => base_params}
41
+ ) do
42
+ thead = ''.html_safe
43
+ has_spinner = false
44
+ spinner = tag('span',
45
+ :id => (grid.magic_id.to_s + "_spinner"),
46
+ :class => "magic_grid_spinner"
47
+ )
48
+ if grid.options[:needs_searcher]
49
+ thead << content_tag('tr') do
50
+ content_tag('td', :class => 'searcher full-width ui-widget-header',
51
+ :colspan => grid.columns.count) do
52
+ searcher_data = {
53
+ :min_length => grid.options[:min_search_length],
54
+ :current => grid.options[:current_search] || "",
55
+ }
56
+ searcher = label_tag(grid.options[:searcher].to_sym,
57
+ grid.options[:searcher_label])
58
+ searcher << search_field_tag(grid.options[:searcher].to_sym,
59
+ grid.param(:q),
60
+ :placeholder => grid.options[:searcher_tooltip],
61
+ :size => grid.options[:searcher_size],
62
+ :data => searcher_data,
63
+ :form => "a form that doesn't exist")
64
+ if grid.options[:search_button]
65
+ searcher << button_tag(grid.options[:searcher_button],
66
+ :class => 'magic-grid-search-button')
67
+ end
68
+ unless has_spinner
69
+ has_spinner = true
70
+ searcher << spinner
71
+ end
72
+ searcher
73
+ end
74
+ end
75
+ end
76
+ if grid.options[:per_page] and grid.options[:top_pager]
77
+ thead << content_tag('tr') do
78
+ content_tag('td', :class => 'full-width ui-widget-header',
79
+ :colspan => grid.columns.count) do
80
+ pager = will_paginate(grid.collection,
81
+ :param_name => grid.param_key(:page),
82
+ :params => base_params
83
+ )
84
+ unless has_spinner
85
+ has_spinner = true
86
+ pager << spinner
87
+ end
88
+ pager
89
+ end
90
+ end
91
+ end
92
+ if thead.empty? and not grid.options[:empty_header]
93
+ thead = content_tag 'tr' do
94
+ content_tag('td', :class => 'full-width ui-widget-header',
95
+ :colspan => grid.columns.count) do
96
+ unless has_spinner
97
+ has_spnner = true
98
+ spinner
99
+ end
100
+ end
101
+ end
102
+ end
103
+ thead << magic_headers(grid)
104
+ end
105
+ table << content_tag('tbody', :class => "ui-widget-content") do
106
+ magic_rows(grid, &block)
107
+ end
108
+ table << content_tag('tfoot') do
109
+ tfoot = ''.html_safe
110
+ if grid.options[:per_page] and grid.options[:bottom_pager]
111
+ tfoot << content_tag('tr') do
112
+ content_tag('td', :class => 'full-width ui-widget-header',
113
+ :colspan => grid.columns.count) do
114
+ will_paginate(grid.collection,
115
+ :param_name => grid.param_key(:page),
116
+ :params => base_params
117
+ )
118
+ end
119
+ end
120
+ end
121
+ if tfoot.empty? and not grid.options[:empty_footer]
122
+ tfoot = content_tag 'tr' do
123
+ content_tag('td', nil, :class => 'full-width ui-widget-header',
124
+ :colspan => grid.columns.count)
125
+ end
126
+ end
127
+ tfoot
128
+ end
129
+ end
130
+ end
131
+
132
+ def magic_headers(cols, collection = nil, opts = {})
133
+ grid = normalize_magic(collection, cols, opts)
134
+ content_tag 'tr' do
135
+ grid.columns.reduce(''.html_safe) do |acc, col|
136
+ classes = ['ui-state-default'] << col[:class]
137
+ acc <<
138
+ if col.is_a? String
139
+ content_tag 'th', col.html_safe, :class => classes.join(' ')
140
+ elsif not col.key? :sql
141
+ content_tag 'th', col[:label].html_safe, :class => classes.join(' ')
142
+ else
143
+ sortable_header(grid, col, opts)
144
+ end
145
+ end
146
+ end
147
+ end
148
+
149
+ def magic_rows(cols, collection = nil, &block)
150
+ grid = normalize_magic(collection, cols)
151
+ if_empty = grid.options[:if_empty]
152
+ rows = grid.collection.map do |row|
153
+ if block_given?
154
+ "<!-- block: -->" << capture(row, &block)
155
+ else
156
+ "<!-- magic row: -->" << magic_row(row, grid)
157
+ end
158
+ end
159
+ if rows.empty? and if_empty
160
+ content_tag 'tr' do
161
+ content_tag('td', :colspan => grid.columns.count,
162
+ :class => 'if-empty') do
163
+ if if_empty.respond_to? :call
164
+ if_empty.call(grid).to_s
165
+ else
166
+ if_empty
167
+ end
168
+ end
169
+ end
170
+ else
171
+ rows.join.html_safe
172
+ end
173
+ end
174
+
175
+ def magic_row(record, cols, collection = nil)
176
+ grid = normalize_magic(collection, cols)
177
+ content_tag 'tr', :class => cycle('odd', 'even') do
178
+ grid.columns.reduce(''.html_safe) do |acc, c|
179
+ acc << content_tag('td', :class => c[:class].try(:join, ' ')) do
180
+ method = c[:to_s] || c[:col]
181
+ if method.respond_to? :call
182
+ method.call(record)
183
+ elsif record.respond_to? method
184
+ record.send(method)
185
+ end.to_s
186
+ end
187
+ end
188
+ end
189
+ end
190
+
191
+ def reverse_order(order)
192
+ order.to_i == 0 ? 1 : 0
193
+ end
194
+
195
+ def order_icon(order = -1)
196
+ content_tag 'span', '', :class => "ui-icon #{order_icon_class(order)}"
197
+ end
198
+
199
+ def order_icon_class(order = -1)
200
+ case order.to_i
201
+ when 0 then 'ui-icon-triangle-1-n'
202
+ when 1 then 'ui-icon-triangle-1-s'
203
+ else 'ui-icon-carat-2-n-s'
204
+ end
205
+ end
206
+
207
+ def order_class(order = -1)
208
+ case order.to_i
209
+ when 0 then 'sort-asc'
210
+ when 1 then 'sort-desc'
211
+ else 'sort-none'
212
+ end
213
+ end
214
+
215
+ def sortable_header(grid, col, opts = {})
216
+ id = col[:id]
217
+ label = col[:label] || id.titleize
218
+ default_sort_order = opts.fetch(:default_order, grid.order(grid.default_order))
219
+ my_params = grid.base_params.merge({
220
+ grid.param_key(:col) => id,
221
+ })
222
+ my_params = HashWithIndifferentAccess.new(my_params)
223
+ order = nil
224
+ classes = ['sorter ui-state-default'] << col[:class]
225
+ current = id.to_s == grid.current_sort_col.to_s
226
+ if current
227
+ order = grid.current_order
228
+ classes << "sort-current" << order_class(order)
229
+ my_params[grid.param_key(:order)] = reverse_order(order)
230
+ label << order_icon(order)
231
+ else
232
+ my_params.delete grid.param_key(:order) if my_params[grid.param_key(:order)]
233
+ label << order_icon()
234
+ end
235
+ my_params.delete(grid.param_key(:order)) if my_params[grid.param_key(:order)].to_i == default_sort_order.to_i
236
+ content_tag 'th', :class => classes.join(' ') do
237
+ link_to label.html_safe, my_params, :remote => grid.options[:remote]
238
+ end
239
+ end
240
+
241
+ ::ActionView::Base.send :include, self
242
+ end
243
+ end
@@ -0,0 +1,9 @@
1
+ module MagicGrid
2
+ class Railtie < Rails::Railtie
3
+ # Once in production, on every page view in development
4
+ config.to_prepare do
5
+ # no caching based on path like require does
6
+ load 'magic_grid/helpers.rb'
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,3 @@
1
+ module MagicGrid
2
+ VERSION = "0.9.2"
3
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :magic_grid do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env rake
2
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
3
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
4
+
5
+ require File.expand_path('../config/application', __FILE__)
6
+
7
+ Dummy::Application.load_tasks
@@ -0,0 +1,9 @@
1
+ // This is a manifest file that'll be compiled into including all the files listed below.
2
+ // Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
3
+ // be included in the compiled file accessible from http://example.com/assets/application.js
4
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
5
+ // the compiled file.
6
+ //
7
+ //= require jquery
8
+ //= require jquery_ujs
9
+ //= require_tree .
@@ -0,0 +1,8 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
3
+ $(function () {
4
+ $("#user_id").addClass("ready");
5
+ $(".magic_grid").on("magic_grid:loaded", function () {
6
+ console.log("Loaded a grid: ", this);
7
+ });
8
+ });
@@ -0,0 +1,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -0,0 +1,7 @@
1
+ /*
2
+ * This is a manifest file that'll automatically include all the stylesheets available in this directory
3
+ * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
4
+ * the top of the compiled file, but it's generally better to create a new file per style scope.
5
+ *= require_self
6
+ *= require_tree .
7
+ */
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,56 @@
1
+ body { background-color: #fff; color: #333; }
2
+
3
+ body, p, ol, ul, td {
4
+ font-family: verdana, arial, helvetica, sans-serif;
5
+ font-size: 13px;
6
+ line-height: 18px;
7
+ }
8
+
9
+ pre {
10
+ background-color: #eee;
11
+ padding: 10px;
12
+ font-size: 11px;
13
+ }
14
+
15
+ a { color: #000; }
16
+ a:visited { color: #666; }
17
+ a:hover { color: #fff; background-color:#000; }
18
+
19
+ div.field, div.actions {
20
+ margin-bottom: 10px;
21
+ }
22
+
23
+ #notice {
24
+ color: green;
25
+ }
26
+
27
+ .field_with_errors {
28
+ padding: 2px;
29
+ background-color: red;
30
+ display: table;
31
+ }
32
+
33
+ #error_explanation {
34
+ width: 450px;
35
+ border: 2px solid red;
36
+ padding: 7px;
37
+ padding-bottom: 0;
38
+ margin-bottom: 20px;
39
+ background-color: #f0f0f0;
40
+ }
41
+
42
+ #error_explanation h2 {
43
+ text-align: left;
44
+ font-weight: bold;
45
+ padding: 5px 5px 5px 15px;
46
+ font-size: 12px;
47
+ margin: -7px;
48
+ margin-bottom: 0px;
49
+ background-color: #c00;
50
+ color: #fff;
51
+ }
52
+
53
+ #error_explanation ul li {
54
+ font-size: 12px;
55
+ list-style: square;
56
+ }
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,3 @@
1
+ class ApplicationController < ActionController::Base
2
+ protect_from_forgery
3
+ end
@@ -0,0 +1,104 @@
1
+ class PostsController < ApplicationController
2
+ # GET /posts
3
+ # GET /posts.json
4
+ def index
5
+ @posts = Post.includes(:user)
6
+ @some_array = []
7
+ def @some_array.search(q)
8
+ Rails.logger.debug "Searching for #{q}, really!"
9
+ self
10
+ end
11
+
12
+ if params.key? :user_id and not params[:user_id].empty?
13
+ @title = "Posts by: #{User.find(params[:user_id]).name}"
14
+ @posts = @posts.where(:user_id => params[:user_id])
15
+ end
16
+
17
+ respond_to do |format|
18
+ format.html # index.html.erb
19
+ format.json { render json: @posts }
20
+ end
21
+ end
22
+
23
+ # GET /posts/by-user/1
24
+ # GET /posts/by-user/1.json
25
+ def by_user
26
+ @title = "Posts by: #{User.find(params[:user_id]).name}"
27
+ @posts = Post.includes(:user).where(:user_id => params[:user_id])
28
+ respond_to do |format|
29
+ format.html # by_user.html.erb
30
+ format.json { render json: @posts }
31
+ end
32
+ end
33
+
34
+ # GET /posts/1
35
+ # GET /posts/1.json
36
+ def show
37
+ @post = Post.find(params[:id])
38
+
39
+ respond_to do |format|
40
+ format.html # show.html.erb
41
+ format.json { render json: @post }
42
+ end
43
+ end
44
+
45
+ # GET /posts/new
46
+ # GET /posts/new.json
47
+ def new
48
+ @post = Post.new
49
+
50
+ respond_to do |format|
51
+ format.html # new.html.erb
52
+ format.json { render json: @post }
53
+ end
54
+ end
55
+
56
+ # GET /posts/1/edit
57
+ def edit
58
+ @post = Post.find(params[:id])
59
+ end
60
+
61
+ # POST /posts
62
+ # POST /posts.json
63
+ def create
64
+ @post = Post.new(params[:post])
65
+
66
+ respond_to do |format|
67
+ if @post.save
68
+ format.html { redirect_to @post, notice: 'Post was successfully created.' }
69
+ format.json { render json: @post, status: :created, location: @post }
70
+ else
71
+ format.html { render action: "new" }
72
+ format.json { render json: @post.errors, status: :unprocessable_entity }
73
+ end
74
+ end
75
+ end
76
+
77
+ # PUT /posts/1
78
+ # PUT /posts/1.json
79
+ def update
80
+ @post = Post.find(params[:id])
81
+
82
+ respond_to do |format|
83
+ if @post.update_attributes(params[:post])
84
+ format.html { redirect_to @post, notice: 'Post was successfully updated.' }
85
+ format.json { head :ok }
86
+ else
87
+ format.html { render action: "edit" }
88
+ format.json { render json: @post.errors, status: :unprocessable_entity }
89
+ end
90
+ end
91
+ end
92
+
93
+ # DELETE /posts/1
94
+ # DELETE /posts/1.json
95
+ def destroy
96
+ @post = Post.find(params[:id])
97
+ @post.destroy
98
+
99
+ respond_to do |format|
100
+ format.html { redirect_to posts_url }
101
+ format.json { head :ok }
102
+ end
103
+ end
104
+ end