secretary 0.0.1
Sign up to get free protection for your applications and to get access to all the features.
- data/History.txt +8 -0
- data/Manifest.txt +15 -0
- data/README.txt +92 -0
- data/Rakefile +12 -0
- data/lib/secretary.rb +57 -0
- data/lib/secretary/error.rb +31 -0
- data/lib/secretary/gopher.rb +59 -0
- data/lib/secretary/gopher/yaml.rb +27 -0
- data/spec/array.yaml +4 -0
- data/spec/broken-yaml.txt +5 -0
- data/spec/error_spec.rb +30 -0
- data/spec/gopher_spec.rb +157 -0
- data/spec/hash.yaml +3 -0
- data/spec/secretary_spec.rb +259 -0
- data/spec/spec_helper.rb +1 -0
- metadata +81 -0
data/History.txt
ADDED
data/Manifest.txt
ADDED
@@ -0,0 +1,15 @@
|
|
1
|
+
History.txt
|
2
|
+
Manifest.txt
|
3
|
+
README.txt
|
4
|
+
Rakefile
|
5
|
+
lib/secretary.rb
|
6
|
+
lib/secretary/error.rb
|
7
|
+
lib/secretary/gopher.rb
|
8
|
+
lib/secretary/gopher/yaml.rb
|
9
|
+
spec/spec_helper.rb
|
10
|
+
spec/secretary_spec.rb
|
11
|
+
spec/error_spec.rb
|
12
|
+
spec/gopher_spec.rb
|
13
|
+
spec/hash.yaml
|
14
|
+
spec/array.yaml
|
15
|
+
spec/broken-yaml.txt
|
data/README.txt
ADDED
@@ -0,0 +1,92 @@
|
|
1
|
+
= secretary
|
2
|
+
|
3
|
+
* http://secretary.rubyforge.org
|
4
|
+
|
5
|
+
== DESCRIPTION:
|
6
|
+
|
7
|
+
Secretary is a gem who makes managing simple preference files in Ruby easy
|
8
|
+
and painless. If you tire of being bound to PStore or YAML and want a
|
9
|
+
consistent, pluggable way to read and write preferences, your Secretary would
|
10
|
+
be happy to help!
|
11
|
+
|
12
|
+
== FEATURES:
|
13
|
+
|
14
|
+
* She's smooth and simple: just import and initialize!
|
15
|
+
* She flexible: though she uses the simple YAML markup language by default, she
|
16
|
+
can easily switch to any other format!
|
17
|
+
* She supports custom defaults, a big essential!
|
18
|
+
* She's easily extensible--subclass Secretary::Gopher to be able to load any
|
19
|
+
kind of file you wish!
|
20
|
+
* All in all, she's a small, no-frills object who gives you one less thing to
|
21
|
+
worry about. It can't be any easier!
|
22
|
+
|
23
|
+
== SYNOPSIS:
|
24
|
+
|
25
|
+
import 'secretary/simple'
|
26
|
+
|
27
|
+
preferences = {'name': 'Samantha', 'port': 151}
|
28
|
+
defaults = {'name': '', 'port': 80}
|
29
|
+
|
30
|
+
secretary = Secretary.new 'preferences.yaml', defaults
|
31
|
+
|
32
|
+
# The first argument is the path to the file that she will sync to.
|
33
|
+
# The second argument indicates the object that contains her defaults.
|
34
|
+
# There's also an optional third argument that determines how she will sync
|
35
|
+
# with her file--by default, it'll be YAML.
|
36
|
+
|
37
|
+
secretary['url'] = 'http://samantha.name'
|
38
|
+
|
39
|
+
secretary['name'] # <- 'Samantha'
|
40
|
+
secretary['url'] = 'http://samantha.name'
|
41
|
+
secretary['port'] # <- 80
|
42
|
+
|
43
|
+
secretary.save # Saves to a YAML file called 'preferences.yaml'.
|
44
|
+
|
45
|
+
== REQUIREMENTS:
|
46
|
+
|
47
|
+
* At least Ruby 1.8.0
|
48
|
+
* The standard forwarable.rb
|
49
|
+
* The standard yaml.rb
|
50
|
+
* At least RSpec 1.1 (but only if you want to unit test using Secretary's
|
51
|
+
included specs)
|
52
|
+
|
53
|
+
== INSTALL:
|
54
|
+
|
55
|
+
The recommended way to install Secretary is to use rubygems to download and
|
56
|
+
install it automatically:
|
57
|
+
|
58
|
+
gem install secretary
|
59
|
+
|
60
|
+
== NOTES:
|
61
|
+
|
62
|
+
I'm planning to add support for managing arbitrary objects, not just Hashes,
|
63
|
+
as well as support for more formats like CSV (easy), JSON (easier), and XML
|
64
|
+
(hard).
|
65
|
+
|
66
|
+
In addition, this is my first programming project ever, and I'm sort of not
|
67
|
+
used to this. I'd be very happy to hear your comments and bugs on Rubyforge.
|
68
|
+
|
69
|
+
== LICENSE:
|
70
|
+
|
71
|
+
(The MIT License)
|
72
|
+
|
73
|
+
Copyright (c) 2008
|
74
|
+
|
75
|
+
Permission is hereby granted, free of charge, to any person obtaining
|
76
|
+
a copy of this software and associated documentation files (the
|
77
|
+
'Software'), to deal in the Software without restriction, including
|
78
|
+
without limitation the rights to use, copy, modify, merge, publish,
|
79
|
+
distribute, sublicense, and/or sell copies of the Software, and to
|
80
|
+
permit persons to whom the Software is furnished to do so, subject to
|
81
|
+
the following conditions:
|
82
|
+
|
83
|
+
The above copyright notice and this permission notice shall be
|
84
|
+
included in all copies or substantial portions of the Software.
|
85
|
+
|
86
|
+
THE SOFTWARE IS PROVIDED 'AS IS', WITHOUT WARRANTY OF ANY KIND,
|
87
|
+
EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
|
88
|
+
MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT.
|
89
|
+
IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY
|
90
|
+
CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT,
|
91
|
+
TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE
|
92
|
+
SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
|
data/Rakefile
ADDED
@@ -0,0 +1,12 @@
|
|
1
|
+
# -*- ruby -*-
|
2
|
+
|
3
|
+
require 'rubygems'
|
4
|
+
require 'hoe'
|
5
|
+
require './lib/secretary'
|
6
|
+
|
7
|
+
Hoe.new('secretary', Secretary::VERSION) do |p|
|
8
|
+
p.developer('Joshua Choi', 'joshua@choi.name')
|
9
|
+
p.remote_rdoc_dir = '' # Release to root
|
10
|
+
end
|
11
|
+
|
12
|
+
# vim: syntax=Ruby
|
data/lib/secretary.rb
ADDED
@@ -0,0 +1,57 @@
|
|
1
|
+
$: << File.expand_path(File.dirname(__FILE__) + '/../lib')
|
2
|
+
require 'secretary/error'
|
3
|
+
require 'secretary/gopher/yaml'
|
4
|
+
require 'forwardable'
|
5
|
+
|
6
|
+
class Secretary
|
7
|
+
VERSION = '0.0.1'
|
8
|
+
extend Forwardable
|
9
|
+
include Enumerable
|
10
|
+
attr_reader :file_path, :defaults, :gopher
|
11
|
+
|
12
|
+
def initialize(file_path, defaults={}, gopher_class=Gopher::YAML)
|
13
|
+
unless gopher_class.respond_to? :new
|
14
|
+
raise ArgumentError, "given gopher class is not a class (#{gopher_class})"
|
15
|
+
end
|
16
|
+
@file_path = file_path
|
17
|
+
@defaults = defaults
|
18
|
+
@gopher = gopher_class.new Hash
|
19
|
+
unless @gopher.respond_to? :load_from_path
|
20
|
+
raise ArgumentError, "given gopher class\'s members do not respond to :load_from_path (#{gopher})"
|
21
|
+
end
|
22
|
+
@contents = {}
|
23
|
+
reload
|
24
|
+
end
|
25
|
+
|
26
|
+
def_delegators :to_hash, :[], :include?, :each, :each_pair, :each_key, :each_value
|
27
|
+
def_delegators :@contents, :[]=, :delete
|
28
|
+
|
29
|
+
def ==(other)
|
30
|
+
other.respond_to? :file_path and other.respond_to? :defaults \
|
31
|
+
and other.respond_to? :gopher and other.respond_to? :to_hash \
|
32
|
+
and file_path == other.file_path and defaults == other.defaults \
|
33
|
+
and gopher == other.gopher and to_hash == other.to_hash
|
34
|
+
end
|
35
|
+
|
36
|
+
def to_hash
|
37
|
+
defaults.merge @contents
|
38
|
+
end
|
39
|
+
|
40
|
+
def reload
|
41
|
+
begin
|
42
|
+
loaded_data = gopher.load_from_path(file_path)
|
43
|
+
unless loaded_data.respond_to? :to_hash
|
44
|
+
raise IOError, "gopher's loaded data is unconvertable to a Hash and thus invalid (#{loaded_data})"
|
45
|
+
end
|
46
|
+
@contents.merge! loaded_data
|
47
|
+
return true
|
48
|
+
rescue Error::MissingFile
|
49
|
+
return false
|
50
|
+
end
|
51
|
+
end
|
52
|
+
|
53
|
+
def save
|
54
|
+
gopher.save self.to_hash, file_path
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
@@ -0,0 +1,31 @@
|
|
1
|
+
class Secretary
|
2
|
+
|
3
|
+
module Error
|
4
|
+
|
5
|
+
class MissingFile < IOError
|
6
|
+
|
7
|
+
def initialize(missing_file_path)
|
8
|
+
super "no such file or directory (#{missing_file_path})"
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
class Parsing < IOError
|
14
|
+
|
15
|
+
def initialize(parsing_error, file_type)
|
16
|
+
super "attempted to parse an invalid #{file_type} file (#{parsing_error})"
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
|
21
|
+
class InvalidData < IOError
|
22
|
+
|
23
|
+
def initialize(data, valid_data_type)
|
24
|
+
super "preference file of invalid data type (should be #{valid_data_type} instead of #{data.class})"
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
28
|
+
|
29
|
+
end
|
30
|
+
|
31
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
require 'secretary/error'
|
2
|
+
|
3
|
+
class Secretary
|
4
|
+
|
5
|
+
class Gopher
|
6
|
+
# Abstract base class that requires methods #parse_file(input_file),
|
7
|
+
# #valid_file_type(), and #emit(data) to survive.
|
8
|
+
VERSION = '0.0.1'
|
9
|
+
attr_reader :valid_data_type
|
10
|
+
class << self
|
11
|
+
attr_reader :valid_file_type
|
12
|
+
end
|
13
|
+
|
14
|
+
def initialize(valid_data_type)
|
15
|
+
@valid_data_type = valid_data_type
|
16
|
+
end
|
17
|
+
|
18
|
+
def load_from_path(file_path)
|
19
|
+
if File.exist? file_path
|
20
|
+
open file_path do |file|
|
21
|
+
return load_from_file(file)
|
22
|
+
end
|
23
|
+
else
|
24
|
+
raise Secretary::Error::MissingFile.new(file_path)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
|
28
|
+
def load_from_file(file)
|
29
|
+
begin
|
30
|
+
file_data = parse_file file
|
31
|
+
rescue Secretary::Error::Parsing => parsing_error
|
32
|
+
raise parsing_error
|
33
|
+
end
|
34
|
+
unless file_data.kind_of? valid_data_type
|
35
|
+
raise Secretary::Error::InvalidData.new(file_data, valid_data_type)
|
36
|
+
end
|
37
|
+
return file_data
|
38
|
+
end
|
39
|
+
|
40
|
+
def save(data, file_path)
|
41
|
+
unless data.kind_of? valid_data_type
|
42
|
+
raise ArgumentError, "invalid data #{data} is not a kind of #{valid_data_type}"
|
43
|
+
end
|
44
|
+
open file_path, 'w' do |file|
|
45
|
+
file.puts emit(data)
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
49
|
+
def valid_file_type
|
50
|
+
self.class.valid_file_type
|
51
|
+
end
|
52
|
+
|
53
|
+
def ==(other)
|
54
|
+
self.class == other.class
|
55
|
+
end
|
56
|
+
|
57
|
+
end
|
58
|
+
|
59
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
require 'secretary/gopher'
|
2
|
+
require 'yaml'
|
3
|
+
|
4
|
+
class Secretary
|
5
|
+
|
6
|
+
class Gopher
|
7
|
+
|
8
|
+
class YAML < Secretary::Gopher
|
9
|
+
@valid_file_type = 'YAML'
|
10
|
+
|
11
|
+
def parse_file(file)
|
12
|
+
begin
|
13
|
+
return ::YAML::load(file)
|
14
|
+
rescue ArgumentError => parsing_error
|
15
|
+
raise Secretary::Error::Parsing.new(parsing_error, valid_file_type)
|
16
|
+
end
|
17
|
+
end
|
18
|
+
|
19
|
+
def emit(data)
|
20
|
+
data.to_yaml
|
21
|
+
end
|
22
|
+
|
23
|
+
end
|
24
|
+
|
25
|
+
end
|
26
|
+
|
27
|
+
end
|
data/spec/array.yaml
ADDED
data/spec/error_spec.rb
ADDED
@@ -0,0 +1,30 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require 'lib/secretary'
|
3
|
+
|
4
|
+
describe Secretary::Error::MissingFile do
|
5
|
+
|
6
|
+
it 'should have a message with the missing file\'s path' do
|
7
|
+
Secretary::Error::MissingFile.new('missing-file.txt').to_s \
|
8
|
+
.should == 'no such file or directory (missing-file.txt)'
|
9
|
+
end
|
10
|
+
|
11
|
+
end
|
12
|
+
|
13
|
+
describe Secretary::Error::Parsing do
|
14
|
+
|
15
|
+
it 'should have a message with the missing file\'s path' do
|
16
|
+
parsing_error = SyntaxError.new 'invalid char found at line 3'
|
17
|
+
Secretary::Error::Parsing.new(parsing_error, 'YAML').to_s \
|
18
|
+
.should == 'attempted to parse an invalid YAML file (invalid char found at line 3)'
|
19
|
+
end
|
20
|
+
|
21
|
+
end
|
22
|
+
|
23
|
+
describe Secretary::Error::InvalidData do
|
24
|
+
|
25
|
+
it 'should have a message with the missing file\'s path' do
|
26
|
+
Secretary::Error::InvalidData.new([3, 2], Hash).to_s \
|
27
|
+
.should == 'preference file of invalid data type (should be Hash instead of Array)'
|
28
|
+
end
|
29
|
+
|
30
|
+
end
|
data/spec/gopher_spec.rb
ADDED
@@ -0,0 +1,157 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require 'lib/secretary/gopher/yaml'
|
3
|
+
|
4
|
+
describe Secretary::Gopher::YAML do
|
5
|
+
|
6
|
+
it 'should be equal with another if they are of equal class' do
|
7
|
+
gopher0 = Secretary::Gopher::YAML.new stub_everything
|
8
|
+
gopher1 = Secretary::Gopher::YAML.new stub_everything
|
9
|
+
gopher0.should == gopher1
|
10
|
+
gopher1.should == gopher0
|
11
|
+
end
|
12
|
+
|
13
|
+
it 'should be unequal with another if they are of unequal class, even if the other is a kind of its class' do
|
14
|
+
gopher0 = Secretary::Gopher::YAML.new stub_everything
|
15
|
+
gopher1 = stub :kind_of? => true
|
16
|
+
gopher0.should_not == gopher1
|
17
|
+
end
|
18
|
+
|
19
|
+
|
20
|
+
it 'should know its valid data type' do
|
21
|
+
data_type = stub 'Data Type'
|
22
|
+
gopher = Secretary::Gopher::YAML.new data_type
|
23
|
+
gopher.valid_data_type.should == data_type
|
24
|
+
end
|
25
|
+
|
26
|
+
it 'should know its valid file type and it should be YAML' do
|
27
|
+
Secretary::Gopher::YAML.new(stub_everything).valid_file_type.should == 'YAML'
|
28
|
+
end
|
29
|
+
|
30
|
+
describe 'when trying to save an object into a YAML file using a file path' do
|
31
|
+
|
32
|
+
it 'should save a given Hash when the valid data type is Hash' do
|
33
|
+
gopher = Secretary::Gopher::YAML.new Hash
|
34
|
+
data = {:name => 'Tracy', :url => 'http://tracy.name'}
|
35
|
+
file_path = File.dirname(__FILE__) + '/temporary.yaml'
|
36
|
+
gopher.save data, file_path
|
37
|
+
open file_path do |file|
|
38
|
+
YAML::load(file).should == data
|
39
|
+
end
|
40
|
+
File.delete file_path
|
41
|
+
end
|
42
|
+
|
43
|
+
it 'should save a given object when the object is of its valid data type' do
|
44
|
+
class Person
|
45
|
+
def initialize
|
46
|
+
@name = 'Sal'
|
47
|
+
@age = 76
|
48
|
+
end
|
49
|
+
def ==(other)
|
50
|
+
self.class == other.class
|
51
|
+
end
|
52
|
+
end
|
53
|
+
data = Person.new
|
54
|
+
gopher = Secretary::Gopher::YAML.new Person
|
55
|
+
file_path = File.dirname(__FILE__) + '/temporary.yaml'
|
56
|
+
gopher.save data, file_path
|
57
|
+
open file_path do |file|
|
58
|
+
YAML::load(file).should == data
|
59
|
+
end
|
60
|
+
File.delete file_path
|
61
|
+
end
|
62
|
+
|
63
|
+
it 'should raise an error if it tries to save data that is not of its valid data type' do
|
64
|
+
gopher = Secretary::Gopher::YAML.new Hash
|
65
|
+
data = [3, 2, 1]
|
66
|
+
file_path = File.dirname(__FILE__) + '/temporary.yaml'
|
67
|
+
lambda { gopher.save data, file_path }.should raise_error(ArgumentError)
|
68
|
+
end
|
69
|
+
|
70
|
+
it 'should not create a new file if it tries to save data that is not of its valid data type' do
|
71
|
+
gopher = Secretary::Gopher::YAML.new Hash
|
72
|
+
data = [3, 2, 1]
|
73
|
+
file_path = File.dirname(__FILE__) + '/temporary.yaml'
|
74
|
+
begin
|
75
|
+
gopher.save data, file_path
|
76
|
+
rescue ArgumentError
|
77
|
+
end
|
78
|
+
File.exists?(file_path).should_not be_true
|
79
|
+
end
|
80
|
+
|
81
|
+
it 'should not modify any file if it tries to save data that is not of its valid data type' do
|
82
|
+
gopher = Secretary::Gopher::YAML.new Hash
|
83
|
+
data0 = {:name => 'Tracy', :url => 'http://tracy.name'}
|
84
|
+
data1 = [3, 2, 1]
|
85
|
+
file_path = File.dirname(__FILE__) + '/temporary.yaml'
|
86
|
+
gopher.save data0, file_path
|
87
|
+
begin
|
88
|
+
gopher.save data1, file_path
|
89
|
+
rescue ArgumentError
|
90
|
+
end
|
91
|
+
open file_path do |file|
|
92
|
+
YAML::load(file).should == data0
|
93
|
+
end
|
94
|
+
File.delete file_path
|
95
|
+
end
|
96
|
+
|
97
|
+
end
|
98
|
+
|
99
|
+
describe 'when reading from a file' do
|
100
|
+
|
101
|
+
it 'should get the file\'s data when the file is YAML and that data is a Hash and the given type is Hash' do
|
102
|
+
preferences0 = {'name' => 'Billy Bob', 'email' => 'xxx@yyy.zzz'}
|
103
|
+
file_path = File.dirname(__FILE__) + '/hash.yaml'
|
104
|
+
open file_path do |file|
|
105
|
+
require 'yaml'
|
106
|
+
gopher = Secretary::Gopher::YAML.new Hash
|
107
|
+
preferences1 = gopher.load_from_file file
|
108
|
+
preferences1.should == preferences0
|
109
|
+
end
|
110
|
+
end
|
111
|
+
|
112
|
+
it 'should raise a ParsingError when the file is not YAML' do
|
113
|
+
gopher = Secretary::Gopher::YAML.new stub_everything
|
114
|
+
file_path = File.dirname(__FILE__) + '/broken-yaml.txt'
|
115
|
+
open file_path do |file|
|
116
|
+
lambda { gopher.load_from_file file } \
|
117
|
+
.should raise_error(Secretary::Error::Parsing)
|
118
|
+
end
|
119
|
+
end
|
120
|
+
|
121
|
+
it 'should raise an InvalidDataError when the file\'s data is not a Hash and the valid data type is Hash' do
|
122
|
+
gopher = Secretary::Gopher::YAML.new Hash
|
123
|
+
file_path = File.dirname(__FILE__) + '/array.yaml'
|
124
|
+
open file_path do |file|
|
125
|
+
lambda { gopher.load_from_file file } \
|
126
|
+
.should raise_error(Secretary::Error::InvalidData)
|
127
|
+
end
|
128
|
+
end
|
129
|
+
|
130
|
+
end
|
131
|
+
|
132
|
+
describe 'when reading from a path to a file' do
|
133
|
+
|
134
|
+
it 'should raise a MissingFileError when the path leads to nowhere' do
|
135
|
+
gopher = Secretary::Gopher::YAML.new Hash
|
136
|
+
lambda { gopher.load_from_path 'missing-file.yaml' } \
|
137
|
+
.should raise_error(Secretary::Error::MissingFile)
|
138
|
+
end
|
139
|
+
|
140
|
+
it 'should get the path\'s file\'s data when the file is YAML and that data is a Hash and the given type is Hash' do
|
141
|
+
preferences0 = {'name' => 'Billy Bob', 'email' => 'xxx@yyy.zzz'}
|
142
|
+
gopher = Secretary::Gopher::YAML.new Hash
|
143
|
+
file_path = File.dirname(__FILE__) + '/hash.yaml'
|
144
|
+
preferences1 = gopher.load_from_path(file_path)
|
145
|
+
preferences1.should == preferences0
|
146
|
+
end
|
147
|
+
|
148
|
+
it 'should raise an InvalidDataError when the path\'s file\'s data is not a Hash and the valid data type is Hash' do
|
149
|
+
gopher = Secretary::Gopher::YAML.new Hash
|
150
|
+
file_path = File.dirname(__FILE__) + '/array.yaml'
|
151
|
+
lambda { gopher.load_from_path file_path } \
|
152
|
+
.should raise_error(Secretary::Error::InvalidData)
|
153
|
+
end
|
154
|
+
|
155
|
+
end
|
156
|
+
|
157
|
+
end
|
data/spec/hash.yaml
ADDED
@@ -0,0 +1,259 @@
|
|
1
|
+
require File.dirname(__FILE__) + '/spec_helper'
|
2
|
+
require 'lib/secretary'
|
3
|
+
require 'lib/secretary/gopher/yaml'
|
4
|
+
|
5
|
+
describe Secretary do
|
6
|
+
|
7
|
+
it 'should be enumerable' do
|
8
|
+
gopher = stub 'Gopher', :load_from_path => {}
|
9
|
+
gopher_class = stub 'Gopher Class', :new => gopher
|
10
|
+
file_path = stub 'File Path'
|
11
|
+
secretary = Secretary.new file_path, {}, gopher_class
|
12
|
+
secretary.should be_a_kind_of(Enumerable)
|
13
|
+
end
|
14
|
+
|
15
|
+
it 'should know the file path it saves to' do
|
16
|
+
gopher = stub 'Gopher', :load_from_path => {}
|
17
|
+
gopher_class = stub 'Gopher Class', :new => gopher
|
18
|
+
file_path = stub 'File Path'
|
19
|
+
secretary = Secretary.new file_path, {}, gopher_class
|
20
|
+
secretary.file_path.should == file_path
|
21
|
+
end
|
22
|
+
|
23
|
+
it 'should know its defaults' do
|
24
|
+
gopher = stub 'Gopher', :load_from_path => {}
|
25
|
+
gopher_class = stub 'Gopher Class', :new => gopher
|
26
|
+
secretary = Secretary.new stub_everything, {'name' => ''}, gopher_class
|
27
|
+
secretary.defaults.should == {'name' => ''}
|
28
|
+
end
|
29
|
+
|
30
|
+
it 'should have an empty hash for its defaults by default' do
|
31
|
+
gopher = stub 'Gopher', :load_from_path => {}
|
32
|
+
gopher_class = stub 'Gopher Class', :new => gopher
|
33
|
+
secretary = Secretary.new stub_everything, {}, gopher_class
|
34
|
+
secretary.defaults.should == {}
|
35
|
+
end
|
36
|
+
|
37
|
+
it 'should create a new gopher, which expects a Hash, from the given gopher class' do
|
38
|
+
gopher = stub 'Gopher', :load_from_path => {}
|
39
|
+
gopher_class = mock 'Gopher Class'
|
40
|
+
gopher_class.should_receive(:new).with(Hash).and_return(gopher)
|
41
|
+
secretary = Secretary.new stub_everything, stub_everything, gopher_class
|
42
|
+
end
|
43
|
+
|
44
|
+
it 'should create a new YAML gopher by default' do
|
45
|
+
secretary = Secretary.new 'file_path'
|
46
|
+
secretary.gopher.should be_a_kind_of(Secretary::Gopher::YAML)
|
47
|
+
end
|
48
|
+
|
49
|
+
it 'should know its gopher' do
|
50
|
+
defaults = stub_everything
|
51
|
+
gopher = stub 'Gopher', :load_from_path => {}
|
52
|
+
gopher_class = stub 'Gopher Class', :new => gopher
|
53
|
+
secretary = Secretary.new stub_everything, defaults, gopher_class
|
54
|
+
end
|
55
|
+
|
56
|
+
it 'should be able to set its values' do
|
57
|
+
data = {'name' => 'Billy Bob'}
|
58
|
+
gopher = stub 'Gopher', :load_from_path => data
|
59
|
+
gopher_class = stub 'Gopher Class', :new => gopher
|
60
|
+
secretary = Secretary.new stub_everything, {}, gopher_class
|
61
|
+
secretary['name'] = 'Joe'
|
62
|
+
secretary['name'].should == 'Joe'
|
63
|
+
end
|
64
|
+
|
65
|
+
it 'should be able to delete its values' do
|
66
|
+
data = {'name' => 'Billy Bob'}
|
67
|
+
gopher = stub 'Gopher', :load_from_path => data
|
68
|
+
gopher_class = stub 'Gopher Class', :new => gopher
|
69
|
+
secretary = Secretary.new stub_everything, {}, gopher_class
|
70
|
+
secretary.delete 'name'
|
71
|
+
secretary.should_not include('name')
|
72
|
+
end
|
73
|
+
|
74
|
+
describe 'when accessing a key' do
|
75
|
+
|
76
|
+
it 'should know the key\'s value' do
|
77
|
+
data = {'name' => 'Billy Bob'}
|
78
|
+
gopher = stub 'Gopher', :load_from_path => data
|
79
|
+
gopher_class = stub 'Gopher Class', :new => gopher
|
80
|
+
secretary = Secretary.new stub_everything, {}, gopher_class
|
81
|
+
secretary['name'].should == 'Billy Bob'
|
82
|
+
end
|
83
|
+
|
84
|
+
it 'should give the key\'s corresponding default value if the key\'s does not otherwise exist in its contents' do
|
85
|
+
defaults = {'name' => 'Billy Bob'}
|
86
|
+
data = {}
|
87
|
+
gopher = stub 'Gopher', :load_from_path => data
|
88
|
+
gopher_class = stub 'Gopher Class', :new => gopher
|
89
|
+
secretary = Secretary.new stub_everything, defaults, gopher_class
|
90
|
+
secretary['name'].should == 'Billy Bob'
|
91
|
+
end
|
92
|
+
|
93
|
+
it 'should give the key\'s corresponding default value after the key is deleted' do
|
94
|
+
defaults = {'name' => 'Joe'}
|
95
|
+
data = {'name' => 'Billy Bob'}
|
96
|
+
gopher = stub 'Gopher', :load_from_path => data
|
97
|
+
gopher_class = stub 'Gopher Class', :new => gopher
|
98
|
+
secretary = Secretary.new stub_everything, defaults, gopher_class
|
99
|
+
secretary.delete 'name'
|
100
|
+
secretary['name'].should == 'Joe'
|
101
|
+
end
|
102
|
+
|
103
|
+
end
|
104
|
+
|
105
|
+
describe 'using a custom gopher' do
|
106
|
+
|
107
|
+
it 'should raise an error when its given gopher class cannot make new gophers' do
|
108
|
+
gopher_class = stub 'Gopher Class'
|
109
|
+
lambda { Secretary.new stub_everything, {}, gopher_class } \
|
110
|
+
.should raise_error(ArgumentError)
|
111
|
+
end
|
112
|
+
|
113
|
+
it 'should raise an error when its given gopher class\'s objects cannot act like gophers' do
|
114
|
+
gopher = stub 'Gopher'
|
115
|
+
gopher_class = stub 'Gopher Class', :new => gopher
|
116
|
+
lambda { Secretary.new stub_everything, {}, gopher_class } \
|
117
|
+
.should raise_error(ArgumentError)
|
118
|
+
end
|
119
|
+
|
120
|
+
describe 'updating itself based on what the gopher brings from its file path' do
|
121
|
+
|
122
|
+
it 'have contents of its defaults merged with the file\'s data, when initialized' do
|
123
|
+
defaults = {:bash => 'bang', :bar => 'bing'}
|
124
|
+
data = {:bar => 'foo', :bam => 'fiz'}
|
125
|
+
gopher = stub 'Gopher', :load_from_path => data
|
126
|
+
gopher_class = stub 'Gopher Class', :new => gopher
|
127
|
+
secretary = Secretary.new stub_everything, defaults, gopher_class
|
128
|
+
secretary.to_hash.should == defaults.merge(data)
|
129
|
+
end
|
130
|
+
|
131
|
+
it 'have contents of its defaults only if its file does not yet exist, when initialized' do
|
132
|
+
defaults = {:bash => 'bang', :bar => 'bing'}
|
133
|
+
gopher = stub 'Gopher'
|
134
|
+
gopher.stub!(:load_from_path).and_raise(
|
135
|
+
Secretary::Error::MissingFile.new(stub_everything)
|
136
|
+
)
|
137
|
+
gopher_class = stub 'Gopher Class', :new => gopher
|
138
|
+
secretary = Secretary.new stub_everything, defaults, gopher_class
|
139
|
+
secretary.to_hash.should == defaults
|
140
|
+
end
|
141
|
+
|
142
|
+
it 'should update its contents with its defaults, based on what the gopher brings from its file path, when reloaded' do
|
143
|
+
defaults = {:bash => 'bang', :bar => 'bing'}
|
144
|
+
data = {:bar => 'foo', :bam => 'fiz'}
|
145
|
+
gopher = stub 'Gopher'
|
146
|
+
gopher.stub!(:load_from_path).and_return({}, data)
|
147
|
+
gopher_class = stub 'Gopher Class', :new => gopher
|
148
|
+
secretary = Secretary.new stub_everything, defaults, gopher_class
|
149
|
+
secretary.reload
|
150
|
+
secretary.to_hash.should == defaults.merge(data)
|
151
|
+
end
|
152
|
+
|
153
|
+
end
|
154
|
+
|
155
|
+
it 'should send its contents to the gopher when saving' do
|
156
|
+
contents = {:bar => 'foo', :bam => 'fiz'}
|
157
|
+
file_path = stub_everything
|
158
|
+
gopher = stub 'Gopher', :load_from_path => contents
|
159
|
+
gopher.should_receive(:save).with(contents, file_path).once
|
160
|
+
gopher_class = stub 'Gopher Class', :new => gopher
|
161
|
+
secretary = Secretary.new file_path, {}, gopher_class
|
162
|
+
secretary.save
|
163
|
+
end
|
164
|
+
|
165
|
+
end
|
166
|
+
|
167
|
+
describe 'using a default YAML gopher' do
|
168
|
+
|
169
|
+
describe 'updating itself based on what the gopher brings from its file path' do
|
170
|
+
|
171
|
+
it 'have contents of its defaults merged with the file\'s data, when initialized' do
|
172
|
+
defaults = {'name' => '', 'port' => 80}
|
173
|
+
data = {'name' => 'Billy Bob', 'email' => 'xxx@yyy.zzz'}
|
174
|
+
file_path = File.dirname(__FILE__) + '/hash.yaml'
|
175
|
+
secretary = Secretary.new file_path, defaults
|
176
|
+
secretary.to_hash.should == defaults.merge(data)
|
177
|
+
end
|
178
|
+
|
179
|
+
it 'have contents of its defaults only if its file does not yet exist, when initialized' do
|
180
|
+
defaults = {'name' => '', 'port' => 80}
|
181
|
+
file_path = File.dirname(__FILE__) + '/missing_file.yaml'
|
182
|
+
secretary = Secretary.new file_path, defaults
|
183
|
+
secretary.to_hash.should == defaults
|
184
|
+
end
|
185
|
+
|
186
|
+
end
|
187
|
+
|
188
|
+
describe 'when saving data' do
|
189
|
+
|
190
|
+
it 'should create a new data file if it does not exist' do
|
191
|
+
defaults = {'name' => '', 'port' => 80}
|
192
|
+
data = {'name' => 'Billy Bob', 'email' => 'xxx@yyy.zzz'}
|
193
|
+
file_path = File.dirname(__FILE__) + '/temporary.yaml'
|
194
|
+
secretary = Secretary.new file_path, defaults
|
195
|
+
secretary.save
|
196
|
+
File.exists?(file_path).should be_true
|
197
|
+
File.delete file_path
|
198
|
+
end
|
199
|
+
|
200
|
+
end
|
201
|
+
|
202
|
+
end
|
203
|
+
|
204
|
+
describe 'when comparing with another object' do
|
205
|
+
|
206
|
+
it 'should be equal if their file paths, defaults, and contents are equal' do
|
207
|
+
file_path = stub_everything
|
208
|
+
defaults = {:boo => 'bash'}
|
209
|
+
contents = {:boo => 'bam', :foo => 'bar'}
|
210
|
+
gopher = stub 'Gopher', :load_from_path => contents
|
211
|
+
gopher_class = stub 'Gopher Class', :new => gopher
|
212
|
+
secretary = Secretary.new file_path, defaults, gopher_class
|
213
|
+
other = stub 'Other', :file_path => file_path, :defaults => defaults,
|
214
|
+
:gopher => gopher, :to_hash => contents
|
215
|
+
secretary.to_hash.should == other.to_hash
|
216
|
+
secretary.should == other
|
217
|
+
end
|
218
|
+
|
219
|
+
it 'should be unequal if their file paths are unequal' do
|
220
|
+
defaults = {:bash => 'bang', :bar => 'bing'}
|
221
|
+
gopher_class = stub 'Gopher Class', :new => stub('Gopher', :load_from_path => {})
|
222
|
+
secretary0 = Secretary.new stub_everything, defaults, gopher_class
|
223
|
+
secretary1 = Secretary.new stub_everything, defaults, gopher_class
|
224
|
+
secretary0.should_not == secretary1
|
225
|
+
end
|
226
|
+
|
227
|
+
it 'should be unequal if their defaults are unequal' do
|
228
|
+
gopher_class = stub 'Gopher Class', :new => stub('Gopher', :load_from_path => {})
|
229
|
+
defaults0 = {:bash => 'bang', :bar => 'bing'}
|
230
|
+
defaults1 = {:bash => 'foo', :bar => 'bing'}
|
231
|
+
secretary0 = Secretary.new stub_everything, defaults0, gopher_class
|
232
|
+
secretary1 = Secretary.new stub_everything, defaults1, gopher_class
|
233
|
+
secretary0.should_not == secretary1
|
234
|
+
end
|
235
|
+
|
236
|
+
it 'should be unequal if the other does not respond to :file_path' do
|
237
|
+
gopher_class = stub 'Gopher Class', :new => stub('Gopher', :load_from_path => {})
|
238
|
+
secretary = Secretary.new stub_everything, {}, gopher_class
|
239
|
+
other = stub 'Other'
|
240
|
+
secretary.should_not == other
|
241
|
+
end
|
242
|
+
|
243
|
+
it 'should be unequal if the other does not respond to :gopher' do
|
244
|
+
gopher_class = stub 'Gopher Class', :new => stub('Gopher', :load_from_path => {})
|
245
|
+
secretary = Secretary.new stub_everything, {}, gopher_class
|
246
|
+
other = stub 'Other'
|
247
|
+
secretary.should_not == other
|
248
|
+
end
|
249
|
+
|
250
|
+
it 'should be unequal if the other does not respond to :file_path' do
|
251
|
+
gopher_class = stub 'Gopher Class', :new => stub('Gopher', :load_from_path => {})
|
252
|
+
secretary = Secretary.new stub_everything, {}, gopher_class
|
253
|
+
other = stub 'Other'
|
254
|
+
secretary.should_not == other
|
255
|
+
end
|
256
|
+
|
257
|
+
end
|
258
|
+
|
259
|
+
end
|
data/spec/spec_helper.rb
ADDED
@@ -0,0 +1 @@
|
|
1
|
+
$: << File.expand_path(File.dirname(__FILE__) + '/../lib')
|
metadata
ADDED
@@ -0,0 +1,81 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: secretary
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Joshua Choi
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-08-12 00:00:00 -07:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: hoe
|
17
|
+
type: :development
|
18
|
+
version_requirement:
|
19
|
+
version_requirements: !ruby/object:Gem::Requirement
|
20
|
+
requirements:
|
21
|
+
- - ">="
|
22
|
+
- !ruby/object:Gem::Version
|
23
|
+
version: 1.7.0
|
24
|
+
version:
|
25
|
+
description: Secretary is a gem who makes managing simple preference files in Ruby easy and painless. If you tire of being bound to PStore or YAML and want a consistent, pluggable way to read and write preferences, your Secretary would be happy to help!
|
26
|
+
email:
|
27
|
+
- joshua@choi.name
|
28
|
+
executables: []
|
29
|
+
|
30
|
+
extensions: []
|
31
|
+
|
32
|
+
extra_rdoc_files:
|
33
|
+
- History.txt
|
34
|
+
- Manifest.txt
|
35
|
+
- README.txt
|
36
|
+
- spec/broken-yaml.txt
|
37
|
+
files:
|
38
|
+
- History.txt
|
39
|
+
- Manifest.txt
|
40
|
+
- README.txt
|
41
|
+
- Rakefile
|
42
|
+
- lib/secretary.rb
|
43
|
+
- lib/secretary/error.rb
|
44
|
+
- lib/secretary/gopher.rb
|
45
|
+
- lib/secretary/gopher/yaml.rb
|
46
|
+
- spec/spec_helper.rb
|
47
|
+
- spec/secretary_spec.rb
|
48
|
+
- spec/error_spec.rb
|
49
|
+
- spec/gopher_spec.rb
|
50
|
+
- spec/hash.yaml
|
51
|
+
- spec/array.yaml
|
52
|
+
- spec/broken-yaml.txt
|
53
|
+
has_rdoc: true
|
54
|
+
homepage: http://secretary.rubyforge.org
|
55
|
+
post_install_message:
|
56
|
+
rdoc_options:
|
57
|
+
- --main
|
58
|
+
- README.txt
|
59
|
+
require_paths:
|
60
|
+
- lib
|
61
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
62
|
+
requirements:
|
63
|
+
- - ">="
|
64
|
+
- !ruby/object:Gem::Version
|
65
|
+
version: "0"
|
66
|
+
version:
|
67
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
68
|
+
requirements:
|
69
|
+
- - ">="
|
70
|
+
- !ruby/object:Gem::Version
|
71
|
+
version: "0"
|
72
|
+
version:
|
73
|
+
requirements: []
|
74
|
+
|
75
|
+
rubyforge_project: secretary
|
76
|
+
rubygems_version: 1.2.0
|
77
|
+
signing_key:
|
78
|
+
specification_version: 2
|
79
|
+
summary: Secretary is a gem who makes managing simple preference files in Ruby easy and painless
|
80
|
+
test_files: []
|
81
|
+
|