the_grid 1.0.10 → 1.1.0

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.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- MTE1YjBhNmMwMmNiZTk4MjJiYTAzYmM4ZTc3Nzk3ZWM1NmRkOGUxNw==
4
+ Y2YyOTU4N2Y4MWQ1NDcxZTZmNmZiZTIxOTE4M2JkNGUwMTNjZDUyNA==
5
5
  data.tar.gz: !binary |-
6
- YzZkNGM1ZDMxOTZkZjg5YmMzMmExZDg1N2Q5YTEzYjNkOWJmOTNjYg==
6
+ ZDJkZThhYjEzYjBjYTc0YmM4NjU3NzkxZWM2NTgwMDFiZGM4ZWMzNQ==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- YWE1NDcwMTYwZDAzOWRiMTAwN2M2ZmIzYzBhOGVlZjYzZjI4ZDcyOTZjOGMy
10
- NDY1MjgyNGE4ZmU2ZDhkOTk4MTE3NTJhYWIxNzdjZTMyMzE2ZGJhYmIzZTBk
11
- ODk2OWUxNjFjNTMxMmViYTExMDI3MmE0ZTE2MzU2NWFiM2UzNzg=
9
+ NGFhZjQ2NTJjMzhkYTFmMzJkNGY4ZTA5NzQ0MWU5YzMwMGRiYzA4ODMwY2U0
10
+ YzAxZWM2ZDgwMDBiMTE3OTRkOGUxYTg5YzBhMTQ5MGE1MDg5NTM4YWUwMDk3
11
+ ODQwOTJlZWM1MmQ1NmJhYzViOWQ2OWFiZWFmMTI1NGFhMTNhYmI=
12
12
  data.tar.gz: !binary |-
13
- NDEyMWRjYjUxNmM5MzUwYzA1YWJmZDM1NDBkMzIzZDY4MzllOTlkZDJkZGM3
14
- NTc1MDU5MzI4YjdhMzM3ZjljYWRmZTMxZjVlMmQ1NGM1NDQ3NjM4MjBkYTQ0
15
- MWY5YzBlMTY1NjM0N2ZjNmY5NTU5YmY4NTg0YjlmNzhkNDYwMzg=
13
+ YjViYzJjMWZhZmMzMTY4YmE2YzVlOTQ3YzU3YTBiOWZiY2I3NDU0MDU4MTY0
14
+ ZDJlYjY4MzkyYmUwNzhhYmE2MjllMWZlODJhYWY1NTExNjFjMzE4YmE4MjNi
15
+ MDAyN2FjNTA1YTQ4NmUzZThjY2FmNjkwZTU2YzZmMWQzOWUwNTQ=
data/README.md CHANGED
@@ -1,13 +1,13 @@
1
1
  Yet Another Grid
2
2
  =========
3
3
 
4
- This plugin is designed to provide API for building json response based on `ActiveRecord::Relation` objects.
4
+ This plugin is designed to provide API for building response based on `ActiveRecord::Relation` objects (json, csv, even using custom view builder).
5
5
  It makes much easier to fetch information from database for displaying it using JavaScript MV* based frameworks such as Knockout, Backbone, Angular, etc.
6
6
 
7
7
  ## Getting started
8
8
 
9
9
  First of all specify grid in your Gemfile and run `bundle install`.
10
- After gem is installed you need to run `rails generate grid:install`. This will generate grid initializer file with basic configuration.
10
+ After gem is installed you need to run `rails generate the_grid:install`. This will generate grid initializer file with basic configuration.
11
11
 
12
12
  ## Usage
13
13
 
@@ -37,6 +37,8 @@ grid_for @articles, :per_page => 25 do
37
37
  end
38
38
  ```
39
39
 
40
+ The same grid defenition can be used with different formats.
41
+
40
42
  ## API
41
43
 
42
44
  The API is based on commands. Term *command* describes client's action which can be simple or complicated as well.
@@ -422,6 +424,38 @@ grid_for @groups, :per_page => 2 do
422
424
  end
423
425
  end
424
426
  ```
427
+ #### CSV builder
428
+
429
+ It's possible to generate csv using grid views.
430
+ The main power is that this view builder also responds to also the api request parameters except pagination.
431
+ So, it's possible to filter records for output csv. All what you need is just create a simple view:
432
+ ```ruby
433
+ # app/views/products/index.csv.grid_builder
434
+ grid_for @products do
435
+ column :title
436
+ column :qty
437
+ column :created_at
438
+ end
439
+ ```
440
+ And in your controller
441
+ ```ruby
442
+ # app/controllers/products_controller.rb
443
+ class ProductsController < ApplicationController
444
+ respond_to :csv, :html
445
+
446
+ def index
447
+ @products = Product.active
448
+ respond_with @products
449
+ end
450
+ end
451
+ ```
452
+
453
+ Then you can send requests like:
454
+ ```
455
+ http://your.domain.com/route.csv?cmd[]=filter&filters[is_active]=1
456
+ ```
457
+ and will get csv file which contains only filtered active products.
458
+
425
459
  ## License
426
460
 
427
461
  Released under the [MIT License](http://www.opensource.org/licenses/MIT)
@@ -6,5 +6,5 @@ TheGrid.configure do |config|
6
6
  config.default_max_per_page = 25
7
7
 
8
8
  # Print json response with new lines and tabs
9
- config.prettify_json = ActionView::Base.pretty_print_json
10
- end
9
+ config.prettify_json = Rails.env.development?
10
+ end
@@ -0,0 +1,45 @@
1
+ require 'csv'
2
+
3
+ module TheGrid
4
+ class Builder::Csv
5
+ attr_reader :api, :context
6
+
7
+ BATCH_SIZE = 1000
8
+
9
+ def initialize(relation, context)
10
+ @api = TheGrid::Api.new(relation)
11
+ @context = context
12
+ end
13
+
14
+ def assemble_with(params)
15
+ options = params.merge context.params
16
+ api.compose!(options.merge :per_page => BATCH_SIZE)
17
+ generate_csv_with(options)
18
+ end
19
+
20
+ private
21
+
22
+ def generate_csv_with(options)
23
+ CSV.generate do |csv|
24
+ csv << headers
25
+ put_relation_to(csv)
26
+ end
27
+ end
28
+
29
+ def put_relation_to(csv)
30
+ (1..api.options[:max_page]).each do |page|
31
+ relation = api.run_command!(:paginate, :page => page, :per_page => BATCH_SIZE)
32
+ put_records_to(csv, relation)
33
+ end
34
+ end
35
+
36
+ def put_records_to(csv, records)
37
+ context.assemble(records).each{ |row| csv << row.values }
38
+ end
39
+
40
+ def headers
41
+ context.options[:headers] || context.columns.keys.map { |col| col.to_s.titleize }
42
+ end
43
+
44
+ end
45
+ end
@@ -10,21 +10,26 @@ module TheGrid
10
10
  end
11
11
 
12
12
  %{
13
- ::TheGrid::Builder.assemble(:view_type => ::TheGrid::Builder::Json, :scope => self) {
13
+ ::TheGrid::Builder.assemble(:format => #{template.formats.first.inspect}, :scope => self) {
14
14
  #{source}
15
15
  }
16
16
  }
17
17
  end
18
18
 
19
+ def self.detect_view(format)
20
+ @@view_types ||= {}
21
+ @@view_types[format] ||= "the_grid/builder/#{format}".camelize.constantize
22
+ end
23
+
19
24
  def self.assemble(options, &block)
20
- new(options, &block)
25
+ new(options, &block).instance_eval(&block)
21
26
  end
22
27
 
23
28
  def initialize(options, &block)
24
- options.assert_valid_keys(:scope, :view_type)
29
+ options.assert_valid_keys(:scope, :format)
25
30
 
26
31
  @_scope = options.delete(:scope)
27
- @_view_type = options.delete(:view_type)
32
+ @_view_type = self.class.detect_view(options.delete(:format))
28
33
 
29
34
  copy_instance_variables_from(@_scope) if @_scope
30
35
  self.instance_eval(&block)
@@ -32,11 +37,7 @@ module TheGrid
32
37
 
33
38
  def grid_for(relation, options = {}, &block)
34
39
  context = Context.new(options.merge(:scope => @_scope), &block)
35
- @_view_handler = @_view_type.new(relation, context)
36
- end
37
-
38
- def assemble(&block)
39
- @_view_handler.assemble_with(@_scope.params, &block)
40
+ @_view_type.new(relation, context)
40
41
  end
41
42
 
42
43
  def method_missing(name, *args, &block)
@@ -47,14 +48,11 @@ module TheGrid
47
48
  end
48
49
  end
49
50
 
50
- def to_s; assemble;end
51
- def to_str; assemble;end
52
-
53
51
  private
54
52
 
55
53
  def copy_instance_variables_from(object)
56
54
  vars = object.instance_variables.map(&:to_s)
57
- vars.each { |name| instance_variable_set(name.to_sym, object.instance_variable_get(name)) }
55
+ vars.each{ |name| instance_variable_set(name.to_sym, object.instance_variable_get(name)) }
58
56
  end
59
57
 
60
58
  end
@@ -1,3 +1,3 @@
1
1
  module TheGrid
2
- VERSION = "1.0.10"
2
+ VERSION = "1.1.0"
3
3
  end
@@ -0,0 +1,47 @@
1
+ require 'spec_helper'
2
+ require_relative 'view_builder_helper'
3
+
4
+ describe TheGrid::Builder::Csv do
5
+ subject{ TheGrid::Builder::Csv.new(relation, build_context) }
6
+
7
+ include_examples "for Grid View Builder"
8
+ before(:each) { subject.api.stub(:compose!){ subject.api.options[:max_page] = 1 } }
9
+
10
+ let(:relation) { double.as_null_object }
11
+ let(:record) {{ :id => 1, :name => "Name", :status => "Active", :text => "Text" }}
12
+ let(:records) {[ record, record, record ]}
13
+ let(:params) {{ :cmd => [:sort], :field => :name, :order => :desc, :per_page => subject.class.const_get("BATCH_SIZE") }}
14
+
15
+ it "generates expected csv string" do
16
+ subject.assemble_with(params).should eql generate_csv(records, subject.context.options[:headers])
17
+ end
18
+
19
+ it "uses titleized column names if headers are not specified" do
20
+ subject.context.stub(:options => {})
21
+ headers = record.keys.map{|c| c.to_s.titleize }
22
+ subject.assemble_with(params).should eql generate_csv(records, headers)
23
+ end
24
+
25
+ it "generates csv records in batches" do
26
+ subject.api.should_receive(:run_command!).with(:paginate, :page => 1, :per_page => params[:per_page])
27
+ subject.assemble_with(params);
28
+ end
29
+
30
+
31
+ def generate_csv(records, headers)
32
+ CSV.generate do |csv|
33
+ csv << headers
34
+ records.each{ |item| csv << item.values }
35
+ end
36
+ end
37
+
38
+ def build_context
39
+ TheGrid::Builder::Context.new do
40
+ headers "Id", "Title", "Status", "Description"
41
+ column :name
42
+ column :status
43
+ column :text
44
+ end
45
+ end
46
+
47
+ end
@@ -1,21 +1,22 @@
1
1
  require 'spec_helper'
2
+ require_relative 'view_builder_helper'
2
3
 
3
4
  describe TheGrid::Builder::Json do
4
- subject { TheGrid::Builder::Json.new(relation, context) }
5
+ subject{ TheGrid::Builder::Json.new(Object.new, build_context) }
5
6
 
6
- before(:each) { subject.api.stub(:compose!) { subject.api.options[:max_page] = 25 } }
7
+ include_examples "for Grid View Builder"
8
+ before(:each) { subject.api.stub(:compose!){ subject.api.options[:max_page] = 25 } }
7
9
 
8
- let(:relation) { double('Relation').as_null_object }
9
- let(:context) { create_context.tap { |c| c.stub(:assemble => [1,2,3,4]) } }
10
- let(:params) {{ :cmd => [:sort], :field => :name, :order => :desc }}
10
+ let(:records) {[ 1, 2, 3, 4 ]}
11
+ let(:params) {{ :cmd => [:sort], :field => :name, :order => :desc }}
11
12
 
12
- let(:meta) {{ "meta" => {"api_key" => context.options[:api_key]}, "columns" => columns }}
13
- let(:columns) { context.visible_columns.stringify_keys.map{ |n, o| o.merge "name" => n } }
14
- let(:json_schema) {{ "max_page" => 25, "items" => context.assemble }}
13
+ let(:meta) {{ "meta" => {"api_key" => subject.context.options[:api_key]}, "columns" => columns }}
14
+ let(:columns) { subject.context.visible_columns.stringify_keys.map{ |n, o| o.merge "name" => n } }
15
+ let(:json_schema) {{ "max_page" => 25, "items" => subject.context.assemble }}
15
16
  let(:assembled_result) { JSON.parse(subject.assemble_with(params)) }
16
17
 
17
18
  it "merges params with context options" do
18
- subject.api.should_receive(:compose!).with(params.merge context.options)
19
+ subject.api.should_receive(:compose!).with(params.merge subject.context.options)
19
20
  subject.assemble_with params
20
21
  end
21
22
 
@@ -29,16 +30,13 @@ describe TheGrid::Builder::Json do
29
30
  end
30
31
 
31
32
  it "generates json notification when get wrong argument" do
32
- subject.api.stub(:compose!) { raise ArgumentError, "123" }
33
- assembled_result.should eql({"status" => "error", "message" => "123"})
33
+ subject.api.stub(:compose!) { raise ArgumentError, "my message" }
34
+ assembled_result.should eql "status" => "error", "message" => "my message"
34
35
  end
35
36
 
36
-
37
- def create_context
37
+ def build_context
38
38
  TheGrid::Builder::Context.new do
39
- api_key "hello_world"
40
- delegate :sort => :articles, :filter => :articles
41
-
39
+ api_key 1234567
42
40
  column :name
43
41
  column :is_active
44
42
  end
@@ -0,0 +1,10 @@
1
+ shared_examples "for Grid View Builder" do
2
+ before(:each) { subject.context.stub(:assemble => records) }
3
+
4
+ it { should respond_to(:assemble_with) }
5
+
6
+ it "merges context with params" do
7
+ subject.api.should_receive(:compose!).with(params.merge subject.context.options)
8
+ subject.assemble_with(params)
9
+ end
10
+ end
data/the_grid.gemspec CHANGED
@@ -15,7 +15,7 @@ Gem::Specification.new do |s|
15
15
  s.description = <<-EOF
16
16
  Provides json API for building ActiveRecord::Relation's. It makes much easier to fetch information from database for displaying it using JavaScript MV* based frameworks such as Knockout, Backbone, Angular, etc.
17
17
 
18
- Tags: json, grid, api, grid builder, activerecord relation builder, relation
18
+ Tags: json, csv, grid, api, grid builder, activerecord relation builder, relation
19
19
  EOF
20
20
 
21
21
  s.post_install_message = <<-_MSG_
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: the_grid
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.0.10
4
+ version: 1.1.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Sergiy Stotskiy
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2013-04-17 00:00:00.000000000 Z
12
+ date: 2013-04-18 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord
@@ -72,7 +72,7 @@ description: ! 'Provides json API for building ActiveRecord::Relation''s. It ma
72
72
  MV* based frameworks such as Knockout, Backbone, Angular, etc.
73
73
 
74
74
 
75
- Tags: json, grid, api, grid builder, activerecord relation builder, relation
75
+ Tags: json, csv, grid, api, grid builder, activerecord relation builder, relation
76
76
 
77
77
  '
78
78
  email: sergiy.stotskiy@gmail.com
@@ -97,6 +97,7 @@ files:
97
97
  - lib/the_grid/api/command/sort.rb
98
98
  - lib/the_grid/builder.rb
99
99
  - lib/the_grid/builder/context.rb
100
+ - lib/the_grid/builder/csv.rb
100
101
  - lib/the_grid/builder/json.rb
101
102
  - lib/the_grid/config.rb
102
103
  - lib/the_grid/version.rb
@@ -109,7 +110,9 @@ files:
109
110
  - spec/api/command_spec.rb
110
111
  - spec/api_spec.rb
111
112
  - spec/builder/context_spec.rb
113
+ - spec/builder/csv_spec.rb
112
114
  - spec/builder/json_spec.rb
115
+ - spec/builder/view_builder_helper.rb
113
116
  - spec/config_spec.rb
114
117
  - spec/spec_helper.rb
115
118
  - the_grid.gemspec