secretary 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,8 @@
1
+ === 0.0.1 / 2008-08-11
2
+
3
+ * 1 major enhancement
4
+
5
+ * Birthday; project created
6
+ * Standard secretary, error, and YAML gopher classes created
7
+ * Secretary, error, and YAML gopher RSpec specifications created
8
+
@@ -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
@@ -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.
@@ -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
@@ -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
@@ -0,0 +1,4 @@
1
+ ---
2
+ - Foo
3
+ - Bar
4
+ - Bam
@@ -0,0 +1,5 @@
1
+ This is an invalid preferences file for use in unit tests and specs. The
2
+ following lines are meant to be invalid YAML syntax.
3
+
4
+ foo bar: bash
5
+ [32, 23, 53]
@@ -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
@@ -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
@@ -0,0 +1,3 @@
1
+ ---
2
+ name: Billy Bob
3
+ email: xxx@yyy.zzz
@@ -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
@@ -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
+