yamlr 2.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.
@@ -0,0 +1,25 @@
1
+ # -*- ruby -*-
2
+
3
+ require "autotest/restart"
4
+
5
+ # Autotest.add_hook :initialize do |at|
6
+ # at.testlib = "minitest/unit"
7
+ #
8
+ # at.extra_files << "../some/external/dependency.rb"
9
+ #
10
+ # at.libs << ":../some/external"
11
+ #
12
+ # at.add_exception "vendor"
13
+ #
14
+ # at.add_mapping(/dependency.rb/) do |f, _|
15
+ # at.files_matching(/test_.*rb$/)
16
+ # end
17
+ #
18
+ # %w(TestA TestB).each do |klass|
19
+ # at.extra_class_map[klass] = "test/test_misc.rb"
20
+ # end
21
+ # end
22
+
23
+ # Autotest.add_hook :run_command do |at|
24
+ # system "rake build"
25
+ # end
File without changes
@@ -0,0 +1,10 @@
1
+ === 1.0.0 / 2009-04-20
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday!
6
+
7
+ === 2.0.0 / 2015-03-22
8
+
9
+ * Changed gem name to Yamlr
10
+ * Works on Ruby 1.9.3 or higher
@@ -0,0 +1,35 @@
1
+ .autotest
2
+ History.txt
3
+ Manifest.txt
4
+ README.txt
5
+ Rakefile
6
+ bin/yamlr
7
+ lib/yamlr.rb
8
+ lib/yamlr/defaults.rb
9
+ lib/yamlr/errors.rb
10
+ lib/yamlr/indicators.rb
11
+ lib/yamlr/reader.rb
12
+ lib/yamlr/reader/builder.rb
13
+ lib/yamlr/reader/format.rb
14
+ lib/yamlr/reader/node.rb
15
+ lib/yamlr/reader/parser.rb
16
+ lib/yamlr/version.rb
17
+ lib/yamlr/writer.rb
18
+ lib/yamlr/writer/builder.rb
19
+ test/files/2009.yml
20
+ test/files/arrays.yml
21
+ test/files/blank.yml
22
+ test/files/comments.yml
23
+ test/files/hashes.yml
24
+ test/files/malformed.yml
25
+ test/files/mixed.yml
26
+ test/files/nested.yml
27
+ test/files/split.yml
28
+ test/test_reader.rb
29
+ test/test_reader_builder.rb
30
+ test/test_reader_format.rb
31
+ test/test_reader_parser.rb
32
+ test/test_writer.rb
33
+ test/test_writer_builder.rb
34
+ test/test_yamlr.rb
35
+ yamlr.gemspec
@@ -0,0 +1,53 @@
1
+ = Yamlr
2
+
3
+ * http://github.com/step1profit/yamlr
4
+ * http://seattlerb.org
5
+
6
+ == DESCRIPTION:
7
+
8
+ Yamlr is a minimal YAML parser written in Ruby.
9
+
10
+ == FEATURES:
11
+
12
+ * Parse Array/Hash/String
13
+ * Read/write .yml files
14
+ * Dotfile configurable
15
+ * Serialize keys
16
+ * Robust command line support
17
+ * No variables or fancy YAML features
18
+
19
+ == INSTALLATION:
20
+
21
+ gem install yamlr
22
+
23
+ == SYNOPSIS:
24
+
25
+ * FIX (code sample of usage)
26
+
27
+ == REQUIREMENTS:
28
+
29
+ * ruby 1.9.3 or higher
30
+
31
+ == LICENSE:
32
+
33
+ The MIT License (MIT)
34
+
35
+ Copyright (c) 2008 - 2015 SoAwesomeMan, Step1Profit
36
+
37
+ Permission is hereby granted, free of charge, to any person obtaining a copy
38
+ of this software and associated documentation files (the "Software"), to deal
39
+ in the Software without restriction, including without limitation the rights
40
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
41
+ copies of the Software, and to permit persons to whom the Software is
42
+ furnished to do so, subject to the following conditions:
43
+
44
+ The above copyright notice and this permission notice shall be included in
45
+ all copies or substantial portions of the Software.
46
+
47
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
48
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
49
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
50
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
51
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
52
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
53
+ THE SOFTWARE.
@@ -0,0 +1,24 @@
1
+ # -*- ruby -*-
2
+
3
+ require "rubygems"
4
+ require "hoe"
5
+
6
+ # Hoe.plugin :compiler
7
+ # Hoe.plugin :gem_prelude_sucks
8
+ # Hoe.plugin :inline
9
+ # Hoe.plugin :racc
10
+ # Hoe.plugin :rcov
11
+
12
+ # https://github.com/jbarnette/hoe-git
13
+ Hoe.plugin :git
14
+ Hoe.plugin :yard
15
+ # https://github.com/flavorjones/hoe-gemspec
16
+ Hoe.plugin :gemspec
17
+
18
+ Hoe.spec "yamlr" do
19
+ developer('Step1Profit', 'sales@step1profit.com')
20
+
21
+ license "MIT"
22
+ end
23
+
24
+ # vim: syntax=ruby
@@ -0,0 +1,68 @@
1
+ #!/usr/bin/env ruby
2
+ require 'rubygems'
3
+ require 'optparse'
4
+ require 'pp'
5
+
6
+ $:.unshift File.join(File.dirname(__FILE__), '..', 'lib')
7
+ require 'yamlr'
8
+
9
+ module YamlrOpt
10
+ DESC = Yamlr::Defaults.descriptions
11
+ def self.command
12
+ hsh = {}
13
+ hsh[:opt] = {}
14
+ ops = nil
15
+ OptionParser.new do |opts|
16
+ opts.banner = "Yamlr #{Yamlr::VERSION}"
17
+ opts.on("-r", "--read FILENAME", String, String, ".yml file to Ruby Hash or Array") do |x|
18
+ hsh[:cmd] = 1
19
+ hsh[:arg] = [x]
20
+ end
21
+
22
+ opts.on("-w", "--write FILENAME,FILENAME", Array, "Hash or Array => .yml file") do |x|
23
+ hsh[:cmd] = 2
24
+ hsh[:arg] = x
25
+ end
26
+
27
+ opts.on("-d", "--dotfile [HOME]", String, "Create .yamlrc dotfile") do |x|
28
+ x.nil? ? (y = Yamlr.dotfile) : (y = Yamlr.dotfile(x))
29
+ z = "#{File.dirname(y).chomp("/")}/.yamlrc"
30
+ puts "existed: #{z}, moved to: #{y}" if File.basename(y) != ".yamlrc"
31
+ puts "created: #{z}"
32
+ return
33
+ end
34
+
35
+ opts.on("-v", "--version", "Display verison number") do
36
+ STDOUT.write("Yamlr #{Yamlr::VERSION}\n\r")
37
+ return
38
+ end
39
+
40
+ opts.on("-h", "--help", "Show this message") do
41
+ puts opts
42
+ return
43
+ end
44
+
45
+ Yamlr::Defaults.constants.sort.each do |x|
46
+ next if x == "DOTFILE"
47
+ opts.on("--[no-]#{x.downcase}", DESC[x.downcase]) do |y|
48
+ hsh[:opt]["#{x.downcase}".to_sym] = y
49
+ puts "use options with --read or --write" && return if hsh[:cmd].nil?
50
+ end
51
+ end
52
+ ops = opts
53
+ end.parse!
54
+ puts ops unless hsh.has_key?(:cmd)
55
+ return unless hsh.has_key?(:cmd)
56
+ self.run(hsh[:cmd], hsh[:arg], hsh[:opt])
57
+ end
58
+
59
+ def self.run(cmd, arg, opt)
60
+ x = Yamlr.read(arg[0], opt)
61
+ case cmd
62
+ when 1 then pp x
63
+ when 2 then Yamlr.write(arg[0], arg[1], opt)
64
+ end
65
+ end
66
+ end
67
+
68
+ YamlrOpt.command
@@ -0,0 +1,46 @@
1
+ require 'yamlr/version'
2
+ require 'yamlr/reader'
3
+ require 'yamlr/writer'
4
+ require 'yamlr/indicators'
5
+ require 'yamlr/defaults'
6
+
7
+ module Yamlr
8
+ DOTFILE = "#{ENV['HOME']}/.yamlr"
9
+
10
+ # File to Hash or Array
11
+ #
12
+ def self.read(input, options = {})
13
+ Yamlr::Reader.read(input, self.options(options))
14
+ end
15
+
16
+ # Hash or Array to .yml Array
17
+ #
18
+ def self.parse(object, options = {})
19
+ Yamlr::Writer.build(object, self.options(options))
20
+ end
21
+
22
+ # Hash or Array to .yml file, e.g. filename.yml
23
+ #
24
+ def self.write(object, filename, options = {})
25
+ Yamlr::Writer.write(object, filename, self.options(options))
26
+ end
27
+
28
+ # writes a .yamlr file HOME, merges with options if :dot is true
29
+ #
30
+ def self.dotfile(home = ENV['HOME'])
31
+ Yamlr::Writer.dotfile(Yamlr::Defaults.options, home)
32
+ end
33
+
34
+ private
35
+
36
+ # options
37
+ #
38
+ def self.options(options = {})
39
+ opt = Yamlr::Defaults.options.merge(Yamlr::Indicators.options.merge(options))
40
+ dot_opt = File.exists?(DOTFILE) ? Yamlr::Reader.read(DOTFILE, opt.merge({:symbolize_keys => true})) : nil
41
+ unless opt[:dot]
42
+ opt = opt.merge(dot_opt) if (dot_opt && dot_opt[:dot])
43
+ end
44
+ opt
45
+ end
46
+ end
@@ -0,0 +1,78 @@
1
+ module Yamlr
2
+ module Defaults
3
+ INT = true
4
+ INT_KEYS = false
5
+ INT_VALS = false
6
+ SYMBOLIZE = false
7
+ SYMBOLIZE_KEYS = false
8
+ SYMBOLIZE_VALS = false
9
+ SYM_STR = false
10
+ SYM_STR_KEYS = false
11
+ SYM_STR_VALS = false
12
+ AUTO_SYM = true
13
+ AUTO_SYM_KEYS = false
14
+ AUTO_SYM_VALS = false
15
+ AUTO_TRUE = true
16
+ AUTO_TRUE_KEYS = false
17
+ AUTO_TRUE_VALS = false
18
+ STRIP = true
19
+ STRIP_KEYS = false
20
+ STRIP_VALS = false
21
+ LIST = false
22
+ DOT = true
23
+ DOTFILE = "#{ENV['HOME']}/.yamlrc"
24
+ YAML = false
25
+ DOCS = false
26
+
27
+ def self.descriptions
28
+ { "auto_sym" => "conv keys & vals: \":both\" => :both",
29
+ "auto_sym_keys" => "conv keys: \":key\" => :key",
30
+ "auto_sym_vals" => "conv vals: \":val\" => :val",
31
+ "auto_true" => "conv keys & vals: \"true\" => true",
32
+ "auto_true_keys" => "conv keys: \"true\" => true",
33
+ "auto_true_vals" => "conv vals: \"true\" => true",
34
+ "docs" => "doc separator first level hash nodes",
35
+ "dot" => "use dotfile if it exists",
36
+ "int" => "conv keys & vals: \"1\" => 1",
37
+ "int_keys" => "conv keys: \"1\" => 1",
38
+ "int_vals" => "conv vals: \"1\" => 1",
39
+ "list" => "return hash of addresses & comments",
40
+ "strip" => "strip keys & vals: \" both \" => \"both\"",
41
+ "strip_keys" => "strip keys: \" key \" => \"key\"",
42
+ "strip_vals" => "strip vals: \" val \" => \"val\"",
43
+ "sym_str" => "conv str (no int) k & v: \"b\" => :b",
44
+ "sym_str_keys" => "conv string keys(no int): \"key\" => :key",
45
+ "sym_str_vals" => "conv string vals(no int): \"val\" => :val",
46
+ "symbolize" => "force conv keys & vals: \"both\" => :both",
47
+ "symbolize_keys" => "force conv keys: \"key\" => :key",
48
+ "symbolize_vals" => "force conv vals: \"val\" => :val",
49
+ "yaml" => "write files with YAML type array syntax"}
50
+ end
51
+
52
+ def self.options
53
+ { :int => INT,
54
+ :int_keys => INT_KEYS,
55
+ :int_vals => INT_VALS,
56
+ :symbolize => SYMBOLIZE,
57
+ :symbolize_keys => SYMBOLIZE_KEYS,
58
+ :symbolize_vals => SYMBOLIZE_VALS,
59
+ :sym_str => SYM_STR,
60
+ :sym_str_keys => SYM_STR_KEYS,
61
+ :sym_str_vals => SYM_STR_VALS,
62
+ :auto_sym => AUTO_SYM,
63
+ :auto_sym_keys => AUTO_SYM_KEYS,
64
+ :auto_sym_vals => AUTO_SYM_VALS,
65
+ :auto_true => AUTO_TRUE,
66
+ :auto_true_keys => AUTO_TRUE_KEYS,
67
+ :auto_true_vals => AUTO_TRUE_VALS,
68
+ :strip => STRIP,
69
+ :strip_keys => STRIP_KEYS,
70
+ :strip_vals => STRIP_VALS,
71
+ :list => LIST,
72
+ :dot => DOT,
73
+ :dotfile => DOTFILE,
74
+ :yaml => YAML,
75
+ :docs => DOCS}
76
+ end
77
+ end
78
+ end
@@ -0,0 +1,8 @@
1
+ module Yamlr
2
+ module Errors
3
+ class EmptyFilenameError < StandardError; end
4
+ class EmptyInputError < StandardError; end
5
+ class InvalidInputError < StandardError; end
6
+ class InvalidFilenameError < StandardError; end
7
+ end
8
+ end
@@ -0,0 +1,25 @@
1
+ module Yamlr
2
+ module Indicators
3
+ DOC_START = '---'
4
+ DOC_TERM = '...'
5
+ HASH = ':'
6
+ SYMBOL = ':'
7
+ ARRAY = '-'
8
+ COMMENT = '#'
9
+ LINE_END = "\n"
10
+ SPACE = "\s"
11
+ INDENT = 2
12
+
13
+ def self.options
14
+ { :doc_start => DOC_START,
15
+ :doc_term => DOC_TERM,
16
+ :hash => HASH,
17
+ :symbol => SYMBOL,
18
+ :array => ARRAY,
19
+ :comment => COMMENT,
20
+ :line_end => LINE_END,
21
+ :space => SPACE,
22
+ :indent => INDENT }
23
+ end
24
+ end
25
+ end
@@ -0,0 +1,33 @@
1
+ require 'yamlr/reader/node'
2
+ require 'yamlr/reader/format'
3
+ require 'yamlr/reader/builder'
4
+ require 'yamlr/reader/parser'
5
+
6
+ module Yamlr
7
+ module Reader
8
+ class EmptyFileError < StandardError; end
9
+ class EmptyInputError < StandardError; end
10
+ class InvalidInputError < StandardError; end
11
+ #
12
+ # parses file or string into Ruby Array, or Hash
13
+ #
14
+ def self.read(input, options)
15
+ raise Yamlr::Reader::EmptyInputError if input.is_a?(String) && input.strip.empty?
16
+ raise Yamlr::Reader::InvalidInputError unless input.is_a?(String) or input.is_a?(File)
17
+ input = File.exists?(input) ? IO.readlines(input) : input.split("\n")
18
+ raise Yamlr::Reader::EmptyFileError if input.empty?
19
+ hash = {}
20
+ lineno = 0
21
+ input.each {|line|
22
+ parsed_hash = Yamlr::Reader::Parser.parse(line, options, (lineno += 1))
23
+ formatted_hash = Yamlr::Reader::Format.format(parsed_hash)
24
+ Yamlr::Reader::Builder.build(hash, formatted_hash)
25
+ }
26
+ hash.delete(:adr)
27
+ case
28
+ when options[:list] then hash
29
+ when !options[:list] then hash.delete(:lst) && (hash.length == 1) ? hash[1] : hash
30
+ end
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,249 @@
1
+ module Yamlr
2
+ module Reader
3
+ module Builder
4
+ #
5
+ # Abbreviations:
6
+ # adr = address
7
+ # phs = parsed hash
8
+ # hsh = hash
9
+ # lst = list
10
+ # met = meta
11
+ # cur = current
12
+ # spc = spaces
13
+ # idt = indent
14
+ # idx = index
15
+ #
16
+ # dcs = doc_start
17
+ # dct = doc_term
18
+ # hpr = hsh_pair
19
+ # hky = hsh_key
20
+ # arr = array
21
+ # com = comment
22
+ # mal = malformed
23
+ # msg = message
24
+ # bla = blank
25
+ #
26
+ class RootNodeError < StandardError; end
27
+
28
+ ##
29
+ # adds parsed_hash of line to hash
30
+ #
31
+ def self.build(hsh, phs)
32
+ add = false
33
+ upd = nil
34
+ msg = phs[:msg]
35
+ self.doc_new(hsh) if hsh.empty? && msg != "doc"
36
+ val = phs[:val]
37
+ lst = hsh[:lst]
38
+
39
+ unless [:com, :mal, :bla].include?(msg)
40
+ cur = self.cur(hsh)
41
+ key = phs[:key]
42
+ spc = phs[:spc]
43
+ idt = phs[:opt][:indent]
44
+ adr = hsh[:adr]
45
+ idx = self.index(spc, idt)
46
+ upd = self.update(adr, idx)
47
+ las = self.adr_obj(hsh, hsh[:adr])
48
+ add = true
49
+ end
50
+
51
+ case msg
52
+ when :dcs then self.doc_start(hsh)
53
+ when :dct then self.doc_term(hsh)
54
+ when :hpr then self.hsh_pair(las, key, val)
55
+ when :hky then self.hsh_key(las, adr, key)
56
+ when :bla then self.blank(lst)
57
+ when :arr
58
+ raise RootNodeError if cur.is_a?(Hash) && !cur.empty? && spc == 0
59
+ if las.is_a?(Hash) && las.empty?
60
+ self.adr_obj_to_array(hsh, hsh[:adr])
61
+ las = self.adr_obj(hsh, hsh[:adr])
62
+ end
63
+ self.arr_parse(las, adr, val, upd)
64
+ when :com then self.comment(lst, val)
65
+ when :mal then self.malformed(lst, val)
66
+ end
67
+
68
+ self.add_to_list(lst, adr) if add
69
+ end
70
+
71
+ ##
72
+ # sub-parses array message, and logic from update-method
73
+ #
74
+ def self.arr_parse(las, adr, val, upd)
75
+ case upd
76
+ when "eq" then self.array_val(las, val)
77
+ when "lt" then self.array_val(las, val)
78
+ when "gt" then self.array_new(las, adr, val)
79
+ else; raise "error"
80
+ end
81
+ end
82
+
83
+ # current hash, returns array of "doc"
84
+ #
85
+ def self.cur(hsh)
86
+ hsh[hsh.length - 2]
87
+ end
88
+
89
+ # address array to string usable in eval on root node
90
+ #
91
+ def self.to_adr(adr)
92
+ m = adr.map {|x|
93
+ case
94
+ when x.is_a?(Symbol) then "[:#{x}]"
95
+ when x.is_a?(String) then "['#{x}']"
96
+ when x.is_a?(Integer) then "[#{x}]"
97
+ end
98
+ }
99
+ m.join
100
+ end
101
+
102
+ # returns the actual object at an address in tree
103
+ #
104
+ def self.adr_obj(hsh, adr)
105
+ m = self.to_adr(adr)
106
+ eval("hsh#{m.to_s}")
107
+ end
108
+
109
+ # converts an object in tree to empty array
110
+ #
111
+ def self.adr_obj_to_array(hsh, adr)
112
+ m = self.to_adr(adr)
113
+ eval("hsh#{m.to_s} = []")
114
+ end
115
+
116
+ # calculates index based on spaces divided by indent unit
117
+ #
118
+ def self.index(spc, idt)
119
+ ((spc % idt) != 0) ? 0 : spc / idt
120
+ end
121
+
122
+ # if indentation less than before, jump up tree, remove extra indices
123
+ #
124
+ def self.update(adr, idx)
125
+ ret = nil
126
+ len = (adr.length - 1)
127
+ if idx < len
128
+ # remove indices after current index
129
+ adr.replace(adr[0..idx])
130
+ ret = "lt"
131
+ elsif idx == len
132
+ ret = "eq"
133
+ elsif idx > len
134
+ ret = "gt"
135
+ end
136
+ ret
137
+ end
138
+
139
+ # create keypair for new doc
140
+ #
141
+ def self.doc_new(hsh)
142
+ hsh[:lst] ||= {}
143
+ hsh[:adr] ||= []
144
+ len = hsh.length - 1
145
+ hsh[len] = {}
146
+ hsh[:adr].clear
147
+ hsh[:adr] << len
148
+ end
149
+
150
+ # new array in tree, provides logic for last modified object
151
+ #
152
+ def self.array_new(las, adr, val)
153
+ case
154
+ when las.is_a?(Array) && las.empty?
155
+ x = [val]
156
+ las = x
157
+ adr << las.rindex(x)
158
+ when las.is_a?(Array) && las.last.is_a?(String) && las.last.empty?
159
+ x = [val]
160
+ las[-1] = [val]
161
+ adr << las.rindex(x)
162
+ when las.is_a?(Array)
163
+ x = [val]
164
+ las << x
165
+ adr << las.rindex(x)
166
+ end
167
+ end
168
+
169
+ # start document
170
+ #
171
+ def self.doc_start(hsh)
172
+ self.doc_new(hsh)
173
+ self.add_to_list(hsh[:lst], "#DOC_START")
174
+ end
175
+
176
+ # document terminate
177
+ #
178
+ def self.doc_term(hsh)
179
+ self.add_to_list(hsh[:lst], "#DOC_TERM")
180
+ end
181
+
182
+ # add val to array in tree
183
+ #
184
+ def self.array_val(las, val)
185
+ case
186
+ # when x is a hash, it's already addressed
187
+ when las.is_a?(Array)
188
+ las << val
189
+ else
190
+ raise las.inspect
191
+ end
192
+ end
193
+
194
+ # add hashkey to tree
195
+ #
196
+ def self.hsh_key(las, adr, key)
197
+ case
198
+ when las.is_a?(Hash)
199
+ las[key] = {}
200
+ when las.is_a?(Array)
201
+ x = {key => {}}
202
+ las << x
203
+ adr << las.rindex(x)
204
+ end
205
+ adr << key
206
+ end
207
+
208
+ # add hashpair to tree
209
+ #
210
+ def self.hsh_pair(las, key, val)
211
+ case
212
+ when las.is_a?(Array)
213
+ x = {key => val}
214
+ las << x
215
+ when las.is_a?(Hash)
216
+ las[key] = val
217
+ end
218
+ end
219
+
220
+ # list stores hsh addresses of lines, comments, etc.
221
+ #
222
+ def self.add_to_list(lst, adr)
223
+ case adr.class.to_s
224
+ when "String" then x = adr
225
+ when "Array" then x = adr.join(",")
226
+ end
227
+ lst[lst.length + 1] = x
228
+ end
229
+
230
+ # add blank to list
231
+ #
232
+ def self.blank(lst)
233
+ self.add_to_list(lst, "#BLANK")
234
+ end
235
+
236
+ # add comment to list
237
+ #
238
+ def self.comment(lst, val)
239
+ lst[lst.length + 1] = "#COMMENT: #{val}"
240
+ end
241
+
242
+ # add malformed to list
243
+ #
244
+ def self.malformed(lst, val)
245
+ lst[lst.length + 1] = "#MALFORMED: #{val}"
246
+ end
247
+ end
248
+ end
249
+ end