toml 0.0.1 → 0.0.2

Sign up to get free protection for your applications and to get access to all the features.
data/Gemfile ADDED
@@ -0,0 +1,8 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in toml.gemspec
4
+ gemspec
5
+
6
+ group :test do
7
+ gem 'minitest'
8
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,20 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ toml (0.0.1)
5
+ parslet
6
+
7
+ GEM
8
+ remote: https://rubygems.org/
9
+ specs:
10
+ blankslate (2.1.2.4)
11
+ minitest (4.6.1)
12
+ parslet (1.5.0)
13
+ blankslate (~> 2.0)
14
+
15
+ PLATFORMS
16
+ ruby
17
+
18
+ DEPENDENCIES
19
+ minitest
20
+ toml!
data/README.md CHANGED
@@ -1,69 +1,35 @@
1
- RakeGem
2
- =======
1
+ # TOML
3
2
 
4
- # DESCRIPTION
3
+ A sane configuration format from @mojombo. More information here: https://github.com/mojombo/toml
5
4
 
6
- Ever wanted to manage your RubyGem in a sane way without having to resort to
7
- external dependencies like Jeweler or Hoe? Ever thought that Rake and a hand
8
- crafted gemspec should be enough to deal with these problems? If so, then
9
- RakeGem is here to make your life awesome!
5
+ ## Usage
10
6
 
11
- RakeGem is not a library. It is just a few simple file templates that you can
12
- copy into your project and easily customize to match your specific needs. It
13
- ships with a few Rake tasks to help you keep your gemspec up-to-date, build
14
- a gem, and release your library and gem to the world.
7
+ It's simple, really.
15
8
 
16
- RakeGem assumes you are using Git. This makes the Rake tasks easy to write. If
17
- you are using something else, you should be able to get RakeGem up and running
18
- with your system without too much editing.
9
+ ```ruby
10
+ content = <<-TOML
19
11
 
20
- The RakeGem tasks were inspired by the
21
- [Sinatra](http://github.com/sinatra/sinatra) project.
12
+ # Hello, this is an example.
13
+ [things]
14
+ other = "things"
15
+ what = 900000
22
16
 
23
- # INSTALLATION
17
+ TOML
24
18
 
25
- Take a look at `Rakefile` and `NAME.gemspec`. For new projects, you can start
26
- with these files and edit a few lines to make them fit into your library. If
27
- you have an existing project, you'll probably want to take the RakeGem
28
- versions and copy any custom stuff from your existing Rakefile and gemspec
29
- into them. As long as you're careful, the rake tasks should keep working.
19
+ parser = TOML::Parser.new(content).parsed
20
+ # => { "things" => { "other" => "things", "what" => 900000 } }
21
+ ```
30
22
 
31
- # ASSUMPTIONS
23
+ You can also use the same API as `YAML` if you'd like:
32
24
 
33
- RakeGem makes a few assumptions. You will either need to satisfy these
34
- assumptions or modify the rake tasks to work with your setup.
25
+ ```ruby
26
+ TOML.load("thing = 9")
27
+ # => {"thing" => 9}
35
28
 
36
- You should have a file named `lib/NAME.rb` (where NAME is the name of your
37
- library) that contains a version line. It should look something like this:
29
+ TOML.load_file("my_file.toml")
30
+ # => {"whatever" => "keys"}
31
+ ```
38
32
 
39
- module NAME
40
- VERSION = '0.1.0'
41
- end
33
+ ## Contributors
42
34
 
43
- It is important that you use the constant `VERSION` and that it appear on a
44
- line by itself.
45
-
46
- # UPDATING THE VERSION
47
-
48
- In order to make a new release, you'll want to update the version. With
49
- RakeGem, you only need to do that in the `lib/NAME.rb` file. Everything else
50
- will use this find the canonical version of the library.
51
-
52
- # TASKS
53
-
54
- RakeGem provides three rake tasks:
55
-
56
- `rake gemspec` will update your gemspec with the latest version (taken from
57
- the `lib/NAME.rb` file) and file list (as reported by `git ls-files`).
58
-
59
- `rake build` will update your gemspec, build your gemspec into a gem, and
60
- place it in the `pkg` directory.
61
-
62
- `rake release` will update your gemspec, build your gem, make a commit with
63
- the message `Release 0.1.0` (with the correct version, obviously), tag the
64
- commit with `v0.1.0` (again with the correct version), and push the `master`
65
- branch and new tag to `origin`.
66
-
67
- Keep in mind that these are just simple Rake tasks and you can edit them
68
- however you please. Don't want to auto-commit or auto-push? Just delete those
69
- lines. You can bend RakeGem to your own needs. That's the whole point!
35
+ Written by Jeremy McAnally (@jm) and Dirk Gadsden (@dirk) based on TOML from Tom Preston-Werner (@mojombo).
data/lib/toml.rb CHANGED
@@ -1,8 +1,22 @@
1
1
  $:.unshift(File.dirname(__FILE__))
2
2
 
3
3
  require 'time'
4
+ require 'parslet'
5
+
6
+ require 'toml/key'
7
+ require 'toml/key_group'
8
+ require 'toml/parslet'
9
+ require 'toml/transformer'
4
10
  require 'toml/parser'
5
11
 
6
12
  module TOML
7
- VERSION = '0.0.1'
13
+ VERSION = '0.0.2'
14
+
15
+ def self.load(content)
16
+ Parser.new(content).parsed
17
+ end
18
+
19
+ def self.load_file(path)
20
+ Parser.new(File.read(path)).parsed
21
+ end
8
22
  end
data/lib/toml/key.rb ADDED
@@ -0,0 +1,9 @@
1
+ module TOML
2
+ class Key
3
+ attr_reader :key, :value
4
+ def initialize(key, value)
5
+ @key = key
6
+ @value = value
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,9 @@
1
+ module TOML
2
+ class KeyGroup
3
+ attr_reader :keys
4
+
5
+ def initialize(keys)
6
+ @keys = keys
7
+ end
8
+ end
9
+ end
data/lib/toml/parser.rb CHANGED
@@ -1,67 +1,40 @@
1
- module TOML
1
+ module TOML
2
2
  class Parser
3
3
  attr_reader :parsed
4
4
 
5
5
  def initialize(markup)
6
- lines = markup.split("\n").reject {|l| l.start_with?("#") }
6
+ # Make sure we have a newline on the end
7
+ markup += "\n" unless markup.end_with?("\n")
7
8
 
9
+ tree = Parslet.new.parse(markup)
10
+ parts = Transformer.new.apply(tree)
11
+
8
12
  @parsed = {}
9
- @current_key_group = ""
10
-
11
- lines.each do |line|
12
- if line.gsub(/\s/, '').empty?
13
- close_key_group
14
- elsif line =~ /^\s?\[(.*)\]/
15
- new_key_group($1)
16
- elsif line =~ /\s?(.*)=(.*)/
17
- add_key($1, $2)
13
+ @current = @parsed
14
+
15
+ parts.each do |part|
16
+ if part.is_a? Key
17
+ @current[part.key] = part.value
18
+ elsif part.is_a? KeyGroup
19
+ resolve_key_group(part)
18
20
  else
19
- raise "lmao i have no clue what you're doing: #{line}"
21
+ raise "Unrecognized part: #{part.inspect}"
20
22
  end
21
23
  end
22
24
  end
23
-
24
- def new_key_group(key_name)
25
- @current_key_group = key_name
26
- end
27
-
28
- def add_key(key, value)
29
- @parsed[@current_key_group] ||= {}
30
- @parsed[@current_key_group][key.strip] = coerce(strip_comments(value))
31
- end
32
-
33
- def strip_comments(text)
34
- text.split("#").first
35
- end
36
-
37
- def coerce(value)
38
- value = value.strip
39
- if value =~ /\[(.*)\]/
40
- # array
41
- array = $1.split(",").map {|s| s.strip.gsub(/\"(.*)\"/, '\1')}
42
- return array
43
- elsif value =~ /\"(.*)\"/
44
- # string
45
- return $1
46
- end
47
-
48
- begin
49
- time = Time.parse(value)
50
- return time
51
- rescue
52
- end
53
-
54
- begin
55
- int = Integer(value)
56
- return int
57
- rescue
25
+
26
+ def resolve_key_group(kg)
27
+ @current = @parsed
28
+
29
+ path = kg.keys.dup
30
+ while k = path.shift
31
+ if @current.has_key? k
32
+ # pass
33
+ else
34
+ @current[k] = {}
35
+ end
36
+ @current = @current[k]
58
37
  end
59
-
60
- raise "lol no clue what [#{value}] is"
61
- end
62
-
63
- def close_key_group
64
- @current_key_group = ""
65
38
  end
66
39
  end
67
- end
40
+ end
@@ -0,0 +1,67 @@
1
+ module TOML
2
+ class Parslet < ::Parslet::Parser
3
+ rule(:document) { (key_group | key_value | comment_line).repeat(0) }
4
+ root :document
5
+
6
+ rule(:value) {
7
+ array.as(:array) |
8
+ string |
9
+ datetime.as(:datetime) |
10
+ float.as(:float) |
11
+ integer.as(:integer) |
12
+ boolean
13
+ }
14
+
15
+ rule(:array) {
16
+ str("[") >> (
17
+ all_space >> value >>
18
+ (all_space >> str(",") >> all_space >> value).repeat(0) >>
19
+ all_space
20
+ ).maybe >> str("]")
21
+ }
22
+
23
+ rule(:key_value) { space >> key.as(:key) >> space >> str("=") >> space >> value >> space >> comment.maybe >> str("\n") >> all_space }
24
+ rule(:key_group) { space >> str("[") >> key_group_name.as(:key_group) >> str("]") >> space >> comment.maybe >> str("\n") >> all_space }
25
+
26
+ rule(:key) { match("[^. \t\\]]").repeat(1) }
27
+ rule(:key_group_name) { key.as(:key) >> (str(".") >> key.as(:key)).repeat(0) }
28
+
29
+ rule(:comment_line) { comment >> str("\n") >> all_space }
30
+ rule(:comment) { str("#") >> match("[^\n]").repeat(0) }
31
+
32
+ rule(:space) { match("[ \t]").repeat(0) }
33
+ rule(:all_space) { match("[ \t\r\n]").repeat(0) }
34
+
35
+ rule(:string) {
36
+ str('"') >> (
37
+ match("[^\"\\\\]") |
38
+ (str("\\") >> match("[0tnr\"\\\\]"))
39
+ ).repeat(0).as(:string) >> str('"')
40
+ }
41
+
42
+ rule(:sign) { str("-") }
43
+ rule(:integer) {
44
+ str("0") | (sign.maybe >> match("[1-9]") >> match("[0-9]").repeat(0))
45
+ }
46
+
47
+ rule(:float) {
48
+ sign.maybe >> match("[0-9]").repeat(1) >> str(".") >> match("[0-9]").repeat(1)
49
+ }
50
+
51
+ rule(:boolean) { str("true").as(:true) | str("false").as(:false) }
52
+
53
+ rule(:date) {
54
+ match("[0-9]").repeat(4,4) >> str("-") >>
55
+ match("[0-9]").repeat(2,2) >> str("-") >>
56
+ match("[0-9]").repeat(2,2)
57
+ }
58
+
59
+ rule(:time) {
60
+ match("[0-9]").repeat(2,2) >> str(":") >>
61
+ match("[0-9]").repeat(2,2) >> str(":") >>
62
+ match("[0-9]").repeat(2,2)
63
+ }
64
+
65
+ rule(:datetime) { date >> str("T") >> time >> str("Z") }
66
+ end
67
+ end
@@ -0,0 +1,74 @@
1
+ module TOML
2
+ class Transformer < ::Parslet::Transform
3
+ # Utility to properly handle escape sequences in parsed string.
4
+ def self.parse_string(val)
5
+ e = val.length
6
+ s = 0
7
+ o = []
8
+ while s < e
9
+ if val[s] == "\\"
10
+ s += 1
11
+ case val[s]
12
+ when "t"
13
+ o << "\t"
14
+ when "n"
15
+ o << "\n"
16
+ when "\\"
17
+ o << "\\"
18
+ when '"'
19
+ o << '"'
20
+ when "r"
21
+ o << "\r"
22
+ when "0"
23
+ o << "\0"
24
+ else
25
+ raise "Unexpected escape character: '\\#{val[s]}'"
26
+ end
27
+ else
28
+ o << val[s]
29
+ end
30
+ s += 1
31
+ end
32
+ o.join
33
+ end
34
+
35
+ # Clean up arrays
36
+ rule(:array => subtree(:ar)) { ar.is_a?(Array) ? ar : [ar] }
37
+
38
+ # Clean up simples (inside arrays)
39
+ rule(:integer => simple(:i)) { i.to_i }
40
+ rule(:float => simple(:f)) { f.to_f }
41
+ rule(:string => simple(:s)) {
42
+ Transformer.parse_string(s.to_s)
43
+ }
44
+ rule(:datetime => simple(:d)) { DateTime.iso8601(d) }
45
+ rule(:true => simple(:b)) { true }
46
+ rule(:false => simple(:b)) { false }
47
+
48
+ # TODO: Refactor to remove redundancy
49
+ rule(:key => simple(:k), :array => subtree(:ar)) { Key.new(k.to_s, ar) }
50
+ rule(:key => simple(:k), :integer => simple(:i)) { Key.new(k.to_s, i.to_i) }
51
+ rule(:key => simple(:k), :float => simple(:f)) { Key.new(k.to_s, f.to_f) }
52
+ rule(:key => simple(:k), :string => simple(:s)) {
53
+ Key.new(k.to_s, Transformer.parse_string(s.to_s))
54
+ }
55
+ rule(:key => simple(:k), :datetime => simple(:d)) {
56
+ Key.new(k.to_s, DateTime.iso8601(d))
57
+ }
58
+ rule(:key => simple(:k), :true => simple(:b)) { Key.new(k.to_s, true) }
59
+ rule(:key => simple(:k), :false => simple(:b)) { Key.new(k.to_s, false) }
60
+
61
+ # Make keys just be strings
62
+ rule(:key => simple(:k)) { k }
63
+
64
+ # Then objectify the key_groups
65
+ rule(:key_group => simple(:kg)) {
66
+ KeyGroup.new([kg.to_s])
67
+ }
68
+
69
+ # Captures array-like key-groups
70
+ rule(:key_group => subtree(:kg)) {
71
+ KeyGroup.new(kg.map &:to_s)
72
+ }
73
+ end
74
+ end
data/test/spec.toml ADDED
@@ -0,0 +1,43 @@
1
+ # Comment
2
+
3
+ # String
4
+ string = "string\n\t\"string"
5
+
6
+ # Integer
7
+ integer = 42
8
+
9
+ # Float
10
+ pi = 3.14159
11
+
12
+ # Booleans
13
+ true = true
14
+ false = false
15
+
16
+ # DateTime
17
+ datetime = 1979-05-27T07:32:00Z
18
+
19
+ # Keygroup
20
+ [a.b.c]
21
+ d = "test"
22
+
23
+ [e]
24
+ f = "test"
25
+
26
+ # Post line comment
27
+ [comments]
28
+ on = "a line" # with markup
29
+
30
+ # Multi-line arrays
31
+ [arrays]
32
+ # Simple array
33
+ simple = [1, 2, 3]
34
+
35
+ # Nested array
36
+ nested = [[[1], 2], 3]
37
+
38
+ multi = ["lines", "are",
39
+ "super", "cool", "lol",
40
+ "amirite"]
41
+
42
+ # Uneven spacing
43
+ uneven = [1, 2, 3, 4, 5 ]
@@ -0,0 +1,50 @@
1
+
2
+ require 'rubygems'
3
+ require 'bundler/setup'
4
+
5
+ require 'toml'
6
+ require 'minitest/autorun'
7
+
8
+ class TestParser < MiniTest::Unit::TestCase
9
+ def setup
10
+ filepath = File.join(File.dirname(__FILE__), 'spec.toml')
11
+ @doc = TOML::Parser.new(File.read(filepath)).parsed
12
+ end
13
+
14
+ def test_string
15
+ assert_equal @doc["string"], "string\n\t\"string"
16
+ end
17
+
18
+ def test_integer
19
+ assert_equal @doc["integer"], 42
20
+ end
21
+
22
+ def test_float
23
+ assert_equal @doc["pi"], 3.14159
24
+ end
25
+
26
+ def test_datetime
27
+ assert_equal @doc["datetime"], DateTime.iso8601("1979-05-27T07:32:00Z")
28
+ end
29
+
30
+ def test_booleans
31
+ assert_equal @doc["true"], true
32
+ assert_equal @doc["false"], false
33
+ end
34
+
35
+ def test_simple_array
36
+ assert_equal @doc["arrays"]["simple"], [1, 2, 3]
37
+ end
38
+
39
+ def test_nested_array
40
+ assert_equal @doc["arrays"]["nested"], [[[1], 2], 3]
41
+ end
42
+
43
+ def test_simple_keygroup
44
+ assert_equal @doc["e"]["f"], "test"
45
+ end
46
+
47
+ def test_nested_keygroup
48
+ assert_equal @doc["a"]["b"]["c"]["d"], "test"
49
+ end
50
+ end
data/toml.gemspec CHANGED
@@ -13,8 +13,8 @@ Gem::Specification.new do |s|
13
13
  ## If your rubyforge_project name is different, then edit it and comment out
14
14
  ## the sub! line in the Rakefile
15
15
  s.name = 'toml'
16
- s.version = '0.0.1'
17
- s.date = '2013-02-23'
16
+ s.version = '0.0.2'
17
+ s.date = '2013-02-24'
18
18
 
19
19
  ## Make sure your summary is short. The description may be as long
20
20
  ## as you like.
@@ -24,7 +24,7 @@ Gem::Specification.new do |s|
24
24
  ## List the primary authors. If there are a bunch of authors, it's probably
25
25
  ## better to set the email to an email list or something. If you don't have
26
26
  ## a custom homepage, consider using your GitHub URL or the like.
27
- s.authors = ["Jeremy McAnally"]
27
+ s.authors = ["Jeremy McAnally", "Dirk Gadsden"]
28
28
  s.email = 'jeremy@github.com'
29
29
  s.homepage = 'http://github.com/jm/toml'
30
30
 
@@ -36,17 +36,27 @@ Gem::Specification.new do |s|
36
36
  ## LICENSE files to the extra_rdoc_files list.
37
37
  s.rdoc_options = ["--charset=UTF-8"]
38
38
  s.extra_rdoc_files = %w[README.md LICENSE]
39
-
39
+
40
+ s.add_dependency 'parslet'
41
+
40
42
  ## Leave this section as-is. It will be automatically generated from the
41
43
  ## contents of your Git repository via the gemspec task. DO NOT REMOVE
42
44
  ## THE MANIFEST COMMENTS, they are used as delimiters by the task.
43
45
  # = MANIFEST =
44
46
  s.files = %w[
47
+ Gemfile
48
+ Gemfile.lock
45
49
  LICENSE
46
50
  README.md
47
51
  Rakefile
48
52
  lib/toml.rb
53
+ lib/toml/key.rb
54
+ lib/toml/key_group.rb
49
55
  lib/toml/parser.rb
56
+ lib/toml/parslet.rb
57
+ lib/toml/transformer.rb
58
+ test/spec.toml
59
+ test/test_parser.rb
50
60
  toml.gemspec
51
61
  ]
52
62
  # = MANIFEST =
metadata CHANGED
@@ -1,16 +1,33 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: toml
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:
8
8
  - Jeremy McAnally
9
+ - Dirk Gadsden
9
10
  autorequire:
10
11
  bindir: bin
11
12
  cert_chain: []
12
- date: 2013-02-23 00:00:00.000000000 Z
13
- dependencies: []
13
+ date: 2013-02-24 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: parslet
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ! '>='
21
+ - !ruby/object:Gem::Version
22
+ version: '0'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ! '>='
29
+ - !ruby/object:Gem::Version
30
+ version: '0'
14
31
  description: Parse your TOML, seriously.
15
32
  email: jeremy@github.com
16
33
  executables: []
@@ -19,11 +36,19 @@ extra_rdoc_files:
19
36
  - README.md
20
37
  - LICENSE
21
38
  files:
39
+ - Gemfile
40
+ - Gemfile.lock
22
41
  - LICENSE
23
42
  - README.md
24
43
  - Rakefile
25
44
  - lib/toml.rb
45
+ - lib/toml/key.rb
46
+ - lib/toml/key_group.rb
26
47
  - lib/toml/parser.rb
48
+ - lib/toml/parslet.rb
49
+ - lib/toml/transformer.rb
50
+ - test/spec.toml
51
+ - test/test_parser.rb
27
52
  - toml.gemspec
28
53
  homepage: http://github.com/jm/toml
29
54
  licenses: []
@@ -50,4 +75,5 @@ rubygems_version: 1.8.23
50
75
  signing_key:
51
76
  specification_version: 2
52
77
  summary: Parse your TOML.
53
- test_files: []
78
+ test_files:
79
+ - test/test_parser.rb