secretary 0.0.1 → 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/History.txt +22 -0
- data/Manifest.txt +5 -3
- data/README.txt +14 -50
- data/lib/secretary.rb +71 -17
- data/lib/secretary/error.rb +17 -17
- data/lib/secretary/gopher.rb +84 -48
- data/lib/secretary/gopher/yaml.rb +13 -19
- data/spec/{broken-yaml.txt → broken-yaml.yaml} +0 -0
- data/spec/error_spec.rb +3 -2
- data/spec/gopher/yaml_spec.rb +124 -0
- data/spec/one-row.csv +1 -0
- data/spec/secretary.rb +321 -0
- data/spec/spec_helper.rb +9 -0
- data/spec/two-rows.csv +2 -0
- metadata +9 -8
- data/spec/gopher_spec.rb +0 -157
- data/spec/secretary_spec.rb +0 -259
data/History.txt
CHANGED
@@ -1,3 +1,25 @@
|
|
1
|
+
|
2
|
+
=== 0.2.0 / 2008-08-29
|
3
|
+
|
4
|
+
* 1 minor enhancements
|
5
|
+
|
6
|
+
* Added more complete RDocs for the Secretary class. RDocs for gophers
|
7
|
+
are planned in the future.
|
8
|
+
|
9
|
+
* 1 major change
|
10
|
+
|
11
|
+
* Fundamentally changed how gophers validate data. Now there is a general
|
12
|
+
Gopher#validate(data) method, which checks if the data's type matches
|
13
|
+
the gopher's data-type, and also checks if the data fulfills the
|
14
|
+
new Gopher#validation_proc attribute, which can be passed through the
|
15
|
+
initializer's block.
|
16
|
+
|
17
|
+
=== 0.1.0 / 2008-08-12
|
18
|
+
|
19
|
+
* 1 minor enhancement
|
20
|
+
|
21
|
+
* An RSpec file that runs all specs, all_spec.rb, has been added
|
22
|
+
|
1
23
|
=== 0.0.1 / 2008-08-11
|
2
24
|
|
3
25
|
* 1 major enhancement
|
data/Manifest.txt
CHANGED
@@ -7,9 +7,11 @@ lib/secretary/error.rb
|
|
7
7
|
lib/secretary/gopher.rb
|
8
8
|
lib/secretary/gopher/yaml.rb
|
9
9
|
spec/spec_helper.rb
|
10
|
-
spec/
|
10
|
+
spec/secretary.rb
|
11
11
|
spec/error_spec.rb
|
12
|
-
spec/
|
12
|
+
spec/gopher/yaml_spec.rb
|
13
13
|
spec/hash.yaml
|
14
14
|
spec/array.yaml
|
15
|
-
spec/broken-yaml.
|
15
|
+
spec/broken-yaml.yaml
|
16
|
+
spec/one-row.csv
|
17
|
+
spec/two-rows.csv
|
data/README.txt
CHANGED
@@ -4,35 +4,25 @@
|
|
4
4
|
|
5
5
|
== DESCRIPTION:
|
6
6
|
|
7
|
-
Secretary is a gem who makes managing simple preference files in Ruby
|
8
|
-
and painless.
|
9
|
-
consistent, pluggable way to read and write preferences, your Secretary would
|
10
|
-
be happy to help!
|
7
|
+
Secretary is a gem who makes managing simple preference files in Ruby easier
|
8
|
+
and more painless.
|
11
9
|
|
12
10
|
== FEATURES:
|
13
11
|
|
14
|
-
* She
|
15
|
-
* She
|
16
|
-
can easily switch to any other format!
|
17
|
-
* She supports custom defaults, a big essential!
|
12
|
+
* She encapsulates preference files in a single, easily manageable object
|
13
|
+
* She's smooth and simple: just import and initialize
|
18
14
|
* 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!
|
15
|
+
kind of file you wish
|
22
16
|
|
23
17
|
== SYNOPSIS:
|
24
18
|
|
25
|
-
import 'secretary
|
19
|
+
import 'secretary'
|
26
20
|
|
27
|
-
|
28
|
-
defaults = {'name': '', 'port': 80}
|
29
|
-
|
30
|
-
secretary = Secretary.new 'preferences.yaml', defaults
|
21
|
+
secretary = Secretary.new 'preferences.yaml', :defaults => {'name': '', 'port': 80}
|
31
22
|
|
32
23
|
# The first argument is the path to the file that she will sync to.
|
33
|
-
# The second argument
|
34
|
-
#
|
35
|
-
# with her file--by default, it'll be YAML.
|
24
|
+
# The second argument is a option hash.
|
25
|
+
# Passing in a :defaults option gives the Hash that contains her defaults.
|
36
26
|
|
37
27
|
secretary['url'] = 'http://samantha.name'
|
38
28
|
|
@@ -47,8 +37,7 @@ be happy to help!
|
|
47
37
|
* At least Ruby 1.8.0
|
48
38
|
* The standard forwarable.rb
|
49
39
|
* The standard yaml.rb
|
50
|
-
* At least RSpec 1.1 (but only if you want to
|
51
|
-
included specs)
|
40
|
+
* At least RSpec 1.1 (but only if you want to use the included specs)
|
52
41
|
|
53
42
|
== INSTALL:
|
54
43
|
|
@@ -59,34 +48,9 @@ install it automatically:
|
|
59
48
|
|
60
49
|
== NOTES:
|
61
50
|
|
62
|
-
|
63
|
-
|
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:
|
51
|
+
Throughout this library's documentation, Secretaries will be referred to as
|
52
|
+
"she"s, and Gophers will be referred to as "he"s.
|
82
53
|
|
83
|
-
|
84
|
-
included in all copies or substantial portions of the Software.
|
54
|
+
I'm planning to add support for for more formats such as JSON and (gulp) XML.
|
85
55
|
|
86
|
-
|
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.
|
56
|
+
I'd be very happy to hear your comments and bugs on Rubyforge.
|
data/lib/secretary.rb
CHANGED
@@ -1,48 +1,101 @@
|
|
1
1
|
$: << File.expand_path(File.dirname(__FILE__) + '/../lib')
|
2
|
+
|
3
|
+
class Secretary
|
4
|
+
end
|
5
|
+
|
2
6
|
require 'secretary/error'
|
3
7
|
require 'secretary/gopher/yaml'
|
4
8
|
require 'forwardable'
|
5
9
|
|
10
|
+
# A Secretary easily manages a data structure (by default a Hash only) and a
|
11
|
+
# corresponding data file (by default a YAML file). To this end it utilizes a
|
12
|
+
# gopher (by default a Secretary::Gopher::YAML object). The Secretary tells
|
13
|
+
# the gopher to fetch or save certain files whenever it's needed, but the
|
14
|
+
# Secretary herself manages the file path, any default options, and the data
|
15
|
+
# itself.
|
16
|
+
|
17
|
+
# If you subclass the Secretary class to be able to store a different kind of
|
18
|
+
# data, like, say, an Array instead of a Hash, then you'll need to override the
|
19
|
+
# contents and to_hash methods. (In addition, you may want to use a different
|
20
|
+
# kind of gopher too. See the documentation on Secretary::Gopher for info about
|
21
|
+
# that.)
|
22
|
+
|
6
23
|
class Secretary
|
7
|
-
|
24
|
+
VERSION = '0.1.0'
|
8
25
|
extend Forwardable
|
9
26
|
include Enumerable
|
10
|
-
|
27
|
+
attr_accessor :file_path, :defaults
|
28
|
+
attr_reader :gopher
|
11
29
|
|
12
|
-
|
30
|
+
# Creates a brand-spanking new Secretary who takes charge of the file at the
|
31
|
+
# given file_path.
|
32
|
+
# You can give an options hash. Two options are eaten by the Secretary
|
33
|
+
# intializer (the rest are passed into the gopher initializer). They are
|
34
|
+
# :defaults, which is a Hash containing the default data, and :gopher, which
|
35
|
+
# is a Gopher class from which the Secretary's gopher will be created.
|
36
|
+
# She will raise an ArgumentError if you give a gopher class object that does
|
37
|
+
# not respond to :new, or if the newly initialized gopher object cannot act
|
38
|
+
# like a Gopher (i.e. if it cannot respond to :load and :save).
|
39
|
+
# If she finds a file at the file_path you give her, then she'll combine the
|
40
|
+
# data that her new gopher loads from the file with the defaults that you
|
41
|
+
# gave her to get her contents.
|
42
|
+
# If no file is found at the given file_path, then just the defaults are
|
43
|
+
# put into the contents.
|
44
|
+
def initialize(file_path, options={})
|
45
|
+
defaults = options.delete(:defaults) || {}
|
46
|
+
gopher_class = options.delete(:gopher) || Gopher::YAML
|
13
47
|
unless gopher_class.respond_to? :new
|
14
48
|
raise ArgumentError, "given gopher class is not a class (#{gopher_class})"
|
15
49
|
end
|
16
50
|
@file_path = file_path
|
17
51
|
@defaults = defaults
|
18
|
-
@gopher = gopher_class.new
|
19
|
-
unless @gopher.respond_to? :
|
20
|
-
raise ArgumentError, "given gopher class\'s members do not respond to
|
52
|
+
@gopher = gopher_class.new options
|
53
|
+
unless @gopher.respond_to? :load
|
54
|
+
raise ArgumentError, "given gopher class\'s members do not respond to " +
|
55
|
+
":load (#{gopher})"
|
56
|
+
end
|
57
|
+
unless @gopher.respond_to? :save
|
58
|
+
raise ArgumentError, "given gopher class\'s members do not respond to " +
|
59
|
+
":save (#{gopher})"
|
21
60
|
end
|
22
61
|
@contents = {}
|
23
|
-
reload
|
62
|
+
reload!
|
24
63
|
end
|
25
64
|
|
26
|
-
def_delegators :to_hash, :[], :include?, :each, :each_pair,
|
27
|
-
|
65
|
+
def_delegators :to_hash, :[], :include?, :each, :each_pair,
|
66
|
+
:each_key, :each_value
|
67
|
+
def_delegators :@contents, :[]=, :delete, :update
|
28
68
|
|
69
|
+
# A Secretary is equal to another object if the other object responds to
|
70
|
+
# :file_path, :defaults, :gopher, and :contents, and if the objects' values of
|
71
|
+
# those corresponding methods are equal to each other.
|
29
72
|
def ==(other)
|
30
73
|
other.respond_to? :file_path and other.respond_to? :defaults \
|
31
|
-
and other.respond_to? :gopher and other.respond_to? :
|
74
|
+
and other.respond_to? :gopher and other.respond_to? :contents \
|
32
75
|
and file_path == other.file_path and defaults == other.defaults \
|
33
|
-
and gopher == other.gopher and
|
76
|
+
and gopher == other.gopher and contents == other.contents
|
34
77
|
end
|
35
78
|
|
36
|
-
|
79
|
+
# Returns a new object containing the Secretary's data that she originally got
|
80
|
+
# from her file. It consists of the her defaults overridden by her file's
|
81
|
+
# data. Note that this method returns a new object, which means that it is
|
82
|
+
# only meant for inspection; modifying the object it returns will not affect
|
83
|
+
# her data. If you want to modify her data, directly tell her to use the []=
|
84
|
+
# or delete methods.
|
85
|
+
def contents
|
37
86
|
defaults.merge @contents
|
38
87
|
end
|
39
88
|
|
40
|
-
|
89
|
+
def to_hash
|
90
|
+
contents
|
91
|
+
end
|
92
|
+
|
93
|
+
# Reloads data from the Secretary's file. If she succeeds, then this method
|
94
|
+
# returns true. If she can't find a file at her file_path, then this method
|
95
|
+
# returns false.
|
96
|
+
def reload!
|
41
97
|
begin
|
42
|
-
loaded_data = gopher.
|
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
|
98
|
+
loaded_data = gopher.load(file_path)
|
46
99
|
@contents.merge! loaded_data
|
47
100
|
return true
|
48
101
|
rescue Error::MissingFile
|
@@ -50,6 +103,7 @@ class Secretary
|
|
50
103
|
end
|
51
104
|
end
|
52
105
|
|
106
|
+
# Saves its contents to its file path.
|
53
107
|
def save
|
54
108
|
gopher.save self.to_hash, file_path
|
55
109
|
end
|
data/lib/secretary/error.rb
CHANGED
@@ -1,31 +1,31 @@
|
|
1
|
+
|
1
2
|
class Secretary
|
2
|
-
|
3
|
-
module Error
|
4
|
-
|
5
|
-
class MissingFile < IOError
|
3
|
+
end
|
6
4
|
|
7
|
-
|
8
|
-
|
9
|
-
|
5
|
+
module Secretary::Error
|
6
|
+
|
7
|
+
class MissingFile < IOError
|
10
8
|
|
9
|
+
def initialize(missing_file_path)
|
10
|
+
super "no such file or directory (#{missing_file_path})"
|
11
11
|
end
|
12
12
|
|
13
|
-
|
13
|
+
end
|
14
14
|
|
15
|
-
|
16
|
-
super "attempted to parse an invalid #{file_type} file (#{parsing_error})"
|
17
|
-
end
|
15
|
+
class Parsing < IOError
|
18
16
|
|
17
|
+
def initialize(parsing_error, file_type)
|
18
|
+
super "attempted to parse an invalid #{file_type} file (#{parsing_error})"
|
19
19
|
end
|
20
20
|
|
21
|
-
|
21
|
+
end
|
22
22
|
|
23
|
-
|
24
|
-
super "preference file of invalid data type (should be #{valid_data_type} instead of #{data.class})"
|
25
|
-
end
|
23
|
+
class InvalidData < IOError
|
26
24
|
|
25
|
+
def initialize(data)
|
26
|
+
super "preference file contains invalid data (#{data.inspect})"
|
27
27
|
end
|
28
28
|
|
29
29
|
end
|
30
|
-
|
31
|
-
end
|
30
|
+
|
31
|
+
end
|
data/lib/secretary/gopher.rb
CHANGED
@@ -1,59 +1,95 @@
|
|
1
1
|
require 'secretary/error'
|
2
2
|
|
3
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
|
4
|
+
end
|
17
5
|
|
18
|
-
|
19
|
-
|
20
|
-
|
21
|
-
|
22
|
-
|
23
|
-
|
24
|
-
|
25
|
-
|
26
|
-
|
6
|
+
# Requires one of the following:
|
7
|
+
# - parse_file
|
8
|
+
# - load_from_file
|
9
|
+
#
|
10
|
+
# As well as one of the following:
|
11
|
+
# - emit
|
12
|
+
# - save_into_file_path
|
13
|
+
#
|
14
|
+
# You may override the following:
|
15
|
+
# - validate
|
16
|
+
# - raw
|
17
|
+
# - cook
|
18
|
+
# - parse_file
|
19
|
+
# - load_from_file
|
20
|
+
# - emit
|
21
|
+
# - save_into_file_path
|
27
22
|
|
28
|
-
|
29
|
-
|
30
|
-
|
31
|
-
|
32
|
-
|
33
|
-
|
34
|
-
|
35
|
-
|
36
|
-
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
|
42
|
-
|
43
|
-
|
44
|
-
|
45
|
-
|
46
|
-
|
23
|
+
class Secretary::Gopher
|
24
|
+
attr_reader :options
|
25
|
+
class << self
|
26
|
+
attr_reader :file_type
|
27
|
+
@file_type = 'data'
|
28
|
+
end
|
29
|
+
|
30
|
+
def initialize options={}
|
31
|
+
@options = options
|
32
|
+
end
|
33
|
+
|
34
|
+
def file_type
|
35
|
+
self.class.file_type
|
36
|
+
end
|
37
|
+
|
38
|
+
def == other
|
39
|
+
self.class == other.class
|
40
|
+
end
|
41
|
+
|
42
|
+
def load file_path
|
43
|
+
if File.exist? file_path
|
44
|
+
file_data = load_from_file_path file_path
|
45
|
+
else
|
46
|
+
raise Secretary::Error::MissingFile.new(file_path)
|
47
47
|
end
|
48
|
-
|
49
|
-
|
50
|
-
self.class.valid_file_type
|
48
|
+
unless validate file_data
|
49
|
+
raise Secretary::Error::InvalidData.new(file_data)
|
51
50
|
end
|
52
|
-
|
53
|
-
|
54
|
-
|
51
|
+
return cook(file_data)
|
52
|
+
end
|
53
|
+
|
54
|
+
def save(data, file_path)
|
55
|
+
unless validate data then raise ArgumentError,
|
56
|
+
"invalid data #{data} is not a kind of #{data_type}"
|
57
|
+
end
|
58
|
+
save_into_file_path raw(data), file_path
|
59
|
+
end
|
60
|
+
|
61
|
+
def validate data
|
62
|
+
true
|
63
|
+
end
|
64
|
+
|
65
|
+
def raw data
|
66
|
+
data
|
67
|
+
end
|
68
|
+
|
69
|
+
def cook data
|
70
|
+
data
|
71
|
+
end
|
72
|
+
|
73
|
+
protected
|
74
|
+
|
75
|
+
def load_from_file_path(file_path)
|
76
|
+
open file_path do |file|
|
77
|
+
return load_from_file(file)
|
78
|
+
end
|
79
|
+
end
|
80
|
+
|
81
|
+
def load_from_file(file)
|
82
|
+
begin
|
83
|
+
parse_file file
|
84
|
+
rescue Secretary::Error::Parsing
|
85
|
+
raise
|
55
86
|
end
|
56
|
-
|
57
87
|
end
|
58
88
|
|
89
|
+
def save_into_file_path(data, file_path)
|
90
|
+
open file_path, 'w' do |file|
|
91
|
+
file.puts emit(data)
|
92
|
+
end
|
93
|
+
end
|
94
|
+
|
59
95
|
end
|