ruby-ext-js 0.0.1 → 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/README.markdown CHANGED
@@ -4,8 +4,22 @@ Provides ultra-basic classes for translating Ext.js GET params to Postgres (via
4
4
 
5
5
  Examples:
6
6
 
7
+ ## MongoDB
8
+
9
+ class PullRequests < ExtJs::Mongo
10
+ def self.allowed_filters
11
+ ["state"]
12
+ end
13
+ end
14
+
15
+ mongo = PullRequests.new( params )
16
+ mongo.conditions # search conditions
17
+ mongo.options # pagination and sorting options
18
+
7
19
  ## Postgres
8
20
 
21
+ (Implementation and API will change significantly in the future, don't use this yet.)
22
+
9
23
  module ExtJs
10
24
  class PullRequests < Postgres
11
25
  # Converts Ext.js' wacky params structure into a Postgres db query opts
@@ -33,18 +47,6 @@ Examples:
33
47
  end
34
48
  end
35
49
 
36
- ## MongoDB
37
-
38
- Mongo's a lot easier to work with:
39
-
40
- module ExtJs
41
- class PullRequests < Mongo
42
- def self.allowed_filters
43
- ["state"]
44
- end
45
- end
46
- end
47
-
48
50
  # Specs
49
51
 
50
52
  `ExtJs` was extracted from private code. Existing specs rely on private models, so there are no specs here yet. Specs will require DataMapper.
data/VERSION CHANGED
@@ -1 +1 @@
1
- 0.0.1
1
+ 0.1.0
data/lib/ruby-ext-js.rb CHANGED
@@ -65,68 +65,81 @@ module ExtJs
65
65
  end
66
66
 
67
67
  class Mongo
68
- MAX_PER_PAGE = 1000
69
- DEFAULT_PAGE = 1
70
- DEFAULT_PER_PAGE = 30
68
+ DEFAULT_SKIP = 0
69
+ MAX_LIMIT = 500
70
+ DEFAULT_LIMIT = 50
71
71
 
72
- def self.db_opts( params )
72
+ def initialize( params )
73
+ @params = params
74
+ end
75
+
76
+ # @return [Hash] `find()` conditions for `Mongo::Collection.find( conditions, opts )`
77
+ def conditions
78
+ self.class.search_param( @params )
79
+ end
80
+
81
+ # @return [Hash] `find()` options for `Mongo::Collection.find( conditions, opts )`
82
+ def options
73
83
  opts = {}
74
84
 
75
- opts.merge! page_param( params )
76
- opts.merge! per_page_param( params )
77
- opts.merge! sort_param( params )
78
- opts.merge! search_param( params )
85
+ opts.merge! self.class.skip_param( @params )
86
+ opts.merge! self.class.limit_param( @params )
87
+ opts.merge! self.class.sort_param( @params )
79
88
 
80
89
  opts
81
90
  end
82
91
 
83
- protected
84
-
92
+ # @return [Array] Array of string values representing keys that are filterable.
85
93
  def self.allowed_filters
86
94
  []
87
95
  end
88
96
 
89
- def self.page_param( params )
90
- return { :page => DEFAULT_PAGE } unless params.key?( "start" ) && params.key?( "limit" )
91
-
92
- start = params["start"].to_i
93
- limit = per_page_param( params )[:per_page]
94
-
95
- { :page => ( start / limit ) + 1 }
97
+ protected
98
+
99
+ def self.skip_param( params )
100
+ return { :skip => DEFAULT_SKIP } unless params.key?( "start" ) && params.key?( "limit" )
101
+ { :skip => [params["start"].to_i, 0].max }
96
102
  end
97
103
 
98
- def self.per_page_param( params )
99
- unless params.key?( "limit" ) && params["limit"].to_i > 0
100
- return { :per_page => DEFAULT_PER_PAGE }
101
- end
102
- { :per_page => [params["limit"].to_i, MAX_PER_PAGE].min }
104
+ def self.limit_param( params )
105
+ return { :limit => DEFAULT_LIMIT } unless params.key?( "limit" ) && params["limit"].to_i > 0
106
+ { :limit => [params["limit"].to_i, MAX_LIMIT].min }
103
107
  end
104
108
 
105
109
  def self.sort_param( params )
106
110
  return {} unless params.key?( "sort" )
107
111
 
108
112
  sort = params["sort"] ? params["sort"].to_sym : :id
109
- sort = params["dir"] =~ /desc/i ? sort.desc : sort.asc
113
+ dir = params["dir"] =~ /desc/i ? :desc : :asc
110
114
 
111
- { :sort => sort }
115
+ { :sort => [sort, dir] }
112
116
  end
113
117
 
114
118
  def self.search_param( params )
115
- if params["filter"] && params["filter"]["0"]
116
- field = params["filter"]["0"]["field"].gsub(/[^\.\w\d_-]/, "").strip
117
- values = Array( params["filter"]["0"]["data"]["value"] )
118
-
119
- if values.size == 1
120
- values = values[0]
121
- else
122
- values = { "$in" => values }
123
- end
124
-
125
- unless field.blank? || !allowed_filters.include?( field ) || values.blank?
126
- return { field => values }
119
+ conds = {}
120
+
121
+ if params["filter"] && params["filter"].size > 0
122
+ 0.upto( params["filter"].size - 1 ).each do |i|
123
+ i = i.to_s
124
+
125
+ next unless params["filter"][i]
126
+
127
+ field = (params["filter"][i]["field"] || "").gsub(/[^\.\w\d_-]/, "").strip
128
+ values = Array( params["filter"][i]["data"] ? params["filter"][i]["data"]["value"] : nil )
129
+
130
+ if values.size == 1
131
+ values = values[0]
132
+ elsif values.size > 1
133
+ values = { "$in" => values }
134
+ end
135
+
136
+ unless field.empty? || !allowed_filters.include?( field ) || values.empty?
137
+ conds.merge! field => values
138
+ end
127
139
  end
128
140
  end
129
- {}
141
+
142
+ conds
130
143
  end
131
144
  end
132
145
  end
data/ruby-ext-js.gemspec CHANGED
@@ -5,7 +5,7 @@
5
5
 
6
6
  Gem::Specification.new do |s|
7
7
  s.name = %q{ruby-ext-js}
8
- s.version = "0.0.1"
8
+ s.version = "0.1.0"
9
9
 
10
10
  s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
11
  s.authors = ["Tyson Tate"]
@@ -1,22 +1,116 @@
1
1
  require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
2
 
3
3
  describe "ExtJs" do
4
- describe "Postgres" do
5
- it "sorts on id when you ask it to sort on created_at" do
6
- ExtJs::Postgres.pagination_opts({
7
- :sort => "created_at",
8
- :order => "desc"
9
- })[:order].should == [:id.asc]
10
- end
11
-
12
- it "specs the rest of the class or it gets the hose" do
13
- pending "The current spec suite is currently rigidly tied to private models. Someday we'll write a generic spec suite here."
14
- end
15
- end
4
+ # describe "Postgres" do
5
+ # it "sorts on id when you ask it to sort on created_at" do
6
+ # ExtJs::Postgres.pagination_opts({
7
+ # :sort => "created_at",
8
+ # :order => "desc"
9
+ # })[:order].should == [:id.asc]
10
+ # end
11
+ #
12
+ # it "specs the rest of the class or it gets the hose" do
13
+ # pending "The current spec suite is currently rigidly tied to private models. Someday we'll write a generic spec suite here."
14
+ # end
15
+ # end
16
16
 
17
17
  describe "Mongo" do
18
- it "specs the rest of the class or it gets the hose" do
19
- pending "The current spec suite is currently rigidly tied to private models. Someday we'll write a generic spec suite here."
18
+ describe "conditions" do
19
+ class TestMongoNoFilters < ExtJs::Mongo; end
20
+
21
+ class TestMongoWithFilters < ExtJs::Mongo
22
+ def self.allowed_filters
23
+ ["state", "score"]
24
+ end
25
+ end
26
+
27
+ it "handles empty params" do
28
+ mongo = TestMongoNoFilters.new( {} )
29
+ mongo.conditions.should == {}
30
+
31
+ mongo = TestMongoWithFilters.new( {} )
32
+ mongo.conditions.should == {}
33
+ end
34
+
35
+ it "handles filter params" do
36
+ params = {
37
+ "filter" => {
38
+ "0" => {
39
+ "field" => "state",
40
+ "data" => {
41
+ "value" => "open"
42
+ }
43
+ }
44
+ }
45
+ }
46
+ mongo = TestMongoNoFilters.new( params )
47
+ mongo.conditions.should == {}
48
+
49
+ mongo = TestMongoWithFilters.new( params )
50
+ mongo.conditions.should == { "state" => "open" }
51
+ end
52
+
53
+ it "handles complex filter params" do
54
+ params = {
55
+ "filter" => {
56
+ "0" => {
57
+ "field" => "state",
58
+ "data" => {
59
+ "value" => "open"
60
+ }
61
+ },
62
+ "1" => {
63
+ "field" => "score",
64
+ "data" => {
65
+ "value" => ["5", "4"]
66
+ }
67
+ },
68
+ "2" => {
69
+ "field" => "score"
70
+ },
71
+ "3" => {
72
+ "data" => {
73
+ "value" => ["5", "4"]
74
+ }
75
+ },
76
+ "4" => {
77
+ "field" => "score",
78
+ "data" => {
79
+ "value" => []
80
+ }
81
+ }
82
+ }
83
+ }
84
+
85
+ mongo = TestMongoWithFilters.new( params )
86
+ mongo.conditions.should == { "state" => "open", "score" => { "$in" => ["5", "4"] } }
87
+ end
88
+ end
89
+
90
+ describe "opts" do
91
+ class TestMongo < ExtJs::Mongo; end
92
+
93
+ it "handles empty params with sensible defaults" do
94
+ TestMongo.new( {} ).options.should == { :limit => 50, :skip => 0 }
95
+ end
96
+
97
+ it "handles pagination" do
98
+ TestMongo.new( "start" => "50", "limit" => "10" ).options.should == { :limit => 10, :skip => 50 }
99
+ TestMongo.new( "start" => "10" ).options.should == { :limit => 50, :skip => 0 }
100
+ TestMongo.new( "limit" => "10" ).options.should == { :limit => 10, :skip => 0 }
101
+ end
102
+
103
+ it "handles sorting" do
104
+ TestMongo.new( "sort" => "foo" ).options.should == { :limit => 50, :skip => 0, :sort => [:foo, :asc] }
105
+ TestMongo.new( "sort" => "foo", "dir" => "desc" ).options.should == { :limit => 50, :skip => 0, :sort => [:foo, :desc] }
106
+ TestMongo.new( "sort" => "foo", "dir" => "no idea" ).options.should == { :limit => 50, :skip => 0, :sort => [:foo, :asc] }
107
+ TestMongo.new( "dir" => "asc" ).options.should == { :limit => 50, :skip => 0 }
108
+ end
109
+
110
+ it "prevents dumb queries" do
111
+ TestMongo.new( "start" => "9999999", "limit" => "9999999" ).options.should == { :limit => 500, :skip => 9999999 }
112
+ TestMongo.new( "start" => "-200", "limit" => "-200" ).options.should == { :limit => 50, :skip => 0 }
113
+ end
20
114
  end
21
115
  end
22
116
  end
metadata CHANGED
@@ -1,13 +1,13 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: ruby-ext-js
3
3
  version: !ruby/object:Gem::Version
4
- hash: 29
4
+ hash: 27
5
5
  prerelease: false
6
6
  segments:
7
7
  - 0
8
- - 0
9
8
  - 1
10
- version: 0.0.1
9
+ - 0
10
+ version: 0.1.0
11
11
  platform: ruby
12
12
  authors:
13
13
  - Tyson Tate