delano-storable 0.5.1

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/CHANGES.txt ADDED
@@ -0,0 +1,8 @@
1
+ STORABLE, CHANGES
2
+
3
+
4
+ #### 0.5 (2009-05-07) ###############################
5
+
6
+ * First public release. See commit history for solutious-stella, solutious-rudy,
7
+ and delano-delanotes for complete history of SysInfo (was SystemInfo).
8
+
data/LICENSE.txt ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2009 Delano Mandelbaum, Solutious Inc
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.rdoc ADDED
@@ -0,0 +1,23 @@
1
+ = Storable - v0.5
2
+
3
+ Marshal Ruby classes into and out of multiple formats (yaml, json, csv, tsv)
4
+
5
+ == Installation
6
+
7
+ Via Rubygems, one of:
8
+
9
+ $ sudo gem install storable
10
+ $ sudo gem install delano-storable --source http://gems.github.com/
11
+
12
+ or via download:
13
+ * storable-latest.tar.gz[http://github.com/delano/storable/tarball/latest]
14
+ * storable-latest.zip[http://github.com/delano/storable/zipball/latest]
15
+
16
+
17
+ == Credits
18
+
19
+ * Delano Mandelbaum (delano@solutious.com)
20
+
21
+ == License
22
+
23
+ See: LICENSE.txt
data/Rakefile ADDED
@@ -0,0 +1,104 @@
1
+ require 'rubygems'
2
+ require 'rake/clean'
3
+ require 'rake/gempackagetask'
4
+ require 'hanna/rdoctask'
5
+ require 'fileutils'
6
+ include FileUtils
7
+
8
+ task :default => :package
9
+
10
+ # CONFIG =============================================================
11
+
12
+ # Change the following according to your needs
13
+ README = "README.rdoc"
14
+ CHANGES = "CHANGES.txt"
15
+ LICENSE = "LICENSE.txt"
16
+
17
+ # Files and directories to be deleted when you run "rake clean"
18
+ CLEAN.include [ 'pkg', '*.gem', '.config']
19
+
20
+ # Virginia assumes your project and gemspec have the same name
21
+ name = (Dir.glob('*.gemspec') || ['virginia']).first.split('.').first
22
+ load "#{name}.gemspec"
23
+ version = @spec.version
24
+
25
+ # That's it! The following defaults should allow you to get started
26
+ # on other things.
27
+
28
+
29
+ # TESTS/SPECS =========================================================
30
+
31
+
32
+
33
+ # INSTALL =============================================================
34
+
35
+ Rake::GemPackageTask.new(@spec) do |p|
36
+ p.need_tar = true if RUBY_PLATFORM !~ /mswin/
37
+ end
38
+
39
+ task :release => [ :rdoc, :package ]
40
+ task :install => [ :rdoc, :package ] do
41
+ sh %{sudo gem install pkg/#{name}-#{version}.gem}
42
+ end
43
+ task :uninstall => [ :clean ] do
44
+ sh %{sudo gem uninstall #{name}}
45
+ end
46
+
47
+
48
+ # RUBYFORGE RELEASE / PUBLISH TASKS ==================================
49
+
50
+ if @spec.rubyforge_project
51
+ desc 'Publish website to rubyforge'
52
+ task 'publish:rdoc' => 'doc/index.html' do
53
+ sh "scp -rp doc/* rubyforge.org:/var/www/gforge-projects/#{name}/"
54
+ end
55
+
56
+ desc 'Public release to rubyforge'
57
+ task 'publish:gem' => [:package] do |t|
58
+ sh <<-end
59
+ rubyforge add_release -o Any -a #{CHANGES} -f -n #{README} #{name} #{name} #{@spec.version} pkg/#{name}-#{@spec.version}.gem &&
60
+ rubyforge add_file -o Any -a #{CHANGES} -f -n #{README} #{name} #{name} #{@spec.version} pkg/#{name}-#{@spec.version}.tgz
61
+ end
62
+ end
63
+ end
64
+
65
+
66
+
67
+ # RUBY DOCS TASK ==================================
68
+
69
+ Rake::RDocTask.new do |t|
70
+ t.rdoc_dir = 'doc'
71
+ t.title = @spec.summary
72
+ t.options << '--line-numbers' << '--inline-source' << '-A cattr_accessor=object'
73
+ t.options << '--charset' << 'utf-8'
74
+ t.rdoc_files.include(LICENSE)
75
+ t.rdoc_files.include(README)
76
+ t.rdoc_files.include(CHANGES)
77
+ #t.rdoc_files.include('bin/*')
78
+ t.rdoc_files.include('lib/**/*.rb')
79
+ end
80
+
81
+
82
+
83
+
84
+ #Hoe.new('rspec', Spec::VERSION::STRING) do |p|
85
+ # p.summary = Spec::VERSION::SUMMARY
86
+ # p.description = "Behaviour Driven Development for Ruby."
87
+ # p.rubyforge_name = 'rspec'
88
+ # p.developer('RSpec Development Team', 'rspec-devel@rubyforge.org')
89
+ # p.extra_dev_deps = [["cucumber",">= 0.1.13"]]
90
+ # p.remote_rdoc_dir = "rspec/#{Spec::VERSION::STRING}"
91
+ # p.rspec_options = ['--options', 'spec/spec.opts']
92
+ # p.history_file = 'History.rdoc'
93
+ # p.readme_file = 'README.rdoc'
94
+ # p.post_install_message = <<-POST_INSTALL_MESSAGE
95
+ ##{'*'*50}
96
+ #
97
+ # Thank you for installing rspec-#{Spec::VERSION::STRING}
98
+ #
99
+ # Please be sure to read History.rdoc and Upgrade.rdoc
100
+ # for useful information about this release.
101
+ #
102
+ #{'*'*50}
103
+ #POST_INSTALL_MESSAGE
104
+ #end
data/lib/storable.rb ADDED
@@ -0,0 +1,292 @@
1
+ #--
2
+ # TODO: Handle nested hashes and arrays.
3
+ # TODO: to_xml, see: http://codeforpeople.com/lib/ruby/xx/xx-2.0.0/README
4
+ # TODO: Rename to Stuffany
5
+ #++
6
+
7
+ require 'yaml'
8
+ require 'fileutils'
9
+
10
+
11
+ # Storable makes data available in multiple formats and can
12
+ # re-create objects from files. Fields are defined using the
13
+ # Storable.field method which tells Storable the order and
14
+ # name.
15
+ class Storable
16
+ unless defined?(SUPPORTED_FORMATS) # We can assume all are defined
17
+ VERSION = 5
18
+ NICE_TIME_FORMAT = "%Y-%m-%d@%H:%M:%S".freeze
19
+ SUPPORTED_FORMATS = [:tsv, :csv, :yaml, :json, :s, :string].freeze
20
+ end
21
+
22
+ # This value will be used as a default unless provided on-the-fly.
23
+ # See SUPPORTED_FORMATS for available values.
24
+ attr_reader :format
25
+
26
+ # See SUPPORTED_FORMATS for available values
27
+ def format=(v)
28
+ v &&= v.to_sym
29
+ raise "Unsupported format: #{v}" unless SUPPORTED_FORMATS.member?(v)
30
+ @format = v
31
+ end
32
+
33
+ def postprocess
34
+ end
35
+
36
+ # TODO: from_args([HASH or ordered params])
37
+
38
+ # Accepts field definitions in the one of the follow formats:
39
+ #
40
+ # field :product
41
+ # field :product => Integer
42
+ #
43
+ # The order they're defined determines the order the will be output. The fields
44
+ # data is available by the standard accessors, class.product and class.product= etc...
45
+ # The value of the field will be cast to the type (if provided) when read from a file.
46
+ # The value is not touched when the type is not provided.
47
+ def self.field(args={})
48
+ # TODO: Examine casting from: http://codeforpeople.com/lib/ruby/fattr/fattr-1.0.3/
49
+ args = {args => nil} unless args.is_a? Hash
50
+
51
+ args.each_pair do |m,t|
52
+
53
+ [[:@@field_names, m], [:@@field_types, t]].each do |tuple|
54
+ class_variable_set(tuple[0], []) unless class_variable_defined?(tuple[0])
55
+ class_variable_set(tuple[0], class_variable_get(tuple[0]) << tuple[1])
56
+ end
57
+
58
+ next if method_defined?(m)
59
+
60
+ define_method(m) do instance_variable_get("@#{m}") end
61
+ define_method("#{m}=") do |val|
62
+ instance_variable_set("@#{m}",val)
63
+ end
64
+ end
65
+ end
66
+
67
+ # Returns an array of field names defined by self.field
68
+ def self.field_names
69
+ class_variable_get(:@@field_names)
70
+ end
71
+ # Returns an array of field names defined by self.field
72
+ def field_names
73
+ self.class.send(:class_variable_get, :@@field_names)
74
+ end
75
+ # Returns an array of field types defined by self.field. Fields that did
76
+ # not receive a type are set to nil.
77
+ def self.field_types
78
+ class_variable_get(:@@field_types)
79
+ end
80
+ # Returns an array of field types defined by self.field. Fields that did
81
+ # not receive a type are set to nil.
82
+ def field_types
83
+ self.class.send(:class_variable_get, :@@field_types)
84
+ end
85
+
86
+ # Dump the object data to the given format.
87
+ def dump(format=nil, with_titles=false)
88
+ format &&= format.to_sym
89
+ format ||= 's' # as in, to_s
90
+ raise "Format not defined (#{format})" unless SUPPORTED_FORMATS.member?(format)
91
+ send("to_#{format}", with_titles)
92
+ end
93
+
94
+ def to_string(*args)
95
+ to_s(*args)
96
+ end
97
+
98
+ # Create a new instance of the object using data from file.
99
+ def self.from_file(file_path, format='yaml')
100
+ raise "Cannot read file (#{file_path})" unless File.exists?(file_path)
101
+ raise "#{self} doesn't support from_#{format}" unless self.respond_to?("from_#{format}")
102
+ format = format || File.extname(file_path).tr('.', '')
103
+ me = send("from_#{format}", read_file_to_array(file_path))
104
+ me.format = format
105
+ me
106
+ end
107
+ # Write the object data to the given file.
108
+ def to_file(file_path=nil, with_titles=true)
109
+ raise "Cannot store to nil path" if file_path.nil?
110
+ format = File.extname(file_path).tr('.', '')
111
+ format &&= format.to_sym
112
+ format ||= @format
113
+ Storable.write_file(file_path, dump(format, with_titles))
114
+ end
115
+
116
+ # Create a new instance of the object from a hash.
117
+ def self.from_hash(from={})
118
+ return nil if !from || from.empty?
119
+ me = self.new
120
+
121
+ fnames = field_names
122
+ fnames.each_with_index do |key,index|
123
+
124
+ stored_value = from[key] || from[key.to_s] # support for symbol keys and string keys
125
+
126
+ # TODO: Correct this horrible implementation (sorry, me. It's just one of those days.)
127
+
128
+ if field_types[index] == Array
129
+ ((value ||= []) << stored_value).flatten
130
+ elsif field_types[index] == Hash
131
+
132
+ value = stored_value
133
+ else
134
+
135
+ # SimpleDB stores attribute shit as lists of values
136
+ value = stored_value.first if stored_value.is_a?(Array) && stored_value.size == 1
137
+
138
+ if field_types[index] == Time
139
+ value = Time.parse(value)
140
+ elsif field_types[index] == DateTime
141
+ value = DateTime.parse(value)
142
+ elsif field_types[index] == TrueClass
143
+ value = (value.to_s == "true")
144
+ elsif field_types[index] == Float
145
+ value = value.to_f
146
+ elsif field_types[index] == Integer
147
+ value = value.to_i
148
+ elsif field_types[index].kind_of?(Storable) && stored_value.is_a?(Hash)
149
+ value = field_types[index].from_hash(stored_value)
150
+ else
151
+ value = (stored_value.is_a?(Array) && stored_value.size == 1) ? stored_value.first : stored_value
152
+ end
153
+ end
154
+
155
+ me.send("#{key}=", value) if self.method_defined?("#{key}=")
156
+ end
157
+
158
+ me.postprocess
159
+
160
+ me
161
+ end
162
+ # Return the object data as a hash
163
+ # +with_titles+ is ignored.
164
+ def to_hash(with_titles=true)
165
+ tmp = {}
166
+ field_names.each do |fname|
167
+ tmp[fname] = self.send(fname)
168
+ end
169
+ tmp
170
+ end
171
+
172
+ # Create a new instance of the object from YAML.
173
+ # +from+ a YAML string split into an array by line.
174
+ def self.from_yaml(from=[])
175
+ # from is an array of strings
176
+ from_str = from.join('')
177
+ hash = YAML::load(from_str)
178
+ hash = from_hash(hash) if hash.is_a? Hash
179
+ hash
180
+ end
181
+ def to_yaml(with_titles=true)
182
+ to_hash.to_yaml
183
+ end
184
+
185
+ # Create a new instance of the object from a JSON string.
186
+ # +from+ a JSON string split into an array by line.
187
+ def self.from_json(from=[])
188
+ require 'json'
189
+ # from is an array of strings
190
+ from_str = from.join('')
191
+ tmp = JSON::load(from_str)
192
+ hash_sym = tmp.keys.inject({}) do |hash, key|
193
+ hash[key.to_sym] = tmp[key]
194
+ hash
195
+ end
196
+ hash_sym = from_hash(hash_sym) if hash_sym.is_a? Hash
197
+ hash_sym
198
+ end
199
+ def to_json(with_titles=true)
200
+ require 'json'
201
+ to_hash.to_json
202
+ end
203
+
204
+ # Return the object data as a delimited string.
205
+ # +with_titles+ specifiy whether to include field names (default: false)
206
+ # +delim+ is the field delimiter.
207
+ def to_delimited(with_titles=false, delim=',')
208
+ values = []
209
+ field_names.each do |fname|
210
+ values << self.send(fname.to_s) # TODO: escape values
211
+ end
212
+ output = values.join(delim)
213
+ output = field_names.join(delim) << $/ << output if with_titles
214
+ output
215
+ end
216
+ # Return the object data as a tab delimited string.
217
+ # +with_titles+ specifiy whether to include field names (default: false)
218
+ def to_tsv(with_titles=false)
219
+ to_delimited(with_titles, "\t")
220
+ end
221
+ # Return the object data as a comma delimited string.
222
+ # +with_titles+ specifiy whether to include field names (default: false)
223
+ def to_csv(with_titles=false)
224
+ to_delimited(with_titles, ',')
225
+ end
226
+ # Create a new instance from tab-delimited data.
227
+ # +from+ a JSON string split into an array by line.
228
+ def self.from_tsv(from=[])
229
+ self.from_delimited(from, "\t")
230
+ end
231
+ # Create a new instance of the object from comma-delimited data.
232
+ # +from+ a JSON string split into an array by line.
233
+ def self.from_csv(from=[])
234
+ self.from_delimited(from, ',')
235
+ end
236
+
237
+ # Create a new instance of the object from a delimited string.
238
+ # +from+ a JSON string split into an array by line.
239
+ # +delim+ is the field delimiter.
240
+ def self.from_delimited(from=[],delim=',')
241
+ return if from.empty?
242
+ # We grab an instance of the class so we can
243
+ hash = {}
244
+
245
+ fnames = values = []
246
+ if (from.size > 1 && !from[1].empty?)
247
+ fnames = from[0].chomp.split(delim)
248
+ values = from[1].chomp.split(delim)
249
+ else
250
+ fnames = self.field_names
251
+ values = from[0].chomp.split(delim)
252
+ end
253
+
254
+ fnames.each_with_index do |key,index|
255
+ next unless values[index]
256
+ hash[key.to_sym] = values[index]
257
+ end
258
+ hash = from_hash(hash) if hash.is_a? Hash
259
+ hash
260
+ end
261
+
262
+ def self.read_file_to_array(path)
263
+ contents = []
264
+ return contents unless File.exists?(path)
265
+
266
+ open(path, 'r') do |l|
267
+ contents = l.readlines
268
+ end
269
+
270
+ contents
271
+ end
272
+
273
+ def self.write_file(path, content, flush=true)
274
+ write_or_append_file('w', path, content, flush)
275
+ end
276
+
277
+ def self.append_file(path, content, flush=true)
278
+ write_or_append_file('a', path, content, flush)
279
+ end
280
+
281
+ def self.write_or_append_file(write_or_append, path, content = '', flush = true)
282
+ #STDERR.puts "Writing to #{ path }..."
283
+ create_dir(File.dirname(path))
284
+
285
+ open(path, write_or_append) do |f|
286
+ f.puts content
287
+ f.flush if flush;
288
+ end
289
+ File.chmod(0600, path)
290
+ end
291
+ end
292
+
data/storable.gemspec ADDED
@@ -0,0 +1,55 @@
1
+ @spec = Gem::Specification.new do |s|
2
+ s.name = "storable"
3
+ s.rubyforge_project = "storable"
4
+ s.version = "0.5.1"
5
+ s.summary = "Storable: Marshal Ruby classes into and out of multiple formats (yaml, json, csv, tsv)"
6
+ s.description = s.summary
7
+ s.author = "Delano Mandelbaum"
8
+ s.email = "delano@solutious.com"
9
+ s.homepage = "http://solutious.com/"
10
+
11
+
12
+ # = EXECUTABLES =
13
+ # The list of executables in your project (if any). Don't include the path,
14
+ # just the base filename.
15
+ s.executables = %w[]
16
+
17
+ # = DEPENDENCIES =
18
+ # Add all gem dependencies
19
+ s.add_dependency 'sysinfo', '>= 0.5.0'
20
+
21
+ # = MANIFEST =
22
+ # The complete list of files to be included in the release. When GitHub packages your gem,
23
+ # it doesn't allow you to run any command that accesses the filesystem. You will get an
24
+ # error. You can ask your VCS for the list of versioned files:
25
+ # git ls-files
26
+ # svn list -R
27
+ s.files = %w(
28
+ CHANGES.txt
29
+ LICENSE.txt
30
+ README.rdoc
31
+ Rakefile
32
+ lib/storable.rb
33
+ storable.gemspec
34
+ )
35
+
36
+ s.extra_rdoc_files = %w[README.rdoc LICENSE.txt]
37
+ s.has_rdoc = true
38
+ s.rdoc_options = ["--line-numbers", "--title", s.summary, "--main", "README.rdoc"]
39
+ s.require_paths = %w[lib]
40
+ s.rubygems_version = '1.3.0'
41
+
42
+ if s.respond_to? :specification_version then
43
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
44
+ s.specification_version = 2
45
+
46
+ if Gem::Version.new(Gem::RubyGemsVersion) >= Gem::Version.new('1.2.0') then
47
+ s.add_runtime_dependency(%q<RedCloth>, [">= 4.0.4"])
48
+ else
49
+ s.add_dependency(%q<RedCloth>, [">= 4.0.4"])
50
+ end
51
+ else
52
+ s.add_dependency(%q<RedCloth>, [">= 4.0.4"])
53
+ end
54
+
55
+ end
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: delano-storable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.5.1
5
+ platform: ruby
6
+ authors:
7
+ - Delano Mandelbaum
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-08 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: sysinfo
17
+ type: :runtime
18
+ version_requirement:
19
+ version_requirements: !ruby/object:Gem::Requirement
20
+ requirements:
21
+ - - ">="
22
+ - !ruby/object:Gem::Version
23
+ version: 0.5.0
24
+ version:
25
+ - !ruby/object:Gem::Dependency
26
+ name: RedCloth
27
+ type: :runtime
28
+ version_requirement:
29
+ version_requirements: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: 4.0.4
34
+ version:
35
+ description: "Storable: Marshal Ruby classes into and out of multiple formats (yaml, json, csv, tsv)"
36
+ email: delano@solutious.com
37
+ executables: []
38
+
39
+ extensions: []
40
+
41
+ extra_rdoc_files:
42
+ - README.rdoc
43
+ - LICENSE.txt
44
+ files:
45
+ - CHANGES.txt
46
+ - LICENSE.txt
47
+ - README.rdoc
48
+ - Rakefile
49
+ - lib/storable.rb
50
+ - storable.gemspec
51
+ has_rdoc: true
52
+ homepage: http://solutious.com/
53
+ post_install_message:
54
+ rdoc_options:
55
+ - --line-numbers
56
+ - --title
57
+ - "Storable: Marshal Ruby classes into and out of multiple formats (yaml, json, csv, tsv)"
58
+ - --main
59
+ - README.rdoc
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: "0"
73
+ version:
74
+ requirements: []
75
+
76
+ rubyforge_project: storable
77
+ rubygems_version: 1.2.0
78
+ signing_key:
79
+ specification_version: 2
80
+ summary: "Storable: Marshal Ruby classes into and out of multiple formats (yaml, json, csv, tsv)"
81
+ test_files: []
82
+