query_string_parser 0.1.0

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