gitter 1.1.3

Sign up to get free protection for your applications and to get access to all the features.
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