o 2.0.2 → 2.0.3
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/.gitignore +1 -0
- data/Gemfile +2 -0
- data/README.md +72 -48
- data/lib/o.rb +330 -269
- data/lib/o/hash_method_fix.rb +23 -10
- data/lib/o/parser.rb +87 -75
- data/lib/o/semantics.rb +11 -9
- data/lib/o/version.rb +7 -7
- data/o.gemspec +2 -5
- data/spec/o_spec.rb +20 -0
- data/spec/test_spec.rb +8 -0
- metadata +5 -5
- data/Gemfile.lock +0 -31
data/.gitignore
CHANGED
data/Gemfile
CHANGED
data/README.md
CHANGED
@@ -1,4 +1,4 @@
|
|
1
|
-
O, a configuration
|
1
|
+
O, a configuration gem for Ruby
|
2
2
|
====================================
|
3
3
|
|
4
4
|
**Homepage**: [https://github.com/GutenYe/o](https://github.com/GutenYe/o) <br/>
|
@@ -7,32 +7,33 @@ O, a configuration libraray for Ruby
|
|
7
7
|
**Documentation**: [http://rubydoc.info/gems/o/frames](http://rubydoc.info/gems/o/frames) <br/>
|
8
8
|
**Issue Tracker**: [https://github.com/GutenYe/o/issues](https://github.com/GutenYe/o/issues) <br/>
|
9
9
|
|
10
|
-
|
10
|
+
The name `o` comes from option/setting, short and handy, eh-ah~
|
11
11
|
|
12
12
|
Features
|
13
13
|
--------
|
14
14
|
|
15
|
-
*
|
16
|
-
* DSL syntax
|
17
|
-
*
|
18
|
-
*
|
15
|
+
* Variable and computed attribute support
|
16
|
+
* Pure Ruby DSL syntax
|
17
|
+
* Multiple configuration levels including system, user, and command-line.
|
18
|
+
* Hash compatibility
|
19
19
|
|
20
20
|
Introduction
|
21
21
|
-------------
|
22
22
|
|
23
|
-
|
23
|
+
The three levels of configuration include system, user, and cmdline:
|
24
24
|
|
25
|
-
lib/guten/rc.rb # system level
|
25
|
+
APP/lib/guten/rc.rb # system level
|
26
26
|
~/.gutenrc # user level
|
27
27
|
$ guten --list or ENV[GEMFILE]=x guten # cmdline level
|
28
|
-
|
28
|
+
|
29
29
|
module Guten
|
30
|
-
Rc = O.require("guten/rc") + O.require("~/.gutenrc")
|
30
|
+
Rc = O.require("guten/rc") + O.require("~/.gutenrc") # require use $:
|
31
31
|
Rc.list = true or Rc.gemfile = ENV[GEMFILE] # from cmdline.
|
32
32
|
end
|
33
33
|
|
34
|
+
a constant works very well in many places, but you are free to use any variable.
|
34
35
|
|
35
|
-
###
|
36
|
+
### An example ###
|
36
37
|
|
37
38
|
Rc = O do
|
38
39
|
host "localhost"
|
@@ -48,7 +49,7 @@ do configuration at three levels: system, user, cmdline
|
|
48
49
|
time proc{|offset| Time.now} # computed attribute
|
49
50
|
end
|
50
51
|
|
51
|
-
alternative syntax
|
52
|
+
### An example using alternative syntax ###
|
52
53
|
|
53
54
|
Rc = O do |c|
|
54
55
|
c.host = "localhost"
|
@@ -64,7 +65,7 @@ alternative syntax
|
|
64
65
|
c.time = proc{|offset| Time.now}
|
65
66
|
end
|
66
67
|
|
67
|
-
|
68
|
+
### An example of some sugar syntax. _works in a file only_ ###
|
68
69
|
|
69
70
|
# file: guten/rc.rb
|
70
71
|
development:
|
@@ -80,36 +81,50 @@ a sugar syntax. _works in a file only_
|
|
80
81
|
username "guten"
|
81
82
|
end
|
82
83
|
|
83
|
-
this is not pure ruby syntax, but it works.
|
84
84
|
|
85
|
-
**WARNNING**: must use \t to indent for this sugar syntax.
|
86
85
|
|
87
|
-
|
86
|
+
**NOTE**: This is not pure ruby syntax, but it works.
|
87
|
+
In order for this to work, a tab ("\t") must be used for indention.
|
88
|
+
|
89
|
+
### Initialize ###
|
88
90
|
|
89
|
-
either
|
91
|
+
In order to initialize the configuration object either of the two ways can be used.
|
90
92
|
|
91
93
|
Rc = O.new
|
92
94
|
Rc = O.require "guten/rc" # from file
|
93
95
|
Rc = O do
|
94
96
|
a 1
|
95
97
|
end
|
96
|
-
Rc = O[a:1] # from hash
|
97
|
-
Rc._merge!
|
98
|
+
Rc = O[a: 1] # from a hash data
|
99
|
+
Rc._merge!(a: 1)
|
98
100
|
|
99
101
|
file: "guten/rc.rb"
|
100
102
|
|
101
103
|
a 1
|
102
104
|
|
105
|
+
Initalize with a default value
|
106
|
+
|
107
|
+
Rc = O.new
|
108
|
+
p Rc[:hello] #=> nil
|
109
|
+
Rc = O.new 1
|
110
|
+
p Rc[:hello] #=> 1
|
111
|
+
p Rc.hello #=> <#O> be careful here
|
112
|
+
|
113
|
+
### Assignment & Access ###
|
103
114
|
|
104
|
-
|
115
|
+
Flexibility has been built in to allow for various ways to assign configuration
|
116
|
+
data values and access the same values within your application. Here are some
|
117
|
+
examples of how this can be done:
|
105
118
|
|
106
|
-
|
119
|
+
Assignment:
|
107
120
|
|
108
121
|
Rc.age 1
|
109
122
|
Rc.age = 1
|
110
123
|
Rc[:age] = 1
|
111
124
|
Rc["age"] = 1
|
112
|
-
|
125
|
+
|
126
|
+
Access:
|
127
|
+
|
113
128
|
Rc.age #=> 1
|
114
129
|
Rc.age? #=> true
|
115
130
|
Rc[:age] #=> 1
|
@@ -121,15 +136,23 @@ either way is fine
|
|
121
136
|
c[:age] = 2
|
122
137
|
end
|
123
138
|
|
124
|
-
###
|
139
|
+
### Node ###
|
125
140
|
|
126
|
-
Rc.
|
141
|
+
Rc = O.new
|
142
|
+
Rc.a.b.c = 1
|
127
143
|
p Rc.a.b.c #=> <#Fixnum 1>
|
128
144
|
p Rc.a.b #=> <#O>
|
129
145
|
p Rc.a #=> <#O>
|
130
|
-
p Rc.i.dont.exists #=> <#O>
|
146
|
+
p Rc.i.dont.exists #=> <#O>
|
131
147
|
|
132
|
-
|
148
|
+
Rc = O.new
|
149
|
+
p Rc.a._empty? #=> true # if a node is empty?
|
150
|
+
Rc.a.b = 1
|
151
|
+
p Rc.a._empty? #=> false
|
152
|
+
p O===Rc.a #=> true # if it is a node?
|
153
|
+
p O===Rc.a.b #=> false
|
154
|
+
|
155
|
+
### Variable & Path ###
|
133
156
|
|
134
157
|
O do
|
135
158
|
age 1
|
@@ -146,9 +169,9 @@ either way is fine
|
|
146
169
|
end
|
147
170
|
end
|
148
171
|
|
149
|
-
###
|
172
|
+
### Namespace ###
|
150
173
|
|
151
|
-
|
174
|
+
Either way is fine:
|
152
175
|
|
153
176
|
O do
|
154
177
|
mail.stmp.address "stmp.gmail.com"
|
@@ -157,7 +180,7 @@ either way is fine
|
|
157
180
|
end
|
158
181
|
end
|
159
182
|
|
160
|
-
|
183
|
+
Another namespace example:
|
161
184
|
|
162
185
|
O do
|
163
186
|
age 1
|
@@ -172,9 +195,9 @@ another example
|
|
172
195
|
end
|
173
196
|
|
174
197
|
|
175
|
-
###
|
198
|
+
### Group ###
|
176
199
|
|
177
|
-
|
200
|
+
Use namespace or use some separate files like rails.
|
178
201
|
|
179
202
|
config/
|
180
203
|
applications.rb
|
@@ -183,7 +206,7 @@ use namespace or use some seperate files like rails.
|
|
183
206
|
test.rb
|
184
207
|
production.rb
|
185
208
|
|
186
|
-
###
|
209
|
+
### Computed attribute ###
|
187
210
|
|
188
211
|
Rc = O do
|
189
212
|
time proc{|n| Time.now}
|
@@ -193,19 +216,25 @@ use namespace or use some seperate files like rails.
|
|
193
216
|
Rc.time = 2 # assign new value
|
194
217
|
p Rc[:time] #=> <#Proc>
|
195
218
|
|
196
|
-
###
|
219
|
+
### Semantic ###
|
197
220
|
|
198
221
|
O do
|
199
222
|
is_started no # yes ...
|
200
223
|
end
|
201
224
|
|
202
|
-
for a list of semantic methods, see O::Semantics
|
225
|
+
Note: for a list of semantic methods, see O::Semantics
|
226
|
+
|
227
|
+
### Hash compatibility ###
|
203
228
|
|
204
|
-
|
229
|
+
Internal, datas are stored as a Hash. You can access all hash methods via `_method`
|
230
|
+
|
231
|
+
Rc = O.new
|
232
|
+
Rc.a = 1
|
233
|
+
Rc._child #=> {:a=>1}
|
205
234
|
|
206
|
-
Rc._keys
|
235
|
+
Rc._keys #=> [:a]
|
207
236
|
|
208
|
-
###
|
237
|
+
### Temporarily change ###
|
209
238
|
|
210
239
|
Rc.a = 1
|
211
240
|
Rc._temp do
|
@@ -214,17 +243,16 @@ for a list of semantic methods, see O::Semantics
|
|
214
243
|
p Rc.a #=> 1
|
215
244
|
|
216
245
|
|
217
|
-
###
|
246
|
+
### Access built-in method inside block ###
|
218
247
|
|
219
248
|
Rc = O do
|
220
249
|
sleep 10 # is a data. Rc.sleep #=> 10
|
221
250
|
O.sleep 10 # call builtin 'sleep' method
|
222
251
|
end
|
223
252
|
|
224
|
-
a list of blocked methods
|
225
|
-
|
253
|
+
Note: for a list of blocked methods, see O::BUILTIN_METHODS
|
226
254
|
|
227
|
-
###
|
255
|
+
### Additional examples ###
|
228
256
|
|
229
257
|
O do
|
230
258
|
name do
|
@@ -243,16 +271,12 @@ a list of blocked methods is in O::BUILTIN_METHODS
|
|
243
271
|
c.first = "Guten"
|
244
272
|
end
|
245
273
|
|
246
|
-
|
247
|
-
|
248
274
|
Contributing
|
249
275
|
-------------
|
250
276
|
|
251
|
-
* join the project
|
252
|
-
*
|
253
|
-
*
|
254
|
-
* improve documentation.
|
255
|
-
* feel free to post any ideas.
|
277
|
+
* Feel free to join the project and make contributions (by submitting a pull request)
|
278
|
+
* Submit any bugs/features/ideas to github issue tracker
|
279
|
+
* Codeing style: https://gist.github.com/1105334
|
256
280
|
|
257
281
|
Install
|
258
282
|
----------
|
data/lib/o.rb
CHANGED
@@ -1,277 +1,338 @@
|
|
1
1
|
libdir = File.dirname(__FILE__)
|
2
2
|
$LOAD_PATH.unshift(libdir) unless $LOAD_PATH.include?(libdir)
|
3
3
|
|
4
|
-
%w(
|
5
|
-
|
4
|
+
%w(
|
5
|
+
semantics
|
6
|
+
hash_method_fix
|
7
|
+
parser
|
8
|
+
).each { |n| require "o/#{n}" }
|
9
|
+
|
10
|
+
# <#O> is a node, it has _child, _parent and _root attribute.
|
11
|
+
#
|
12
|
+
# Rc = O do
|
13
|
+
# a.b 1
|
14
|
+
# a.c do
|
15
|
+
# d 2
|
16
|
+
# end
|
17
|
+
# end
|
18
|
+
#
|
19
|
+
# p Rc
|
20
|
+
# #=> <#O
|
21
|
+
# :a => <#O
|
22
|
+
# :b => 1
|
23
|
+
# :c => <#O
|
24
|
+
# :d => 2>>>
|
25
|
+
#
|
26
|
+
# Rc.a #=> <#O>
|
27
|
+
# Rc.a._child #=> {:b => 1, :c => <#O>}
|
28
|
+
# Rc.a._parent #=> is Rc
|
29
|
+
# Rc.a._root #=> is Rc
|
30
|
+
#
|
31
|
+
# Rc._parent #=> nil
|
32
|
+
# Rc._root #=> is Rc
|
33
|
+
#
|
6
34
|
class O
|
7
|
-
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
13
|
-
|
14
|
-
|
15
|
-
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
27
|
-
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
47
|
-
|
48
|
-
|
49
|
-
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
|
55
|
-
|
56
|
-
|
57
|
-
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
83
|
-
|
84
|
-
|
85
|
-
|
86
|
-
|
87
|
-
|
88
|
-
|
89
|
-
|
90
|
-
|
91
|
-
|
92
|
-
|
93
|
-
|
94
|
-
|
95
|
-
|
96
|
-
|
97
|
-
|
98
|
-
|
99
|
-
|
100
|
-
|
101
|
-
|
102
|
-
|
103
|
-
|
104
|
-
|
105
|
-
|
106
|
-
|
107
|
-
|
108
|
-
|
109
|
-
|
110
|
-
|
111
|
-
|
112
|
-
|
113
|
-
|
114
|
-
|
115
|
-
|
116
|
-
|
117
|
-
|
118
|
-
|
119
|
-
|
120
|
-
|
121
|
-
|
122
|
-
|
123
|
-
|
124
|
-
|
125
|
-
|
126
|
-
|
127
|
-
|
128
|
-
|
129
|
-
|
130
|
-
|
131
|
-
|
132
|
-
|
133
|
-
|
134
|
-
|
135
|
-
|
136
|
-
|
137
|
-
|
138
|
-
|
139
|
-
|
140
|
-
|
141
|
-
|
142
|
-
|
143
|
-
|
144
|
-
|
145
|
-
|
146
|
-
|
147
|
-
|
148
|
-
|
149
|
-
|
150
|
-
|
151
|
-
|
152
|
-
|
153
|
-
|
154
|
-
|
155
|
-
|
156
|
-
|
157
|
-
|
158
|
-
|
159
|
-
|
160
|
-
|
161
|
-
|
162
|
-
|
163
|
-
|
164
|
-
|
165
|
-
|
166
|
-
|
167
|
-
|
168
|
-
|
169
|
-
|
170
|
-
|
171
|
-
|
172
|
-
|
173
|
-
|
174
|
-
|
175
|
-
|
176
|
-
|
177
|
-
|
178
|
-
|
179
|
-
|
180
|
-
|
181
|
-
|
182
|
-
|
183
|
-
|
184
|
-
|
185
|
-
|
186
|
-
|
187
|
-
|
188
|
-
|
189
|
-
|
190
|
-
|
191
|
-
|
192
|
-
|
193
|
-
|
194
|
-
|
195
|
-
|
196
|
-
|
197
|
-
|
198
|
-
|
199
|
-
|
200
|
-
|
201
|
-
|
202
|
-
|
203
|
-
|
204
|
-
|
205
|
-
|
206
|
-
|
207
|
-
|
208
|
-
|
209
|
-
|
210
|
-
|
211
|
-
|
212
|
-
|
213
|
-
|
214
|
-
|
215
|
-
|
216
|
-
|
217
|
-
|
218
|
-
|
219
|
-
|
220
|
-
|
221
|
-
|
222
|
-
|
223
|
-
|
224
|
-
|
225
|
-
|
226
|
-
|
227
|
-
|
228
|
-
|
229
|
-
|
230
|
-
|
231
|
-
|
232
|
-
|
233
|
-
|
234
|
-
|
235
|
-
|
236
|
-
|
237
|
-
|
238
|
-
|
239
|
-
|
240
|
-
|
241
|
-
|
242
|
-
|
243
|
-
|
244
|
-
|
245
|
-
|
246
|
-
|
247
|
-
|
248
|
-
|
249
|
-
|
250
|
-
|
251
|
-
|
252
|
-
|
253
|
-
|
254
|
-
|
255
|
-
|
256
|
-
|
257
|
-
|
258
|
-
|
259
|
-
|
260
|
-
|
261
|
-
|
262
|
-
|
263
|
-
|
264
|
-
|
265
|
-
|
266
|
-
|
267
|
-
|
268
|
-
|
269
|
-
|
270
|
-
|
35
|
+
autoload :VERSION, "o/version"
|
36
|
+
|
37
|
+
Error = Class.new Exception
|
38
|
+
LoadError = Class.new Error
|
39
|
+
|
40
|
+
BUILTIN_METHODS = [ :p, :raise, :sleep, :rand, :srand, :exit, :require, :at_exit, :autoload, :open]
|
41
|
+
|
42
|
+
class << self
|
43
|
+
public *BUILTIN_METHODS
|
44
|
+
|
45
|
+
# eval a file/string configuration.
|
46
|
+
#
|
47
|
+
# @params [String] content
|
48
|
+
# @return [O] configuration
|
49
|
+
def eval(content=nil, &blk)
|
50
|
+
o = O.new nil
|
51
|
+
content ? o.instance_eval(Parser.compile(content)) : o.instance_eval(&blk)
|
52
|
+
|
53
|
+
o._root
|
54
|
+
end
|
55
|
+
|
56
|
+
# convert Hash, O to O
|
57
|
+
# @param [O,Hash] data
|
58
|
+
# @return [O]
|
59
|
+
def [](data)
|
60
|
+
case data
|
61
|
+
when O
|
62
|
+
data
|
63
|
+
when Hash
|
64
|
+
o = O.new
|
65
|
+
o._child = data
|
66
|
+
o
|
67
|
+
end
|
68
|
+
end
|
69
|
+
|
70
|
+
# get Hash data from any object
|
71
|
+
#
|
72
|
+
# @param [O, Hash] obj
|
73
|
+
# @return [Hash]
|
74
|
+
def get(obj)
|
75
|
+
case obj
|
76
|
+
when Hash
|
77
|
+
obj
|
78
|
+
when O
|
79
|
+
obj._child
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
# load a configuration file,
|
84
|
+
# use $: and support '~/.gutenrc'
|
85
|
+
#
|
86
|
+
# @example
|
87
|
+
# Rc = O.require("~/.gutenrc")
|
88
|
+
#
|
89
|
+
# Rc = O.require("/absolute/path/rc.rb")
|
90
|
+
#
|
91
|
+
# Rc = O.require("guten/rc") #=> load 'APP/lib/guten/rc.rb'
|
92
|
+
# # first try 'guten/rc.rb', then 'guten/rc'
|
93
|
+
#
|
94
|
+
# @param [String] name
|
95
|
+
# @return [O]
|
96
|
+
def require(name)
|
97
|
+
path = nil
|
98
|
+
|
99
|
+
# ~/.gutenrc
|
100
|
+
if name =~ /^~/
|
101
|
+
file = File.expand_path(name)
|
102
|
+
path = file if File.exists?(file)
|
103
|
+
|
104
|
+
# /absolute/path/to/rc
|
105
|
+
elsif File.absolute_path(name) == name
|
106
|
+
path = name if File.exists?(name)
|
107
|
+
|
108
|
+
# relative/rc
|
109
|
+
else
|
110
|
+
catch :break do
|
111
|
+
$:.each { |p|
|
112
|
+
['.rb', ''].each { |ext|
|
113
|
+
file = File.join(p, name+ext)
|
114
|
+
if File.exists? file
|
115
|
+
path = file
|
116
|
+
throw :break
|
117
|
+
end
|
118
|
+
}
|
119
|
+
}
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
raise LoadError, "can't find file -- #{name}" unless path
|
124
|
+
|
125
|
+
O.eval File.read(path)
|
126
|
+
end
|
127
|
+
end
|
128
|
+
|
129
|
+
undef_method *BUILTIN_METHODS
|
130
|
+
include Semantics
|
131
|
+
include HashMethodFix
|
132
|
+
|
133
|
+
# parent node, a <#O>
|
134
|
+
attr_accessor :_parent
|
135
|
+
|
136
|
+
# child node, a hash data
|
137
|
+
attr_accessor :_child
|
138
|
+
|
139
|
+
# root node, a <#O>
|
140
|
+
attr_accessor :_root
|
141
|
+
|
142
|
+
# @param [Object] (nil) default create a new hash with the defalut value
|
143
|
+
def initialize(default=nil, options={}, &blk)
|
144
|
+
@_root = options[:_root]
|
145
|
+
@_child = Hash.new(default)
|
146
|
+
|
147
|
+
if blk
|
148
|
+
method = _blk2method(&blk)
|
149
|
+
if blk.arity == 0
|
150
|
+
method.call
|
151
|
+
else
|
152
|
+
method.call self
|
153
|
+
end
|
154
|
+
end
|
155
|
+
end
|
156
|
+
|
157
|
+
# a temporarily change
|
158
|
+
def _temp(&blk)
|
159
|
+
data = _child.dup
|
160
|
+
blk.call
|
161
|
+
self._child = data
|
162
|
+
|
163
|
+
self
|
164
|
+
end
|
165
|
+
|
166
|
+
def _parent
|
167
|
+
@_parent || nil
|
168
|
+
end
|
169
|
+
|
170
|
+
def _root
|
171
|
+
@_root || self
|
172
|
+
end
|
173
|
+
|
174
|
+
def _child=(obj)
|
175
|
+
@_child = O.get(obj)
|
176
|
+
end
|
177
|
+
|
178
|
+
# set data
|
179
|
+
def []=(key, value)
|
180
|
+
key = key.respond_to?(:to_sym) ? key.to_sym : key
|
181
|
+
|
182
|
+
@_child[key] = value
|
183
|
+
end
|
184
|
+
|
185
|
+
# get data
|
186
|
+
def [](key)
|
187
|
+
key = key.respond_to?(:to_sym) ? key.to_sym : key
|
188
|
+
|
189
|
+
@_child[key]
|
190
|
+
end
|
191
|
+
|
192
|
+
def ==(other)
|
193
|
+
_child == other._child
|
194
|
+
end
|
195
|
+
|
196
|
+
# duplicate
|
197
|
+
#
|
198
|
+
# @return [O] new <#O>
|
199
|
+
def _dup
|
200
|
+
o = O.new
|
201
|
+
o._child = _child.dup
|
202
|
+
|
203
|
+
o
|
204
|
+
end
|
205
|
+
|
206
|
+
# replace with a new data
|
207
|
+
#
|
208
|
+
# @param [Hash,O] obj
|
209
|
+
# @return [O] self
|
210
|
+
def _replace(obj)
|
211
|
+
self._child = O.get(obj)
|
212
|
+
|
213
|
+
self
|
214
|
+
end
|
215
|
+
|
216
|
+
def +(other)
|
217
|
+
raise Error, "not support type for + -- #{other.inspect}" unless O === other
|
218
|
+
|
219
|
+
O.new _child, other._child
|
220
|
+
end
|
221
|
+
|
222
|
+
# everything goes here.
|
223
|
+
#
|
224
|
+
# .name?
|
225
|
+
# .name= value
|
226
|
+
# .name value
|
227
|
+
# ._name
|
228
|
+
#
|
229
|
+
# .c
|
230
|
+
# .a.b.c
|
231
|
+
#
|
232
|
+
def method_missing(name, *args, &blk)
|
233
|
+
# path: root
|
234
|
+
if name == :_
|
235
|
+
return _root
|
236
|
+
|
237
|
+
# relative path: __
|
238
|
+
elsif name =~ /^__+$/
|
239
|
+
num = name.to_s.count('_') - 1
|
240
|
+
node = self
|
241
|
+
num.times {
|
242
|
+
return unless node
|
243
|
+
node = node._parent
|
244
|
+
}
|
245
|
+
return node
|
246
|
+
|
247
|
+
# .name=
|
248
|
+
elsif name =~ /(.*)=$/
|
249
|
+
return @_child[$1.to_sym] = args[0]
|
250
|
+
|
251
|
+
# ._name
|
252
|
+
elsif name =~ /^_(.*)/
|
253
|
+
name = $1.to_sym
|
254
|
+
args.map!{|arg| O===arg ? arg._child : arg}
|
255
|
+
return @_child.send(name, *args, &blk)
|
256
|
+
|
257
|
+
# .name?
|
258
|
+
elsif name =~ /(.*)\?$/
|
259
|
+
return !! @_child[$1.to_sym]
|
260
|
+
|
261
|
+
elsif Proc === @_child[name]
|
262
|
+
return @_child[name].call *args
|
263
|
+
|
264
|
+
# a.c # return data if has :c
|
265
|
+
# a.c # create new <#O> if no :c
|
266
|
+
#
|
267
|
+
elsif args.empty?
|
268
|
+
|
269
|
+
# a.b.c 1
|
270
|
+
# a.b do
|
271
|
+
# c 2
|
272
|
+
# end
|
273
|
+
if @_child.has_key?(name)
|
274
|
+
o = @_child[name]
|
275
|
+
o.instance_eval(&blk) if blk
|
276
|
+
return o
|
277
|
+
|
278
|
+
else
|
279
|
+
next_o = O.new(nil, {_root: _root})
|
280
|
+
next_o._parent = self
|
281
|
+
self._child[name] = next_o
|
282
|
+
next_o.instance_eval(&blk) if blk
|
283
|
+
return next_o
|
284
|
+
end
|
285
|
+
|
286
|
+
# .name value
|
287
|
+
else
|
288
|
+
@_child[name] = args[0]
|
289
|
+
return args[0]
|
290
|
+
end
|
291
|
+
end
|
292
|
+
|
293
|
+
# pretty print
|
294
|
+
#
|
295
|
+
# <#O
|
296
|
+
# :b => 1
|
297
|
+
# :c => 2
|
298
|
+
# :d => <#O
|
299
|
+
# :c => 2>>
|
300
|
+
def inspect(indent=" ")
|
301
|
+
o={rst: ""}
|
302
|
+
o[:rst] << "<#O\n"
|
303
|
+
_child.each { |k,v|
|
304
|
+
o[:rst] << "#{indent}#{k.inspect} => "
|
305
|
+
o[:rst] << (O === v ? "#{v.inspect(indent+" ")}\n" : "#{v.inspect}\n")
|
306
|
+
}
|
307
|
+
o[:rst].rstrip! << ">"
|
308
|
+
|
309
|
+
o[:rst]
|
310
|
+
end
|
311
|
+
|
312
|
+
alias to_s inspect
|
313
|
+
|
314
|
+
private
|
315
|
+
# convert block to method.
|
316
|
+
#
|
317
|
+
# you can call a block with arguments
|
318
|
+
#
|
319
|
+
# @example USAGE
|
320
|
+
# instance_eval(&blk)
|
321
|
+
# blk2method(&blk).call *args
|
322
|
+
#
|
323
|
+
def _blk2method(&blk)
|
324
|
+
self.class.class_eval {
|
325
|
+
define_method(:__blk2method, &blk)
|
326
|
+
}
|
327
|
+
|
328
|
+
method(:__blk2method)
|
329
|
+
end
|
330
|
+
|
271
331
|
end
|
272
332
|
|
273
333
|
module Kernel
|
274
|
-
|
275
|
-
|
276
|
-
|
334
|
+
# a handy method
|
335
|
+
def O(default=nil, &blk)
|
336
|
+
O.new default, &blk
|
337
|
+
end
|
277
338
|
end
|
data/lib/o/hash_method_fix.rb
CHANGED
@@ -1,13 +1,26 @@
|
|
1
1
|
class O
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
#
|
3
|
+
# make some hash methods works for <#O>
|
4
|
+
#
|
5
|
+
module HashMethodFix
|
6
|
+
|
7
|
+
# merge new data IN PLACE
|
8
|
+
#
|
9
|
+
# @params [Hash,O] obj
|
10
|
+
# @return [self]
|
11
|
+
def _merge! obj
|
12
|
+
_child.merge! O.get(obj)
|
13
|
+
self
|
14
|
+
end
|
7
15
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
12
|
-
|
16
|
+
# merge new data
|
17
|
+
#
|
18
|
+
# @params [Hash,O] obj
|
19
|
+
# @return [O] new <#O>
|
20
|
+
def _merge obj
|
21
|
+
data = _child.merge(O.get(obj))
|
22
|
+
O[data]
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
13
26
|
end
|
data/lib/o/parser.rb
CHANGED
@@ -1,87 +1,99 @@
|
|
1
1
|
class O
|
2
|
-
|
3
|
-
|
2
|
+
# convert sugar syntax
|
3
|
+
#
|
4
|
+
# develoment:
|
5
|
+
# database 'postgresql'
|
6
|
+
#
|
7
|
+
# to a pure ruby syntax
|
8
|
+
#
|
9
|
+
# development do
|
10
|
+
# database 'postgresql'
|
11
|
+
# end
|
12
|
+
#
|
13
|
+
class Parser
|
14
|
+
# the string data.
|
15
|
+
attr_reader :content
|
4
16
|
|
5
|
-
|
6
|
-
|
7
|
-
|
8
|
-
|
9
|
-
end
|
10
|
-
end
|
17
|
+
class << self
|
18
|
+
# a handy method for Parser.new(content).compile
|
19
|
+
def compile(content)
|
20
|
+
parser = Parser.new(content)
|
11
21
|
|
12
|
-
|
13
|
-
|
14
|
-
|
22
|
+
parser.compile
|
23
|
+
end
|
24
|
+
end
|
15
25
|
|
16
|
-
|
17
|
-
|
18
|
-
|
19
|
-
block_start = false
|
26
|
+
def initialize content
|
27
|
+
@content = content
|
28
|
+
end
|
20
29
|
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
script << statement << "\n"
|
27
|
-
when :statement
|
28
|
-
script << statement << "\n"
|
29
|
-
when :indent
|
30
|
-
indent_counts += 1
|
31
|
-
script << "\t"*indent_counts
|
32
|
-
when :undent
|
33
|
-
script << "\t"*indent_counts
|
34
|
-
when :dedent
|
35
|
-
if block_start
|
36
|
-
block_start = false
|
37
|
-
script << "\t"*(indent_counts-1) + "end\n"
|
38
|
-
else
|
39
|
-
script << "\t"*(indent_counts-1)
|
40
|
-
end
|
41
|
-
indent_counts -= 1
|
42
|
-
end
|
43
|
-
end
|
44
|
-
script
|
45
|
-
end
|
46
|
-
|
47
|
-
private
|
48
|
-
|
49
|
-
def scan
|
50
|
-
last_indent = 0
|
30
|
+
# compile sugar-syntax into ruby-syntax
|
31
|
+
def compile
|
32
|
+
script = ""
|
33
|
+
indent_counts = 0
|
34
|
+
block_start = false
|
51
35
|
|
52
|
-
|
36
|
+
scan { |token, statement|
|
37
|
+
case token
|
38
|
+
when :block_start
|
39
|
+
block_start = true
|
40
|
+
statement = statement.sub(":", " do")
|
41
|
+
script << statement << "\n"
|
42
|
+
when :statement
|
43
|
+
script << statement << "\n"
|
44
|
+
when :indent
|
45
|
+
indent_counts += 1
|
46
|
+
script << "\t"*indent_counts
|
47
|
+
when :undent
|
48
|
+
script << "\t"*indent_counts
|
49
|
+
when :dedent
|
50
|
+
if block_start
|
51
|
+
block_start = false
|
52
|
+
script << "\t"*(indent_counts-1) + "end\n"
|
53
|
+
else
|
54
|
+
script << "\t"*(indent_counts-1)
|
55
|
+
end
|
56
|
+
indent_counts -= 1
|
57
|
+
end
|
58
|
+
}
|
53
59
|
|
54
|
-
|
60
|
+
script
|
61
|
+
end
|
62
|
+
|
63
|
+
private
|
64
|
+
def scan
|
65
|
+
last_indent = 0
|
55
66
|
|
56
|
-
|
57
|
-
|
58
|
-
# b 1
|
59
|
-
# c:
|
60
|
-
# d 1
|
61
|
-
# e:
|
62
|
-
# f 1
|
63
|
-
# g 1
|
64
|
-
indent = indents.count("\t")
|
65
|
-
counts = indent - last_indent
|
66
|
-
last_indent = indent
|
67
|
+
content.scan(/(.*?)(\n+|\Z)/).each { |line, newline|
|
68
|
+
_, indents, statement = line.match(/^(\t*)(.*)/).to_a
|
67
69
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
70
|
+
# indent
|
71
|
+
# a:
|
72
|
+
# b 1
|
73
|
+
# c:
|
74
|
+
# d 1
|
75
|
+
# e:
|
76
|
+
# f 1
|
77
|
+
# g 1
|
78
|
+
indent = indents.count("\t")
|
79
|
+
counts = indent - last_indent
|
80
|
+
last_indent = indent
|
75
81
|
|
76
|
-
|
77
|
-
|
78
|
-
|
79
|
-
|
80
|
-
|
81
|
-
|
82
|
-
|
82
|
+
if counts == 0
|
83
|
+
yield :undent
|
84
|
+
else
|
85
|
+
counts.abs.times {
|
86
|
+
yield (counts>0 ? :indent : :dedent)
|
87
|
+
}
|
88
|
+
end
|
83
89
|
|
84
|
-
|
85
|
-
|
86
|
-
|
90
|
+
# statement
|
91
|
+
if statement =~ /:\s*$/
|
92
|
+
yield :block_start, statement.gsub(/\s*:\s*$/, ':')
|
93
|
+
else
|
94
|
+
yield :statement, statement
|
95
|
+
end
|
96
|
+
}
|
97
|
+
end
|
98
|
+
end
|
87
99
|
end
|
data/lib/o/semantics.rb
CHANGED
@@ -1,12 +1,14 @@
|
|
1
1
|
class O
|
2
|
-
|
3
|
-
|
4
|
-
|
5
|
-
|
6
|
-
|
2
|
+
# some semantic names for meaningfull configuration.
|
3
|
+
module Semantics
|
4
|
+
# @return false
|
5
|
+
def no
|
6
|
+
false
|
7
|
+
end
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
# @return true
|
10
|
+
def yes
|
11
|
+
true
|
12
|
+
end
|
13
|
+
end
|
12
14
|
end
|
data/lib/o/version.rb
CHANGED
data/o.gemspec
CHANGED
@@ -4,9 +4,9 @@ require "o/version"
|
|
4
4
|
Gem::Specification.new do |s|
|
5
5
|
s.name = "o"
|
6
6
|
s.version = O::VERSION::IS
|
7
|
-
s.summary = "a configuration
|
7
|
+
s.summary = "a configuration library for Ruby"
|
8
8
|
s.description = <<-EOF
|
9
|
-
a coonfiguration
|
9
|
+
a coonfiguration library for Ruby
|
10
10
|
EOF
|
11
11
|
|
12
12
|
s.author = "Guten"
|
@@ -15,7 +15,4 @@ a coonfiguration libraray for Ruby
|
|
15
15
|
s.rubyforge_project = "xx"
|
16
16
|
|
17
17
|
s.files = `git ls-files`.split("\n")
|
18
|
-
#s.executables = ["x"]
|
19
|
-
|
20
|
-
#s.add_dependency "x"
|
21
18
|
end
|
data/spec/o_spec.rb
CHANGED
@@ -82,6 +82,19 @@ describe O do
|
|
82
82
|
@rc[:a].should == 1
|
83
83
|
end
|
84
84
|
end
|
85
|
+
|
86
|
+
context "initalize with default value" do
|
87
|
+
it "default value is nil" do
|
88
|
+
rc = O.new
|
89
|
+
rc[:foo].should == nil
|
90
|
+
end
|
91
|
+
|
92
|
+
it "init with default value 1" do
|
93
|
+
rc = O.new 1
|
94
|
+
rc[:foo].should == 1
|
95
|
+
end
|
96
|
+
end
|
97
|
+
|
85
98
|
it "return <#O> if key doesn't exist" do
|
86
99
|
rc = O.new
|
87
100
|
rc.i.dont.exists.should be_an_instance_of O
|
@@ -245,6 +258,13 @@ describe O do
|
|
245
258
|
|
246
259
|
rc._keys.should == [:a]
|
247
260
|
end
|
261
|
+
|
262
|
+
it "_method? must comes before method?" do
|
263
|
+
rc = O.new
|
264
|
+
rc.i._empty?.should be_true
|
265
|
+
rc.i.empty?.should be_false
|
266
|
+
end
|
267
|
+
|
248
268
|
end
|
249
269
|
|
250
270
|
context "temporarily change" do
|
data/spec/test_spec.rb
ADDED
metadata
CHANGED
@@ -2,7 +2,7 @@
|
|
2
2
|
name: o
|
3
3
|
version: !ruby/object:Gem::Version
|
4
4
|
prerelease:
|
5
|
-
version: 2.0.
|
5
|
+
version: 2.0.3
|
6
6
|
platform: ruby
|
7
7
|
authors:
|
8
8
|
- Guten
|
@@ -10,11 +10,11 @@ autorequire:
|
|
10
10
|
bindir: bin
|
11
11
|
cert_chain: []
|
12
12
|
|
13
|
-
date: 2011-07-
|
13
|
+
date: 2011-07-27 00:00:00 Z
|
14
14
|
dependencies: []
|
15
15
|
|
16
16
|
description: |
|
17
|
-
a coonfiguration
|
17
|
+
a coonfiguration library for Ruby
|
18
18
|
|
19
19
|
email: ywzhaifei@Gmail.com
|
20
20
|
executables: []
|
@@ -27,7 +27,6 @@ files:
|
|
27
27
|
- .gitignore
|
28
28
|
- .rspec
|
29
29
|
- Gemfile
|
30
|
-
- Gemfile.lock
|
31
30
|
- LICENSE
|
32
31
|
- README.md
|
33
32
|
- Ragfile
|
@@ -46,6 +45,7 @@ files:
|
|
46
45
|
- spec/o/parser_spec.rb
|
47
46
|
- spec/o_spec.rb
|
48
47
|
- spec/spec_helper.rb
|
48
|
+
- spec/test_spec.rb
|
49
49
|
homepage: http://github.com/GutenYe/o
|
50
50
|
licenses: []
|
51
51
|
|
@@ -72,6 +72,6 @@ rubyforge_project: xx
|
|
72
72
|
rubygems_version: 1.8.5
|
73
73
|
signing_key:
|
74
74
|
specification_version: 3
|
75
|
-
summary: a configuration
|
75
|
+
summary: a configuration library for Ruby
|
76
76
|
test_files: []
|
77
77
|
|
data/Gemfile.lock
DELETED
@@ -1,31 +0,0 @@
|
|
1
|
-
PATH
|
2
|
-
remote: .
|
3
|
-
specs:
|
4
|
-
o (2.0.0)
|
5
|
-
|
6
|
-
GEM
|
7
|
-
remote: http://rubygems.org/
|
8
|
-
specs:
|
9
|
-
activesupport (3.0.9)
|
10
|
-
pa (1.0.2)
|
11
|
-
pa (~> 1.0.0)
|
12
|
-
tagen (~> 1.0.0)
|
13
|
-
thor (~> 0.14.0)
|
14
|
-
pd (1.0.2)
|
15
|
-
rag (1.0.6)
|
16
|
-
pa (~> 1.0.0)
|
17
|
-
tagen (~> 1.0.0)
|
18
|
-
thor (~> 0.14.0)
|
19
|
-
tagen (1.0.2)
|
20
|
-
activesupport
|
21
|
-
pd
|
22
|
-
thor (0.14.6)
|
23
|
-
watchr (0.7)
|
24
|
-
|
25
|
-
PLATFORMS
|
26
|
-
ruby
|
27
|
-
|
28
|
-
DEPENDENCIES
|
29
|
-
o!
|
30
|
-
rag
|
31
|
-
watchr
|