secretary 0.0.1 → 0.1.0
Sign up to get free protection for your applications and to get access to all the features.
- 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
|