hashery 1.5.0 → 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.
- data/.ruby +30 -17
- data/.yardopts +1 -0
- data/Config.rb +28 -0
- data/{QED.rdoc → DEMO.rdoc} +0 -0
- data/HISTORY.rdoc +37 -0
- data/LICENSE.txt +26 -0
- data/NOTICE.txt +46 -0
- data/README.rdoc +10 -7
- data/lib/hashery.rb +6 -6
- data/lib/hashery.yml +30 -17
- data/lib/hashery/association.rb +169 -109
- data/lib/hashery/casting_hash.rb +128 -135
- data/lib/hashery/core_ext.rb +89 -61
- data/lib/hashery/crud_hash.rb +365 -0
- data/lib/hashery/dictionary.rb +545 -345
- data/lib/hashery/fuzzy_hash.rb +177 -125
- data/lib/hashery/ini_hash.rb +321 -0
- data/lib/hashery/key_hash.rb +54 -179
- data/lib/hashery/linked_list.rb +245 -191
- data/lib/hashery/lru_hash.rb +292 -202
- data/lib/hashery/open_cascade.rb +133 -78
- data/lib/hashery/open_hash.rb +127 -61
- data/lib/hashery/ordered_hash.rb +128 -122
- data/lib/hashery/path_hash.rb +238 -0
- data/lib/hashery/property_hash.rb +144 -80
- data/lib/hashery/query_hash.rb +85 -29
- data/lib/hashery/stash.rb +7 -3
- data/lib/hashery/static_hash.rb +46 -41
- data/test/case_association.rb +65 -4
- data/test/case_dictionary.rb +149 -5
- data/test/{case_keyhash.rb → case_key_hash.rb} +20 -14
- data/test/case_lru_hash.rb +162 -0
- data/test/{case_opencascade.rb → case_open_cascade.rb} +4 -8
- data/test/case_open_hash.rb +87 -0
- data/test/case_query_hash.rb +226 -0
- data/test/helper.rb +8 -0
- metadata +33 -63
- data/COPYING.rdoc +0 -45
- data/lib/hashery/basic_object.rb +0 -74
- data/lib/hashery/basic_struct.rb +0 -288
- data/lib/hashery/basicobject.rb +0 -1
- data/lib/hashery/basicstruct.rb +0 -1
- data/lib/hashery/castinghash.rb +0 -1
- data/lib/hashery/fuzzyhash.rb +0 -1
- data/lib/hashery/ini.rb +0 -268
- data/lib/hashery/keyhash.rb +0 -1
- data/lib/hashery/linkedlist.rb +0 -1
- data/lib/hashery/lruhash.rb +0 -1
- data/lib/hashery/memoizer.rb +0 -64
- data/lib/hashery/open_object.rb +0 -1
- data/lib/hashery/opencascade.rb +0 -1
- data/lib/hashery/openhash.rb +0 -1
- data/lib/hashery/openobject.rb +0 -1
- data/lib/hashery/orderedhash.rb +0 -1
- data/lib/hashery/ostructable.rb +0 -186
- data/lib/hashery/propertyhash.rb +0 -1
- data/lib/hashery/queryhash.rb +0 -1
- data/lib/hashery/statichash.rb +0 -1
- data/qed/01_openhash.rdoc +0 -57
- data/qed/02_queryhash.rdoc +0 -21
- data/qed/03_castinghash.rdoc +0 -13
- data/qed/04_statichash.rdoc +0 -22
- data/qed/05_association.rdoc +0 -59
- data/qed/06_opencascade.rdoc +0 -58
- data/qed/07_fuzzyhash.rdoc +0 -141
- data/qed/08_properyhash.rdoc +0 -38
- data/qed/09_ostructable.rdoc +0 -56
- data/qed/applique/ae.rb +0 -1
- data/test/case_basicstruct.rb +0 -192
- data/test/case_openhash.rb +0 -22
data/lib/hashery/fuzzy_hash.rb
CHANGED
@@ -1,154 +1,206 @@
|
|
1
1
|
require 'set'
|
2
2
|
|
3
|
-
|
4
|
-
|
5
|
-
#
|
6
|
-
#
|
7
|
-
# This is useful when you want to have a lookup table that can either contain strings or regexes.
|
8
|
-
# For instance, you might want a catch all for certain regexes that perform a certain logic.
|
9
|
-
#
|
10
|
-
# >> hash = FuzzyHash.new
|
11
|
-
# >> hash[/^\d+$/] = 'number'
|
12
|
-
# >> hash[/.*/] = 'something'
|
13
|
-
# >> hash['chunky'] = 'bacon'
|
14
|
-
# >> hash['foo'] = 'vader'
|
15
|
-
#
|
16
|
-
# >> hash['foo']
|
17
|
-
# << 'vader'
|
18
|
-
# >> hash['food']
|
19
|
-
# << 'something'
|
20
|
-
# >> hash['123']
|
21
|
-
# << 'number'
|
22
|
-
#
|
23
|
-
#
|
24
|
-
|
25
|
-
class FuzzyHash
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
3
|
+
module Hashery
|
4
|
+
|
5
|
+
# FuzzyHash is a weird hash with special semantics for regex keys.
|
6
|
+
#
|
7
|
+
# This is useful when you want to have a lookup table that can either contain strings or regexes.
|
8
|
+
# For instance, you might want a catch all for certain regexes that perform a certain logic.
|
9
|
+
#
|
10
|
+
# >> hash = FuzzyHash.new
|
11
|
+
# >> hash[/^\d+$/] = 'number'
|
12
|
+
# >> hash[/.*/] = 'something'
|
13
|
+
# >> hash['chunky'] = 'bacon'
|
14
|
+
# >> hash['foo'] = 'vader'
|
15
|
+
#
|
16
|
+
# >> hash['foo']
|
17
|
+
# << 'vader'
|
18
|
+
# >> hash['food']
|
19
|
+
# << 'something'
|
20
|
+
# >> hash['123']
|
21
|
+
# << 'number'
|
22
|
+
#
|
23
|
+
# This class is based on Joshua Hull's original FuzzyHash class.
|
24
|
+
#
|
25
|
+
class FuzzyHash
|
26
|
+
|
27
|
+
#
|
28
|
+
#
|
29
|
+
#
|
30
|
+
def initialize(init_hash = nil)
|
31
|
+
@fuzzies = []
|
32
|
+
@hash_reverse = {}
|
33
|
+
@fuzzies_reverse = {}
|
34
|
+
@fuzzy_hash = {}
|
35
|
+
@hash = {}
|
36
|
+
init_hash.each{ |key,value| self[key] = value } if init_hash
|
37
|
+
end
|
35
38
|
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
39
|
+
#
|
40
|
+
#
|
41
|
+
#
|
42
|
+
def clear
|
43
|
+
hash.clear
|
44
|
+
fuzzies.clear
|
45
|
+
hash_reverse.clear
|
46
|
+
fuzzies_reverse.clear
|
47
|
+
end
|
42
48
|
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
49
|
+
#
|
50
|
+
#
|
51
|
+
#
|
52
|
+
def size
|
53
|
+
hash.size + fuzzies.size
|
54
|
+
end
|
47
55
|
|
56
|
+
alias_method :count, :size
|
48
57
|
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
o
|
53
|
-
|
58
|
+
#
|
59
|
+
#
|
60
|
+
#
|
61
|
+
def ==(o)
|
62
|
+
o.is_a?(FuzzyHash)
|
63
|
+
o.send(:hash) == hash &&
|
64
|
+
o.send(:fuzzies) == fuzzies
|
65
|
+
end
|
54
66
|
|
55
|
-
|
56
|
-
|
57
|
-
|
67
|
+
#
|
68
|
+
#
|
69
|
+
#
|
70
|
+
def empty?
|
71
|
+
hash.empty? && fuzzies.empty?
|
72
|
+
end
|
58
73
|
|
59
|
-
|
60
|
-
|
61
|
-
|
74
|
+
#
|
75
|
+
#
|
76
|
+
#
|
77
|
+
def keys
|
78
|
+
hash.keys + fuzzy_hash.keys
|
79
|
+
end
|
62
80
|
|
63
|
-
|
64
|
-
|
65
|
-
|
81
|
+
#
|
82
|
+
#
|
83
|
+
#
|
84
|
+
def values
|
85
|
+
hash.values + fuzzies.collect{|r| r.last}
|
86
|
+
end
|
66
87
|
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
88
|
+
#
|
89
|
+
#
|
90
|
+
#
|
91
|
+
def each
|
92
|
+
hash.each{|k,v| yield k,v }
|
93
|
+
fuzzies.each{|v| yield v.first, v.last }
|
94
|
+
end
|
71
95
|
|
72
|
-
|
73
|
-
|
74
|
-
|
96
|
+
#
|
97
|
+
#
|
98
|
+
#
|
99
|
+
def delete_value(value)
|
100
|
+
hash.delete(hash_reverse[value]) || ((rr = fuzzies_reverse[value]) && fuzzies.delete_at(rr[0]))
|
101
|
+
end
|
75
102
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
103
|
+
#
|
104
|
+
#
|
105
|
+
#
|
106
|
+
def []=(key, value)
|
107
|
+
if Regexp === key
|
108
|
+
fuzzies.delete_if{|f| f.first.inspect.hash == key.inspect.hash}
|
109
|
+
fuzzies_reverse.delete_if{|k, v| v[1].inspect.hash == key.inspect.hash}
|
110
|
+
hash_reverse.delete_if{|k,v| v.inspect.hash == key.inspect.hash}
|
111
|
+
|
112
|
+
fuzzy_hash[key] = value
|
113
|
+
fuzzies << [key, value]
|
114
|
+
reset_fuzz_test!
|
115
|
+
fuzzies_reverse[value] = [fuzzies.size - 1, key, value]
|
116
|
+
else
|
117
|
+
hash[key] = value
|
118
|
+
hash_reverse.delete_if{|k,v| v.hash == key.hash}
|
119
|
+
hash_reverse[value] = key
|
120
|
+
end
|
121
|
+
value
|
122
|
+
end
|
93
123
|
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
hash_reverse.
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
fuzzies_reverse.
|
104
|
-
|
124
|
+
#
|
125
|
+
#
|
126
|
+
#
|
127
|
+
def replace(src, dest)
|
128
|
+
if hash_reverse.key?(src)
|
129
|
+
key = hash_reverse[src]
|
130
|
+
hash[key] = dest
|
131
|
+
hash_reverse.delete(src)
|
132
|
+
hash_reverse[dest] = key
|
133
|
+
elsif fuzzies_reverse.key?(src)
|
134
|
+
key = fuzzies_reverse[src]
|
135
|
+
fuzzies[rkey[0]] = [rkey[1], dest]
|
136
|
+
fuzzies_reverse.delete(src)
|
137
|
+
fuzzies_reverse[dest] = [rkey[0], rkey[1], dest]
|
138
|
+
end
|
105
139
|
end
|
106
|
-
end
|
107
140
|
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
141
|
+
#
|
142
|
+
#
|
143
|
+
#
|
144
|
+
def [](key)
|
145
|
+
(hash.key?(key) && hash[key]) ||
|
146
|
+
((lookup = fuzzy_lookup(key)) && lookup && lookup.first) ||
|
147
|
+
fuzzy_hash[key]
|
148
|
+
end
|
113
149
|
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
150
|
+
#
|
151
|
+
#
|
152
|
+
#
|
153
|
+
def match_with_result(key)
|
154
|
+
if hash.key?(key)
|
155
|
+
[hash[key], key]
|
156
|
+
else
|
157
|
+
fuzzy_lookup(key)
|
158
|
+
end
|
119
159
|
end
|
120
|
-
end
|
121
160
|
|
122
161
|
private
|
123
|
-
attr_reader :fuzzies, :hash_reverse, :fuzzies_reverse, :hash, :fuzzy_hash
|
124
|
-
attr_writer :fuzz_test
|
125
162
|
|
126
|
-
|
127
|
-
|
128
|
-
end
|
163
|
+
attr_reader :fuzzies, :hash_reverse, :fuzzies_reverse, :hash, :fuzzy_hash
|
164
|
+
attr_writer :fuzz_test
|
129
165
|
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
166
|
+
#
|
167
|
+
#
|
168
|
+
#
|
169
|
+
def reset_fuzz_test!
|
170
|
+
self.fuzz_test = nil
|
171
|
+
end
|
172
|
+
|
173
|
+
#
|
174
|
+
#
|
175
|
+
#
|
176
|
+
def fuzz_test
|
177
|
+
unless @fuzz_test
|
178
|
+
@fuzz_test = Object.new
|
179
|
+
@fuzz_test.instance_variable_set(:'@fuzzies', fuzzies)
|
180
|
+
method = "
|
181
|
+
def match(str)
|
182
|
+
case str\n
|
183
|
+
"
|
184
|
+
fuzzies.each_with_index do |reg, index|
|
185
|
+
method << "when #{reg.first.inspect}; [@fuzzies[#{index}][1], Regexp.last_match(0)];"
|
186
|
+
end
|
187
|
+
method << "end\nend\n"
|
188
|
+
@fuzz_test.instance_eval method
|
140
189
|
end
|
141
|
-
|
142
|
-
@fuzz_test.instance_eval method
|
190
|
+
@fuzz_test
|
143
191
|
end
|
144
|
-
@fuzz_test
|
145
|
-
end
|
146
192
|
|
147
|
-
|
148
|
-
|
149
|
-
|
193
|
+
#
|
194
|
+
#
|
195
|
+
#
|
196
|
+
def fuzzy_lookup(key)
|
197
|
+
if !fuzzies.empty? && (value = fuzz_test.match(key))
|
198
|
+
value
|
199
|
+
end
|
150
200
|
end
|
201
|
+
|
151
202
|
end
|
152
203
|
|
153
204
|
end
|
154
205
|
|
206
|
+
# Copyright (c) 2009 Joshua Hull
|
@@ -0,0 +1,321 @@
|
|
1
|
+
module Hashery
|
2
|
+
|
3
|
+
# Hash class with methods to read from and write into ini files.
|
4
|
+
#
|
5
|
+
# A ini file is a text file in a specific format,
|
6
|
+
# it may include several fields which are sparated by
|
7
|
+
# field headlines which are enclosured by "[]".
|
8
|
+
# Each field may include several key-value pairs.
|
9
|
+
#
|
10
|
+
# Each key-value pair is represented by one line and
|
11
|
+
# the value is sparated from the key by a "=".
|
12
|
+
#
|
13
|
+
# == Examples
|
14
|
+
#
|
15
|
+
# === Example ini file
|
16
|
+
#
|
17
|
+
# # this is the first comment which will be saved in the comment attribute
|
18
|
+
# mail=info@example.com
|
19
|
+
# domain=example.com # this is a comment which will not be saved
|
20
|
+
# [database]
|
21
|
+
# db=example
|
22
|
+
# user=john
|
23
|
+
# passwd=very-secure
|
24
|
+
# host=localhost
|
25
|
+
# # this is another comment
|
26
|
+
# [filepaths]
|
27
|
+
# tmp=/tmp/example
|
28
|
+
# lib=/home/john/projects/example/lib
|
29
|
+
# htdocs=/home/john/projects/example/htdocs
|
30
|
+
# [ texts ]
|
31
|
+
# wellcome=Wellcome on my new website!
|
32
|
+
# Website description = This is only a example. # and another comment
|
33
|
+
#
|
34
|
+
# === Example object
|
35
|
+
#
|
36
|
+
# Ini#comment stores:
|
37
|
+
#
|
38
|
+
# "this is the first comment which will be saved in the comment attribute"
|
39
|
+
#
|
40
|
+
# Ini's internal hash stores:
|
41
|
+
#
|
42
|
+
# {
|
43
|
+
# "mail" => "info@example.com",
|
44
|
+
# "domain" => "example.com",
|
45
|
+
# "database" => {
|
46
|
+
# "db" => "example",
|
47
|
+
# "user" => "john",
|
48
|
+
# "passwd" => "very-secure",
|
49
|
+
# "host" => "localhost"
|
50
|
+
# },
|
51
|
+
# "filepaths" => {
|
52
|
+
# "tmp" => "/tmp/example",
|
53
|
+
# "lib" => "/home/john/projects/example/lib",
|
54
|
+
# "htdocs" => "/home/john/projects/example/htdocs"
|
55
|
+
# }
|
56
|
+
# "texts" => {
|
57
|
+
# "wellcome" => "Wellcome on my new website!",
|
58
|
+
# "Website description" => "This is only a example."
|
59
|
+
# }
|
60
|
+
# }
|
61
|
+
#
|
62
|
+
# As you can see this module gets rid of all comments, linebreaks
|
63
|
+
# and unnecessary spaces at the beginning and the end of each
|
64
|
+
# field headline, key or value.
|
65
|
+
#
|
66
|
+
# === Using the object
|
67
|
+
#
|
68
|
+
# Using the object is stright forward:
|
69
|
+
#
|
70
|
+
# ini = IniHash.new("path/settings.ini")
|
71
|
+
# ini["mail"] = "info@example.com"
|
72
|
+
# ini["filepaths"] = { "tmp" => "/tmp/example" }
|
73
|
+
# ini.comment = "This is\na comment"
|
74
|
+
# puts ini["filepaths"]["tmp"]
|
75
|
+
# # => /tmp/example
|
76
|
+
# ini.write()
|
77
|
+
#
|
78
|
+
# == Acknowlegements
|
79
|
+
#
|
80
|
+
# IniHash is based on ini.rb.
|
81
|
+
#
|
82
|
+
# Copyright (C) 2007 Jeena Paradies <info@jeenaparadies.net>
|
83
|
+
|
84
|
+
class IniHash
|
85
|
+
|
86
|
+
# TODO: Use class method for loading from file, not initializer.
|
87
|
+
|
88
|
+
#
|
89
|
+
# NOTE: In future versions, `#new` will not take a path, and `#load`
|
90
|
+
# will have to be used.
|
91
|
+
#
|
92
|
+
def self.load(path, load=true)
|
93
|
+
new(path, load)
|
94
|
+
end
|
95
|
+
|
96
|
+
#
|
97
|
+
# The hash which holds all INI data.
|
98
|
+
#
|
99
|
+
attr_accessor :inihash
|
100
|
+
|
101
|
+
#
|
102
|
+
# The string which holds the comments on the top of the file
|
103
|
+
#
|
104
|
+
attr_accessor :comment
|
105
|
+
|
106
|
+
#
|
107
|
+
# Creating a new IniHash object.
|
108
|
+
#
|
109
|
+
# path - is a path to the ini file
|
110
|
+
# load - if nil restores the data if possible
|
111
|
+
# if true restores the data, if not possible raises an error
|
112
|
+
# if false does not resotre the data
|
113
|
+
#
|
114
|
+
def initialize(path, load=nil)
|
115
|
+
@path = path if String === path
|
116
|
+
@inihash = (Hash === path ? path.dup : {})
|
117
|
+
|
118
|
+
if load or ( load.nil? and FileTest.readable_real? @path )
|
119
|
+
restore()
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
#
|
124
|
+
# Retrive the ini data for the key +key+
|
125
|
+
#
|
126
|
+
def [](key)
|
127
|
+
@inihash[key]
|
128
|
+
end
|
129
|
+
|
130
|
+
#
|
131
|
+
# Set the ini data for the key +key+
|
132
|
+
#
|
133
|
+
# key - Index key.
|
134
|
+
# value - The value to index.
|
135
|
+
#
|
136
|
+
# Returns +value+.
|
137
|
+
#
|
138
|
+
def []=(key, value)
|
139
|
+
#raise TypeError, "String expected" unless key.is_a? String
|
140
|
+
key = key.to_str
|
141
|
+
|
142
|
+
#raise TypeError, "String or Hash expected" unless value.is_a? String or value.is_a? Hash
|
143
|
+
value = value.to_str unless Hash === value
|
144
|
+
|
145
|
+
@inihash[key] = value
|
146
|
+
end
|
147
|
+
|
148
|
+
#
|
149
|
+
# Restores the data from file into the object
|
150
|
+
#
|
151
|
+
def restore
|
152
|
+
@inihash = IniHash.read_from_file(@path)
|
153
|
+
@comment = IniHash.read_comment_from_file(@path)
|
154
|
+
end
|
155
|
+
|
156
|
+
#
|
157
|
+
# Store data from the object in the file.
|
158
|
+
#
|
159
|
+
def save
|
160
|
+
IniHash.write_to_file(@path, @inihash, @comment)
|
161
|
+
end
|
162
|
+
|
163
|
+
#
|
164
|
+
# Deprecated: Save INI data to file path. Use #save instead.
|
165
|
+
#
|
166
|
+
def update
|
167
|
+
warn 'IniHash#update is deprecated for this use, use IniHash#save instead.'
|
168
|
+
save
|
169
|
+
end
|
170
|
+
|
171
|
+
#
|
172
|
+
# Convert to hash by duplicating the underlying hash table.
|
173
|
+
#
|
174
|
+
def to_h
|
175
|
+
@inihash.dup
|
176
|
+
end
|
177
|
+
|
178
|
+
alias :inspect :to_s
|
179
|
+
|
180
|
+
#
|
181
|
+
# Turn a hash (up to 2 levels deepness) into a ini string
|
182
|
+
#
|
183
|
+
# inihash - Hash representing the ini File. Default is a empty hash.
|
184
|
+
#
|
185
|
+
# Returns a string in the ini file format.
|
186
|
+
#
|
187
|
+
def to_s
|
188
|
+
str = ""
|
189
|
+
inihash.each do |key, value|
|
190
|
+
if value.is_a? Hash
|
191
|
+
str << "[#{key.to_s}]\n"
|
192
|
+
value.each do |under_key, under_value|
|
193
|
+
str << "#{under_key.to_s}=#{under_value.to_s unless under_value.nil?}\n"
|
194
|
+
end
|
195
|
+
else
|
196
|
+
str << "#{key.to_s}=#{value.to_s unless value.nil?}\n"
|
197
|
+
end
|
198
|
+
end
|
199
|
+
str
|
200
|
+
end
|
201
|
+
|
202
|
+
#
|
203
|
+
# Delegate missing mthods to underlying Hash.
|
204
|
+
#
|
205
|
+
# TODO: Sublcass Hash instead of delegating.
|
206
|
+
#
|
207
|
+
def method_missing(s,*a,&b)
|
208
|
+
@inihash.send(s, *a, &b) if @inihash.respond_to?(s)
|
209
|
+
end
|
210
|
+
|
211
|
+
#
|
212
|
+
# Reading data from file
|
213
|
+
#
|
214
|
+
# path - a path to the ini file
|
215
|
+
#
|
216
|
+
# Returns a `Hash` which represents the data from the file.
|
217
|
+
#
|
218
|
+
def self.read_from_file(path)
|
219
|
+
raise "file not found - #{path}" unless File.file?(path)
|
220
|
+
|
221
|
+
inihash = {}
|
222
|
+
headline = nil
|
223
|
+
|
224
|
+
IO.foreach(path) do |line|
|
225
|
+
line = line.strip.split(/#/)[0].to_s
|
226
|
+
|
227
|
+
# read it only if the line doesn't begin with a "=" and is long enough
|
228
|
+
unless line.length < 2 and line[0,1] == "="
|
229
|
+
|
230
|
+
# it's a headline if the line begins with a "[" and ends with a "]"
|
231
|
+
if line[0,1] == "[" and line[line.length - 1, line.length] == "]"
|
232
|
+
|
233
|
+
# get rid of the [] and unnecessary spaces
|
234
|
+
headline = line[1, line.length - 2 ].strip
|
235
|
+
inihash[headline] = {}
|
236
|
+
else
|
237
|
+
key, value = line.split(/=/, 2)
|
238
|
+
|
239
|
+
key = key.strip unless key.nil?
|
240
|
+
value = value.strip unless value.nil?
|
241
|
+
|
242
|
+
unless headline.nil?
|
243
|
+
inihash[headline][key] = value
|
244
|
+
else
|
245
|
+
inihash[key] = value unless key.nil?
|
246
|
+
end
|
247
|
+
end
|
248
|
+
end
|
249
|
+
end
|
250
|
+
|
251
|
+
inihash
|
252
|
+
end
|
253
|
+
|
254
|
+
#
|
255
|
+
# Reading comments from file
|
256
|
+
#
|
257
|
+
# path - a path to the INI file
|
258
|
+
#
|
259
|
+
# Returns a `String` with the comments from the beginning of the INI file.
|
260
|
+
#
|
261
|
+
def self.read_comment_from_file(path)
|
262
|
+
comment = ""
|
263
|
+
|
264
|
+
IO.foreach(path) do |line|
|
265
|
+
line.strip!
|
266
|
+
|
267
|
+
break unless line[0,1] == "#" or line == ""
|
268
|
+
|
269
|
+
comment_line = line[1, line.length].to_s
|
270
|
+
comment << "#{comment_line.strip}\n"
|
271
|
+
end
|
272
|
+
|
273
|
+
comment
|
274
|
+
end
|
275
|
+
|
276
|
+
#
|
277
|
+
# Writing a ini hash into a file
|
278
|
+
#
|
279
|
+
# path - Path to the INI file.
|
280
|
+
# inihash - Hash representing the ini File. Default is a empty hash.
|
281
|
+
# comment - String with comments which appear on the
|
282
|
+
# top of the file. Each line will get a "#" before.
|
283
|
+
# Default is no comment.
|
284
|
+
#
|
285
|
+
def self.write_to_file(path, inihash={}, comment=nil)
|
286
|
+
raise TypeError, "String expected" unless comment.is_a? String or comment.nil?
|
287
|
+
|
288
|
+
raise TypeError, "Hash expected" unless inihash.is_a? Hash
|
289
|
+
File.open(path, "w") { |file|
|
290
|
+
|
291
|
+
unless comment.nil?
|
292
|
+
comment.each do |line|
|
293
|
+
file << "# #{line}"
|
294
|
+
end
|
295
|
+
end
|
296
|
+
|
297
|
+
file << IniHash.text(inihash)
|
298
|
+
}
|
299
|
+
end
|
300
|
+
|
301
|
+
#
|
302
|
+
# Turn a hash (up to 2 levels deepness) into a ini string
|
303
|
+
#
|
304
|
+
# inihash - Hash representing the ini File. Default is a empty hash.
|
305
|
+
#
|
306
|
+
# Returns a String in the ini file format.
|
307
|
+
#
|
308
|
+
# TODO: Rename `IniHash.text` method to something else ?
|
309
|
+
#
|
310
|
+
def self.text(inihash={})
|
311
|
+
new(inihash).to_s
|
312
|
+
end
|
313
|
+
|
314
|
+
class << self
|
315
|
+
# @deprecated
|
316
|
+
alias_method :to_s, :text
|
317
|
+
end
|
318
|
+
|
319
|
+
end
|
320
|
+
|
321
|
+
end
|