fini 1.0.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.
- checksums.yaml +7 -0
- data/.gitignore +19 -0
- data/.yardopts +2 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +128 -0
- data/Rakefile +1 -0
- data/fini.gemspec +23 -0
- data/lib/fini.rb +132 -0
- data/lib/fini/version.rb +3 -0
- data/spec/fini_spec.rb +74 -0
- data/spec/ini/file.ini +20 -0
- data/spec/ini_spec.rb +10 -0
- data/spec/spec_helper.rb +1 -0
- metadata +88 -0
checksums.yaml
ADDED
@@ -0,0 +1,7 @@
|
|
1
|
+
---
|
2
|
+
SHA1:
|
3
|
+
metadata.gz: d439dd98e260d3baefa2aae841441819e5198346
|
4
|
+
data.tar.gz: 308f39f9f6630fa1e2425ada57231d7e71b33506
|
5
|
+
SHA512:
|
6
|
+
metadata.gz: 9d37ba9f66fd6404daff64826db1aedbc58141f8d1c871d4da8b124be8005bf5dde1e763aa81191d5b0362f54e748d77e6b82bd7be17221f6105b891eeb7c0e8
|
7
|
+
data.tar.gz: 3a56a495c1d59c80483e16bd7a53023fa1e617a60c88fff05a3fe5ae493ed08448475cf5b94708f451c90d903e3d30a4290fb26b1fe68c6ca8c6bb6b798dc53a
|
data/.gitignore
ADDED
data/.yardopts
ADDED
data/Gemfile
ADDED
data/LICENSE.txt
ADDED
@@ -0,0 +1,22 @@
|
|
1
|
+
Copyright (c) 2013 Mikael Arvola
|
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,128 @@
|
|
1
|
+
# Fini
|
2
|
+
|
3
|
+
Fini is a performance-optimized ini parser that supports strings, booleans, integers and floats.
|
4
|
+
|
5
|
+
## Syntax for ini files
|
6
|
+
|
7
|
+
* A section is marked with [section]
|
8
|
+
* Key-value pairs are separated by an equals sign, eg. "key = value"
|
9
|
+
* Quotations for strings are optional
|
10
|
+
* Lines beginning with ; are comments
|
11
|
+
* Literal "true" and "false" (without quotes) as the value will be converted to boolean
|
12
|
+
* A number without a period will be converted into an integer
|
13
|
+
* A number with one period will be converted into a float
|
14
|
+
* Keys can be any arbitrary string as long as it doesn't contain an equals sign, and are kept as strings
|
15
|
+
|
16
|
+
Sections can also have sub-sections by appending the sub-section's name to the section with a
|
17
|
+
period, eg: [section.subsection]
|
18
|
+
|
19
|
+
Subsections will be hashes in the section with the sub-section name as the key (symbol).
|
20
|
+
|
21
|
+
### Sample ini content
|
22
|
+
|
23
|
+
A basic ini might look like this:
|
24
|
+
|
25
|
+
```ini
|
26
|
+
[section]
|
27
|
+
string = some text
|
28
|
+
boolean = true
|
29
|
+
number = 1
|
30
|
+
|
31
|
+
[section.subsection]
|
32
|
+
key = value
|
33
|
+
```
|
34
|
+
|
35
|
+
## Installation
|
36
|
+
|
37
|
+
Add this line to your application's Gemfile:
|
38
|
+
|
39
|
+
gem 'fini'
|
40
|
+
|
41
|
+
And then execute:
|
42
|
+
|
43
|
+
$ bundle
|
44
|
+
|
45
|
+
Or install it yourself as:
|
46
|
+
|
47
|
+
$ gem install fini
|
48
|
+
|
49
|
+
## Usage
|
50
|
+
|
51
|
+
The quickest way to use Fini is to invoke the module method `parse`:
|
52
|
+
|
53
|
+
```ruby
|
54
|
+
require 'fini'
|
55
|
+
|
56
|
+
data = Fini.parse(ini)
|
57
|
+
```
|
58
|
+
|
59
|
+
The `data` variable will now contain an `IniObject` where each section is a method. If using the sample ini from
|
60
|
+
above, you might access data like this:
|
61
|
+
|
62
|
+
```ruby
|
63
|
+
data.section 'string'
|
64
|
+
# => "some text"
|
65
|
+
|
66
|
+
# Array notation works as well
|
67
|
+
data.section['string']
|
68
|
+
# => "some text"
|
69
|
+
```
|
70
|
+
|
71
|
+
You can also supply a default value as a second argument, which is `nil` by default:
|
72
|
+
|
73
|
+
```ruby
|
74
|
+
data.section 'does not exist', 'none'
|
75
|
+
# => "none"
|
76
|
+
|
77
|
+
data.section 'does not exist'
|
78
|
+
# => nil
|
79
|
+
```
|
80
|
+
|
81
|
+
### Subsections
|
82
|
+
|
83
|
+
Fini also supports subsections. Here's an ini file with a use case:
|
84
|
+
|
85
|
+
```ini
|
86
|
+
[server.testing]
|
87
|
+
database = foo
|
88
|
+
user = fooman
|
89
|
+
password = foopass
|
90
|
+
|
91
|
+
[server.staging]
|
92
|
+
database = stagefoo
|
93
|
+
user = foomanstage
|
94
|
+
password = foopassstage
|
95
|
+
```
|
96
|
+
|
97
|
+
Subsections are added as symbol keys to the section, and are just basic hashes from there on:
|
98
|
+
|
99
|
+
```ruby
|
100
|
+
data.server[:testing]
|
101
|
+
# => {'database' => 'foo', 'user' => 'fooman', 'password' => 'foopass'}
|
102
|
+
|
103
|
+
data.server[:testing]['database']
|
104
|
+
# => "foo"
|
105
|
+
```
|
106
|
+
|
107
|
+
### Instance usage
|
108
|
+
|
109
|
+
If an instance object is needed, Fini has `Fini::Ini` with the methods `parse` and `load`. `load` is a
|
110
|
+
convenience method for loading an ini file instead of a string.
|
111
|
+
|
112
|
+
```ruby
|
113
|
+
require 'fini'
|
114
|
+
|
115
|
+
parser = Fini::Ini.new
|
116
|
+
data = parser.parse(ini)
|
117
|
+
|
118
|
+
data2 = parser.load('path/to/ini.ini')
|
119
|
+
```
|
120
|
+
|
121
|
+
## Planned features
|
122
|
+
|
123
|
+
* An option to convert all keys to symbols
|
124
|
+
* Support for escape characters in quoted strings
|
125
|
+
* Support for symbol values
|
126
|
+
|
127
|
+
## License
|
128
|
+
[MIT license](http://www.opensource.org/licenses/MIT).
|
data/Rakefile
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require "bundler/gem_tasks"
|
data/fini.gemspec
ADDED
@@ -0,0 +1,23 @@
|
|
1
|
+
# -*- encoding: utf-8 -*-
|
2
|
+
lib = File.expand_path('../lib', __FILE__)
|
3
|
+
$LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
|
4
|
+
require 'fini/version'
|
5
|
+
|
6
|
+
Gem::Specification.new do |gem|
|
7
|
+
gem.name = "fini"
|
8
|
+
gem.version = Fini::VERSION
|
9
|
+
gem.authors = ["Mikael Arvola"]
|
10
|
+
gem.email = ["mikael@arvola.com"]
|
11
|
+
gem.description = %q{Fini is an ini-parser optimized for performance.}
|
12
|
+
gem.summary = %q{Fast ini parser}
|
13
|
+
gem.homepage = "https://github.com/arvola/fini"
|
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
|
+
gem.has_rdoc = 'yard'
|
20
|
+
|
21
|
+
gem.add_development_dependency "rspec"
|
22
|
+
gem.add_development_dependency "simplecov"
|
23
|
+
end
|
data/lib/fini.rb
ADDED
@@ -0,0 +1,132 @@
|
|
1
|
+
require_relative "fini/version"
|
2
|
+
|
3
|
+
module Fini
|
4
|
+
|
5
|
+
# Instance class for accessing Fini functionality
|
6
|
+
#
|
7
|
+
class Ini
|
8
|
+
def parse content
|
9
|
+
Fini.parse content
|
10
|
+
end
|
11
|
+
def load file
|
12
|
+
Fini.parse File.read(file)
|
13
|
+
end
|
14
|
+
end
|
15
|
+
|
16
|
+
NUMBER_REGEX = /\A[+-]?[\d]+\.?\d*\Z/
|
17
|
+
|
18
|
+
def self.is_number? string
|
19
|
+
# Regex has the best performance overall, and it doesn't take longer with
|
20
|
+
# long strings that are not numbers. In other words, getting a false is very fast,
|
21
|
+
# and getting a true is only slightly slower than using Float()
|
22
|
+
if NUMBER_REGEX === string
|
23
|
+
true
|
24
|
+
else
|
25
|
+
false
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
29
|
+
# A static method for parsing a string with ini content.
|
30
|
+
#
|
31
|
+
# @param [String] content Ini text to parse
|
32
|
+
# @return [Fini::IniObject] Parsed values
|
33
|
+
def self.parse content
|
34
|
+
source = content
|
35
|
+
data = IniObject.new
|
36
|
+
section = nil
|
37
|
+
source.lines do |line|
|
38
|
+
line.strip!
|
39
|
+
case line[0]
|
40
|
+
when '['
|
41
|
+
key = line.delete "[]"
|
42
|
+
section = data.get_section key
|
43
|
+
when ';'
|
44
|
+
next
|
45
|
+
else
|
46
|
+
next if section.nil?
|
47
|
+
pos = line.index '='
|
48
|
+
unless pos.nil?
|
49
|
+
key = line[0, pos]
|
50
|
+
val = line[(pos + 1)..-1]
|
51
|
+
unless key.nil? || val.nil?
|
52
|
+
key.strip!
|
53
|
+
val.strip!
|
54
|
+
if Fini.is_number? val
|
55
|
+
if val.include?('.')
|
56
|
+
section[key] = Float(val)
|
57
|
+
else
|
58
|
+
section[key] = Integer(val)
|
59
|
+
end
|
60
|
+
elsif (val[0] == '"' && val[-1] == '"') or (val[0] == "'" && val[-1] == "'")
|
61
|
+
section[key] = val.chop.reverse.chop.reverse
|
62
|
+
elsif val == "true"
|
63
|
+
section[key] = true
|
64
|
+
elsif val == "false"
|
65
|
+
section[key] = false
|
66
|
+
else
|
67
|
+
section[key] = val
|
68
|
+
end
|
69
|
+
end
|
70
|
+
end
|
71
|
+
end
|
72
|
+
end
|
73
|
+
@data = data
|
74
|
+
end
|
75
|
+
|
76
|
+
# An object that holds the data from the parsed ini-file. Sections are
|
77
|
+
# accessed as methods, which returns a hash.
|
78
|
+
class IniObject
|
79
|
+
def initialize
|
80
|
+
@data = {}
|
81
|
+
end
|
82
|
+
|
83
|
+
# @param [String] key
|
84
|
+
# @return [Hash]
|
85
|
+
def get_section key
|
86
|
+
if key.include? '.'
|
87
|
+
sections = key.split('.')
|
88
|
+
obj = do_get_section sections.shift
|
89
|
+
|
90
|
+
sections.each do |key|
|
91
|
+
key = key.to_sym
|
92
|
+
if obj.has_key? key
|
93
|
+
obj = obj[key]
|
94
|
+
else
|
95
|
+
obj = obj[key] = {}
|
96
|
+
end
|
97
|
+
end
|
98
|
+
obj
|
99
|
+
else
|
100
|
+
do_get_section key
|
101
|
+
end
|
102
|
+
end
|
103
|
+
|
104
|
+
protected
|
105
|
+
|
106
|
+
# @param [String] key
|
107
|
+
# @return [Hash]
|
108
|
+
def do_get_section key
|
109
|
+
key = key.to_sym
|
110
|
+
if @data.has_key? key
|
111
|
+
@data[key]
|
112
|
+
else
|
113
|
+
create_section key
|
114
|
+
end
|
115
|
+
end
|
116
|
+
|
117
|
+
# @param [Symbol] key
|
118
|
+
# @return [Hash]
|
119
|
+
def create_section key
|
120
|
+
data = @data[key] = {}
|
121
|
+
method = Proc.new do |k = nil, default = nil|
|
122
|
+
if k.nil?
|
123
|
+
data
|
124
|
+
else
|
125
|
+
data.has_key?(k) ? data[k] : default
|
126
|
+
end
|
127
|
+
end
|
128
|
+
define_singleton_method(key, method)
|
129
|
+
data
|
130
|
+
end
|
131
|
+
end
|
132
|
+
end
|
data/lib/fini/version.rb
ADDED
data/spec/fini_spec.rb
ADDED
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'spec_helper'
|
2
|
+
|
3
|
+
describe Fini do
|
4
|
+
let(:data) do
|
5
|
+
content = %q{
|
6
|
+
[section]
|
7
|
+
string = string
|
8
|
+
boolean = true
|
9
|
+
number = 1
|
10
|
+
float = 1.1
|
11
|
+
;[commented]
|
12
|
+
;foo = foo}
|
13
|
+
Fini.parse content
|
14
|
+
end
|
15
|
+
let (:subsection) do
|
16
|
+
content = %q{
|
17
|
+
[section]
|
18
|
+
string = string
|
19
|
+
|
20
|
+
[section.subsection]
|
21
|
+
key = value
|
22
|
+
|
23
|
+
[section2.subsection]
|
24
|
+
name = content}
|
25
|
+
Fini.parse content
|
26
|
+
end
|
27
|
+
|
28
|
+
describe "::is_number?" do
|
29
|
+
it "detects numbers correctly" do
|
30
|
+
expect(Fini.is_number?("100")).to be_true
|
31
|
+
expect(Fini.is_number?("100.1")).to be_true
|
32
|
+
expect(Fini.is_number?("0.9876543210")).to be_true
|
33
|
+
end
|
34
|
+
|
35
|
+
it "rejects non-numbers" do
|
36
|
+
expect(Fini.is_number?("1000000000-")).to be_false
|
37
|
+
expect(Fini.is_number?("a.123")).to be_false
|
38
|
+
expect(Fini.is_number?("123.123.123")).to be_false
|
39
|
+
end
|
40
|
+
end
|
41
|
+
|
42
|
+
describe Fini::IniObject do
|
43
|
+
it "has a section method" do
|
44
|
+
expect(data).to respond_to(:section)
|
45
|
+
end
|
46
|
+
end
|
47
|
+
|
48
|
+
describe "::parse" do
|
49
|
+
it "parses a string" do
|
50
|
+
expect(data.section).to include('string' => 'string')
|
51
|
+
end
|
52
|
+
|
53
|
+
it "parses a boolean" do
|
54
|
+
expect(data.section).to include('boolean' => true)
|
55
|
+
end
|
56
|
+
|
57
|
+
it "parses numbers" do
|
58
|
+
expect(data.section).to include('number' => 1)
|
59
|
+
expect(data.section).to include('float' => 1.1)
|
60
|
+
end
|
61
|
+
|
62
|
+
it "ignores comments" do
|
63
|
+
expect{data.commented}.to raise_error(NoMethodError)
|
64
|
+
end
|
65
|
+
|
66
|
+
it "parses subsections" do
|
67
|
+
expect(subsection.section[:subsection]).to include('key' => 'value')
|
68
|
+
end
|
69
|
+
|
70
|
+
it "creates sections automatically with subsections" do
|
71
|
+
expect(subsection.section2[:subsection]).to include('name' => 'content')
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
data/spec/ini/file.ini
ADDED
@@ -0,0 +1,20 @@
|
|
1
|
+
[section]
|
2
|
+
string = string
|
3
|
+
boolean = true
|
4
|
+
number = 1
|
5
|
+
|
6
|
+
[section.subsection]
|
7
|
+
key = value
|
8
|
+
|
9
|
+
[new.sub]
|
10
|
+
name = content
|
11
|
+
|
12
|
+
[server.testing]
|
13
|
+
database = foo
|
14
|
+
user = fooman
|
15
|
+
password = foopass
|
16
|
+
|
17
|
+
[server.staging]
|
18
|
+
database = stagefoo
|
19
|
+
user = foomanstage
|
20
|
+
password = foopassstage
|
data/spec/ini_spec.rb
ADDED
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
require_relative '../lib/fini'
|
metadata
ADDED
@@ -0,0 +1,88 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: fini
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 1.0.0
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Mikael Arvola
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
date: 2013-04-17 00:00:00.000000000 Z
|
12
|
+
dependencies:
|
13
|
+
- !ruby/object:Gem::Dependency
|
14
|
+
name: rspec
|
15
|
+
requirement: !ruby/object:Gem::Requirement
|
16
|
+
requirements:
|
17
|
+
- - '>='
|
18
|
+
- !ruby/object:Gem::Version
|
19
|
+
version: '0'
|
20
|
+
type: :development
|
21
|
+
prerelease: false
|
22
|
+
version_requirements: !ruby/object:Gem::Requirement
|
23
|
+
requirements:
|
24
|
+
- - '>='
|
25
|
+
- !ruby/object:Gem::Version
|
26
|
+
version: '0'
|
27
|
+
- !ruby/object:Gem::Dependency
|
28
|
+
name: simplecov
|
29
|
+
requirement: !ruby/object:Gem::Requirement
|
30
|
+
requirements:
|
31
|
+
- - '>='
|
32
|
+
- !ruby/object:Gem::Version
|
33
|
+
version: '0'
|
34
|
+
type: :development
|
35
|
+
prerelease: false
|
36
|
+
version_requirements: !ruby/object:Gem::Requirement
|
37
|
+
requirements:
|
38
|
+
- - '>='
|
39
|
+
- !ruby/object:Gem::Version
|
40
|
+
version: '0'
|
41
|
+
description: Fini is an ini-parser optimized for performance.
|
42
|
+
email:
|
43
|
+
- mikael@arvola.com
|
44
|
+
executables: []
|
45
|
+
extensions: []
|
46
|
+
extra_rdoc_files: []
|
47
|
+
files:
|
48
|
+
- .gitignore
|
49
|
+
- .yardopts
|
50
|
+
- Gemfile
|
51
|
+
- LICENSE.txt
|
52
|
+
- README.md
|
53
|
+
- Rakefile
|
54
|
+
- fini.gemspec
|
55
|
+
- lib/fini.rb
|
56
|
+
- lib/fini/version.rb
|
57
|
+
- spec/fini_spec.rb
|
58
|
+
- spec/ini/file.ini
|
59
|
+
- spec/ini_spec.rb
|
60
|
+
- spec/spec_helper.rb
|
61
|
+
homepage: https://github.com/arvola/fini
|
62
|
+
licenses: []
|
63
|
+
metadata: {}
|
64
|
+
post_install_message:
|
65
|
+
rdoc_options: []
|
66
|
+
require_paths:
|
67
|
+
- lib
|
68
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
69
|
+
requirements:
|
70
|
+
- - '>='
|
71
|
+
- !ruby/object:Gem::Version
|
72
|
+
version: '0'
|
73
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
74
|
+
requirements:
|
75
|
+
- - '>='
|
76
|
+
- !ruby/object:Gem::Version
|
77
|
+
version: '0'
|
78
|
+
requirements: []
|
79
|
+
rubyforge_project:
|
80
|
+
rubygems_version: 2.0.2
|
81
|
+
signing_key:
|
82
|
+
specification_version: 4
|
83
|
+
summary: Fast ini parser
|
84
|
+
test_files:
|
85
|
+
- spec/fini_spec.rb
|
86
|
+
- spec/ini/file.ini
|
87
|
+
- spec/ini_spec.rb
|
88
|
+
- spec/spec_helper.rb
|