gitter 1.1.3

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 (56) hide show
  1. data/.gitignore +5 -0
  2. data/.travis.yml +6 -0
  3. data/Gemfile +14 -0
  4. data/Guardfile +21 -0
  5. data/License +20 -0
  6. data/Rakefile +9 -0
  7. data/Readme.markdown +159 -0
  8. data/assets/images/sort_asc.gif +0 -0
  9. data/assets/images/sort_desc.gif +0 -0
  10. data/gitter.gemspec +28 -0
  11. data/lib/gitter.rb +13 -0
  12. data/lib/gitter/axis.rb +48 -0
  13. data/lib/gitter/base.rb +149 -0
  14. data/lib/gitter/breadcrumbs.rb +44 -0
  15. data/lib/gitter/cell.rb +11 -0
  16. data/lib/gitter/column.rb +142 -0
  17. data/lib/gitter/columns.rb +110 -0
  18. data/lib/gitter/controller.rb +19 -0
  19. data/lib/gitter/csv.rb +9 -0
  20. data/lib/gitter/driver.rb +26 -0
  21. data/lib/gitter/drivers/abstract_driver.rb +36 -0
  22. data/lib/gitter/drivers/active_record_driver.rb +84 -0
  23. data/lib/gitter/facet.rb +95 -0
  24. data/lib/gitter/filters.rb +4 -0
  25. data/lib/gitter/filters/abstract_filter.rb +94 -0
  26. data/lib/gitter/filters/block_filter.rb +16 -0
  27. data/lib/gitter/filters/column_filter.rb +50 -0
  28. data/lib/gitter/filters/select_filter.rb +43 -0
  29. data/lib/gitter/grid.rb +23 -0
  30. data/lib/gitter/header.rb +44 -0
  31. data/lib/gitter/helpers.rb +44 -0
  32. data/lib/gitter/i18n.rb +11 -0
  33. data/lib/gitter/model.rb +40 -0
  34. data/lib/gitter/pivot.rb +107 -0
  35. data/lib/gitter/pivot_grid.rb +23 -0
  36. data/lib/gitter/railtie.rb +8 -0
  37. data/lib/gitter/table.rb +149 -0
  38. data/lib/gitter/utils.rb +12 -0
  39. data/lib/gitter/version.rb +3 -0
  40. data/spec/breadcrumbs_spec.rb +24 -0
  41. data/spec/column_filter_spec.rb +80 -0
  42. data/spec/column_spec.rb +159 -0
  43. data/spec/facets_spec.rb +75 -0
  44. data/spec/grid_spec.rb +92 -0
  45. data/spec/helper_spec.rb +8 -0
  46. data/spec/i18n_spec.rb +39 -0
  47. data/spec/inputs_spec.rb +0 -0
  48. data/spec/locales/de.yml +10 -0
  49. data/spec/locales/en.yml +10 -0
  50. data/spec/range_filter_spec.rb +32 -0
  51. data/spec/scope_filter_spec.rb +12 -0
  52. data/spec/select_filter_spec.rb +22 -0
  53. data/spec/spec_helper.rb +31 -0
  54. data/spec/support/database.rb +24 -0
  55. data/spec/support/person_grid.rb +71 -0
  56. metadata +152 -0
data/.gitignore ADDED
@@ -0,0 +1,5 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ .svn
5
+ pkg/*
data/.travis.yml ADDED
@@ -0,0 +1,6 @@
1
+ language: ruby
2
+ rvm:
3
+ - 1.9.3
4
+ - jruby-19mode
5
+ - rbx-19mode
6
+
data/Gemfile ADDED
@@ -0,0 +1,14 @@
1
+ source 'http://rubygems.org'
2
+
3
+ group :development, :test do
4
+ gem 'rake'
5
+ gem 'bundler'
6
+ end
7
+
8
+ group :test do
9
+ gem 'sqlite3'
10
+ gem 'rspec'
11
+ gem 'guard-rspec'
12
+ end
13
+
14
+ gemspec
data/Guardfile ADDED
@@ -0,0 +1,21 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2 do
5
+ watch(%r{^spec/.+_spec\.rb$})
6
+ watch(%r{^lib/(.+/)*(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
7
+ watch('spec/spec_helper.rb') { "spec" }
8
+
9
+ # Rails example
10
+ watch(%r{^spec/.+_spec\.rb$})
11
+ watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
12
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
13
+ watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
14
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec" }
15
+ watch('spec/spec_helper.rb') { "spec" }
16
+ watch('config/routes.rb') { "spec/routing" }
17
+ watch('app/controllers/application_controller.rb') { "spec/controllers" }
18
+ # Capybara request specs
19
+ watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
20
+ end
21
+
data/License ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2011 Thomas Sonntag
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/Rakefile ADDED
@@ -0,0 +1,9 @@
1
+ #!/usr/bin/env rake
2
+ require "bundler/gem_tasks"
3
+ require 'rake/testtask'
4
+
5
+ task default: [:test]
6
+
7
+ Rake::TestTask.new do |t|
8
+ t.pattern = "test/*.rb"
9
+ end
data/Readme.markdown ADDED
@@ -0,0 +1,159 @@
1
+ # gitter
2
+
3
+ Ruby library for Rails which enables you to create
4
+ data grids, i.e table like data with customizable
5
+
6
+ * Filters
7
+ * Sortables columns
8
+ * Faceted search
9
+ * Localization
10
+
11
+ ## Data Grids
12
+
13
+ In order to define a grid you need to provide:
14
+
15
+ * a scope which returns the objects for the grid's rows
16
+ * filters that will be used to filter the rows
17
+ * columns to be displayed
18
+
19
+ Example:
20
+
21
+ ```ruby
22
+ class ArticleGrid << Gitter::Grid
23
+
24
+ ### First define the source for your data
25
+ # helpers are accessible by #h
26
+ scope do
27
+ Article.where(:owner => h.current_user)
28
+ end
29
+
30
+ ### Then you may define filters
31
+
32
+ # filter by attribute
33
+ filter :name
34
+
35
+ # filter by multiple columns: filters by :name OR :description
36
+ filter :search, :columns => [:name, :description]
37
+
38
+ # filter by named scope
39
+ filter :topsellers, :scope => :topsellers
40
+
41
+ # customized filter
42
+ filter :on_stock, do |scope|
43
+ scope.where('stock > 0')
44
+ end
45
+
46
+ filter :out_of_stock do |scope|
47
+ scope.where(:stock => 0)
48
+ end
49
+
50
+ # select from given filters
51
+ filter :availability, :select => [:on_stock, :out_of_stock]
52
+
53
+ # add to facets
54
+ filter :category, :facet => true
55
+
56
+ # select among named scopes
57
+ filter :price_range, :scopes => [:niceprice, :regular]
58
+
59
+ # you can provide 'search' like attributes
60
+ filter :search, :ignore_case => true, :exact => false
61
+
62
+ # The former can be abbreviated by
63
+ search :search
64
+
65
+ ### Define your data grid
66
+
67
+ # show an attribute
68
+ column :article_no
69
+
70
+ # provide a hardcoded header (i18n support also available)
71
+ column :description, :header => 'Details'
72
+
73
+ # make the column sortable
74
+ column :name, :sort => true
75
+
76
+ # customize your data cell
77
+ column :price, :sort => true do
78
+ "#{price/100.floor},#{price%100} USD"
79
+ end
80
+
81
+ # helpers are accessible via #h
82
+ column :details, :header => false do
83
+ h.link_to 'details', h.edit_article_path(self)
84
+ end
85
+
86
+ end
87
+ ```
88
+
89
+ [More about filters](https://github.com/tracksun/gitter/wiki/Filters)
90
+
91
+ [More about columns](https://github.com/tracksun/gitter/wiki/Columns)
92
+
93
+
94
+ #Rendering your grid
95
+
96
+ For the most common use case -- your controller -- you simply do:
97
+
98
+ ```ruby
99
+ def index
100
+ @grid = ArticleGrid.new(self)
101
+ end
102
+ ```
103
+
104
+ Render you grid:
105
+
106
+ ```haml
107
+ %table
108
+ %tr
109
+ - @grid.headers.each do |header|
110
+ %th = header
111
+
112
+ - @grid.rows.each do |row|
113
+ %tr
114
+ - row.each do |cell|
115
+ %th = cell
116
+ ```
117
+ [More about grids](https://github.com/tracksun/gitter/wiki/Grids)
118
+
119
+ # Facets
120
+
121
+ Render your facets:
122
+
123
+ ```haml
124
+ %ul
125
+ - @grid.facets do |facet|
126
+ %li
127
+ = facet.label
128
+ %ul
129
+ - facet.data.each do |data|
130
+ = data.value
131
+ = link_to "(#{data.count})", data.link
132
+
133
+ ```
134
+ [More about facets](https://github.com/tracksun/gitter/wiki/Facets)
135
+
136
+ # Breadcrumbs
137
+
138
+ Render your breadcrumbs:
139
+
140
+ ```haml
141
+ @grid.render_breadcrumbs
142
+ ```
143
+
144
+ [More about inputs](https://github.com/tracksun/gitter/wiki/Inputs)
145
+
146
+
147
+ # ORM Support
148
+
149
+ * ActiveRecord
150
+ * others: Help or suggestions are welcome
151
+
152
+
153
+ # Credits
154
+
155
+ API inspired by [datagrid](https://github.com/bogdan/datagrid)
156
+
157
+ # License
158
+
159
+ Gitter is released under the MIT license
Binary file
Binary file
data/gitter.gemspec ADDED
@@ -0,0 +1,28 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "gitter/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "gitter"
7
+ s.version = Gitter::VERSION
8
+ s.platform = Gem::Platform::RUBY
9
+ s.authors = ["Thomas Sommtag"]
10
+ s.email = ["git@sonntagsbox.de"]
11
+ s.homepage = "http://github.com/tracksun/gitter"
12
+ s.summary = %q{Ruby gem to define searches, facets and data grids for Rails applications}
13
+ s.description = <<-EOS
14
+ To be used within Rails applications.
15
+ Helps you to define searches with filters and facets
16
+ and data tables with sortable columns and filters.
17
+ EOS
18
+
19
+ s.rubyforge_project = "gitter"
20
+
21
+ s.files = `git ls-files`.split("\n")
22
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
23
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
24
+ s.require_paths = %w(lib assets)
25
+ s.add_dependency 'activesupport', '>=3.2'
26
+ s.add_dependency 'activerecord', '>=3.2'
27
+ s.add_dependency 'artdeco', '>=1.2'
28
+ end
data/lib/gitter.rb ADDED
@@ -0,0 +1,13 @@
1
+ module Gitter
2
+
3
+ autoload :Version, 'gitter/version'
4
+ autoload :Grid, 'gitter/grid'
5
+ autoload :PivotGrid, 'gitter/pivot_grid'
6
+ autoload :Helper, 'gitter/helper'
7
+ autoload :Controller, 'gitter/controller'
8
+ autoload :Table, 'gitter/table'
9
+
10
+ class ConfigurationError < StandardError
11
+ end
12
+
13
+ end
@@ -0,0 +1,48 @@
1
+ module Gitter
2
+
3
+ class Axis
4
+
5
+ attr_reader :grid, :name, :attr
6
+
7
+ def initialize grid, name, opts = {}
8
+ @grid, @name = grid, name
9
+ @attr = opts.delete(:column){name}
10
+ only = opts.delete(:only){nil}
11
+ case only
12
+ when Hash
13
+ @only_data, @titles= only.keys, only
14
+ else
15
+ @only_data, @titles = only, nil
16
+ end
17
+ @except = opts.delete(:except){[]}
18
+ end
19
+
20
+
21
+ def data
22
+ data = case attr
23
+ when Symbol,String
24
+ grid.scope.select(attr).uniq.map(&:"#{attr}").sort
25
+ else
26
+ attr
27
+ end
28
+
29
+ data = ((data + @only_data) & @only_data).uniq if @only_data
30
+ data = data - @except
31
+ end
32
+
33
+ def titles
34
+ if @titles
35
+ data.map{|d|@titles[d]}
36
+ else
37
+ data
38
+ end
39
+ end
40
+
41
+ def data_titles
42
+ res = {}
43
+ data.each{|d| res[d] = @titles ? @titles[d] : d}
44
+ res
45
+ end
46
+
47
+ end
48
+ end
@@ -0,0 +1,149 @@
1
+ require 'active_support/concern'
2
+ require 'artdeco'
3
+ require 'gitter/filters.rb'
4
+ require 'gitter/facet.rb'
5
+
6
+ module Gitter
7
+
8
+ module Base
9
+ extend ActiveSupport::Concern
10
+
11
+ module ClassMethods
12
+ def grid &grid
13
+ if grid
14
+ @grid = grid
15
+ else
16
+ @grid or raise ArgumentError, 'undefined grid'
17
+ end
18
+ end
19
+ end
20
+
21
+ attr_reader :params, :decorator, :options, :values
22
+
23
+ def initialize *args
24
+ opts = args.extract_options!
25
+ @decorator = Artdeco::Decorator.new *args, opts
26
+
27
+ @params = @decorator.params.fetch(key){{}}.symbolize_keys
28
+
29
+ @filters, @values, @facets = {}, {}, {}
30
+ scope = opts.delete(:scope){nil}
31
+ @options = opts.dup
32
+
33
+ instance_eval &self.class.grid
34
+
35
+ @scope = scope || @scope
36
+
37
+ @params.each do |name, value|
38
+ if filter = @filters[name]
39
+ @values[name] = value
40
+ end
41
+ end
42
+ end
43
+
44
+ def filter_value filter_name
45
+ @values[filter_name]
46
+ end
47
+
48
+ def filters
49
+ @filters.values
50
+ end
51
+
52
+ def filter_for name
53
+ @filters[name]
54
+ end
55
+
56
+ def label name
57
+ filter_for(name).label
58
+ end
59
+
60
+ def driver
61
+ @driver ||= begin
62
+ scope = Proc === @scope ? instance_eval(&@scope) : @scope
63
+ create_driver scope
64
+ end
65
+ end
66
+
67
+ def filtered_driver
68
+ @filter_driver ||= begin
69
+ d = driver
70
+ @values.each do |name, value|
71
+ d = @filters[name].apply d, value
72
+ end
73
+ d
74
+ end
75
+ end
76
+
77
+ def scope &scope
78
+ if scope
79
+ @scope = scope
80
+ else
81
+ filtered_driver.scope
82
+ end
83
+ end
84
+
85
+ def filter *args, &block
86
+ opts = args.extract_options!
87
+ raise ConfigurationError, 'only zero or one argument allowed' if args.size > 1
88
+ name = args.first
89
+
90
+ filter = case
91
+ when opts.delete(:range)
92
+ raise ConfigurationError, "no block allowed for range filter #{name}" if block
93
+ return range_filter name, opts # return is required
94
+ when block
95
+ BlockFilter.new self, name, opts, &block
96
+ when select = opts.delete(:select)
97
+ if opts[:facet] && opts[:facet] != true
98
+ opts.merge! values: opts[:facet]
99
+ end
100
+ filters = [select].flatten.map{|name| @filters[name] || scope_filter(name)}
101
+ SelectFilter.new self, name, filters, opts
102
+ when s = opts.delete(:scope)
103
+ scope_filter( s == true ? name : s, opts )
104
+ else
105
+ if opts[:facet] && opts[:facet] != true
106
+ opts.merge! values: opts[:facet]
107
+ end
108
+ ColumnFilter.new self, name, opts
109
+ end
110
+
111
+ @facets[name] = Facet.new(filter) if opts[:facet]
112
+ @filters[name] = filter
113
+ end
114
+
115
+ # shortcut for filter name, { exact: false, ignore_case: true, strip_blank: true }.merge(options)
116
+ def search name, opts = {}
117
+ filter name, { exact: false, ignore_case: true, strip_blank: true }.merge(opts)
118
+ end
119
+
120
+ def facets
121
+ @_facets_ ||= @facets.values
122
+ end
123
+
124
+ def facet name
125
+ @facets[name]
126
+ end
127
+
128
+ private
129
+
130
+ def range_filter name, opts
131
+ column = opts.delete(:column){name}
132
+
133
+ filter opts.delete(:from){:"from_#{name}"}, opts do |scope, value|
134
+ create_driver(scope).greater_or_equal(column, value).scope
135
+ end
136
+
137
+ filter opts.delete(:to){:"to_#{name}"}, opts do |scope, value|
138
+ create_driver(scope).less_or_equal(column, value).scope
139
+ end
140
+
141
+ filter name, :column => column
142
+ end
143
+
144
+ def scope_filter name, opts = {}
145
+ BlockFilter.new(self,name, opts){|scope| create_driver(scope).named_scope(name).scope}
146
+ end
147
+
148
+ end
149
+ end