the_grid 1.0.7

Sign up to get free protection for your applications and to get access to all the features.
data/spec/api_spec.rb ADDED
@@ -0,0 +1,58 @@
1
+ require "spec_helper"
2
+
3
+ describe TheGrid::Api do
4
+ subject{ TheGrid::Api.new(relation) }
5
+
6
+ let(:child_relation) { double("Child Relation").as_null_object }
7
+ let(:relation) { double("Relation", :reflections => { :child_relation => child_relation }).as_null_object }
8
+ let(:commands) { lambda{ |name| ::TheGrid::Api::Command.find(name) } }
9
+
10
+ context "when run single command" do
11
+ let(:options) {{ :field => "title", :order => "desc" }}
12
+ let(:cmd_name) { :sort }
13
+
14
+ after(:each) { subject.run_command!(cmd_name, options) }
15
+
16
+ it "run specified command" do
17
+ commands[cmd_name].should_receive(:execute_on).with(subject.relation, options)
18
+ end
19
+
20
+ it "should be prepared by command instance" do
21
+ commands[cmd_name].should_receive(:contextualize).with(subject.relation, options).and_return({})
22
+ end
23
+
24
+ it "run delegated command on specified target" do
25
+ subject.delegate(cmd_name => :child_relation)
26
+ commands[cmd_name].should_receive(:execute_on).with(child_relation, options)
27
+ end
28
+ end
29
+
30
+ context "when run few commands" do
31
+ let(:params) {{ :cmd => [:sort, :search, :batch_update], :field => "title", :query => "test" }}
32
+ before(:each) { subject.stub(:run_command!) }
33
+ after(:each) { subject.compose!(params) }
34
+
35
+ it "skip batch commands" do
36
+ commands[:batch_update].should_not_receive(:execute_on)
37
+ end
38
+
39
+ it "run specified commands" do
40
+ subject.should_receive(:run_command!).with(:sort, params)
41
+ subject.should_receive(:run_command!).with(:search, params)
42
+ end
43
+
44
+ it "adds paginate command by default" do
45
+ subject.should_receive(:run_command!).with(:paginate, params)
46
+ end
47
+
48
+ it "doesn't add paginate command if per_page equals false" do
49
+ params[:per_page] = false
50
+ subject.should_not_receive(:run_command!).with(:paginate, params)
51
+ end
52
+
53
+ it "delegates specified commands" do
54
+ params[:delegate] = {:sort => child_relation}
55
+ subject.should_receive(:delegate).with(params[:delegate])
56
+ end
57
+ end
58
+ end
@@ -0,0 +1,182 @@
1
+ require 'spec_helper'
2
+
3
+ describe TheGrid::Builder::Context do
4
+ subject{ TheGrid::Builder::Context }
5
+
6
+ let(:parent_scope) { double(:dsl => Proc.new{ column :title }) }
7
+ let(:options) {{ :per_page => 25, :scope => parent_scope }}
8
+
9
+ context "by default" do
10
+ it "has hidden id column" do
11
+ build_context.columns[:id].should have_key(:hidden)
12
+ end
13
+
14
+ it "stores specified options" do
15
+ build_context.options[:per_page].should eql options[:per_page]
16
+ end
17
+
18
+ it "requires block" do
19
+ expect{ subject.new(options) }.to raise_error ArgumentError
20
+ end
21
+
22
+ it "respects parent context" do
23
+ parent_scope.should_receive(:article_path)
24
+ build_context{ url article_path }
25
+ end
26
+
27
+ it "responds to assemble" do
28
+ build_context.should respond_to(:assemble).with(1).argument
29
+ end
30
+ end
31
+
32
+ context "DSL" do
33
+ it "defines columns" do
34
+ build_context{ column :name; column :url }.columns.keys.should include(:name, :url)
35
+ end
36
+
37
+ it "defines column specific options" do
38
+ columns = build_context{ column :name, :test => true, :value => 5 }.columns
39
+ columns[:name].values_at(:test, :value).should eql [true, 5]
40
+ end
41
+
42
+ it "marks columns as featurable" do
43
+ columns = build_context{ searchable_columns :name, :text }.columns
44
+ columns.slice(:name, :text).should be_all{ |k, v| v[:searchable] == true }
45
+ end
46
+
47
+ it "stores featureable columns in options" do
48
+ build_context{ searchable_columns :name, :text }.options[:searchable_columns].should eql [:name, :text]
49
+ end
50
+
51
+ it "accepts single option's values" do
52
+ options = build_context{ title "test"; email "test@example.com" }.options
53
+ options.values_at(:title, :email).should eql %w{ test test@example.com }
54
+ end
55
+
56
+ it "accepts multiple option's values" do
57
+ build_context{ my :name, :age, :pan }.options[:my].should eql [:name, :age, :pan]
58
+ end
59
+
60
+ it "accepts block as column generator" do
61
+ build_context{ column(:name){ "test" } }.columns[:name][:as].should respond_to(:call)
62
+ end
63
+
64
+ it "accepts method_name as column generator" do
65
+ build_context{ column :name, :as => :title}.columns[:name][:as].should eql :title
66
+ end
67
+
68
+ it "defines nested scope" do
69
+ context = build_context{ scope_for(:articles, &dsl) }
70
+ context.columns[:articles][:as].should be_kind_of subject
71
+ end
72
+
73
+ it "defines nested scope with specified name" do
74
+ context = build_context{ scope_for(:articles, :as => :children, &dsl) }
75
+ context.columns[:children][:as].should be_kind_of subject
76
+ end
77
+ end
78
+
79
+ context "when collects visible columns" do
80
+ let(:parent_scope){ double(:dsl => Proc.new{ column :title; column :name, :hidden => true }) }
81
+
82
+ it "returns only visible columns" do
83
+ build_context(&parent_scope.dsl).visible_columns.keys.should eql [:title]
84
+ end
85
+
86
+ it "respects visible columns of nested scopes" do
87
+ build_context{ scope_for(:children, &dsl) }.visible_columns[:children].keys.should eql [:title]
88
+ end
89
+ end
90
+
91
+ context "when assembles" do
92
+ let(:fields) {{ :title => "item", :id => 5, :live? => false, :short_details => "details" }}
93
+ let(:records) {[ double(fields), double(fields.merge :live? => true) ]}
94
+
95
+ context "records" do
96
+ subject { context.assemble(records) }
97
+ let(:context) { build_plane_context }
98
+
99
+ it { should be_kind_of Array }
100
+ it { should have(records.size).items }
101
+ it { should be_all{ |r| r.kind_of? Hash } }
102
+ it { should be_all{ |r| r.keys == context.columns.keys }}
103
+ end
104
+
105
+ context "any structure" do
106
+ subject{ build_plane_context }
107
+ after(:each){ subject.assemble(records) }
108
+
109
+ it "builds records by name" do
110
+ records.each{ |r| r.should_receive(:title) }
111
+ end
112
+
113
+ it "builds records by alias" do
114
+ records.each{ |r| r.should_receive(:short_details) }
115
+ end
116
+
117
+ it "builds records by given block" do
118
+ records.each{ |r| r.should_receive(:live?) }
119
+ end
120
+ end
121
+
122
+ context "tree-like structure" do
123
+ let(:conditional_option) { :if }
124
+ let(:fields) {{ :id => 5, :live? => false, :permanent => [], :conditional => [], :conditional_block => [] }}
125
+ after(:each) { build_tree_like_context(conditional_option).assemble(records) }
126
+
127
+ it "creates nested scope for each record" do
128
+ records.each{ |r| r.should_receive(:permanent) }
129
+ end
130
+
131
+ context "when specified :if option" do
132
+ it "creates nested scope if record column is true" do
133
+ records.first.should_not_receive(:conditional)
134
+ records.last.should_receive(:conditional)
135
+ end
136
+
137
+ it "creates nested scope if block returns true" do
138
+ records.first.should_not_receive(:conditional_block)
139
+ records.last.should_receive(:conditional_block)
140
+ end
141
+ end
142
+
143
+ context "when specified :unless option" do
144
+ let(:conditional_option) { :unless }
145
+
146
+ it "creates nested :unless block returns true" do
147
+ records.first.should_receive(:conditional)
148
+ records.last.should_not_receive(:conditional)
149
+ end
150
+
151
+ it "creates nested :unless block returns true" do
152
+ records.first.should_receive(:conditional_block)
153
+ records.last.should_not_receive(:conditional_block)
154
+ end
155
+ end
156
+ end
157
+
158
+ def build_plane_context
159
+ build_context do
160
+ column :title
161
+ column :details, :as => :short_details
162
+ column(:is_active){ |r| r.live? }
163
+ end
164
+ end
165
+
166
+ def build_tree_like_context(conditional_option)
167
+ build_context do
168
+ column :is_active, :as => :live?
169
+ scope_for(:permanent, &dsl)
170
+ scope_for(:conditional, conditional_option => :is_active, &dsl)
171
+ scope_for(:conditional_block, conditional_option => proc{ |r| r.live? }, &dsl)
172
+ end
173
+ end
174
+
175
+ end
176
+
177
+ def build_context(&dsl)
178
+ dsl = Proc.new{} unless block_given?
179
+ TheGrid::Builder::Context.new(options, &dsl)
180
+ end
181
+
182
+ end
@@ -0,0 +1,46 @@
1
+ require 'spec_helper'
2
+
3
+ describe TheGrid::Builder::Json do
4
+ subject { TheGrid::Builder::Json.new(relation, context) }
5
+
6
+ before(:each) { subject.api.stub(:compose!) { subject.api.options[:max_page] = 25 } }
7
+
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 }}
11
+
12
+ let(:meta) {{ "meta" => {"api_key" => context.options[:api_key]}, "columns" => context.visible_columns.stringify_keys }}
13
+ let(:json_schema) {{ "max_page" => 25, "items" => context.assemble }}
14
+ let(:assembled_result) { JSON.parse(subject.assemble_with(params)) }
15
+
16
+ it "merges params with context options" do
17
+ subject.api.should_receive(:compose!).with(params.merge context.options)
18
+ subject.assemble_with params
19
+ end
20
+
21
+ it "generates json with meta information" do
22
+ params[:with_meta] = true
23
+ assembled_result.should eql json_schema.merge(meta)
24
+ end
25
+
26
+ it "generates json without meta" do
27
+ assembled_result.should eql json_schema
28
+ end
29
+
30
+ it "generates json notification when get wrong argument" do
31
+ subject.api.stub(:compose!) { raise ArgumentError, "123" }
32
+ assembled_result.should eql({"status" => "error", "message" => "123"})
33
+ end
34
+
35
+
36
+ def create_context
37
+ TheGrid::Builder::Context.new do
38
+ api_key "hello_world"
39
+ delegate :sort => :articles, :filter => :articles
40
+
41
+ column :name
42
+ column :is_active
43
+ end
44
+ end
45
+
46
+ end
@@ -0,0 +1,35 @@
1
+ require 'spec_helper'
2
+
3
+ describe TheGrid::Config do
4
+ context "when initializes" do
5
+ its(:commands_lookup_scopes) { should eql [] }
6
+ its(:prettify_json) { should be_false }
7
+ end
8
+
9
+ context "when applying specified values" do
10
+ let(:command) { TheGrid::Api::Command }
11
+ let(:builder) { TheGrid::Builder::Json }
12
+ before(:each) { configure(subject) }
13
+
14
+ it "registers specified lookup scopes from configuration" do
15
+ command.scopes.should include(*subject.commands_lookup_scopes)
16
+ end
17
+
18
+ it "sets up default_per_page for Paginate command" do
19
+ command.find(:paginate).default_per_page.should eql subject.default_max_per_page
20
+ end
21
+
22
+ it "sets up prettify_json option for json builder" do
23
+ builder.prettify_json.should eql subject.prettify_json
24
+ end
25
+ end
26
+
27
+ private
28
+
29
+ def configure(config)
30
+ config.commands_lookup_scopes += %w{ command_scope_1 command_scope_2 }
31
+ config.default_max_per_page = 10
32
+ config.prettify_json = true
33
+ config.apply
34
+ end
35
+ end
@@ -0,0 +1,2 @@
1
+ require 'active_support/all'
2
+ require 'the_grid'
data/the_grid.gemspec ADDED
@@ -0,0 +1,34 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "the_grid/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "the_grid"
7
+ s.version = TheGrid::VERSION
8
+ s.authors = ["Sergiy Stotskiy", "Yuriy Buchchenko"]
9
+ s.email = "sergiy.stotskiy@gmail.com"
10
+ s.homepage = "http://github.com/stalniy/grid"
11
+ s.license = "MIT"
12
+ s.summary = %q{Yet another grid api.}
13
+ s.platform = Gem::Platform::RUBY
14
+
15
+ s.description = <<-EOF
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
+
18
+ Tags: json, grid, api, grid builder, activerecord relation builder, relation
19
+ EOF
20
+
21
+ s.rubyforge_project = "the_grid"
22
+
23
+ s.files = `git ls-files`.split("\n")
24
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
25
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
26
+ s.require_paths = ["lib"]
27
+
28
+ s.add_dependency 'activerecord', '>= 3.0'
29
+ s.add_dependency 'json'
30
+
31
+ # specify any dependencies here; for example:
32
+ s.add_development_dependency "bundler", ">= 1.0.0"
33
+ s.add_development_dependency "rspec", "~> 2.13"
34
+ end
metadata ADDED
@@ -0,0 +1,140 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: the_grid
3
+ version: !ruby/object:Gem::Version
4
+ version: 1.0.7
5
+ platform: ruby
6
+ authors:
7
+ - Sergiy Stotskiy
8
+ - Yuriy Buchchenko
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-08 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: activerecord
16
+ requirement: !ruby/object:Gem::Requirement
17
+ requirements:
18
+ - - ! '>='
19
+ - !ruby/object:Gem::Version
20
+ version: '3.0'
21
+ type: :runtime
22
+ prerelease: false
23
+ version_requirements: !ruby/object:Gem::Requirement
24
+ requirements:
25
+ - - ! '>='
26
+ - !ruby/object:Gem::Version
27
+ version: '3.0'
28
+ - !ruby/object:Gem::Dependency
29
+ name: json
30
+ requirement: !ruby/object:Gem::Requirement
31
+ requirements:
32
+ - - ! '>='
33
+ - !ruby/object:Gem::Version
34
+ version: '0'
35
+ type: :runtime
36
+ prerelease: false
37
+ version_requirements: !ruby/object:Gem::Requirement
38
+ requirements:
39
+ - - ! '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ - !ruby/object:Gem::Dependency
43
+ name: bundler
44
+ requirement: !ruby/object:Gem::Requirement
45
+ requirements:
46
+ - - ! '>='
47
+ - !ruby/object:Gem::Version
48
+ version: 1.0.0
49
+ type: :development
50
+ prerelease: false
51
+ version_requirements: !ruby/object:Gem::Requirement
52
+ requirements:
53
+ - - ! '>='
54
+ - !ruby/object:Gem::Version
55
+ version: 1.0.0
56
+ - !ruby/object:Gem::Dependency
57
+ name: rspec
58
+ requirement: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ~>
61
+ - !ruby/object:Gem::Version
62
+ version: '2.13'
63
+ type: :development
64
+ prerelease: false
65
+ version_requirements: !ruby/object:Gem::Requirement
66
+ requirements:
67
+ - - ~>
68
+ - !ruby/object:Gem::Version
69
+ version: '2.13'
70
+ description: ! 'Provides json API for building ActiveRecord::Relation''s. It makes
71
+ much easier to fetch information from database for displaying it using JavaScript
72
+ MV* based frameworks such as Knockout, Backbone, Angular, etc.
73
+
74
+
75
+ Tags: json, grid, api, grid builder, activerecord relation builder, relation
76
+
77
+ '
78
+ email: sergiy.stotskiy@gmail.com
79
+ executables: []
80
+ extensions: []
81
+ extra_rdoc_files: []
82
+ files:
83
+ - .rvmrc
84
+ - Gemfile
85
+ - README.md
86
+ - Rakefile
87
+ - lib/generators/the_grid/install/install_generator.rb
88
+ - lib/generators/the_grid/install/templates/the_grid.rb
89
+ - lib/the_grid.rb
90
+ - lib/the_grid/api.rb
91
+ - lib/the_grid/api/command.rb
92
+ - lib/the_grid/api/command/batch_remove.rb
93
+ - lib/the_grid/api/command/batch_update.rb
94
+ - lib/the_grid/api/command/filter.rb
95
+ - lib/the_grid/api/command/paginate.rb
96
+ - lib/the_grid/api/command/search.rb
97
+ - lib/the_grid/api/command/sort.rb
98
+ - lib/the_grid/builder.rb
99
+ - lib/the_grid/builder/context.rb
100
+ - lib/the_grid/builder/json.rb
101
+ - lib/the_grid/config.rb
102
+ - lib/the_grid/version.rb
103
+ - spec/api/command/batch_remove_spec.rb
104
+ - spec/api/command/batch_update_spec.rb
105
+ - spec/api/command/filter_spec.rb
106
+ - spec/api/command/paginate_spec.rb
107
+ - spec/api/command/search_spec.rb
108
+ - spec/api/command/sort_spec.rb
109
+ - spec/api/command_spec.rb
110
+ - spec/api_spec.rb
111
+ - spec/builder/context_spec.rb
112
+ - spec/builder/json_spec.rb
113
+ - spec/config_spec.rb
114
+ - spec/spec_helper.rb
115
+ - the_grid.gemspec
116
+ homepage: http://github.com/stalniy/grid
117
+ licenses:
118
+ - MIT
119
+ metadata: {}
120
+ post_install_message:
121
+ rdoc_options: []
122
+ require_paths:
123
+ - lib
124
+ required_ruby_version: !ruby/object:Gem::Requirement
125
+ requirements:
126
+ - - ! '>='
127
+ - !ruby/object:Gem::Version
128
+ version: '0'
129
+ required_rubygems_version: !ruby/object:Gem::Requirement
130
+ requirements:
131
+ - - ! '>='
132
+ - !ruby/object:Gem::Version
133
+ version: '0'
134
+ requirements: []
135
+ rubyforge_project: the_grid
136
+ rubygems_version: 2.0.3
137
+ signing_key:
138
+ specification_version: 4
139
+ summary: Yet another grid api.
140
+ test_files: []