glyph 0.1.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/README.textile +80 -0
- data/Rakefile +51 -0
- data/VERSION +1 -0
- data/bin/glyph +7 -0
- data/book/config.yml +5 -0
- data/book/document.glyph +55 -0
- data/book/images/glyph.png +0 -0
- data/book/images/glyph.svg +351 -0
- data/book/lib/macros/reference.rb +98 -0
- data/book/output/html/glyph.html +1809 -0
- data/book/output/html/images/glyph.png +0 -0
- data/book/output/html/images/glyph.svg +351 -0
- data/book/output/pdf/glyph.pdf +4277 -0
- data/book/snippets.yml +13 -0
- data/book/styles/css3.css +220 -0
- data/book/styles/default.css +190 -0
- data/book/text/authoring.textile +351 -0
- data/book/text/extending.textile +148 -0
- data/book/text/getting_started.textile +152 -0
- data/book/text/introduction.textile +88 -0
- data/book/text/ref_commands.textile +74 -0
- data/book/text/ref_config.textile +0 -0
- data/book/text/ref_macros.textile +256 -0
- data/book/text/troubleshooting.textile +118 -0
- data/config.yml +63 -0
- data/document.glyph +29 -0
- data/glyph.gemspec +138 -0
- data/lib/glyph.rb +128 -0
- data/lib/glyph/commands.rb +124 -0
- data/lib/glyph/config.rb +152 -0
- data/lib/glyph/document.rb +145 -0
- data/lib/glyph/glyph_language.rb +530 -0
- data/lib/glyph/glyph_language.treetop +27 -0
- data/lib/glyph/interpreter.rb +84 -0
- data/lib/glyph/macro.rb +69 -0
- data/lib/glyph/node.rb +126 -0
- data/lib/glyph/system_extensions.rb +77 -0
- data/macros/common.rb +66 -0
- data/macros/filters.rb +69 -0
- data/macros/html/block.rb +119 -0
- data/macros/html/inline.rb +43 -0
- data/macros/html/structure.rb +138 -0
- data/spec/files/container.textile +5 -0
- data/spec/files/document.glyph +2 -0
- data/spec/files/document_with_toc.glyph +3 -0
- data/spec/files/included.textile +4 -0
- data/spec/files/ligature.jpg +449 -0
- data/spec/files/markdown.markdown +8 -0
- data/spec/files/test.sass +2 -0
- data/spec/lib/commands_spec.rb +83 -0
- data/spec/lib/config_spec.rb +79 -0
- data/spec/lib/document_spec.rb +100 -0
- data/spec/lib/glyph_spec.rb +76 -0
- data/spec/lib/interpreter_spec.rb +90 -0
- data/spec/lib/macro_spec.rb +60 -0
- data/spec/lib/node_spec.rb +76 -0
- data/spec/macros/filters_spec.rb +42 -0
- data/spec/macros/macros_spec.rb +159 -0
- data/spec/spec_helper.rb +92 -0
- data/spec/tasks/generate_spec.rb +31 -0
- data/spec/tasks/load_spec.rb +37 -0
- data/spec/tasks/project_spec.rb +41 -0
- data/styles/css3.css +220 -0
- data/styles/default.css +190 -0
- data/tasks/generate.rake +57 -0
- data/tasks/load.rake +55 -0
- data/tasks/project.rake +33 -0
- metadata +192 -0
data/glyph.gemspec
ADDED
@@ -0,0 +1,138 @@
|
|
1
|
+
# Generated by jeweler
|
2
|
+
# DO NOT EDIT THIS FILE DIRECTLY
|
3
|
+
# Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
|
4
|
+
# -*- encoding: utf-8 -*-
|
5
|
+
|
6
|
+
Gem::Specification.new do |s|
|
7
|
+
s.name = %q{glyph}
|
8
|
+
s.version = "0.1.0"
|
9
|
+
|
10
|
+
s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
|
11
|
+
s.authors = ["Fabio Cevasco"]
|
12
|
+
s.date = %q{2010-04-09}
|
13
|
+
s.default_executable = %q{glyph}
|
14
|
+
s.description = %q{Glyph is a framework for structured document authoring.}
|
15
|
+
s.email = %q{h3rald@h3rald.com}
|
16
|
+
s.executables = ["glyph"]
|
17
|
+
s.extra_rdoc_files = [
|
18
|
+
"README.textile"
|
19
|
+
]
|
20
|
+
s.files = [
|
21
|
+
"README.textile",
|
22
|
+
"Rakefile",
|
23
|
+
"VERSION",
|
24
|
+
"bin/glyph",
|
25
|
+
"book/config.yml",
|
26
|
+
"book/document.glyph",
|
27
|
+
"book/images/glyph.png",
|
28
|
+
"book/images/glyph.svg",
|
29
|
+
"book/lib/macros/reference.rb",
|
30
|
+
"book/output/html/glyph.html",
|
31
|
+
"book/output/html/images/glyph.png",
|
32
|
+
"book/output/html/images/glyph.svg",
|
33
|
+
"book/output/pdf/glyph.pdf",
|
34
|
+
"book/snippets.yml",
|
35
|
+
"book/styles/css3.css",
|
36
|
+
"book/styles/default.css",
|
37
|
+
"book/text/authoring.textile",
|
38
|
+
"book/text/extending.textile",
|
39
|
+
"book/text/getting_started.textile",
|
40
|
+
"book/text/introduction.textile",
|
41
|
+
"book/text/ref_commands.textile",
|
42
|
+
"book/text/ref_config.textile",
|
43
|
+
"book/text/ref_macros.textile",
|
44
|
+
"book/text/troubleshooting.textile",
|
45
|
+
"config.yml",
|
46
|
+
"document.glyph",
|
47
|
+
"glyph.gemspec",
|
48
|
+
"lib/glyph.rb",
|
49
|
+
"lib/glyph/commands.rb",
|
50
|
+
"lib/glyph/config.rb",
|
51
|
+
"lib/glyph/document.rb",
|
52
|
+
"lib/glyph/glyph_language.rb",
|
53
|
+
"lib/glyph/glyph_language.treetop",
|
54
|
+
"lib/glyph/interpreter.rb",
|
55
|
+
"lib/glyph/macro.rb",
|
56
|
+
"lib/glyph/node.rb",
|
57
|
+
"lib/glyph/system_extensions.rb",
|
58
|
+
"macros/common.rb",
|
59
|
+
"macros/filters.rb",
|
60
|
+
"macros/html/block.rb",
|
61
|
+
"macros/html/inline.rb",
|
62
|
+
"macros/html/structure.rb",
|
63
|
+
"spec/files/container.textile",
|
64
|
+
"spec/files/document.glyph",
|
65
|
+
"spec/files/document_with_toc.glyph",
|
66
|
+
"spec/files/included.textile",
|
67
|
+
"spec/files/ligature.jpg",
|
68
|
+
"spec/files/markdown.markdown",
|
69
|
+
"spec/files/test.sass",
|
70
|
+
"spec/lib/commands_spec.rb",
|
71
|
+
"spec/lib/config_spec.rb",
|
72
|
+
"spec/lib/document_spec.rb",
|
73
|
+
"spec/lib/glyph_spec.rb",
|
74
|
+
"spec/lib/interpreter_spec.rb",
|
75
|
+
"spec/lib/macro_spec.rb",
|
76
|
+
"spec/lib/node_spec.rb",
|
77
|
+
"spec/macros/filters_spec.rb",
|
78
|
+
"spec/macros/macros_spec.rb",
|
79
|
+
"spec/spec_helper.rb",
|
80
|
+
"spec/tasks/generate_spec.rb",
|
81
|
+
"spec/tasks/load_spec.rb",
|
82
|
+
"spec/tasks/project_spec.rb",
|
83
|
+
"styles/css3.css",
|
84
|
+
"styles/default.css",
|
85
|
+
"tasks/generate.rake",
|
86
|
+
"tasks/load.rake",
|
87
|
+
"tasks/project.rake"
|
88
|
+
]
|
89
|
+
s.homepage = %q{http://www.h3rald.com/glyph/}
|
90
|
+
s.rdoc_options = ["--charset=UTF-8"]
|
91
|
+
s.require_paths = ["lib"]
|
92
|
+
s.rubygems_version = %q{1.3.5}
|
93
|
+
s.summary = %q{Glyph -- A Ruby-powered Document Authoring Framework}
|
94
|
+
s.test_files = [
|
95
|
+
"spec/macros/filters_spec.rb",
|
96
|
+
"spec/macros/macros_spec.rb",
|
97
|
+
"spec/lib/interpreter_spec.rb",
|
98
|
+
"spec/lib/commands_spec.rb",
|
99
|
+
"spec/lib/node_spec.rb",
|
100
|
+
"spec/lib/macro_spec.rb",
|
101
|
+
"spec/lib/config_spec.rb",
|
102
|
+
"spec/lib/glyph_spec.rb",
|
103
|
+
"spec/lib/document_spec.rb",
|
104
|
+
"spec/tasks/load_spec.rb",
|
105
|
+
"spec/tasks/generate_spec.rb",
|
106
|
+
"spec/tasks/project_spec.rb",
|
107
|
+
"spec/spec_helper.rb"
|
108
|
+
]
|
109
|
+
|
110
|
+
if s.respond_to? :specification_version then
|
111
|
+
current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
|
112
|
+
s.specification_version = 3
|
113
|
+
|
114
|
+
if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
|
115
|
+
s.add_runtime_dependency(%q<gli>, [">= 0.3.1"])
|
116
|
+
s.add_runtime_dependency(%q<extlib>, [">= 0.9.12"])
|
117
|
+
s.add_runtime_dependency(%q<treetop>, [">= 0.4.3"])
|
118
|
+
s.add_runtime_dependency(%q<rake>, [">= 0.8.7"])
|
119
|
+
s.add_development_dependency(%q<rspec>, [">= 0"])
|
120
|
+
s.add_development_dependency(%q<yard>, [">= 0"])
|
121
|
+
else
|
122
|
+
s.add_dependency(%q<gli>, [">= 0.3.1"])
|
123
|
+
s.add_dependency(%q<extlib>, [">= 0.9.12"])
|
124
|
+
s.add_dependency(%q<treetop>, [">= 0.4.3"])
|
125
|
+
s.add_dependency(%q<rake>, [">= 0.8.7"])
|
126
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
127
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
128
|
+
end
|
129
|
+
else
|
130
|
+
s.add_dependency(%q<gli>, [">= 0.3.1"])
|
131
|
+
s.add_dependency(%q<extlib>, [">= 0.9.12"])
|
132
|
+
s.add_dependency(%q<treetop>, [">= 0.4.3"])
|
133
|
+
s.add_dependency(%q<rake>, [">= 0.8.7"])
|
134
|
+
s.add_dependency(%q<rspec>, [">= 0"])
|
135
|
+
s.add_dependency(%q<yard>, [">= 0"])
|
136
|
+
end
|
137
|
+
end
|
138
|
+
|
data/lib/glyph.rb
ADDED
@@ -0,0 +1,128 @@
|
|
1
|
+
# Copyright (c) 2009-2010 Fabio Cevasco
|
2
|
+
# website: http://www.h3rald.com/glyph
|
3
|
+
# license: BSD
|
4
|
+
|
5
|
+
require 'rubygems'
|
6
|
+
require 'pathname'
|
7
|
+
require 'yaml'
|
8
|
+
require 'gli'
|
9
|
+
require 'extlib'
|
10
|
+
require 'treetop'
|
11
|
+
require 'rake'
|
12
|
+
|
13
|
+
# Glyph is a Rapid Document Authoring Framework able to produce structured documents effortlessly.
|
14
|
+
module Glyph
|
15
|
+
|
16
|
+
LIB = Pathname(__FILE__).dirname.expand_path/'glyph'
|
17
|
+
|
18
|
+
HOME = LIB/'../../'
|
19
|
+
|
20
|
+
require LIB/'system_extensions'
|
21
|
+
require LIB/'config'
|
22
|
+
require LIB/'node'
|
23
|
+
require LIB/'document'
|
24
|
+
require LIB/'glyph_language'
|
25
|
+
require LIB/'macro'
|
26
|
+
require LIB/'interpreter'
|
27
|
+
|
28
|
+
VERSION = file_load HOME/'VERSION'
|
29
|
+
|
30
|
+
SPEC_DIR = Pathname(__FILE__).dirname.expand_path/'../spec'
|
31
|
+
|
32
|
+
TASKS_DIR = Pathname(__FILE__).dirname.expand_path/'../tasks'
|
33
|
+
|
34
|
+
APP = Rake.application
|
35
|
+
|
36
|
+
SNIPPETS = {}
|
37
|
+
|
38
|
+
MACROS = {}
|
39
|
+
|
40
|
+
TODOS = []
|
41
|
+
|
42
|
+
ERRORS = []
|
43
|
+
|
44
|
+
# Returns true if Glyph is running in test mode
|
45
|
+
def self.testing?
|
46
|
+
const_defined? :TEST_MODE rescue false
|
47
|
+
end
|
48
|
+
|
49
|
+
PROJECT = (Glyph.testing?) ? Glyph::SPEC_DIR/"test_project" : Pathname.new(Dir.pwd)
|
50
|
+
|
51
|
+
CONFIG = Glyph::Config.new :resettable => true, :mutable => false
|
52
|
+
|
53
|
+
home_dir = Pathname.new(RUBY_PLATFORM.match(/win32|mingw/) ? ENV['HOMEPATH'] : ENV['HOME'])
|
54
|
+
SYSTEM_CONFIG = Glyph::Config.new(:file => HOME/'config.yml')
|
55
|
+
GLOBAL_CONFIG = Glyph.testing? ? Glyph::Config.new(:file => SPEC_DIR/'.glyphrc') : Glyph::Config.new(:file => home_dir/'.glyphrc')
|
56
|
+
PROJECT_CONFIG = Glyph::Config.new(:file => PROJECT/'config.yml')
|
57
|
+
|
58
|
+
# Loads all Rake tasks
|
59
|
+
def self.setup
|
60
|
+
FileList["#{TASKS_DIR}/**/*.rake"].each do |f|
|
61
|
+
load f
|
62
|
+
end
|
63
|
+
end
|
64
|
+
|
65
|
+
# Overrides a configuration setting
|
66
|
+
# @param setting [String, Symbol] the configuration setting to change
|
67
|
+
# @param value the new value
|
68
|
+
def self.config_override(setting, value)
|
69
|
+
PROJECT_CONFIG.set setting, value
|
70
|
+
reset_config
|
71
|
+
end
|
72
|
+
|
73
|
+
# Resets Glyph configuration
|
74
|
+
def self.reset_config
|
75
|
+
CONFIG.merge!(SYSTEM_CONFIG.merge(GLOBAL_CONFIG.merge(PROJECT_CONFIG)))
|
76
|
+
end
|
77
|
+
|
78
|
+
# Returns true if the PROJECT constant is set to a valid Glyph project directory
|
79
|
+
def self.project?
|
80
|
+
children = ["styles", "text", "output", "snippets.yml", "config.yml", "document.glyph"].sort
|
81
|
+
actual_children = PROJECT.children.map{|c| c.basename.to_s}.sort
|
82
|
+
(actual_children & children) == children
|
83
|
+
end
|
84
|
+
|
85
|
+
# Enables a Rake task
|
86
|
+
# @param task the task to enable
|
87
|
+
def self.enable(task)
|
88
|
+
Rake::Task[task].reenable
|
89
|
+
end
|
90
|
+
|
91
|
+
# Reenables and runs a Rake task
|
92
|
+
# @param task the task to enable
|
93
|
+
# @param *args the task arguments
|
94
|
+
def self.run!(task, *args)
|
95
|
+
Rake::Task[task].reenable
|
96
|
+
self.run task, *args
|
97
|
+
end
|
98
|
+
|
99
|
+
# Runs a Rake task
|
100
|
+
# @param task the task to enable
|
101
|
+
# @param *args the task arguments
|
102
|
+
def self.run(task, *args)
|
103
|
+
Rake::Task[task].invoke *args
|
104
|
+
end
|
105
|
+
|
106
|
+
# Defines a new macro
|
107
|
+
# @param name [Symbol, String] the name of the macro
|
108
|
+
def self.macro(name, &block)
|
109
|
+
MACROS[name] = block
|
110
|
+
end
|
111
|
+
|
112
|
+
# Defines an alias for an existing macro
|
113
|
+
# @param [Hash] pair the single-key hash defining the alias
|
114
|
+
# @example
|
115
|
+
# {:old_name => :new_name}
|
116
|
+
def self.macro_alias(pair)
|
117
|
+
name = pair.keys[0].to_sym
|
118
|
+
found = MACROS[name]
|
119
|
+
if found then
|
120
|
+
warning "Invalid alias: macro '#{name}' already exists."
|
121
|
+
return
|
122
|
+
end
|
123
|
+
MACROS[name] = MACROS[pair.values[0].to_sym]
|
124
|
+
end
|
125
|
+
|
126
|
+
end
|
127
|
+
|
128
|
+
Glyph.setup
|
@@ -0,0 +1,124 @@
|
|
1
|
+
include GLI
|
2
|
+
|
3
|
+
|
4
|
+
GLI.desc "Enable debugging"
|
5
|
+
switch [:d, :debug]
|
6
|
+
|
7
|
+
GLI.desc 'Create a new Glyph project'
|
8
|
+
command :init do |c|
|
9
|
+
c.action do |global_options,options,args|
|
10
|
+
Glyph.run 'project:create', Dir.pwd
|
11
|
+
end
|
12
|
+
end
|
13
|
+
|
14
|
+
GLI.desc 'Add a new text file to the project'
|
15
|
+
arg_name "file_name"
|
16
|
+
command :add do |c|
|
17
|
+
c.action do |global_options,options,args|
|
18
|
+
raise ArgumentError, "Please specify a file name." if args.blank?
|
19
|
+
Glyph.run 'project:add', args[0]
|
20
|
+
end
|
21
|
+
end
|
22
|
+
|
23
|
+
GLI.desc 'Compile the project'
|
24
|
+
arg_name "[output_target]"
|
25
|
+
command :compile do |c|
|
26
|
+
c.desc "Specify a glyph file to compile (default: document.glyph)"
|
27
|
+
c.flag [:s, :source]
|
28
|
+
c.desc "Specify the format of the output file (default: html)"
|
29
|
+
c.flag [:f, :format]
|
30
|
+
c.action do |global_options, options, args|
|
31
|
+
Glyph.run 'load:config'
|
32
|
+
output_targets = Glyph::CONFIG.get('document.output_targets')
|
33
|
+
target = nil
|
34
|
+
Glyph.config_override('document.output', options[:f]) if options[:f]
|
35
|
+
target = cfg('document.output')
|
36
|
+
target = nil if target.blank?
|
37
|
+
target ||= cfg('filters.target')
|
38
|
+
Glyph.config_override('document.source', options[:s]) if options[:s]
|
39
|
+
raise ArgumentError, "Output target not specified" unless target
|
40
|
+
raise ArgumentError, "Unknown output target '#{target}'" unless output_targets.include? target.to_sym
|
41
|
+
Glyph.run "generate:#{target}"
|
42
|
+
end
|
43
|
+
end
|
44
|
+
|
45
|
+
GLI.desc 'Display all project TODO items'
|
46
|
+
command :todo do |c|
|
47
|
+
c.action do |global_options, options, args|
|
48
|
+
Glyph.run "generate:document"
|
49
|
+
unless Glyph::TODOS.blank?
|
50
|
+
info "*** TODOs: ***"
|
51
|
+
Glyph::TODOS.each do |t|
|
52
|
+
info t
|
53
|
+
end
|
54
|
+
else
|
55
|
+
info "Nothing left to do."
|
56
|
+
end
|
57
|
+
end
|
58
|
+
end
|
59
|
+
|
60
|
+
GLI.desc 'Get/set configuration settings'
|
61
|
+
arg_name "setting [new_value]"
|
62
|
+
command :config do |c|
|
63
|
+
c.desc "Save to global configuration"
|
64
|
+
c.switch [:g, :global]
|
65
|
+
c.action do |global_options,options,args|
|
66
|
+
Glyph.run 'load:config'
|
67
|
+
if options[:g] then
|
68
|
+
cfg = Glyph::GLOBAL_CONFIG
|
69
|
+
else
|
70
|
+
cfg = Glyph::PROJECT_CONFIG
|
71
|
+
end
|
72
|
+
case args.length
|
73
|
+
when 0 then
|
74
|
+
raise ArgumentError, "Too few arguments."
|
75
|
+
when 1 then # read current config
|
76
|
+
setting = cfg(args[0])
|
77
|
+
raise RuntimeError, "Unknown setting '#{args[0]}'" if setting.blank?
|
78
|
+
info Glyph::CONFIG.get(args[0])
|
79
|
+
when 2 then
|
80
|
+
cfg.set args[0], args[1]
|
81
|
+
Glyph.reset_config
|
82
|
+
else
|
83
|
+
raise ArgumentError, "Too many arguments."
|
84
|
+
end
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
pre do |global,command,options,args|
|
89
|
+
# Pre logic here
|
90
|
+
# Return true to proceed; false to abourt and not call the
|
91
|
+
# chosen command
|
92
|
+
if global[:d] then
|
93
|
+
Glyph::DEBUG = true
|
94
|
+
end
|
95
|
+
if !command || command.name == :help then
|
96
|
+
puts "====================================="
|
97
|
+
puts "Glyph v#{Glyph::VERSION}"
|
98
|
+
puts "====================================="
|
99
|
+
end
|
100
|
+
true
|
101
|
+
end
|
102
|
+
|
103
|
+
post do |global,command,options,args|
|
104
|
+
# Post logic here
|
105
|
+
end
|
106
|
+
|
107
|
+
on_error do |exception|
|
108
|
+
if exception.is_a? MacroError then
|
109
|
+
#warning exception.message
|
110
|
+
puts exception.message
|
111
|
+
false
|
112
|
+
else
|
113
|
+
if Glyph.const_defined? :DEBUG then
|
114
|
+
puts "Exception: #{exception.message}"
|
115
|
+
puts "Backtrace:"
|
116
|
+
exception.backtrace.each do |b|
|
117
|
+
puts b
|
118
|
+
end
|
119
|
+
end
|
120
|
+
true
|
121
|
+
end
|
122
|
+
# Error logic here
|
123
|
+
# return false to skip default error handling
|
124
|
+
end
|
data/lib/glyph/config.rb
ADDED
@@ -0,0 +1,152 @@
|
|
1
|
+
module Glyph
|
2
|
+
|
3
|
+
# The Glyph::Config class is used (you don't say!) to store configuration data. Essentially it wraps a Hash of Hashes
|
4
|
+
# and provides some useful methods to access keys and subkeys.
|
5
|
+
class Config
|
6
|
+
|
7
|
+
# Initializes the configuration with a hash of options:
|
8
|
+
# * :file (default: nil) - A YAML file to read data from
|
9
|
+
# * :data (default: {})- The initial contents
|
10
|
+
# * :resettable (default: false) - Whether the configuration can be reset (cleared) or not
|
11
|
+
# * :mutable (default: true) - Whether the configuration can be changed or not
|
12
|
+
#
|
13
|
+
# @param [Hash] options the configuration options (merged with the the defaults)
|
14
|
+
def initialize(options={})
|
15
|
+
default_options = {:file => nil, :data => {}, :resettable => false, :mutable => true}
|
16
|
+
@options = default_options.merge options
|
17
|
+
@file = @options[:file]
|
18
|
+
@data = @options[:data]
|
19
|
+
read if @file
|
20
|
+
end
|
21
|
+
|
22
|
+
# Returns the underlying data hash
|
23
|
+
# @return [Hash] Configuration data
|
24
|
+
def to_hash
|
25
|
+
@data
|
26
|
+
end
|
27
|
+
|
28
|
+
# Resets all configuration data
|
29
|
+
# @param [Hash] hash the new configuration data to store
|
30
|
+
# @raise [RuntimeError] unless the configuration is resettable or if no hash is passed
|
31
|
+
# @return [Hash] Configuration data
|
32
|
+
def reset(hash={})
|
33
|
+
raise RuntimeError, "Configuration cannot be reset" unless @options[:resettable]
|
34
|
+
raise RuntimeError, "Configuration data is not stored in a Hash" unless hash.is_a? Hash
|
35
|
+
@data = hash
|
36
|
+
end
|
37
|
+
|
38
|
+
# Updates configuration data by applying Hash#update to each sub-hash of data, recursively.
|
39
|
+
# @param [Glyph::Config] cfg the configuration to update from
|
40
|
+
# @raise [ArgumentError] unless cfg is a Glyph::Config
|
41
|
+
# @return self
|
42
|
+
def update(cfg)
|
43
|
+
merge_or_update cfg, :update
|
44
|
+
end
|
45
|
+
|
46
|
+
# Merges configuration data by applying Hash#merge to each sub-hash of data, recursively.
|
47
|
+
# @param [Glyph::Config] cfg the configuration to merge with
|
48
|
+
# @raise [ArgumentError] unless cfg is a Glyph::Config
|
49
|
+
# @return [Glyph::Config] a new merged configuration
|
50
|
+
def merge(cfg)
|
51
|
+
merge_or_update cfg, :merge
|
52
|
+
end
|
53
|
+
|
54
|
+
alias merge! update
|
55
|
+
|
56
|
+
# Reads the contents of a file and stores them as configuration data
|
57
|
+
# @return [Hash] Configuration data
|
58
|
+
# @raise [RuntimeError] if self is not linked to a file or if the file does not contain a serialized Hash
|
59
|
+
def read
|
60
|
+
raise RuntimeError, "Configuration is not stored in a file." if @file.blank?
|
61
|
+
if @file.exist? then
|
62
|
+
contents = yaml_load @file
|
63
|
+
raise RuntimeError, "Invalid configuration file '#{@file}'" unless contents.is_a? Hash
|
64
|
+
@data = contents
|
65
|
+
else
|
66
|
+
@data = {}
|
67
|
+
end
|
68
|
+
@data
|
69
|
+
end
|
70
|
+
|
71
|
+
# Updates a configuration setting
|
72
|
+
# @param [String, Symbol] setting the setting to update
|
73
|
+
# @param [Object] value the value to store. Where applicable (Array, Hash, Boolean, Nil), attempts
|
74
|
+
# to evaluate string values
|
75
|
+
# @return [Object] the new value
|
76
|
+
# @raise [RuntimeError] unless the configuration is mutable
|
77
|
+
# @raise [ArgumentError] if the setting refers to an invalid namespace
|
78
|
+
# @example
|
79
|
+
# cfg = Glyph::Config.new
|
80
|
+
# cfg.set "quiet", true # Sets :quiet => true
|
81
|
+
# cfg.set "test.test_value", "[1,2,3]" # Sets :test => {:test_value => [1,2,3]}
|
82
|
+
# cfg.sef :quiet, "false" # Sets :quiet => false
|
83
|
+
def set(setting, value)
|
84
|
+
raise RuntimeError, "Configuration cannot be changed" unless @options[:mutable]
|
85
|
+
if value.is_a?(String) && value.match(/^(["'].*["']|:.+|\[.*\]|\{.*\}|true|false|nil)$/) then
|
86
|
+
value = Kernel.instance_eval value
|
87
|
+
end
|
88
|
+
hash = @data
|
89
|
+
path = setting.to_s.split(".").map{|s| s.intern}
|
90
|
+
count = 1
|
91
|
+
path.each do |s|
|
92
|
+
if hash.has_key? s then
|
93
|
+
if count == path.length then
|
94
|
+
# destination
|
95
|
+
hash[s] = value
|
96
|
+
else
|
97
|
+
if hash[s].is_a?(Hash) then
|
98
|
+
hash = hash[s]
|
99
|
+
count +=1
|
100
|
+
else
|
101
|
+
raise ArgumentError, "Invalid namespace #{s}"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
else
|
105
|
+
# key not found
|
106
|
+
if count == path.length then # destination
|
107
|
+
hash[s] = value
|
108
|
+
else
|
109
|
+
# create a new namespace
|
110
|
+
hash[s] = {}
|
111
|
+
hash = hash[s]
|
112
|
+
count +=1
|
113
|
+
end
|
114
|
+
end
|
115
|
+
end
|
116
|
+
value
|
117
|
+
end
|
118
|
+
|
119
|
+
# Returns a configuration setting
|
120
|
+
# @param [String, Symbol] setting the setting to retrieve
|
121
|
+
# @return [Object] the new value
|
122
|
+
# @see Glyph::Config#set
|
123
|
+
# @example
|
124
|
+
# cfg = Glyph::Config.new
|
125
|
+
# cfg.get :quiet # true
|
126
|
+
# cfg.get "test.test_value" # [1,2,3]
|
127
|
+
def get(setting)
|
128
|
+
@data.instance_eval "self#{setting.to_s.split(".").map{|key| "[:#{key}]" }.join}" rescue nil
|
129
|
+
end
|
130
|
+
|
131
|
+
# Serialize configuration data and writes it to a file
|
132
|
+
# @raise [RuntimeError] if the configuration is not linked to a file
|
133
|
+
def write
|
134
|
+
raise RuntimeError, "Configuration is not stored in a file." if @file.blank?
|
135
|
+
yaml_dump @file, @data
|
136
|
+
end
|
137
|
+
|
138
|
+
private
|
139
|
+
|
140
|
+
def merge_or_update(cfg, method=:merge)
|
141
|
+
raise ArgumentError, "#{cfg} is not a Glyph::Config" unless cfg.is_a? Glyph::Config
|
142
|
+
block = lambda do |key, v1, v2|
|
143
|
+
(v1.is_a?(Hash) && v2.is_a?(Hash)) ? v1.send(method, v2, &block) : v2
|
144
|
+
end
|
145
|
+
new_data = @data.send(method, cfg.to_hash, &block)
|
146
|
+
opts = @options.merge :data => new_data, :file => nil
|
147
|
+
(method == :merge) ? Config.new(opts) : self
|
148
|
+
end
|
149
|
+
|
150
|
+
end
|
151
|
+
|
152
|
+
end
|