query_string_parser 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
data/Rakefile ADDED
@@ -0,0 +1,88 @@
1
+ require 'rake'
2
+ require 'rake/clean'
3
+ require 'rake/rdoctask'
4
+ require 'rake/gempackagetask'
5
+ require 'rake/testtask'
6
+ require 'spec/rake/spectask'
7
+ require 'fileutils'
8
+ include FileUtils
9
+
10
+ # Default Rake task is compile
11
+ task :default => :compile
12
+
13
+ def make(makedir)
14
+ Dir.chdir(makedir) { sh 'make' }
15
+ end
16
+
17
+ def extconf(dir)
18
+ Dir.chdir(dir) { ruby "extconf.rb" }
19
+ end
20
+
21
+ def setup_extension(dir, extension)
22
+ ext = "ext/#{dir}"
23
+ ext_so = "#{ext}/#{extension}.#{Config::CONFIG['DLEXT']}"
24
+ ext_files = FileList[
25
+ "#{ext}/*.c",
26
+ "#{ext}/*.h",
27
+ "#{ext}/extconf.rb",
28
+ "#{ext}/Makefile",
29
+ "lib"
30
+ ]
31
+
32
+ task "lib" do
33
+ directory "lib"
34
+ end
35
+
36
+ desc "Builds just the #{extension} extension"
37
+ task extension.to_sym => ["#{ext}/Makefile", ext_so ]
38
+
39
+ file "#{ext}/Makefile" => ["#{ext}/extconf.rb"] do
40
+ extconf "#{ext}"
41
+ end
42
+
43
+ file ext_so => ext_files do
44
+ make "#{ext}"
45
+ cp ext_so, "lib"
46
+ end
47
+ end
48
+
49
+ setup_extension("", "pr_query_parser")
50
+
51
+ desc "Compile the extension"
52
+ task :compile => [:pr_query_parser]
53
+
54
+ Spec::Rake::SpecTask.new('do_spec') do |t|
55
+ t.spec_files = FileList['spec/**/*_spec.rb']
56
+ end
57
+ desc "run spec tests"
58
+ task :spec => [:clean, :compile, :do_spec]
59
+
60
+ CLEAN.include ['build/*', '**/*.o', '**/*.so', '**/*.bundle', '**/*.a', '**/*.log', 'pkg']
61
+ CLEAN.include ['ext/Makefile']
62
+
63
+ begin
64
+ require 'jeweler'
65
+ Jeweler::Tasks.new do |s|
66
+ s.name = "query_string_parser"
67
+ s.summary = "Simple query string parser"
68
+ s.email = "dj2@everburning.com"
69
+ s.homepage = "http://github.com/dj2/query_string_parser"
70
+ s.description = s.summary
71
+ s.authors = ["dan sinclair"]
72
+ s.files = [
73
+ "Rakefile",
74
+ "ext/extconf.rb",
75
+ "ext/pr_query_parser.c",
76
+ "lib/query_string_parser.rb",
77
+ "spec/query_string_parser_spec.rb"
78
+ ]
79
+ s.extensions << 'ext/extconf.rb'
80
+ s.require_paths = ['lib']
81
+ s.test_files = ['spec/query_string_parser_spec.rb']
82
+ end
83
+
84
+ Jeweler::GemcutterTasks.new
85
+ rescue LoadError
86
+ puts "Jeweler, or one of its dependencies, is not available. Install it with: sudo gem install technicalpickles-jeweler -s http://gems.github.com"
87
+ end
88
+
data/ext/extconf.rb ADDED
@@ -0,0 +1,9 @@
1
+ require 'mkmf'
2
+
3
+ #$CFLAGS = "-Wall -Werror -Wextra"
4
+
5
+ extension_name = 'pr_query_parser'
6
+ dir_config(extension_name)
7
+
8
+ have_library('curl', 'curl_easy_unescape')
9
+ create_makefile(extension_name)
@@ -0,0 +1,129 @@
1
+ #include "ruby.h"
2
+
3
+ #include <stdio.h>
4
+ #include <ctype.h>
5
+ #include <curl/curl.h>
6
+
7
+ static void
8
+ pr_set_param(VALUE params, char *key, char *value)
9
+ {
10
+ VALUE rkey, rvalue, cur;
11
+ char *v;
12
+
13
+ /* no key do nothing */
14
+ if (key == NULL || *key == 0) return;
15
+
16
+ /* strip [] from the key name */
17
+ v = curl_easy_unescape(NULL, key, 0, NULL);
18
+ if (strlen(v) > 2)
19
+ {
20
+ char *t = v;
21
+ char *open_bracket = NULL;
22
+
23
+ while (*(t + 1) != 0)
24
+ {
25
+ if (*t == '[') open_bracket = t;
26
+ t++;
27
+ }
28
+
29
+ if ((*t == ']') && (open_bracket != NULL))
30
+ {
31
+ char *pos = (open_bracket + 1);
32
+ int is_digit = 1;
33
+
34
+ while (pos != t)
35
+ {
36
+ if (!isdigit(*pos))
37
+ {
38
+ is_digit = 0;
39
+ break;
40
+ }
41
+ pos ++;
42
+ }
43
+
44
+ if ((*t == ']') && (*(t - 1) == '['))
45
+ {
46
+ *(t - 1) = 0;
47
+ }
48
+ else if ((*t == ']') && (*open_bracket == '[') && is_digit)
49
+ {
50
+ *open_bracket = 0;
51
+ }
52
+ }
53
+ }
54
+
55
+ /* key was only [], do nothing */
56
+ if (v == NULL || *v == 0) return;
57
+
58
+ rkey = rb_str_new2(v);
59
+ free(v);
60
+
61
+ v = curl_easy_unescape(NULL, value, 0, NULL);
62
+ rvalue = rb_str_new2(v);
63
+ free(v);
64
+
65
+ cur = rb_hash_aref(params, rkey);
66
+ if (NIL_P(cur))
67
+ {
68
+ rb_hash_aset(params, rkey, rvalue);
69
+ }
70
+ else
71
+ {
72
+ VALUE arry;
73
+ if (rb_obj_is_kind_of(cur, rb_cArray) == Qtrue)
74
+ {
75
+ arry = cur;
76
+ }
77
+ else
78
+ {
79
+ arry = rb_ary_new();
80
+ rb_ary_push(arry, cur);
81
+ rb_hash_aset(params, rkey, arry);
82
+ }
83
+ rb_ary_push(arry, rvalue);
84
+ }
85
+ }
86
+
87
+ VALUE
88
+ pr_query_parser(VALUE self, VALUE query_string, VALUE delim)
89
+ {
90
+ VALUE params = Qnil;
91
+ char *qs, *p, *s, *key = NULL;
92
+ char delimiter = RSTRING_PTR(delim)[0];
93
+
94
+ params = rb_hash_new();
95
+ qs = strdup(RSTRING_PTR(query_string));
96
+ for (s = qs, p = qs, key = qs; *p != 0; p++)
97
+ {
98
+ if (*p == delimiter)
99
+ {
100
+ if (key != NULL)
101
+ {
102
+ *p = 0;
103
+ pr_set_param(params, key, s);
104
+ }
105
+ s = (p + 1);
106
+ key = s;
107
+ }
108
+ else if (*p == '=')
109
+ {
110
+ *p = 0;
111
+ key = s;
112
+ s = (p + 1);
113
+ }
114
+ }
115
+
116
+ if (key != NULL) pr_set_param(params, key, s);
117
+ free(qs);
118
+
119
+ return params;
120
+ self = self;
121
+ }
122
+
123
+ void
124
+ Init_pr_query_parser(void)
125
+ {
126
+ VALUE QueryStringParser = rb_define_module("QueryStringParser");
127
+
128
+ rb_define_module_function(QueryStringParser, "pr_query_parser", pr_query_parser, 2);
129
+ }
@@ -0,0 +1,13 @@
1
+ require 'iconv'
2
+ require 'pr_query_parser'
3
+
4
+ module QueryStringParser
5
+ module_function
6
+
7
+ def qs_parse(query_string, delim = '&')
8
+ return {} if query_string.nil? || query_string == ""
9
+
10
+ ret = Iconv.iconv("UTF-8//IGNORE", "ISO-8859-1", (query_string + "\x20")).first[0..-2]
11
+ pr_query_parser(ret, delim)
12
+ end
13
+ end
@@ -0,0 +1,166 @@
1
+ # coding:utf-8
2
+
3
+ $: << File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib'))
4
+
5
+ require 'query_string_parser'
6
+
7
+ describe 'QueryStringParser' do
8
+ include QueryStringParser
9
+
10
+ it 'should parse a well formed query string' do
11
+ h = qs_parse('a=b&c=d&e=f')
12
+ h['a'].should == 'b'
13
+ h['c'].should == 'd'
14
+ h['e'].should == 'f'
15
+ end
16
+
17
+ it 'should parse a nil query string' do
18
+ qs_parse(nil).should == {}
19
+ end
20
+
21
+ it 'should allow different delimiters' do
22
+ h = qs_parse('a=b|c=d|e=f', '|')
23
+ h['a'].should == 'b'
24
+ h['c'].should == 'd'
25
+ h['e'].should == 'f'
26
+ end
27
+
28
+ it 'should parse a blank query string' do
29
+ qs_parse("").should == {}
30
+ end
31
+
32
+ it 'should parse a query string with multiple delimiters together' do
33
+ h = qs_parse("appkey=ff-postrank&&id=http%3A%2F%2Fmail.google.com%2Fmail%2Ffeed%2Fatom")
34
+ h.keys.length.should == 2
35
+ h['appkey'].should == 'ff-postrank'
36
+ h['id'].should == 'http://mail.google.com/mail/feed/atom'
37
+ end
38
+
39
+ it 'should parse a key with no value at the end of the string' do
40
+ h = qs_parse("appkey=ff-postrank&id=http%3A%2F%2Fmail.google.com%2Fmail%2Ffeed%2Fatom&noindex")
41
+ h.keys.length.should == 3
42
+ h['appkey'].should == 'ff-postrank'
43
+ h['id'].should == 'http://mail.google.com/mail/feed/atom'
44
+ h['noindex'].should == 'noindex'
45
+ end
46
+
47
+ it 'should parse a key with no value at the beginning of the string' do
48
+ h = qs_parse("noindex&appkey=ff-postrank&id=http%3A%2F%2Fmail.google.com%2Fmail%2Ffeed%2Fatom")
49
+ h.keys.length.should == 3
50
+ h['appkey'].should == 'ff-postrank'
51
+ h['id'].should == 'http://mail.google.com/mail/feed/atom'
52
+ h['noindex'].should == 'noindex'
53
+ end
54
+
55
+ it 'should parse a key with no value in the middle of the string' do
56
+ h = qs_parse("appkey=ff-postrank&noindex&id=http%3A%2F%2Fmail.google.com%2Fmail%2Ffeed%2Fatom")
57
+ h.keys.length.should == 3
58
+ h['appkey'].should == 'ff-postrank'
59
+ h['id'].should == 'http://mail.google.com/mail/feed/atom'
60
+ h['noindex'].should == 'noindex'
61
+ end
62
+
63
+ it 'should handle receiving only a key without a value' do
64
+ h = qs_parse('noindex')
65
+ h.keys.length.should == 1
66
+ h['noindex'].should == 'noindex'
67
+ end
68
+
69
+ it 'should parse a string with only =' do
70
+ qs_parse("=").should == {}
71
+ end
72
+
73
+ it 'should parse a string with only &' do
74
+ qs_parse('&').should == {}
75
+ end
76
+
77
+ it 'should parse a key with a blank value' do
78
+ h = qs_parse("mykey=&otherkey=zsf")
79
+ h.keys.length.should == 2
80
+ h['mykey'].should == ''
81
+ h['otherkey'].should == 'zsf'
82
+ end
83
+
84
+ it 'should parse a value with no key' do
85
+ h = qs_parse("mykey=asdf&=myvalue")
86
+ h.keys.length.should == 1
87
+ h['mykey'].should == 'asdf'
88
+ end
89
+
90
+ it 'should handle arrays of values' do
91
+ h = qs_parse("url[]=http%3A%2F%2Fscience.slashdot.org%2Farticle.pl%3Fsid%3D08%2F09%2F23%2F1210218%26from%3Drss&feed_id[]=132&url[]=http%3A%2F%2Fhardware.slashdot.org%2Farticle.pl%3Fsid%3D08%2F09%2F23%2F0313242%26from%3Drss&feed_id[]=132")
92
+ h['url'].class.should == Array
93
+ h['url'].length.should == 2
94
+ h['url'][0].should == 'http://science.slashdot.org/article.pl?sid=08/09/23/1210218&from=rss'
95
+ h['url'][1].should == 'http://hardware.slashdot.org/article.pl?sid=08/09/23/0313242&from=rss'
96
+
97
+ h['feed_id'].class.should == Array
98
+ h['feed_id'].length.should == 2
99
+ h['feed_id'][0].should == '132'
100
+ h['feed_id'][1].should == '132'
101
+ end
102
+
103
+ it 'should handle arrays of values with indexes' do
104
+ h = qs_parse("url[0]=http%3A%2F%2Fscience.slashdot.org%2Farticle.pl%3Fsid%3D08%2F09%2F23%2F1210218%26from%3Drss&feed_id[0]=132&url[1]=http%3A%2F%2Fhardware.slashdot.org%2Farticle.pl%3Fsid%3D08%2F09%2F23%2F0313242%26from%3Drss&feed_id[1]=132")
105
+ h['url'].class.should == Array
106
+ h['url'].length.should == 2
107
+ h['url'][0].should == 'http://science.slashdot.org/article.pl?sid=08/09/23/1210218&from=rss'
108
+ h['url'][1].should == 'http://hardware.slashdot.org/article.pl?sid=08/09/23/0313242&from=rss'
109
+
110
+ h['feed_id'].class.should == Array
111
+ h['feed_id'].length.should == 2
112
+ h['feed_id'][0].should == '132'
113
+ h['feed_id'][1].should == '132'
114
+ end
115
+
116
+ it "should handle escaped []'s on array keys" do
117
+ h = qs_parse("url%5B%5D=http%3A%2F%2Fscience.slashdot.org%2Farticle.pl%3Fsid%3D08%2F09%2F23%2F1210218%26from%3Drss&feed_id%5B%5D=132&url%5B%5D=http%3A%2F%2Fhardware.slashdot.org%2Farticle.pl%3Fsid%3D08%2F09%2F23%2F0313242%26from%3Drss&feed_id%5B%5D=132")
118
+ h['url'].class.should == Array
119
+ h['url'].length.should == 2
120
+ h['url'][0].should == 'http://science.slashdot.org/article.pl?sid=08/09/23/1210218&from=rss'
121
+ h['url'][1].should == 'http://hardware.slashdot.org/article.pl?sid=08/09/23/0313242&from=rss'
122
+
123
+ h['feed_id'].class.should == Array
124
+ h['feed_id'].length.should == 2
125
+ h['feed_id'][0].should == '132'
126
+ h['feed_id'][1].should == '132'
127
+ end
128
+
129
+ it 'should handle arrays without [] provided' do
130
+ h = qs_parse('key=1&key[]=2&key%5B%5D=3')
131
+ h['key'].class.should == Array
132
+ h['key'].length.should == 3
133
+ h['key'][0].should == '1'
134
+ h['key'][1].should == '2'
135
+ h['key'][2].should == '3'
136
+ end
137
+
138
+ it 'should handle blank items at the beginning and end of the strings' do
139
+ h = qs_parse("&baz=1&bar=2&splat=23&splat[]=42&splat=543&splat%5b%5d=99&splay=http:%2f%2f%3casdf&")
140
+ h.keys.length.should == 4
141
+
142
+ h['baz'].should == "1"
143
+ h['bar'].should == "2"
144
+ h['splat'].class.should == Array
145
+ h['splat'].length.should == 4
146
+ h['splat'][0].should == "23"
147
+ h['splat'][1].should == "42"
148
+ h['splat'][2].should == "543"
149
+ h['splat'][3].should == "99"
150
+ h['splay'].should == 'http://<asdf'
151
+ end
152
+
153
+ it 'should handle UTF-8 values' do
154
+ h = qs_parse("url%5B%5D=http%3A%2F%2Fwattf.com%2Fwp%2F2008%2F12%2F03%2Fmichael-nielsen-lectures-on-the-google-technology-stack%2F&feed_id%5B%5D=2e2b55b3058696ccbe633e7241d53b16&url%5B%5D=http%3A%2F%2Fjamesgolick.com%2F2008%2F10%2F27%2Foff-topic-caf%C3%A9-myriade&feed_id%5B%5D=0381dd0545852d28d82ad7d0befbfd92")
155
+ h.keys.length.should == 2
156
+ h['url'].class.should == Array
157
+ h['url'].length.should == 2
158
+ h['url'][0].should == 'http://wattf.com/wp/2008/12/03/michael-nielsen-lectures-on-the-google-technology-stack/'
159
+ h['url'][1].should == 'http://jamesgolick.com/2008/10/27/off-topic-café-myriade'
160
+
161
+ h['feed_id'].class.should == Array
162
+ h['feed_id'].length.should == 2
163
+ h['feed_id'][0].should == '2e2b55b3058696ccbe633e7241d53b16'
164
+ h['feed_id'][1].should == '0381dd0545852d28d82ad7d0befbfd92'
165
+ end
166
+ end
metadata ADDED
@@ -0,0 +1,67 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: query_string_parser
3
+ version: !ruby/object:Gem::Version
4
+ prerelease: false
5
+ segments:
6
+ - 0
7
+ - 1
8
+ - 0
9
+ version: 0.1.0
10
+ platform: ruby
11
+ authors:
12
+ - dan sinclair
13
+ autorequire:
14
+ bindir: bin
15
+ cert_chain: []
16
+
17
+ date: 2010-06-15 00:00:00 -04:00
18
+ default_executable:
19
+ dependencies: []
20
+
21
+ description: Simple query string parser
22
+ email: dj2@everburning.com
23
+ executables: []
24
+
25
+ extensions:
26
+ - ext/extconf.rb
27
+ - ext/extconf.rb
28
+ extra_rdoc_files: []
29
+
30
+ files:
31
+ - Rakefile
32
+ - ext/extconf.rb
33
+ - ext/pr_query_parser.c
34
+ - lib/query_string_parser.rb
35
+ - spec/query_string_parser_spec.rb
36
+ has_rdoc: true
37
+ homepage: http://github.com/dj2/query_string_parser
38
+ licenses: []
39
+
40
+ post_install_message:
41
+ rdoc_options:
42
+ - --charset=UTF-8
43
+ require_paths:
44
+ - lib
45
+ required_ruby_version: !ruby/object:Gem::Requirement
46
+ requirements:
47
+ - - ">="
48
+ - !ruby/object:Gem::Version
49
+ segments:
50
+ - 0
51
+ version: "0"
52
+ required_rubygems_version: !ruby/object:Gem::Requirement
53
+ requirements:
54
+ - - ">="
55
+ - !ruby/object:Gem::Version
56
+ segments:
57
+ - 0
58
+ version: "0"
59
+ requirements: []
60
+
61
+ rubyforge_project:
62
+ rubygems_version: 1.3.6
63
+ signing_key:
64
+ specification_version: 3
65
+ summary: Simple query string parser
66
+ test_files:
67
+ - spec/query_string_parser_spec.rb