the_grid 1.1.3 → 1.1.4

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NGE5YjY3ZGZiZjJjOWUxMjBlMDlkMGNmYzRlMGQ0ODEzMTYyODc3Yg==
4
+ YTM1YzE1MmUxNzZjZTZlNjIyYjNkNjhhYWNiNmY4YWQ2ZGU4YjAzMQ==
5
5
  data.tar.gz: !binary |-
6
- ZWVmMDM4YTAxOGM3N2Y1NmI2ZmI5YTY0ZTcxMDZhMmQ4MTc0NmRiMQ==
6
+ NTkyN2FmNTZlMWUxZWM0NDI4YmVkNTYzMjMyODdkYWE5MGVlOWE1Yg==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- YjEwYWU3Y2E5MGE5YmYwYWZmYmE5MmMyY2YwNjVhNjYxOTBiYWE2NmYwZDk4
10
- NjY3MzY5Y2Y4YmU4M2UxNjRhMmNjNDE5NzVlMDNlOTU0YzdlMTgxOWU4Y2Ni
11
- Njg4YjUwNjA2NThmNjgwNDYxNjMyOWY5MjI4NmU5Y2Q5MDI3ZGI=
9
+ NDkyYzc4ZDQ4NjM3YWU3OTNjZDQ2YzQzZTNkYzU1NWJjY2IyYTYzMjQ2ZmEy
10
+ YjI3YmIyYmM5MzRjZTJkNjE5NDU0YWM1YzFhZTQ5NjljNjdkMGU3NmFlNTlk
11
+ ZDQyMmEzMjk2ZTkxN2QwM2YxMDIwYmEwM2Q3NzVlNTZhNTEzY2E=
12
12
  data.tar.gz: !binary |-
13
- YTI3NzIwMTc2MmJiNzY5YTQzZmVjZjU0ODVjYmM0NGQ5N2NmNGEwNDljZWYw
14
- MGU4NzhhZmQ5NjAwZjIyMzc4YWY4MDMxZjUyMDllMGQ2MGE1NmMzZDIxYzQx
15
- ZTNjMGY3YzQ2Y2I0Y2FjYzM2MWFkNDU1OGIxMmRkNGI0OWE5MGQ=
13
+ ZTM1YzgyZTYwZjI4MDU2MjUwODRhMDFlNDM0ZGIxNGU3YzI1YjdiZWE0OTE5
14
+ Nzk1MTc2MGViZDEzN2VhNTFlYjg3NGU1MDg5N2E3MTFhODRkMjc0MTQzODJl
15
+ ODA0ZDMwZDQwMWI4Nzc4NGE4ZGFjNmYwMWI4ODdiODE2MDJlMzA=
@@ -1,6 +1,14 @@
1
1
  module TheGrid
2
2
  class Api::Command
3
3
 
4
+ autoload :BatchRemove, 'the_grid/api/command/batch_remove'
5
+ autoload :BatchUpdate, 'the_grid/api/command/batch_update'
6
+ autoload :Filter, 'the_grid/api/command/filter'
7
+ autoload :Paginate, 'the_grid/api/command/paginate'
8
+ autoload :Search, 'the_grid/api/command/search'
9
+ autoload :Sort, 'the_grid/api/command/sort'
10
+
11
+
4
12
  def self.find(cmd)
5
13
  @@commands ||= {}
6
14
  @@commands[cmd] ||= build(cmd)
data/lib/the_grid/api.rb CHANGED
@@ -36,7 +36,7 @@ module TheGrid
36
36
  end
37
37
 
38
38
  def command_delegated?(cmd)
39
- options[:delegated_commands].has_key?(cmd.to_s)
39
+ options[:delegated_commands] && options[:delegated_commands].has_key?(cmd.to_s)
40
40
  end
41
41
 
42
42
  def configure(params)
@@ -48,6 +48,12 @@ module TheGrid
48
48
  end
49
49
  end
50
50
 
51
+ def column_titles
52
+ self.options[:titles] ||= columns.flat_map do |name, options|
53
+ options[:as].kind_of?(self.class) ? options[:as].column_titles : options.fetch(:title, name.to_s.titleize)
54
+ end
55
+ end
56
+
51
57
  def assemble(records)
52
58
  records.map{ |record| assemble_row_for(record) }
53
59
  end
@@ -1,49 +1,49 @@
1
1
  require 'csv'
2
2
 
3
3
  module TheGrid
4
- class Builder::Csv
5
- attr_reader :api, :context
6
-
4
+ module Builder::Csv
5
+ extend self, Builder::Base
7
6
  BATCH_SIZE = 1000
8
7
 
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) if api.relation.respond_to?(:connection)
17
- generate_csv_with(options)
18
- end
19
-
20
- private
21
-
22
- def generate_csv_with(options)
8
+ def build(context, options)
9
+ records, params = options.values_at(:for, :with)
10
+ options.merge! :per_page => BATCH_SIZE if records.respond_to?(:connection)
11
+ api = compose(records, options)
23
12
  CSV.generate do |csv|
24
- csv << headers
25
- api.relation.kind_of?(Array) ? put_data_to(csv) : put_relation_to(csv)
13
+ csv << context.column_titles
14
+ api.relation.respond_to?(:connection) ? put(context, :to => csv, :with => api) : put_this(context, :to => csv, :with => records)
26
15
  end
27
16
  end
28
17
 
29
- def put_data_to(csv)
30
- put_records_to(csv, api.relation)
31
- end
18
+ private
32
19
 
33
- def put_relation_to(csv)
20
+ def put(context, options)
21
+ api, csv = options.values_at(:with, :to)
34
22
  pages = api.options[:max_page]
35
23
  (1..pages).each do |page|
36
24
  relation = api.run_command!(:paginate, :page => page, :per_page => BATCH_SIZE, :size => pages * BATCH_SIZE)
37
- put_records_to(csv, relation)
25
+ put_this(context, :to => csv, :with => relation)
38
26
  end
39
27
  end
40
28
 
41
- def put_records_to(csv, records)
42
- context.assemble(records).each{ |row| csv << row.values }
29
+ def put_this(context, options)
30
+ context.assemble(options[:with]).each do |row|
31
+ flatten(row).each{ |row| options[:to] << row }
32
+ end
43
33
  end
44
34
 
45
- def headers
46
- context.options[:titles] || context.columns.keys.map { |col| col.to_s.titleize }
35
+ def flatten(row)
36
+ # TODO: optimize it
37
+ row_values, row_nested_values = [], []
38
+ row.each do |field, value|
39
+ if value.kind_of?(Array)
40
+ row_nested_values += value.flat_map{ |r| flatten(r) }
41
+ else
42
+ row_values << value
43
+ end
44
+ end
45
+ return [ row_values ] if row_nested_values.empty?
46
+ row_nested_values.each{ |nested_values| nested_values.unshift(*row_values) }
47
47
  end
48
48
 
49
49
  end
@@ -1,38 +1,22 @@
1
1
  module TheGrid
2
- class Builder::Json
3
- cattr_accessor :prettify_json
4
- attr_reader :api, :context
2
+ module Builder::Json
3
+ extend self, Builder::Base
4
+ mattr_accessor :prettify_json
5
5
 
6
- def initialize(relation, context)
7
- @api = TheGrid::Api.new(relation)
8
- @context = context
9
- end
10
-
11
- def assemble_with(params)
12
- options = params.merge context.params
13
- api.compose!(options)
14
- stringify as_json_with(options)
15
- rescue ArgumentError => error
16
- stringify as_json_message('error', error.message)
17
- end
18
-
19
- private
20
-
21
- def stringify(json_hash)
22
- self.class.prettify_json ? JSON.pretty_generate(json_hash) : json_hash.to_json
23
- end
24
-
25
- def as_json_with(options)
6
+ def build(context, params)
7
+ api, options = compose(params[:for], params[:with]), params[:with]
26
8
  json = {:max_page => api.options[:max_page], :items => context.assemble(api.relation)}
27
9
  if options[:with_meta]
28
10
  json[:meta] = context.options.except(:delegate, :search_over)
29
11
  json[:columns] = columns_as_array(context.visible_columns)
30
12
  end
31
- json
13
+ stringify json
32
14
  end
33
15
 
34
- def as_json_message(status, message)
35
- {:status => status, :message => message}
16
+ private
17
+
18
+ def stringify(json_hash)
19
+ self.prettify_json ? JSON.pretty_generate(json_hash) : json_hash.to_json
36
20
  end
37
21
 
38
22
  def columns_as_array(columns)
@@ -2,6 +2,10 @@ module TheGrid
2
2
  class Builder
3
3
  private_class_method :new
4
4
 
5
+ autoload :Context, 'the_grid/builder/context'
6
+ autoload :Csv, 'the_grid/builder/csv'
7
+ autoload :Json, 'the_grid/builder/json'
8
+
5
9
  def self.call(template)
6
10
  source = if template.source.empty?
7
11
  File.read(template.identifier)
@@ -37,7 +41,7 @@ module TheGrid
37
41
 
38
42
  def grid_for(relation, options = {}, &block)
39
43
  context = Context.new(options.merge(:scope => @_scope), &block)
40
- @_view_type.new(relation, context).assemble_with(@_scope.params)
44
+ @_view_type.assemble(context, :on => relation, :with => @_scope.params)
41
45
  end
42
46
 
43
47
  def method_missing(name, *args, &block)
@@ -55,5 +59,32 @@ module TheGrid
55
59
  vars.each{ |name| instance_variable_set(name.to_sym, object.instance_variable_get(name)) }
56
60
  end
57
61
 
62
+
63
+ module Base
64
+
65
+ def compose(records, params)
66
+ api = ::TheGrid::Api.new(records)
67
+ api.compose!(params)
68
+ api
69
+ end
70
+
71
+ def assemble(context, options)
72
+ options.assert_valid_keys(:on, :with)
73
+ params = options[:with].merge context.params
74
+ build(context, :for => options[:on], :with => params)
75
+ rescue ArgumentError => error
76
+ stringify :status => 'error', :message => error.message
77
+ end
78
+
79
+ def stringify(data)
80
+ data.inspect
81
+ end
82
+
83
+ def build
84
+ raise NotImplementedError
85
+ end
86
+
87
+ end
88
+
58
89
  end
59
90
  end
@@ -1,3 +1,3 @@
1
1
  module TheGrid
2
- VERSION = "1.1.3"
2
+ VERSION = "1.1.4"
3
3
  end
data/lib/the_grid.rb CHANGED
@@ -3,9 +3,6 @@ require 'the_grid/api'
3
3
  require 'the_grid/api/command'
4
4
  require 'the_grid/builder'
5
5
  require 'the_grid/config'
6
- Dir.chdir(File.dirname(__FILE__)) do
7
- Dir['the_grid/builder/**/*.rb', 'the_grid/api/command/**/*.rb'].each{ |f| require f }
8
- end
9
6
 
10
7
  module TheGrid
11
8
  def self.configure
@@ -88,6 +88,11 @@ describe TheGrid::Builder::Context do
88
88
  context = build_context{ scope_for(:articles, :as => :children, &dsl) }
89
89
  context.columns[:children][:as].should be_kind_of subject
90
90
  end
91
+
92
+ it "returns titles for columns from attributes" do
93
+ context = build_context{ column(:id, :title => "Name") }
94
+ context.column_titles.should eql %w{Name}
95
+ end
91
96
  end
92
97
 
93
98
  context "when collects visible columns" do
@@ -2,30 +2,32 @@ require 'spec_helper'
2
2
  require_relative 'view_builder_helper'
3
3
 
4
4
  describe TheGrid::Builder::Csv do
5
- subject{ TheGrid::Builder::Csv.new(relation, build_context) }
5
+ subject{ TheGrid::Builder::Csv }
6
6
 
7
7
  include_examples "for Grid View Builder"
8
- before(:each) { subject.api.stub(:compose!) }
9
- before(:each) { subject.api.options[:max_page] = 1 }
10
8
 
11
- let(:relation) { double.as_null_object }
12
- let(:record) {{ :id => 1, :name => "Name", :status => "Active", :text => "Text" }}
13
- let(:records) {[ record, record, record ]}
14
- let(:params) {{ :cmd => [:sort], :field => :name, :order => :desc, :per_page => subject.class.const_get("BATCH_SIZE") }}
9
+ let(:api_options) {{ :max_page => 1 }}
10
+ let(:context) { build_context }
11
+ let(:relation) { double(:connection => double.as_null_object).as_null_object }
12
+ let(:params) {{ :cmd => [:sort], :field => :name, :order => :desc, :per_page => subject.const_get("BATCH_SIZE") }}
15
13
 
16
14
  it "generates expected csv string" do
17
- subject.assemble_with(params).should eql generate_csv(records, subject.context.options[:titles])
15
+ subject.assemble(context, :on => relation, :with => params).should eql generate_csv(records, context.column_titles)
18
16
  end
19
17
 
20
18
  it "uses titleized column names if titles are not specified" do
21
- subject.context.stub(:options => {})
22
- titles = record.keys.map{|c| c.to_s.titleize }
23
- subject.assemble_with(params).should eql generate_csv(records, titles)
19
+ context.stub(:options => {})
20
+ subject.assemble(context, :on => relation, :with => params).should eql generate_csv(records, context.column_titles)
24
21
  end
25
22
 
26
23
  it "generates csv records in batches" do
27
- subject.api.should_receive(:run_command!).with(:paginate, :page => 1, :per_page => params[:per_page], :size => subject.api.options[:max_page] * params[:per_page])
28
- subject.assemble_with(params);
24
+ TheGrid::Api.any_instance.should_receive(:run_command!).with(:paginate, :page => 1, :per_page => params[:per_page], :size => api_options[:max_page] * params[:per_page])
25
+ subject.assemble(context, :on => relation, :with => params)
26
+ end
27
+
28
+ it "generates csv with nested data" do
29
+ context.stub(:assemble => [ record.merge(:children => [ child_record(1), child_record(2) ]) ])
30
+ subject.assemble(context, :on => relation, :with => params).should eql generate_csv(2.times.map{ |i| record.merge child_record(i + 1) }, context.column_titles)
29
31
  end
30
32
 
31
33
 
@@ -42,6 +44,10 @@ describe TheGrid::Builder::Csv do
42
44
  column :name
43
45
  column :status
44
46
  column :text
47
+
48
+ scope_for :children do
49
+ column :name
50
+ end
45
51
  end
46
52
  end
47
53
 
@@ -2,22 +2,23 @@ require 'spec_helper'
2
2
  require_relative 'view_builder_helper'
3
3
 
4
4
  describe TheGrid::Builder::Json do
5
- subject{ TheGrid::Builder::Json.new(Object.new, build_context) }
5
+ subject{ TheGrid::Builder::Json }
6
6
 
7
7
  include_examples "for Grid View Builder"
8
- before(:each) { subject.api.stub(:compose!){ subject.api.options[:max_page] = 25 } }
9
8
 
9
+ let(:context) { build_context }
10
10
  let(:records) {[ 1, 2, 3, 4 ]}
11
11
  let(:params) {{ :cmd => [:sort], :field => :name, :order => :desc }}
12
12
 
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 }}
16
- let(:assembled_result) { JSON.parse(subject.assemble_with(params)) }
13
+ let(:meta) {{ "meta" => {"api_key" => context.options[:api_key]}, "columns" => columns }}
14
+ let(:columns) { context.visible_columns.stringify_keys.map{ |n, o| o.merge "name" => n } }
15
+ let(:json_schema) {{ "max_page" => 25, "items" => context.assemble(records) }}
16
+ let(:assembled_result) { JSON.parse(subject.assemble(context, :on => Object.new, :with => params)) }
17
+
17
18
 
18
19
  it "merges params with context options" do
19
- subject.api.should_receive(:compose!).with(params.merge subject.context.options)
20
- subject.assemble_with params
20
+ TheGrid::Api.any_instance.should_receive(:compose!).with(params.merge context.options)
21
+ subject.assemble(context, :on => Object.new, :with => params)
21
22
  end
22
23
 
23
24
  it "generates json with meta information" do
@@ -30,7 +31,7 @@ describe TheGrid::Builder::Json do
30
31
  end
31
32
 
32
33
  it "generates json notification when get wrong argument" do
33
- subject.api.stub(:compose!) { raise ArgumentError, "my message" }
34
+ TheGrid::Api.any_instance.stub(:compose!) { raise ArgumentError, "my message" }
34
35
  assembled_result.should eql "status" => "error", "message" => "my message"
35
36
  end
36
37
 
@@ -1,10 +1,18 @@
1
1
  shared_examples "for Grid View Builder" do
2
- before(:each) { subject.context.stub(:assemble => records) }
2
+ before(:each) { context.stub(:assemble => records) }
3
+ before(:each) { TheGrid::Api.any_instance.stub(:compose!) }
4
+ before(:each) { TheGrid::Api.any_instance.stub(:options => api_options) }
3
5
 
4
- it { should respond_to(:assemble_with) }
6
+ let(:records) {[ record, record, record ]}
7
+ let(:api_options){{ :max_page => 25 }}
5
8
 
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
+ it { should respond_to(:assemble) }
10
+
11
+ def child_record(id)
12
+ { :child_id => id, :child_name => "Child Name #{id}" }
13
+ end
14
+
15
+ def record(id = nil)
16
+ { :id => id || 1, :name => "Name #{id}", :status => "Active", :text => "Text" }
9
17
  end
10
18
  end
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.1.3
4
+ version: 1.1.4
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-05-27 00:00:00.000000000 Z
12
+ date: 2013-07-15 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: activerecord