pg_array_parser 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore CHANGED
@@ -16,3 +16,5 @@ test/tmp
16
16
  test/version_tmp
17
17
  tmp
18
18
  benchmark.rb
19
+ bin/*
20
+ .rbenv-version
@@ -2,6 +2,10 @@ rvm:
2
2
  - 1.8.7
3
3
  - 1.9.2
4
4
  - 1.9.3
5
+ - jruby-18mode
6
+ - jruby-19mode
7
+ before_script:
8
+ - rake install
5
9
 
6
10
  notifications:
7
11
  email: false
@@ -0,0 +1,6 @@
1
+ # 0.0.2
2
+ * Refactored C extension - [jeremyevans](https://github.com/jeremyevens) - Merged at [ad4987](https://github.com/dockyard/pg_array_parser/commit/ad4987dba411decca4aebd0750c990212dc81039)
3
+ * Adds JRuby support - thanks to [tychobrailleur](https://github.com/tychobrailleur) for help with the java class
4
+
5
+ # 0.0.1
6
+ * Initial release
data/README.md CHANGED
@@ -18,9 +18,24 @@ Or install it yourself as:
18
18
 
19
19
  ## Usage
20
20
 
21
- Include the `PgArrayParser` module. Call `PgArrayParser#parse_pg_array`
22
- method to return a multi-dimensional array of strings from a PostgreSQL
23
- array value
21
+ Include the `PgArrayParser` module, which provides the `parse_pg_array`
22
+ method.
23
+
24
+ ```ruby
25
+ class MyPostgresParser
26
+ include PgArrayParser
27
+ end
28
+
29
+ parser = MyPostgresParser.new
30
+ parser.parse_pg_array '{}'
31
+ # => []
32
+ parser.parse_pg_array '{1,2,3,4}'
33
+ # => ["1", "2", "3", "4"]
34
+ parser.parse_pg_array '{1,{2,3},4}'
35
+ # => ["1", ["2", "3"], "4"]
36
+ parser.parse_pg_array '{some,strings that,"May have some ,\'s"}'
37
+ # => ["some", "strings that", "May have some ,'s"]
38
+ ```
24
39
 
25
40
  ## Authors
26
41
 
data/Rakefile CHANGED
@@ -3,6 +3,13 @@ require 'bundler/gem_tasks'
3
3
 
4
4
  require 'rspec/core/rake_task'
5
5
 
6
+ spec = Gem::Specification.load('pg_array_parser.gemspec')
7
+
8
+ if RUBY_PLATFORM =~ /java/
9
+ require 'rake/javaextensiontask'
10
+ Rake::JavaExtensionTask.new('pg_array_parser', spec)
11
+ end
12
+
6
13
  task :spec => :install
7
14
 
8
15
  RSpec::Core::RakeTask.new(:spec)
@@ -0,0 +1,113 @@
1
+ package pgarrayparser;
2
+
3
+ import org.jruby.Ruby;
4
+ import org.jruby.RubyArray;
5
+ import org.jruby.RubyClass;
6
+ import org.jruby.RubyFixnum;
7
+ import org.jruby.RubyString;
8
+ import org.jruby.RubyNil;
9
+ import org.jruby.RubyModule;
10
+ import org.jruby.RubyObject;
11
+ import org.jruby.anno.JRubyMethod;
12
+ import org.jruby.anno.JRubyClass;
13
+ import org.jruby.runtime.ObjectAllocator;
14
+ import org.jruby.runtime.ThreadContext;
15
+ import org.jruby.runtime.builtin.IRubyObject;
16
+ import org.jruby.runtime.load.BasicLibraryService;
17
+
18
+ @JRubyClass(name = "PgArrayParser::PgArrayParserEngine")
19
+ public class PgArrayParserEngine extends RubyObject {
20
+ public PgArrayParserEngine(final Ruby runtime, RubyClass rubyClass) {
21
+ super(runtime, rubyClass);
22
+ }
23
+
24
+ private Ruby runtime;
25
+ private int index;
26
+ private String arrayString;
27
+ @JRubyMethod(name = "parse_pg_array")
28
+ public IRubyObject parse_pg_array(ThreadContext context, IRubyObject value) {
29
+ runtime = context.getRuntime();
30
+ index = 1;
31
+ arrayString = value.asJavaString();
32
+ IRubyObject returnValue = readArray();
33
+ return returnValue;
34
+
35
+ }
36
+
37
+ private RubyArray readArray()
38
+ {
39
+ RubyArray array = RubyArray.newArray(runtime, 1);
40
+
41
+ int openQuote = 0;
42
+ boolean escapeNext = false;
43
+ char currentChar;
44
+ StringBuilder word = new StringBuilder();
45
+
46
+ if(index < arrayString.length() && arrayString.charAt(index) == '}')
47
+ {
48
+ return array;
49
+ }
50
+
51
+ for(;index < arrayString.length(); ++index)
52
+ {
53
+ currentChar = arrayString.charAt(index);
54
+ if(openQuote < 1)
55
+ {
56
+ if(currentChar == ',' || currentChar == '}')
57
+ {
58
+ if(!escapeNext)
59
+ {
60
+ if(openQuote == 0 && word.length() == 4 && word.toString().equals("NULL"))
61
+ {
62
+ array.append(runtime.getNil());
63
+ }
64
+ else
65
+ {
66
+ array.append(RubyString.newString(runtime, word.toString()));
67
+ }
68
+ }
69
+ if(currentChar == '}')
70
+ {
71
+ return array;
72
+ }
73
+ escapeNext = false;
74
+ openQuote = 0;
75
+ word = new StringBuilder();
76
+ }
77
+ else if(currentChar == '"')
78
+ {
79
+ openQuote = 1;
80
+ }
81
+ else if(currentChar == '{')
82
+ {
83
+ index++;
84
+ array.append(readArray());
85
+ escapeNext = true;
86
+ }
87
+ else
88
+ {
89
+ word.append(String.valueOf(currentChar));
90
+ }
91
+ }
92
+ else if(escapeNext)
93
+ {
94
+ word.append(String.valueOf(currentChar));
95
+ escapeNext = false;
96
+ }
97
+ else if(currentChar == '\\')
98
+ {
99
+ escapeNext = true;
100
+ }
101
+ else if(currentChar == '"')
102
+ {
103
+ openQuote = -1;
104
+ }
105
+ else
106
+ {
107
+ word.append(String.valueOf(currentChar));
108
+ }
109
+ }
110
+
111
+ return array;
112
+ }
113
+ }
@@ -0,0 +1,32 @@
1
+ package pgarrayparser;
2
+
3
+ import java.lang.Long;
4
+ import java.io.IOException;
5
+
6
+ import org.jruby.Ruby;
7
+ import org.jruby.RubyArray;
8
+ import org.jruby.RubyClass;
9
+ import org.jruby.RubyFixnum;
10
+ import org.jruby.RubyModule;
11
+ import org.jruby.RubyObject;
12
+ import org.jruby.anno.JRubyMethod;
13
+ import org.jruby.runtime.ObjectAllocator;
14
+ import org.jruby.runtime.ThreadContext;
15
+ import org.jruby.runtime.builtin.IRubyObject;
16
+ import org.jruby.runtime.load.BasicLibraryService;
17
+
18
+ public class PgArrayParserEngineService implements BasicLibraryService {
19
+
20
+ public boolean basicLoad(Ruby runtime) throws IOException {
21
+
22
+ RubyModule pgArrayParser = runtime.defineModule("PgArrayParser");
23
+ RubyClass pgArrayParserEngine = pgArrayParser.defineClassUnder("PgArrayParserEngine", runtime.getObject(), new ObjectAllocator() {
24
+ public IRubyObject allocate(Ruby runtime, RubyClass rubyClass) {
25
+ return new PgArrayParserEngine(runtime, rubyClass);
26
+ }
27
+ });
28
+
29
+ pgArrayParserEngine.defineAnnotatedMethods(PgArrayParserEngine.class);
30
+ return true;
31
+ }
32
+ }
@@ -1,90 +1,109 @@
1
1
  #include <ruby.h>
2
2
 
3
- VALUE PgArrayParser = Qnil;
4
-
5
- //Prototypes
6
- VALUE read_array(int *index, char *string, int *length, char *word);
7
- VALUE parse_pg_array(VALUE self, VALUE pg_array_string);
3
+ /* Prototype */
4
+ VALUE read_array(int *index, char *string, int length, char *word);
8
5
 
9
6
  VALUE parse_pg_array(VALUE self, VALUE pg_array_string) {
10
7
 
11
- //convert to c-string, create a buffer of the same length, as that will be the worst case
8
+ /* convert to c-string, create a buffer of the same length, as that will be the worst case */
12
9
  char *c_pg_array_string = StringValueCStr(pg_array_string);
13
10
  int array_string_length = RSTRING_LEN(pg_array_string);
14
- char *word = malloc(sizeof(char) * (array_string_length + 1));
11
+ char *word = malloc(array_string_length + 1);
15
12
 
16
13
  int index = 1;
17
14
 
18
- VALUE return_value = read_array(&index, c_pg_array_string, &array_string_length, word);
15
+ VALUE return_value = read_array(&index, c_pg_array_string, array_string_length, word);
19
16
  free(word);
20
17
  return return_value;
21
18
  }
22
19
 
23
- VALUE read_array(int *index, char *c_pg_array_string, int *array_string_length, char *word)
20
+ VALUE read_array(int *index, char *c_pg_array_string, int array_string_length, char *word)
24
21
  {
25
- // Return value: array
22
+ /* Return value: array */
26
23
  VALUE array;
27
- array = rb_ary_new();
28
24
  int word_index = 0;
25
+
26
+ /* The current character in the input string. */
27
+ char c;
28
+
29
+ /* 0: Currently outside a quoted string, current word never quoted
30
+ * 1: Currently inside a quoted string
31
+ * -1: Currently outside a quoted string, current word previously quoted */
29
32
  int openQuote = 0;
30
- for(;(*index) < (*array_string_length); ++(*index))
33
+
34
+ /* Inside quoted input means the next character should be treated literally,
35
+ * instead of being treated as a metacharacter.
36
+ * Outside of quoted input, means that the word shouldn't be pushed to the array,
37
+ * used when the last entry was a subarray (which adds to the array itself). */
38
+ int escapeNext = 0;
39
+
40
+ array = rb_ary_new();
41
+
42
+ /* Special case the empty array, so it doesn't need to be handled manually inside
43
+ * the loop. */
44
+ if(((*index) < array_string_length) && c_pg_array_string[(*index)] == '}')
45
+ {
46
+ return array;
47
+ }
48
+
49
+ for(;(*index) < array_string_length; ++(*index))
31
50
  {
32
- if(!openQuote && (c_pg_array_string[*index] == ','))
51
+ c = c_pg_array_string[*index];
52
+ if(openQuote < 1)
33
53
  {
34
- if(c_pg_array_string[(*index) - 1] != '"' && c_pg_array_string[(*index) - 1] != '}')
54
+ if(c == ',' || c == '}')
35
55
  {
36
- word[word_index] = '\0';
37
- if (word_index == 4 && !strcmp(word,"NULL"))
56
+ if(!escapeNext)
38
57
  {
39
- rb_ary_push(array, Qnil);
58
+ if(openQuote == 0 && word_index == 4 && !strncmp(word, "NULL", word_index))
59
+ {
60
+ rb_ary_push(array, Qnil);
61
+ }
62
+ else
63
+ {
64
+ rb_ary_push(array, rb_str_new(word, word_index));
65
+ }
40
66
  }
41
- else
67
+ if(c == '}')
42
68
  {
43
- rb_ary_push(array, rb_str_new2(word));
69
+ return array;
44
70
  }
71
+ escapeNext = 0;
72
+ openQuote = 0;
45
73
  word_index = 0;
46
74
  }
47
- }
48
- else if(!openQuote && c_pg_array_string[*index] == '}')
49
- {
50
- if(word_index > 0 && c_pg_array_string[(*index) - 1] != '"')
75
+ else if(c == '"')
51
76
  {
52
- word[word_index] = '\0';
53
- if (word_index == 4 && !strcmp(word,"NULL"))
54
- {
55
- rb_ary_push(array, Qnil);
56
- }
57
- else
58
- {
59
- rb_ary_push(array, rb_str_new2(word));
60
- }
61
- word_index = 0;
77
+ openQuote = 1;
78
+ }
79
+ else if(c == '{')
80
+ {
81
+ (*index)++;
82
+ rb_ary_push(array, read_array(index, c_pg_array_string, array_string_length, word));
83
+ escapeNext = 1;
84
+ }
85
+ else
86
+ {
87
+ word[word_index] = c;
88
+ word_index++;
62
89
  }
63
- return array;
64
- }
65
- else if (openQuote && c_pg_array_string[*index] == '"' && c_pg_array_string[(*index) - 1] == '\\')
66
- {
67
- word[word_index - 1] = '"';
68
90
  }
69
- else if (openQuote && c_pg_array_string[*index] == '"' && c_pg_array_string[(*index) - 1] != '\\')
70
- {
71
- word[word_index] = '\0';
72
- word_index = 0;
73
- openQuote = 0;
74
- rb_ary_push(array, rb_str_new2(word));
91
+ else if (escapeNext) {
92
+ word[word_index] = c;
93
+ word_index++;
94
+ escapeNext = 0;
75
95
  }
76
- else if(c_pg_array_string[*index] == '"')
96
+ else if (c == '\\')
77
97
  {
78
- openQuote = 1;
98
+ escapeNext = 1;
79
99
  }
80
- else if(!openQuote && c_pg_array_string[*index] == '{')
100
+ else if (c == '"')
81
101
  {
82
- (*index)++;
83
- rb_ary_push(array, read_array(index, c_pg_array_string, array_string_length, word));
102
+ openQuote = -1;
84
103
  }
85
104
  else
86
105
  {
87
- word[word_index] = c_pg_array_string[*index];
106
+ word[word_index] = c;
88
107
  word_index++;
89
108
  }
90
109
  }
@@ -93,9 +112,6 @@ VALUE read_array(int *index, char *c_pg_array_string, int *array_string_length,
93
112
  }
94
113
 
95
114
  void Init_pg_array_parser(void) {
96
- PgArrayParser = rb_define_module("PgArrayParser");
97
- rb_define_method(PgArrayParser, "parse_pg_array", parse_pg_array, 1);
98
-
115
+ rb_define_method(rb_define_module("PgArrayParser"), "parse_pg_array", parse_pg_array, 1);
99
116
  }
100
117
 
101
-
Binary file
@@ -1,2 +1,18 @@
1
- require "pg_array_parser/version"
2
- require 'pg_array_parser/pg_array_parser'
1
+ require File.expand_path('../pg_array_parser/version', __FILE__)
2
+ require File.expand_path('../pg_array_parser', __FILE__)
3
+
4
+ if RUBY_PLATFORM =~ /java/
5
+ module PgArrayParser
6
+ require 'jruby'
7
+ require File.expand_path('../pg_array_parser.jar', __FILE__)
8
+ require 'pgArrayParser/pg_array_parser_engine'
9
+
10
+ def parse_pg_array(value)
11
+ @parser ||= PgArrayParserEngine.new
12
+ @parser.parse_pg_array(value)
13
+ end
14
+ end
15
+ else
16
+ require 'pg_array_parser/pg_array_parser'
17
+ end
18
+
@@ -1,3 +1,3 @@
1
1
  module PgArrayParser
2
- VERSION = "0.0.1"
2
+ VERSION = '0.0.2'
3
3
  end
@@ -3,19 +3,24 @@ require File.expand_path('../lib/pg_array_parser/version', __FILE__)
3
3
 
4
4
  Gem::Specification.new do |gem|
5
5
  gem.authors = ["Dan McClain"]
6
- gem.email = ["git@danseaver.com"]
6
+ gem.email = ["git@danmcclain.net"]
7
7
  gem.description = %q{Simple library to parse PostgreSQL arrays into a array of strings}
8
8
  gem.summary = %q{Converts PostgreSQL array strings into arrays of strings}
9
9
  gem.homepage = ""
10
10
 
11
11
  gem.files = `git ls-files`.split($\)
12
12
  gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
13
- gem.extensions = ['ext/pg_array_parser/extconf.rb']
13
+ if RUBY_PLATFORM =~ /java/
14
+ gem.files << 'lib/pg_array_parser.jar'
15
+ else
16
+ gem.extensions = ['ext/pg_array_parser/extconf.rb']
17
+ end
14
18
  gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
15
19
  gem.name = "pg_array_parser"
16
20
  gem.require_paths = ["lib"]
17
21
  gem.version = PgArrayParser::VERSION
18
22
 
19
- gem.add_development_dependency 'rspec', '~> 2.10.0'
23
+ gem.add_development_dependency 'rspec', '~> 2.11.0'
20
24
  gem.add_development_dependency 'rake', '~> 0.9.2.2'
25
+ gem.add_development_dependency 'rake-compiler'
21
26
  end
@@ -9,6 +9,12 @@ describe 'PgArrayParser' do
9
9
 
10
10
  describe '#parse_pg_array' do
11
11
  context 'one dimensional arrays' do
12
+ context 'empty' do
13
+ it 'returns an empty array' do
14
+ parser.parse_pg_array(%[{}]).should eq []
15
+ end
16
+ end
17
+
12
18
  context 'no strings' do
13
19
  it 'returns an array of strings' do
14
20
  parser.parse_pg_array(%[{1,2,3}]).should eq ['1','2','3']
@@ -35,10 +41,21 @@ describe 'PgArrayParser' do
35
41
  it 'returns an array of strings when containing an escaped quote' do
36
42
  parser.parse_pg_array(%[{1,"2\\",3",4}]).should eq ['1','2",3','4']
37
43
  end
44
+
45
+ it 'returns an array of strings when containing an escaped backslash' do
46
+ parser.parse_pg_array(%[{1,"2\\\\",3,4}]).should eq ['1','2\\','3','4']
47
+ parser.parse_pg_array(%[{1,"2\\\\\\",3",4}]).should eq ['1','2\\",3','4']
48
+ end
38
49
  end
39
50
  end
40
51
 
41
52
  context 'two dimensional arrays' do
53
+ context 'empty' do
54
+ it 'returns an empty array' do
55
+ parser.parse_pg_array(%[{{}}]).should eq [[]]
56
+ parser.parse_pg_array(%[{{},{}}]).should eq [[],[]]
57
+ end
58
+ end
42
59
  context 'no strings' do
43
60
  it 'returns an array of strings with a sub array' do
44
61
  parser.parse_pg_array(%[{1,{2,3},4}]).should eq ['1',['2','3'],'4']
@@ -60,6 +77,12 @@ describe 'PgArrayParser' do
60
77
  end
61
78
  end
62
79
  context 'three dimensional arrays' do
80
+ context 'empty' do
81
+ it 'returns an empty array' do
82
+ parser.parse_pg_array(%[{{{}}}]).should eq [[[]]]
83
+ parser.parse_pg_array(%[{{{},{}},{{},{}}}]).should eq [[[],[]],[[],[]]]
84
+ end
85
+ end
63
86
  it 'returns an array of strings with sub arrays' do
64
87
  parser.parse_pg_array(%[{1,{2,{3,4}},{NULL,6},7}]).should eq ['1',['2',['3','4']],[nil,'6'],'7']
65
88
  end
metadata CHANGED
@@ -1,7 +1,7 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: pg_array_parser
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.0.1
4
+ version: 0.0.2
5
5
  prerelease:
6
6
  platform: ruby
7
7
  authors:
@@ -9,7 +9,7 @@ authors:
9
9
  autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
- date: 2012-06-11 00:00:00.000000000 Z
12
+ date: 2012-09-04 00:00:00.000000000 Z
13
13
  dependencies:
14
14
  - !ruby/object:Gem::Dependency
15
15
  name: rspec
@@ -18,7 +18,7 @@ dependencies:
18
18
  requirements:
19
19
  - - ~>
20
20
  - !ruby/object:Gem::Version
21
- version: 2.10.0
21
+ version: 2.11.0
22
22
  type: :development
23
23
  prerelease: false
24
24
  version_requirements: !ruby/object:Gem::Requirement
@@ -26,7 +26,7 @@ dependencies:
26
26
  requirements:
27
27
  - - ~>
28
28
  - !ruby/object:Gem::Version
29
- version: 2.10.0
29
+ version: 2.11.0
30
30
  - !ruby/object:Gem::Dependency
31
31
  name: rake
32
32
  requirement: !ruby/object:Gem::Requirement
@@ -43,9 +43,25 @@ dependencies:
43
43
  - - ~>
44
44
  - !ruby/object:Gem::Version
45
45
  version: 0.9.2.2
46
+ - !ruby/object:Gem::Dependency
47
+ name: rake-compiler
48
+ requirement: !ruby/object:Gem::Requirement
49
+ none: false
50
+ requirements:
51
+ - - ! '>='
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ type: :development
55
+ prerelease: false
56
+ version_requirements: !ruby/object:Gem::Requirement
57
+ none: false
58
+ requirements:
59
+ - - ! '>='
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
46
62
  description: Simple library to parse PostgreSQL arrays into a array of strings
47
63
  email:
48
- - git@danseaver.com
64
+ - git@danmcclain.net
49
65
  executables: []
50
66
  extensions:
51
67
  - ext/pg_array_parser/extconf.rb
@@ -53,13 +69,16 @@ extra_rdoc_files: []
53
69
  files:
54
70
  - .gitignore
55
71
  - .travis.yml
72
+ - CHANGELOG.md
56
73
  - Gemfile
57
74
  - LICENSE
58
75
  - README.md
59
76
  - Rakefile
77
+ - ext/pg_array_parser/PgArrayParserEngine.java
78
+ - ext/pg_array_parser/PgArrayParserEngineService.java
60
79
  - ext/pg_array_parser/extconf.rb
61
80
  - ext/pg_array_parser/pg_array_parser.c
62
- - lib/pg_array_parser.bundle
81
+ - lib/pg_array_parser.jar
63
82
  - lib/pg_array_parser.rb
64
83
  - lib/pg_array_parser/version.rb
65
84
  - pg_array_parser.gemspec
@@ -92,3 +111,4 @@ summary: Converts PostgreSQL array strings into arrays of strings
92
111
  test_files:
93
112
  - spec/parser_spec.rb
94
113
  - spec/spec_helper.rb
114
+ has_rdoc:
Binary file