grapple 0.0.1

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 (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