zable 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.
data/MIT-LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright 2010 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,8 @@
1
+ = Zable
2
+
3
+
4
+ zable @items, Item, :table_class => ["items-table", "shiny-colorful-table"] do
5
+ column(:name)
6
+ column(:color)
7
+ column(:owner_name) { |item| item.owner.name }
8
+ end
data/Rakefile ADDED
@@ -0,0 +1,29 @@
1
+ # encoding: UTF-8
2
+ require 'rubygems'
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ $stderr.puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ require 'rake'
10
+ require 'rdoc/task'
11
+
12
+ require 'rake/testtask'
13
+
14
+ Rake::TestTask.new(:test) do |t|
15
+ t.libs << 'lib'
16
+ t.libs << 'test'
17
+ t.pattern = 'test/**/*_test.rb'
18
+ t.verbose = false
19
+ end
20
+
21
+ task :default => :test
22
+
23
+ Rake::RDocTask.new(:rdoc) do |rdoc|
24
+ rdoc.rdoc_dir = 'rdoc'
25
+ rdoc.title = 'Zable'
26
+ rdoc.options << '--line-numbers' << '--inline-source'
27
+ rdoc.rdoc_files.include('README.rdoc')
28
+ rdoc.rdoc_files.include('lib/**/*.rb')
29
+ end
@@ -0,0 +1,59 @@
1
+ module Zable
2
+ module ActiveRecord
3
+
4
+ module ClassMethods
5
+ PAGE_DEFAULTS = { 'num' => 1, 'size' => 2 }
6
+
7
+ def scoped_for_sort(params, scoped_object)
8
+ hash = (params[:sort] || {}).stringify_keys
9
+ scoped_object = scoped_object.for_sort_params(hash) unless hash.empty?
10
+ scoped_object
11
+ end
12
+
13
+ def scoped_for_search(params, scoped_object)
14
+ hash = (params[:search] || {}).stringify_keys
15
+ scoped_object = scoped_object.for_search_params(hash) unless hash.empty?
16
+ scoped_object
17
+ end
18
+
19
+ def scoped_for_paginate(params, scoped_object)
20
+ page = (PAGE_DEFAULTS.merge(params[:page] || {})).stringify_keys
21
+ scoped_object.paginate :page => page['num'], :per_page => page['size']
22
+ end
23
+
24
+ def populate(params={})
25
+ obj = scoped_for_sort(params, self)
26
+ obj = scoped_for_search(params, obj)
27
+ scoped_for_paginate(params, obj)
28
+ end
29
+
30
+ module Helpers
31
+ def attribute_columns_only
32
+ self.column_names.reject { |c|
33
+ is_foreign_key?(c) || is_rails_column?(c)
34
+ }
35
+ end
36
+
37
+ protected
38
+ def is_foreign_key?(column_name)
39
+ !(self.reflect_on_all_associations(:belongs_to).detect do |e|
40
+ if (e.options.has_key? :foreign_key)
41
+ e.options[:foreign_key] == column_name
42
+ else
43
+ "#{e.name}_id" == column_name
44
+ end
45
+ end.nil?)
46
+ end
47
+
48
+ def is_rails_column?(column_name)
49
+ %w{id created_at updated_at}.include?(column_name)
50
+ end
51
+ end
52
+ end
53
+
54
+ end
55
+
56
+ end
57
+
58
+ ActiveRecord::Base.send :extend, Zable::ActiveRecord::ClassMethods
59
+ ActiveRecord::Base.send :extend, Zable::ActiveRecord::ClassMethods::Helpers
@@ -0,0 +1,11 @@
1
+ require 'zable'
2
+ require 'rails'
3
+ require 'action_controller'
4
+
5
+ module Zable
6
+ class Engine < Rails::Engine
7
+ initializer "static assets" do |app|
8
+ app.middleware.use ::ActionDispatch::Static, "#{root}/public"
9
+ end
10
+ end
11
+ end
data/lib/zable/hash.rb ADDED
@@ -0,0 +1,5 @@
1
+ class Hash
2
+ def reject_empty_values
3
+ reject { |k, v| v.respond_to?(:empty?) ? v.empty? : !v }
4
+ end
5
+ end
data/lib/zable/html.rb ADDED
@@ -0,0 +1,154 @@
1
+ module Zable
2
+ module Html
3
+
4
+ ## Table header methods
5
+ def table_header(klass, columns)
6
+ content_tag :thead do
7
+ table_header_row(klass, columns)
8
+ end
9
+ end
10
+
11
+ def table_header_row(klass, columns)
12
+ content_tag :tr do
13
+ table_header_cells(klass, columns)
14
+ end
15
+ end
16
+
17
+ def table_header_cells(klass, columns)
18
+ columns.inject("".html_safe) do |str, attr|
19
+ str << table_header_cell(klass, attr, columns)
20
+ end
21
+ end
22
+
23
+ def table_header_cell(klass, attr, columns)
24
+ content_tag :th, :id => header_cell_id(klass, attr) do
25
+ header_cell_content(attr, columns)
26
+ end
27
+ end
28
+
29
+ def header_cell_content(attr, columns)
30
+ str = link_to_if attr[:sort], header_cell_link_text(attr), header_cell_href(attr, columns)
31
+ str << header_cell_sort_image(attr)
32
+ end
33
+
34
+ def header_cell_id(klass, attr)
35
+ "#{idify klass.name}-#{idify attr[:name]}".html_safe
36
+ end
37
+
38
+ def current_url
39
+ "#{controller.request.fullpath.split("?")[0]}"
40
+ end
41
+
42
+ def param(param_type, param_key, attr)
43
+ "#{param_type}[#{param_key}]=#{attr}"
44
+ end
45
+
46
+ def sort_params(attr)
47
+ params = []
48
+ params << param(:sort, "attr", attr[:name])
49
+ params << param(:sort, "order", attr[:sort_order]) if attr[:sorted?]
50
+ params.join("&".html_safe)
51
+ end
52
+
53
+ def search_params(params)
54
+ params.to_a.collect do |param|
55
+ param(:search, param.first, param.second)
56
+ end.join("&".html_safe)
57
+ end
58
+
59
+ def sort_arrow_image_file(attr)
60
+ attr[:sort_order] == :desc ? "common/ascending.gif" : "common/descending.gif"
61
+ end
62
+
63
+ def header_cell_href(attr, columns)
64
+ search = columns.instance_variable_get :@search_params
65
+ extra_params = columns.instance_variable_get(:@_extra_params) || {}
66
+ all_params = [sort_params(attr), search_params(search), extra_params.to_query.html_safe].reject(&:blank?).join("&".html_safe)
67
+ current_url << "?".html_safe << all_params
68
+ end
69
+
70
+ def header_cell_link_text(attr)
71
+ (attr[:title] || attr[:name].to_s.titleize).html_safe
72
+ end
73
+
74
+ def header_cell_sort_image(attr)
75
+ return ''.html_safe unless attr[:sort] && attr[:sorted?]
76
+ arrow = sort_arrow_image_file(attr)
77
+ image_tag arrow
78
+ end
79
+
80
+ ## Table body methods
81
+ def table_body(collection, columns, args)
82
+ content_tag :tbody do
83
+ return empty_table_body_row(columns,args) if collection.empty?
84
+ (table_body_rows(collection, columns) || "".html_safe) + (args[:append] || "".html_safe)
85
+ end
86
+ end
87
+
88
+ def empty_table_body_row(columns, args)
89
+ content_tag :tr, :class => 'empty', :id=> "zable-empty-set" do
90
+ content_tag :td, :colspan => columns.size do
91
+ (args[:empty_message] || "No items found.").html_safe
92
+ end
93
+ end
94
+ end
95
+
96
+ def table_body_rows(collection, columns)
97
+ collection.inject("".html_safe) do |str, elem|
98
+ str << table_body_row(elem, columns)
99
+ end
100
+ end
101
+
102
+ def table_body_row(elem, columns)
103
+ content_tag :tr, :id => body_row_id(elem), :class => body_row_class do
104
+ table_body_row_cells(elem, columns)
105
+ end
106
+ end
107
+
108
+ def table_body_row_cells(elem, columns)
109
+ columns.inject("".html_safe) do |str, ac|
110
+ block = ac[:block] if ac.has_key?(:block)
111
+ str << table_body_row_cell(ac, elem, &block)
112
+ end
113
+ end
114
+
115
+ def table_body_row_cell(ac, elem)
116
+ content_tag(:td, :id => body_cell_id(ac, elem)) do
117
+ if block_given?
118
+ yield elem
119
+ else
120
+ val = elem.send(ac[:name])
121
+ val.respond_to?(:strftime) ? val.strftime("%m/%d/%Y") : val.to_s
122
+ end
123
+ end
124
+ end
125
+
126
+ def body_row_id(elem)
127
+ "#{idify_class_name(elem)}-#{elem.id}".html_safe
128
+ end
129
+
130
+ def body_cell_id(ac, elem)
131
+ "#{idify_class_name(elem)}-#{elem.id}-#{idify ac[:name]}".html_safe
132
+ end
133
+
134
+ def body_row_class
135
+ cycle("odd", "even", name: "zable_cycle")
136
+ end
137
+
138
+ protected
139
+ def idify_class_name(elem)
140
+ idify elem.class.name
141
+ end
142
+
143
+ def idify(val)
144
+ val.to_s.demodulize.underscore.dasherize
145
+ end
146
+
147
+ def tag_args(args)
148
+ tag_args = {}
149
+ tag_args[:class] = args[:table_class].join(" ".html_safe) if args[:table_class] and !args[:table_class].empty?
150
+ tag_args
151
+ end
152
+
153
+ end
154
+ end
@@ -0,0 +1,53 @@
1
+ module Zable
2
+ module Search
3
+ module ActiveRecord
4
+ module ClassMethods
5
+ def inherited(subclass)
6
+ subclass.class_eval do
7
+ scope :for_search_params, -> search_params { inject_search_scopes(search_params) }
8
+ end
9
+ super(subclass)
10
+ end
11
+
12
+ # Allows +Model.for_search_params+ to do equality-based searching for the given +attr_names+.
13
+ # Date attributes (ending in "_on") will take Ruby-parseable date strings as well as the common
14
+ # US case "mm/dd/yyyy".
15
+ #
16
+ # searchable :first_name, :last_name, :born_on
17
+ def searchable(*attr_names)
18
+ attr_names.each do |attr_name|
19
+ scope "search_#{attr_name}", -> attr_value do
20
+ where(attr_name => (attr_name =~ /_on$/ ? parse_date_string(attr_value) : attr_value))
21
+ end
22
+ end
23
+ end
24
+
25
+ protected
26
+
27
+ def inject_search_scopes(search_params)
28
+ non_empty_search_params = search_params.try(:reject_empty_values) || {}
29
+
30
+ non_empty_search_params.to_a.inject(self) { |result,value|
31
+ scope_for_search_attribute(result, value)
32
+ } unless non_empty_search_params.empty?
33
+ end
34
+
35
+ def parse_date_string(str)
36
+ str =~ /\d\d\/\d\d\/\d{4}/ ? Date.strptime(str, '%m/%d/%Y') : str.to_date
37
+ end
38
+
39
+ def scope_name_for_attribute(type, attr)
40
+ "#{type}_#{attr}".to_sym
41
+ end
42
+
43
+ def scope_for_search_attribute(target, tuple)
44
+ attr = tuple.first
45
+ target.send scope_name_for_attribute(:search, attr), tuple.second
46
+ end
47
+
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ ActiveRecord::Base.send :extend, Zable::Search::ActiveRecord::ClassMethods
data/lib/zable/sort.rb ADDED
@@ -0,0 +1,47 @@
1
+ module Zable
2
+ module Sort
3
+
4
+ module ActiveRecord
5
+
6
+ module ClassMethods
7
+ def inherited(subclass)
8
+ subclass.class_eval do
9
+ scope :for_sort_params, -> sort_params { inject_sort_scope(sort_params) }
10
+ end
11
+ super(subclass)
12
+ end
13
+
14
+ # Allows +Model.for_sort_params+ to sort for the given +attr_names+ for the common sorting
15
+ # case (i.e. "attr_name ASC" or "attr_name DESC")
16
+ #
17
+ # sortable :last_name, :created_at
18
+ def sortable(*attr_names)
19
+ attr_names.each do |attr_name|
20
+ scope "sort_#{attr_name}", -> options { order("#{table_name}.#{attr_name} #{options[:order]}") }
21
+ end
22
+ end
23
+
24
+ protected
25
+
26
+ def inject_sort_scope(sort_params)
27
+ return unless sort_params
28
+ self.send scope_for_sort_attribute(sort_params), sort_params
29
+ end
30
+
31
+ def scope_name_for_attribute(type, attr)
32
+ "#{type}_#{attr}".to_sym
33
+ end
34
+
35
+ def scope_for_sort_attribute(sort_params)
36
+ sort_params.stringify_keys!
37
+ sort_params['order'] = 'ASC' if sort_params['order'].nil? || sort_params['order'].upcase != "DESC"
38
+ scope_name_for_attribute(:sort, sort_params['attr'])
39
+ end
40
+ end
41
+
42
+ end
43
+
44
+ end
45
+ end
46
+
47
+ ActiveRecord::Base.send :extend, Zable::Sort::ActiveRecord::ClassMethods
@@ -0,0 +1,19 @@
1
+ require 'will_paginate/view_helpers/link_renderer'
2
+
3
+ module Zable
4
+ module WillPaginate
5
+ class LinkWithParamsRenderer < ::WillPaginate::ViewHelpers::LinkRenderer
6
+
7
+ def initialize(params = {})
8
+ @params = params
9
+ end
10
+
11
+ protected
12
+
13
+ def default_url_params
14
+ super.merge(@params)
15
+ end
16
+
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,11 @@
1
+ require 'zable/zable_test_helper'
2
+
3
+ module ZableTestHelper
4
+ def self.included(base)
5
+ ZableHelper.module_eval do
6
+ def pagination_element(*args)
7
+ ''.html_safe
8
+ end
9
+ end
10
+ end
11
+ end
data/lib/zable.rb ADDED
@@ -0,0 +1,11 @@
1
+ module Zable
2
+
3
+ require 'zable/engine'
4
+ require 'zable/active_record'
5
+ require 'zable/html'
6
+ require 'zable/sort'
7
+ require 'zable/search'
8
+ require 'zable/hash'
9
+ require 'zable/will_paginate'
10
+
11
+ end
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: zable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Derek Croft
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-04-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: will_paginate
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :runtime
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: HTML searching, sorting and pagination made dead simple
31
+ email:
32
+ executables: []
33
+ extensions: []
34
+ extra_rdoc_files: []
35
+ files:
36
+ - lib/zable/active_record.rb
37
+ - lib/zable/engine.rb
38
+ - lib/zable/hash.rb
39
+ - lib/zable/html.rb
40
+ - lib/zable/search.rb
41
+ - lib/zable/sort.rb
42
+ - lib/zable/will_paginate.rb
43
+ - lib/zable/zable_test_helper.rb
44
+ - lib/zable.rb
45
+ - MIT-LICENSE
46
+ - Rakefile
47
+ - README.rdoc
48
+ homepage:
49
+ licenses: []
50
+ post_install_message:
51
+ rdoc_options: []
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ none: false
56
+ requirements:
57
+ - - ! '>='
58
+ - !ruby/object:Gem::Version
59
+ version: '0'
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ none: false
62
+ requirements:
63
+ - - ! '>='
64
+ - !ruby/object:Gem::Version
65
+ version: '0'
66
+ requirements: []
67
+ rubyforge_project:
68
+ rubygems_version: 1.8.21
69
+ signing_key:
70
+ specification_version: 3
71
+ summary: HTML tables
72
+ test_files: []