grapple 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (52) hide show
  1. checksums.yaml +7 -0
  2. data/LICENSE +22 -0
  3. data/README.md +182 -0
  4. data/Rakefile +9 -0
  5. data/app/assets/images/grapple/arrow-down.png +0 -0
  6. data/app/assets/images/grapple/arrow-up.png +0 -0
  7. data/app/assets/images/grapple/loading-bar.gif +0 -0
  8. data/app/assets/images/grapple/minus.png +0 -0
  9. data/app/assets/images/grapple/plus.png +0 -0
  10. data/app/assets/javascripts/grapple-history.js +81 -0
  11. data/app/assets/javascripts/grapple-jquery.js +202 -0
  12. data/app/assets/javascripts/grapple.js +39 -0
  13. data/app/assets/stylesheets/grapple.css +252 -0
  14. data/config/locales/en.yml +2 -0
  15. data/lib/grapple/ajax_data_grid_builder.rb +38 -0
  16. data/lib/grapple/base_table_builder.rb +58 -0
  17. data/lib/grapple/components/actions.rb +24 -0
  18. data/lib/grapple/components/base_component.rb +91 -0
  19. data/lib/grapple/components/column_headings.rb +42 -0
  20. data/lib/grapple/components/html_body.rb +37 -0
  21. data/lib/grapple/components/html_colgroup.rb +14 -0
  22. data/lib/grapple/components/html_component.rb +24 -0
  23. data/lib/grapple/components/html_footer.rb +14 -0
  24. data/lib/grapple/components/html_header.rb +16 -0
  25. data/lib/grapple/components/html_row.rb +11 -0
  26. data/lib/grapple/components/search_form.rb +27 -0
  27. data/lib/grapple/components/search_query_field.rb +14 -0
  28. data/lib/grapple/components/search_submit.rb +11 -0
  29. data/lib/grapple/components/toolbar.rb +15 -0
  30. data/lib/grapple/components/will_paginate_infobar.rb +22 -0
  31. data/lib/grapple/components/will_paginate_pagination.rb +30 -0
  32. data/lib/grapple/components.rb +23 -0
  33. data/lib/grapple/data_grid_builder.rb +24 -0
  34. data/lib/grapple/engine.rb +11 -0
  35. data/lib/grapple/helpers/table_helper.rb +31 -0
  36. data/lib/grapple/helpers.rb +8 -0
  37. data/lib/grapple/html_table_builder.rb +31 -0
  38. data/lib/grapple.rb +14 -0
  39. data/spec/builders/ajax_data_grid_builder_spec.rb +19 -0
  40. data/spec/components/actions_spec.rb +33 -0
  41. data/spec/components/column_headings_spec.rb +33 -0
  42. data/spec/components/html_body_spec.rb +53 -0
  43. data/spec/components/html_colgroup_spec.rb +38 -0
  44. data/spec/components/html_footer_spec.rb +38 -0
  45. data/spec/components/search_form_spec.rb +29 -0
  46. data/spec/components/toolbar_spec.rb +38 -0
  47. data/spec/components/will_paginate_spec.rb +33 -0
  48. data/spec/fixtures/schema.rb +17 -0
  49. data/spec/fixtures/users.yml +56 -0
  50. data/spec/spec_helper.rb +137 -0
  51. data/spec/support/test_environment.rb +30 -0
  52. metadata +207 -0
@@ -0,0 +1,22 @@
1
+ module Grapple
2
+ module Components
3
+ class WillPaginateInfobar < HtmlComponent
4
+
5
+ setting :message, "Displaying %d - %d of %d items"
6
+ setting :no_results_message, "0 items"
7
+
8
+ def render
9
+ if records.total_entries > 0
10
+ start_range = records.offset + 1
11
+ end_range = [records.offset + records.per_page, records.total_entries].min
12
+ html = sprintf(message, start_range, end_range, records.total_entries)
13
+ else
14
+ html = no_results_message
15
+ end
16
+
17
+ builder.row "<th colspan=\"#{num_columns}\">#{html}</th>", :class => 'infobar'
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ module Grapple
2
+ module Components
3
+ # Generates paging links using will_paginate.
4
+ #
5
+ # @example
6
+ # <%= table_for(columns, Post.paginate(page: 2)) do |t| %>
7
+ # <%= t.footer do %>
8
+ # <%= t.pagination %>
9
+ # <% end %>
10
+ # <% end %>
11
+ #
12
+ class WillPaginatePagination < HtmlComponent
13
+
14
+ setting :no_results_message, :no_search_results
15
+
16
+ def render(paginate_parameters = {})
17
+ if records.instance_of?(Array)
18
+ html = '&nbsp;'
19
+ elsif !params[:query].blank? and records.empty?
20
+ html = h(t(no_results_message))
21
+ else
22
+ html = template.will_paginate(records, paginate_parameters) || '&nbsp;'
23
+ end
24
+
25
+ builder.row "<td colspan=\"#{num_columns}\">#{html}</td>"
26
+ end
27
+
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,23 @@
1
+ module Grapple
2
+ module Components
3
+ extend ActiveSupport::Autoload
4
+
5
+ autoload :BaseComponent
6
+ autoload :HtmlComponent
7
+ autoload :HtmlRow
8
+ autoload :HtmlHeader
9
+ autoload :HtmlBody
10
+ autoload :HtmlFooter
11
+ autoload :HtmlColgroup
12
+ autoload :ColumnHeadings
13
+ autoload :WillPaginate
14
+ autoload :Toolbar
15
+ autoload :Actions
16
+ autoload :SearchForm
17
+ autoload :SearchQueryField
18
+ autoload :SearchSubmit
19
+ autoload :WillPaginatePagination
20
+ autoload :WillPaginateInfobar
21
+
22
+ end
23
+ end
@@ -0,0 +1,24 @@
1
+ module Grapple
2
+ class DataGridBuilder < HtmlTableBuilder
3
+
4
+ helper :infobar, Grapple::Components::WillPaginateInfobar
5
+
6
+ # Toolbar
7
+ helper :search_submit, Grapple::Components::SearchSubmit
8
+ helper :search_query_field, Grapple::Components::SearchQueryField
9
+ helper :search_form, Grapple::Components::SearchForm
10
+ helper :actions, Grapple::Components::Actions
11
+ helper :toolbar, Grapple::Components::Toolbar, components: [:search_form, :actions]
12
+
13
+ # Sortable column headings
14
+ helper :column_headings, Grapple::Components::ColumnHeadings
15
+
16
+ configure :header, components: [:infobar, :toolbar, :column_headings]
17
+
18
+ # Paging
19
+ helper :pagination, Grapple::Components::WillPaginatePagination
20
+
21
+ configure :footer, components: [:pagination]
22
+
23
+ end
24
+ end
@@ -0,0 +1,11 @@
1
+ require 'grapple/helpers/table_helper'
2
+
3
+ module Grapple
4
+ class Engine < Rails::Engine
5
+ initializer 'grapple.initialize' do
6
+ ActiveSupport.on_load(:action_view) do
7
+ include Grapple::Helpers::TableHelper
8
+ end
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,31 @@
1
+ module Grapple
2
+ module Helpers
3
+ module TableHelper
4
+
5
+ @@builder = Grapple::DataGridBuilder
6
+ mattr_accessor :builder
7
+
8
+ def grapple_container(*args, &block)
9
+ options = args[0] || {}
10
+ builder_class = options[:builder] || @@builder
11
+ container_attr = builder_class.container_attributes(self, options)
12
+ html = ''
13
+ html << builder_class.before_container(self, options)
14
+ html << tag('div', container_attr, false) + "\n"
15
+ html << capture(&block)
16
+ html << "</div>\n"
17
+ html << builder_class.after_container(self, options)
18
+ return html.html_safe
19
+ end
20
+
21
+ def table_for(columns, records, *args, &block)
22
+ options = args[0] || {}
23
+ builder_class = options[:builder] || @@builder
24
+ builder = builder_class.new(self, columns, records, params, options)
25
+ output = capture(builder, &block)
26
+ (builder.before_table + builder.table(output) + builder.after_table).html_safe
27
+ end
28
+
29
+ end
30
+ end
31
+ end
@@ -0,0 +1,8 @@
1
+ module Grapple
2
+ module Helpers
3
+ extend ActiveSupport::Autoload
4
+
5
+ autoload :TableHelper, 'grapple/helpers/table_helper'
6
+
7
+ end
8
+ end
@@ -0,0 +1,31 @@
1
+ module Grapple
2
+
3
+ class HtmlTableBuilder < BaseTableBuilder
4
+
5
+ helper :row, Grapple::Components::HtmlRow
6
+ helper :colgroup, Grapple::Components::HtmlColgroup
7
+ helper :header, Grapple::Components::HtmlHeader
8
+ helper :body, Grapple::Components::HtmlBody
9
+ helper :footer, Grapple::Components::HtmlFooter
10
+
11
+ def table(content)
12
+ "<table>#{content}</table>\n".html_safe
13
+ end
14
+
15
+ def self.container_attributes(template, options)
16
+ return {
17
+ :class => 'grapple'
18
+ }
19
+ end
20
+
21
+ def self.before_container(template, options)
22
+ ''
23
+ end
24
+
25
+ def self.after_container(template, options)
26
+ ''
27
+ end
28
+
29
+ end
30
+
31
+ end
data/lib/grapple.rb ADDED
@@ -0,0 +1,14 @@
1
+ require 'active_support'
2
+
3
+ module Grapple
4
+ extend ActiveSupport::Autoload
5
+
6
+ autoload :Components
7
+ autoload :BaseTableBuilder
8
+ autoload :HtmlTableBuilder
9
+ autoload :DataGridBuilder
10
+ autoload :AjaxDataGridBuilder
11
+ autoload :Helpers
12
+ end
13
+
14
+ require 'grapple/engine' if defined?(::Rails)
@@ -0,0 +1,19 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'Ajax Data Grid Builder' do
4
+ include GrappleSpecHelper
5
+
6
+ before do
7
+ @output_buffer = ''
8
+ mock_everything
9
+ end
10
+
11
+ it "container_attributes should be correct" do
12
+ attr = Grapple::AjaxDataGridBuilder.container_attributes(self, { :id => 'my_table' })
13
+ expect(attr[:id]).to eq "my_table"
14
+ expect(attr[:class]).to include("grapple")
15
+ expect(attr[:data]['grapple-ajax-url']).to eq '/mock/path'
16
+ puts attr
17
+ end
18
+
19
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'actions' do
4
+ include GrappleSpecHelper
5
+
6
+
7
+
8
+ before do
9
+ @output_buffer = ''
10
+ mock_everything
11
+ end
12
+
13
+ it "should be valid" do
14
+ actions = [
15
+ { label: "New User", url: "/test" },
16
+ { label: "Export Users", url: url_for({controller: 'users', action: 'new'}) }
17
+ ]
18
+ builder = Grapple::DataGridBuilder.new(self, [], [], {})
19
+ html = builder.actions(actions.clone)
20
+
21
+ #puts html
22
+
23
+ expect(html).to have_tag('div.actions') do
24
+ with_tag("a", count: actions.length)
25
+ actions.each do |action|
26
+ with_tag("a", with: {href: action[:url]}) do
27
+ with_text(action[:label])
28
+ end
29
+ end
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'toolbar' do
4
+ include GrappleSpecHelper
5
+
6
+ columns = [
7
+ { label: '' },
8
+ { label: 'first name', sort: 'LOWER(firstname)', width: 140 },
9
+ { label: 'last name', sort: 'LOWER(lastname)', width: 140 },
10
+ { label: 'username', sort: 'LOWER(login)', width: 150 },
11
+ { label: 'email', sort: 'LOWER(email)', width: 300 },
12
+ { label: 'Created At', sort: 'created_at', width: 175 }
13
+ ]
14
+
15
+ before do
16
+ @output_buffer = ''
17
+ mock_everything
18
+ end
19
+
20
+ it "should be valid" do
21
+ records = User.all.paginate({ page: 1, per_page: 3 })
22
+ builder = Grapple::DataGridBuilder.new(self, columns, records, {})
23
+ #puts builder.methods.inspect
24
+
25
+ html = builder.column_headings
26
+ #puts html
27
+
28
+ expect(html).to have_tag('tr.column-headers') do
29
+ with_tag('th', count: columns.length)
30
+ end
31
+ end
32
+
33
+ end
@@ -0,0 +1,53 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'html body' do
4
+ include GrappleSpecHelper
5
+
6
+ columns = [
7
+ { label: '' },
8
+ { label: 'first name', sort: 'LOWER(firstname)', width: 140 },
9
+ { label: 'last name', sort: 'LOWER(lastname)', width: 140 },
10
+ { label: 'username', sort: 'LOWER(login)', width: 150 },
11
+ { label: 'email', sort: 'LOWER(email)', width: 300 },
12
+ { label: 'Created At', sort: 'created_at', width: 175 }
13
+ ]
14
+
15
+ before do
16
+ @output_buffer = ''
17
+ mock_everything
18
+ end
19
+
20
+ it "should be valid" do
21
+ records = User.all.paginate({ page: 1, per_page: 3 })
22
+ builder = Grapple::HtmlTableBuilder.new(self, columns, records, {})
23
+
24
+ html = builder.body(tr: false) do |item|
25
+ concat((
26
+ "<tr>\n\t\t<td>#{item.login}</td>\n\t\t" +
27
+ '<td class="text-right">' + item.firstname + '</td>' +
28
+ "</tr>"
29
+ ).html_safe)
30
+ end
31
+
32
+ #puts html
33
+
34
+ expect(html).to have_tag('tbody') do
35
+ with_tag('tr', count: records.length)
36
+
37
+ records.each do |user|
38
+ with_tag('tr') do
39
+ with_tag("td") do
40
+ with_text(user.login)
41
+ end
42
+
43
+ with_tag("td") do
44
+ with_text(user.firstname)
45
+ end
46
+ end
47
+ end
48
+
49
+ end
50
+
51
+ end
52
+
53
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'html_colgroup' do
4
+ include GrappleSpecHelper
5
+
6
+ columns = [
7
+ { label: '' },
8
+ { label: 'first name', sort: 'LOWER(firstname)', width: 140 },
9
+ { label: 'last name', sort: 'LOWER(lastname)', width: 140 },
10
+ { label: 'username', sort: 'LOWER(login)', width: 150 },
11
+ { label: 'email', sort: 'LOWER(email)', width: 300 },
12
+ { label: 'Created At', sort: 'created_at', width: 175 }
13
+ ]
14
+
15
+ before do
16
+ @output_buffer = ''
17
+ mock_everything
18
+ end
19
+
20
+ it "should be valid" do
21
+ records = User.all.paginate({ page: 1, per_page: 3 })
22
+ builder = Grapple::HtmlTableBuilder.new(self, columns, records, {})
23
+ html = builder.colgroup()
24
+
25
+ #puts html
26
+
27
+ expect(html).to have_tag('colgroup') do
28
+ with_tag('col', count: columns.length)
29
+
30
+ columns.each do |column|
31
+ with_tag('col', with: {width: column[:width]}) if column[:width].present?
32
+ end
33
+
34
+ end
35
+
36
+ end
37
+
38
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'html footer' do
4
+ include GrappleSpecHelper
5
+
6
+ before do
7
+ @output_buffer = ''
8
+ mock_everything
9
+ end
10
+
11
+ it "should be valid" do
12
+ records = User.all.paginate({ page: 1, per_page: 3 })
13
+ builder = Grapple::DataGridBuilder.new(self, users_columns, records, {})
14
+
15
+ html = builder.footer do |item|
16
+ concat(("<tr><td>TEST</td></tr>").html_safe)
17
+ end
18
+
19
+ #puts html
20
+
21
+ expect(html).to have_tag('tfoot') do
22
+ with_tag('tr', count: 1) do
23
+ with_tag('td', count: 1) do
24
+ with_text("TEST")
25
+ end
26
+ end
27
+ end
28
+
29
+ html = builder.footer
30
+
31
+ expect(html).to have_tag('tfoot') do
32
+ with_tag('tr', count: 1)
33
+ end
34
+
35
+ #puts html
36
+ end
37
+
38
+ end
@@ -0,0 +1,29 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'search form' do
4
+ include GrappleSpecHelper
5
+
6
+ before do
7
+ @output_buffer = ''
8
+ mock_everything
9
+ end
10
+
11
+ it "should be valid" do
12
+ records = User.all.paginate({ page: 1, per_page: 3 })
13
+ builder = Grapple::DataGridBuilder.new(self, users_columns, records, {})
14
+
15
+ html = builder.search_form()
16
+
17
+ puts html
18
+
19
+ expect(html).to have_tag('form.search-form', with: {method: "post"}) do
20
+ with_tag('input', with: {type: "hidden", name: "page"})
21
+ with_tag('input', with: {type: "hidden", name: "utf8"})
22
+
23
+ with_tag('input', with: {name: "query", type: "text", id: 'query', class: 'search-query'})
24
+ with_tag('input', with: {type: "submit"})
25
+ end
26
+
27
+ end
28
+
29
+ end
@@ -0,0 +1,38 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'toolbar' do
4
+ include GrappleSpecHelper
5
+
6
+ columns = [
7
+ { label: '' },
8
+ { label: 'first name', sort: 'LOWER(firstname)', width: 140 },
9
+ { label: 'last name', sort: 'LOWER(lastname)', width: 140 },
10
+ { label: 'username', sort: 'LOWER(login)', width: 150 },
11
+ { label: 'email', sort: 'LOWER(email)', width: 300 },
12
+ { label: 'Created At', sort: 'created_at', width: 175 }
13
+ ]
14
+
15
+ before do
16
+ @output_buffer = ''
17
+ mock_everything
18
+ end
19
+
20
+ it "should be valid" do
21
+ records = User.all.paginate({ page: 1, per_page: 3 })
22
+ builder = Grapple::DataGridBuilder.new(self, columns, records, {})
23
+ #puts builder.methods.inspect
24
+
25
+ html = builder.toolbar do
26
+ concat('<div class="test">test</div>'.html_safe)
27
+ end
28
+
29
+ expect(html).to have_tag('tr', with: { class: 'toolbar' }) do
30
+ with_tag('th', with: { colspan: columns.length }) do
31
+ with_tag('div', with: { class: 'test' })
32
+ end
33
+ end
34
+
35
+ #puts html
36
+ end
37
+
38
+ end
@@ -0,0 +1,33 @@
1
+ require 'spec_helper'
2
+
3
+ describe 'will_paginate' do
4
+ include GrappleSpecHelper
5
+
6
+ columns = [
7
+ { label: '' },
8
+ { label: 'first name', sort: 'LOWER(firstname)', width: 140 },
9
+ { label: 'last name', sort: 'LOWER(lastname)', width: 140 },
10
+ { label: 'username', sort: 'LOWER(login)', width: 150 },
11
+ { label: 'email', sort: 'LOWER(email)', width: 300 },
12
+ { label: 'Created At', sort: 'created_at', width: 175 }
13
+ ]
14
+
15
+ before do
16
+ @output_buffer = ''
17
+ mock_everything
18
+ end
19
+
20
+ it "should be valid" do
21
+ records = User.all.paginate({ page: 1, per_page: 3 })
22
+ builder = Grapple::DataGridBuilder.new(self, columns, records, {})
23
+ puts builder.infobar()
24
+ puts builder.pagination()
25
+
26
+ records = User.where('id < 0').paginate({ page: 1, per_page: 3 })
27
+ builder = Grapple::DataGridBuilder.new(self, columns, records, {:query => 'test'})
28
+ puts builder.infobar()
29
+ puts builder.pagination()
30
+
31
+ end
32
+
33
+ end
@@ -0,0 +1,17 @@
1
+ ActiveRecord::Schema.define do
2
+
3
+ create_table "users", :force => true do |t|
4
+ t.string "login", :limit => 100, :default => "", :null => false
5
+ t.string "crypted_password", :limit => 40, :default => "", :null => false
6
+ t.string "email", :limit => 100, :default => "", :null => false
7
+ t.string "firstname", :limit => 50
8
+ t.string "lastname", :limit => 50
9
+ t.string "salt", :limit => 40, :default => "", :null => false
10
+ t.datetime "created_at"
11
+ t.datetime "updated_at"
12
+ t.datetime "logged_in_at"
13
+ t.boolean "deleted", :default => false
14
+ t.datetime "deleted_at"
15
+ end
16
+
17
+ end
@@ -0,0 +1,56 @@
1
+ ---
2
+ users_001:
3
+ id: 1
4
+ login: jdoe
5
+ crypted_password: f95659157fe670690ef522c8bc15ea6f583fba77
6
+ email: jdoe@test12345.com
7
+ firstname: John
8
+ lastname: Doe
9
+ salt: 1565b93dd4226040d687ee21163113a8729532ea
10
+ created_at: 2009-03-01 14:01:34 Z
11
+ updated_at: 2010-01-01 02:07:50 Z
12
+ deleted: false
13
+ users_002:
14
+ id: 2
15
+ login: admin
16
+ crypted_password: f95659157fe670690ef522c8bc15ea6f583fba77
17
+ email: admin@test12345.com
18
+ firstname: Admin
19
+ lastname: User
20
+ salt: 1565b93dd4226040d687ee21163113a8729532ea
21
+ created_at: 2009-03-01 14:01:34 Z
22
+ updated_at: 2010-01-01 02:07:50 Z
23
+ deleted: false
24
+ users_003:
25
+ id: 2
26
+ login: admin
27
+ crypted_password: f95659157fe670690ef522c8bc15ea6f583fba77
28
+ email: admin@test12345.com
29
+ firstname: Admin
30
+ lastname: User
31
+ salt: 1565b93dd4226040d687ee21163113a8729532ea
32
+ created_at: 2009-03-01 14:01:34 Z
33
+ updated_at: 2010-01-01 02:07:50 Z
34
+ deleted: false
35
+ users_003:
36
+ id: 3
37
+ login: user
38
+ crypted_password: f95659157fe670690ef522c8bc15ea6f583fba77
39
+ email: user@test12345.com
40
+ firstname: User
41
+ lastname: User
42
+ salt: 1565b93dd4226040d687ee21163113a8729532ea
43
+ created_at: 2009-03-01 14:01:34 Z
44
+ updated_at: 2010-01-01 02:07:50 Z
45
+ deleted: false
46
+ users_004:
47
+ id: 4
48
+ login: eddie
49
+ crypted_password: f95659157fe670690ef522c8bc15ea6f583fba77
50
+ email: eddie@test12345.com
51
+ firstname: Eddie
52
+ lastname: Test
53
+ salt: 1565b93dd4226040d687ee21163113a8729532ea
54
+ created_at: 2009-03-01 14:01:34 Z
55
+ updated_at: 2010-01-01 02:07:50 Z
56
+ deleted: false