yamlr 2.0.0

Sign up to get free protection for your applications and to get access to all the features.
@@ -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