the_grid 1.1.3 → 1.1.4

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
- 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