ltsv 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,18 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
18
+ *.sw*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in ltsv.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 condor
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,69 @@
1
+ # Ltsv
2
+
3
+ LTSV: A Parser / Dumper for Labelled Tab-Separated Values (LTSV)
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'ltsv'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install ltsv
18
+
19
+ ## Usage
20
+
21
+ At first, you should require ltsv:
22
+
23
+ require 'ltsv'
24
+
25
+ In addition, if you manage gems with bundler, you should add the statement below into your Gemfile:
26
+
27
+ gem 'ltsv'
28
+
29
+
30
+ ### parsing LTSV
31
+
32
+ # parse string
33
+ string = "label1:value1\tlabel2:value2"
34
+ values = LTSV.parse(string) # => {:label1 => "value1", :label2 => "value2"}
35
+
36
+ # parse via stream
37
+ # content: as below
38
+ # label1_1:value1_1\tlabel1_2:value1_2
39
+ # label2_1:value2_1\tlabel2_2:value2_2
40
+ stream = File.open("some_file.ltsv", "r")
41
+ values = LTSV.parse(stream)
42
+ # => [{:label1_1 => "value1_2", :label1_2 => "value1_2"},
43
+ # {:label2_1 => "value2_2", :label2_2 => "value2_2"}]
44
+
45
+ Current limitation: parsed string should be in one line. If you include any special chars that may affect to the processing( "\r", "\n", "\t", "\\"), you should properly escape it with backslash.
46
+
47
+ ### loading LTSV file
48
+
49
+ # parse via path
50
+ values = LTSV.parse("some_path.ltsv")
51
+
52
+ # parse via stream
53
+ stream = File.open("some_file.ltsv", "r")
54
+ values = LTSV.load(stream) # => same as LTSV.parse(stream)
55
+
56
+ ### dumping into LTSV
57
+
58
+ value = {label1: "value1", label2: "value2"}
59
+ dumped = LTSV.dump(value) # => "label1:value1\tlabel2:value2"
60
+
61
+ Dumped objects should respond to :to_hash.
62
+
63
+ ## Contributing
64
+
65
+ 1. Fork it
66
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
67
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
68
+ 4. Push to the branch (`git push origin my-new-feature`)
69
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
data/lib/ltsv.rb ADDED
@@ -0,0 +1,140 @@
1
+ # = ltsv - A parser / dumper for Labelled Tab-Separated Values(LTSV)
2
+ #
3
+ # Copyright (C) 2013 TOYODA Naoto.
4
+ #
5
+ #
6
+ module LTSV
7
+ VERSION = "0.0.1"
8
+
9
+ # Parsing given stream or string.
10
+ # If you specified a stream as the first argument,
11
+ # this method behaves as same as #load.
12
+ #
13
+ # == Arguments:
14
+ # * _io_or_string_: a target to parse. Possible values are: String, IO.
15
+ # If you give the string value, it stands the content to parse.
16
+ # If you give the IO value, it stands the stream which provides the contents to parse.
17
+ # == Options:
18
+ # * _symbolize_keys_ : Whether the label will be available as symbol or not.
19
+ # Default value is true.
20
+ # * _encoding_ : The encoding of the stream given as the first argument.
21
+ # It is effective only when the first argument is an instance of IO.
22
+ # Default value: Encoding.default_external
23
+ # == Returns:
24
+ # * An instance of Hash : When you give the string as the first argument.
25
+ # * An Array of Hash : When you give the IO as the first argument.
26
+ def parse(io_or_string, options = {})
27
+ case io_or_string
28
+ when String
29
+ parse_string(io_or_string, options)
30
+ when IO
31
+ parse_io(io_or_string = {})
32
+ end
33
+ end
34
+
35
+ # Parsing the content of the given stream or path.
36
+ # If you specified a stream as the first argument,
37
+ # this method behaves as same as #load.
38
+ #
39
+ # == Arguments:
40
+ # * _io_or_string_: a target to parse. Possible values are: String, IO.
41
+ # If you give the string value, it stands the path of the file to parse.
42
+ # If you give the IO value, it stands the stream which provides the contents to parse.
43
+ # *Note* : If you give the IO value, this method behaves like #parse.
44
+ # == Options:
45
+ # * _symbolize_keys_ : Whether the label will be available as symbol or not.
46
+ # Default value is true.
47
+ # * _encoding_ : The encoding of the stream given as the first argument.
48
+ # Default value: Encoding.default_external
49
+ # == Returns:
50
+ # * An Array of Hash : Each hash stands for a line of the io or the file.
51
+ def load(io_or_string, options = {})
52
+ encoding_opt = options.delete :encoding
53
+ encoding =
54
+ encoding_opt ? Encoding.find(encoding_opt) : Encoding.default_external
55
+
56
+ case io_or_string
57
+ when String
58
+ File.open(io_or_string, "r:#{encoding}"){|f|parse_io(f)}
59
+ when IO
60
+ parse_io(io)
61
+ end
62
+ end
63
+
64
+ # Dumping the value given into a new String.
65
+ # Each special character will be escaped with backslash('\'), and the expression should be contained in a single line.
66
+ #
67
+ # == Arguments:
68
+ # * _value_: a target to dump. It should respond to :to_hash.
69
+ # == Returns:
70
+ # * A LTSV String
71
+ def dump(value)
72
+ raise ArgumentError, "dump should take an argument of hash" unless
73
+ value.respond_to? :to_hash
74
+
75
+ hash = value.to_hash
76
+
77
+ hash.inject('') do |s, kv|
78
+ s << "\t" if s.bytesize > 0
79
+
80
+ (k, v) = kv
81
+ value = escape v
82
+ s << k.to_s << ':' << value
83
+ end
84
+ end
85
+
86
+ private
87
+
88
+ def parse_io(io, options)#:nodoc:
89
+ io.map{|l|parse_string l, options}
90
+ end
91
+
92
+ def parse_string(string, options)#:nodoc:
93
+ symbolize_keys = options.delete(:symbolize_keys)
94
+ symbolize_keys = true if symbolize_keys.nil?
95
+
96
+ string.split("\t").inject({}) do |h, i|
97
+ (key, value) = i.split(':', 2)
98
+ key = key.to_sym if symbolize_keys
99
+ unescape!(value)
100
+ h[key] = value.empty? ? nil : value
101
+ h
102
+ end
103
+ end
104
+
105
+ def unescape!(string)#:nodoc:
106
+ return nil if !string || string == ''
107
+
108
+ string.gsub!(/\\([a-z\\])/) do |m|
109
+ case $1
110
+ when 'r'
111
+ "\r"
112
+ when 'n'
113
+ "\n"
114
+ when 't'
115
+ "\t"
116
+ when '\\'
117
+ '\\'
118
+ else
119
+ m
120
+ end
121
+ end
122
+ end
123
+
124
+ def escape(string)#:nodoc:
125
+ value = string.kind_of?(String) ? string.dup : string.to_s
126
+
127
+ value.gsub!("\\", "\\\\")
128
+ value.gsub!("\n", "\\n")
129
+ value.gsub!("\r", "\\r")
130
+ value.gsub!("\t", "\\t")
131
+
132
+ value
133
+ end
134
+
135
+ module_function :load, :parse, :dump, :parse_io, :parse_string, :unescape!, :escape
136
+
137
+ class <<self
138
+ private :parse_io, :parse_string, :unescape!, :escape
139
+ end
140
+ end
data/ltsv.gemspec ADDED
@@ -0,0 +1,21 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'ltsv'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "ltsv"
8
+ gem.version = LTSV::VERSION
9
+ gem.authors = ["condor"]
10
+ gem.email = ["condor1226@gmail.com"]
11
+ gem.description = %q{A Parser / Dumper for LTSV}
12
+ gem.summary = %q{A Parser / Dumper for LTSV}
13
+ gem.homepage = "https://github.com/condor/ltsv"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+
20
+ gem.add_development_dependency 'rspec'
21
+ end
data/spec/ltsv_spec.rb ADDED
@@ -0,0 +1,75 @@
1
+ require 'spec_helper'
2
+
3
+ describe LTSV do
4
+
5
+ describe :parse do
6
+ context 'String argument' do
7
+ it 'can parse labeled tab separated values into hash' do
8
+ line = "label1:value1\tlabel2:value2"
9
+ LTSV.parse(line).should == {:label1 => 'value1', :label2 => 'value2'}
10
+ end
11
+
12
+ it 'can parse the value that contains escape sequences' do
13
+
14
+ LTSV.parse("label1:value1\tlabel2:value\\nvalue").should ==
15
+ {:label1 => 'value1', :label2 => "value\nvalue"}
16
+
17
+ LTSV.parse("label1:value1\tlabel2:value\\rvalue").should ==
18
+ {:label1 => 'value1', :label2 => "value\rvalue"}
19
+
20
+ LTSV.parse("label1:value1\tlabel2:value\\tvalue").should ==
21
+ {:label1 => 'value1', :label2 => "value\tvalue"}
22
+
23
+ LTSV.parse("label1:value1\tlabel2:value\\\\value").should ==
24
+ {:label1 => 'value1', :label2 => "value\\value"}
25
+ end
26
+
27
+ it 'parses the value as-is when the backslash with a following ordinal character' do
28
+
29
+ LTSV.parse("label1:value1\tlabel2:value\\avalue").should ==
30
+ {:label1 => 'value1', :label2 => "value\\avalue"}
31
+ end
32
+
33
+ it 'parses the empty value field as nil' do
34
+ LTSV.parse("label1:\tlabel2:value2").should ==
35
+ {:label1 => nil, :label2 => 'value2'}
36
+ end
37
+ end
38
+ end
39
+
40
+ describe :load do
41
+ end
42
+
43
+ describe :dump do
44
+
45
+ specify 'dump into the format "label1:value1\tlabel2:value2"' do
46
+ LTSV.dump({:label1 => "value1", :label2 => "value2"}).should ==
47
+ "label1:value1\tlabel2:value2"
48
+ end
49
+
50
+ specify 'CRs, LFs, TABs, and backslashes in the value should be escaped' do
51
+ LTSV.dump({:label1 => "value\rvalue"}).should == "label1:value\\rvalue"
52
+ LTSV.dump({:label1 => "value\nvalue"}).should == "label1:value\\nvalue"
53
+ LTSV.dump({:label1 => "value\tvalue"}).should == "label1:value\\tvalue"
54
+ LTSV.dump({:label1 => "value\\value"}).should == "label1:value\\value"
55
+ end
56
+
57
+ specify ':s in the value should not be escaped' do
58
+ LTSV.dump({:label1 => "value:value"}).should == "label1:value:value"
59
+ end
60
+
61
+ specify 'should not fail when object to dump responds to :to_hash' do
62
+ target = Object.new
63
+ target.instance_eval do
64
+ def to_hash
65
+ {:label => 'value'}
66
+ end
67
+ end
68
+ LTSV.dump(target).should == "label:value"
69
+ end
70
+
71
+ specify 'fails when object to dump does not respond to :to_hash' do
72
+ lambda{LTSV.dump(Object.new)}.should raise_exception(ArgumentError)
73
+ end
74
+ end
75
+ end
@@ -0,0 +1 @@
1
+ require File.expand_path(File.join(File.dirname(__FILE__), '..', 'lib', 'ltsv'))
metadata ADDED
@@ -0,0 +1,72 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: ltsv
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - condor
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-02-07 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ! '>='
28
+ - !ruby/object:Gem::Version
29
+ version: '0'
30
+ description: A Parser / Dumper for LTSV
31
+ email:
32
+ - condor1226@gmail.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - .gitignore
38
+ - Gemfile
39
+ - LICENSE.txt
40
+ - README.md
41
+ - Rakefile
42
+ - lib/ltsv.rb
43
+ - ltsv.gemspec
44
+ - spec/ltsv_spec.rb
45
+ - spec/spec_helper.rb
46
+ homepage: https://github.com/condor/ltsv
47
+ licenses: []
48
+ post_install_message:
49
+ rdoc_options: []
50
+ require_paths:
51
+ - lib
52
+ required_ruby_version: !ruby/object:Gem::Requirement
53
+ none: false
54
+ requirements:
55
+ - - ! '>='
56
+ - !ruby/object:Gem::Version
57
+ version: '0'
58
+ required_rubygems_version: !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ! '>='
62
+ - !ruby/object:Gem::Version
63
+ version: '0'
64
+ requirements: []
65
+ rubyforge_project:
66
+ rubygems_version: 1.8.23
67
+ signing_key:
68
+ specification_version: 3
69
+ summary: A Parser / Dumper for LTSV
70
+ test_files:
71
+ - spec/ltsv_spec.rb
72
+ - spec/spec_helper.rb