pwnbus-configdb 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.
@@ -0,0 +1,11 @@
1
+ # .document is used by rdoc and yard to know how to generate documentation
2
+ # for example, it can be used to control how rdoc gets built when you do `gem install foo`
3
+
4
+ README.rdoc
5
+ lib/**/*.rb
6
+ bin/*
7
+
8
+ # Files below this - are treated as 'extra files', and aren't parsed for ruby code
9
+ -
10
+ features/**/*.feature
11
+ LICENSE
@@ -0,0 +1,42 @@
1
+ # rcov generated
2
+ coverage
3
+
4
+ # rdoc generated
5
+ rdoc
6
+
7
+ # yard generated
8
+ doc
9
+ .yardoc
10
+
11
+ # bundler
12
+ .bundle
13
+
14
+ # jeweler generated
15
+ pkg
16
+
17
+ # Have editor/IDE/OS specific files you need to ignore? Consider using a global gitignore:
18
+ #
19
+ # * Create a file at ~/.gitignore
20
+ # * Include files you want ignored
21
+ # * Run: git config --global core.excludesfile ~/.gitignore
22
+ #
23
+ # After doing this, these files will be ignored in all your git projects,
24
+ # saving you from having to 'pollute' every project you touch with them
25
+ #
26
+ # Not sure what to needs to be ignored for particular editors/OSes? Here's some ideas to get you started. (Remember, remove the leading # of the line)
27
+ #
28
+ # For MacOS:
29
+ #
30
+ #.DS_Store
31
+ #
32
+ # For TextMate
33
+ #*.tmproj
34
+ #tmtags
35
+ #
36
+ # For emacs:
37
+ #*~
38
+ #\#*
39
+ #.\#*
40
+ #
41
+ # For vim:
42
+ #*.swp
@@ -0,0 +1,12 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <projectDescription>
3
+ <name>pwnbus-configdb</name>
4
+ <comment></comment>
5
+ <projects>
6
+ </projects>
7
+ <buildSpec>
8
+ </buildSpec>
9
+ <natures>
10
+ <nature>org.radrails.rails.core.railsnature</nature>
11
+ </natures>
12
+ </projectDescription>
data/Gemfile ADDED
@@ -0,0 +1,13 @@
1
+ source "http://rubygems.org"
2
+ # Add dependencies required to use your gem here.
3
+ # Example:
4
+ # gem "activesupport", ">= 2.3.5"
5
+
6
+ # Add dependencies to develop your gem here.
7
+ # Include everything needed to run rake, tests, features, etc.
8
+ group :development do
9
+ gem "rspec", ">= 2.0.0.beta.20"
10
+ gem "bundler", "~> 1.0.0"
11
+ gem "jeweler", "~> 1.5.0.pre3"
12
+ gem "rcov", ">= 0"
13
+ end
@@ -0,0 +1,30 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ diff-lcs (1.1.2)
5
+ git (1.2.5)
6
+ jeweler (1.5.0.pre3)
7
+ bundler (~> 1.0.0)
8
+ git (>= 1.2.5)
9
+ rake
10
+ rake (0.8.7)
11
+ rcov (0.9.9)
12
+ rspec (2.0.0.beta.22)
13
+ rspec-core (= 2.0.0.beta.22)
14
+ rspec-expectations (= 2.0.0.beta.22)
15
+ rspec-mocks (= 2.0.0.beta.22)
16
+ rspec-core (2.0.0.beta.22)
17
+ rspec-expectations (2.0.0.beta.22)
18
+ diff-lcs (>= 1.1.2)
19
+ rspec-mocks (2.0.0.beta.22)
20
+ rspec-core (= 2.0.0.beta.22)
21
+ rspec-expectations (= 2.0.0.beta.22)
22
+
23
+ PLATFORMS
24
+ ruby
25
+
26
+ DEPENDENCIES
27
+ bundler (~> 1.0.0)
28
+ jeweler (~> 1.5.0.pre3)
29
+ rcov
30
+ rspec (>= 2.0.0.beta.20)
data/LICENSE ADDED
@@ -0,0 +1,20 @@
1
+ Copyright (c) 2010 Victor Costan
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,17 @@
1
+ = pwnbus-configdb
2
+
3
+ Description goes here.
4
+
5
+ == Note on Patches/Pull Requests
6
+
7
+ * Fork the project.
8
+ * Make your feature addition or bug fix.
9
+ * Add tests for it. This is important so I don't break it in a
10
+ future version unintentionally.
11
+ * Commit, do not mess with rakefile, version, or history.
12
+ (if you want to have your own version, that is fine but bump version in a commit by itself I can ignore when I pull)
13
+ * Send me a pull request. Bonus points for topic branches.
14
+
15
+ == Copyright
16
+
17
+ Copyright (c) 2010 Victor Costan. See LICENSE for details.
@@ -0,0 +1,53 @@
1
+ require 'rubygems'
2
+ require 'bundler'
3
+ begin
4
+ Bundler.setup(:default, :development)
5
+ rescue Bundler::BundlerError => e
6
+ $stderr.puts e.message
7
+ $stderr.puts "Run `bundle install` to install missing gems"
8
+ exit e.status_code
9
+ end
10
+ require 'rake'
11
+
12
+ require 'jeweler'
13
+ Jeweler::Tasks.new do |gem|
14
+ # gem is a Gem::Specification... see http://docs.rubygems.org/read/chapter/20 for more options
15
+ gem.name = "pwnbus-configdb"
16
+ gem.summary = %Q{Pure-ruby database for configuration variables.}
17
+ gem.description = %Q{Dead-simple key-value store with atomic updates.}
18
+ gem.email = "victor@costan.us"
19
+ gem.homepage = "http://github.com/pwnall/pwnbus_configdb"
20
+ gem.authors = ["Victor Costan"]
21
+ # Include your dependencies below. Runtime dependencies are required when using your gem,
22
+ # and development dependencies are only needed for development (ie running rake tasks, tests, etc)
23
+ # spec.add_runtime_dependency 'jabber4r', '> 0.1'
24
+ # spec.add_development_dependency 'rspec', '> 1.2.3'
25
+ gem.add_development_dependency "rspec", ">= 2.0.0.beta.20"
26
+ gem.add_development_dependency "bundler", "~> 1.0.0"
27
+ gem.add_development_dependency "jeweler", "~> 1.5.0.pre3"
28
+ gem.add_development_dependency "rcov", ">= 0"
29
+ end
30
+ Jeweler::RubygemsDotOrgTasks.new
31
+
32
+ require 'rspec/core'
33
+ require 'rspec/core/rake_task'
34
+ RSpec::Core::RakeTask.new(:spec) do |spec|
35
+ spec.pattern = FileList['spec/**/*_spec.rb']
36
+ end
37
+
38
+ RSpec::Core::RakeTask.new(:rcov) do |spec|
39
+ spec.pattern = 'spec/**/*_spec.rb'
40
+ spec.rcov = true
41
+ end
42
+
43
+ task :default => :spec
44
+
45
+ require 'rake/rdoctask'
46
+ Rake::RDocTask.new do |rdoc|
47
+ version = File.exist?('VERSION') ? File.read('VERSION') : ""
48
+
49
+ rdoc.rdoc_dir = 'rdoc'
50
+ rdoc.title = "new #{version}"
51
+ rdoc.rdoc_files.include('README*')
52
+ rdoc.rdoc_files.include('lib/**/*.rb')
53
+ end
data/VERSION ADDED
@@ -0,0 +1 @@
1
+ 0.1.0
@@ -0,0 +1,47 @@
1
+ require File.expand_path('../configdb/db.rb', __FILE__)
2
+ require File.expand_path('../configdb/files.rb', __FILE__)
3
+
4
+ # :nodoc: namespace
5
+ module Pwnbus
6
+
7
+ # Pure-ruby database for configuration variables.
8
+ module Configdb
9
+ # Opens a configuration database. The database is created if it doesn't exist.
10
+ #
11
+ # Args:
12
+ # name:: the database name; if the name starts with a ., the database is
13
+ # only readable to the current user, otherwise it is public (readable
14
+ # to everyone, but only writable by the current user)
15
+ # options:: the following keys are recognized
16
+ # :read:: opens the database only for reading
17
+ #
18
+ # Yields a proxy object that can be used to access the database.
19
+ #
20
+ # Returns the value produced by the block.
21
+ #
22
+ # Example:
23
+ # Pwnbus::Configdb.open('system') do |system|
24
+ # system.os.name = 'Ubuntu'
25
+ # system.os.version = '10.04.1'
26
+ # end
27
+ def self.open(name, options = {})
28
+ db_path = Files.find_db(name) || Files.create_db(name, options)
29
+
30
+ db_file = Files.open_db(db_path, options)
31
+ db = Db.new db_file
32
+ begin
33
+ return_value = yield db.proxy
34
+ if db.dirty?
35
+ Files.write_db db_path, options do |wf|
36
+ db.write wf
37
+ end
38
+ end
39
+ return_value
40
+ ensure
41
+ db.close
42
+ db_file.close
43
+ end
44
+ end
45
+ end # namespace Pwnbus::Configdb
46
+
47
+ end # namespace Pwnbus
@@ -0,0 +1,160 @@
1
+ require 'yaml'
2
+
3
+ # :nodoc: namespace
4
+ module Pwnbus
5
+
6
+ # :nodoc: file-related functionality
7
+ module Configdb
8
+
9
+ # Database instance.
10
+ class Db
11
+ attr_reader :data
12
+ attr_reader :proxy
13
+
14
+ # Database initialized with the contents of a file.
15
+ #
16
+ # Args:
17
+ # file:: File instance
18
+ def initialize(file)
19
+ @data = YAML.load file
20
+ @dirty = false
21
+ @proxy = Proxy.new self, ''
22
+ end
23
+
24
+ # Writes the database contents to a file.
25
+ #
26
+ # Args:
27
+ # file:: File instance
28
+ #
29
+ # Returns true.
30
+ def write(file)
31
+ file.truncate 0
32
+ YAML.dump @data, file
33
+ true
34
+ end
35
+
36
+ # Any future reads / writes will result in a crash.
37
+ def close
38
+ @data = nil
39
+ end
40
+
41
+ # Reads a key from the database.
42
+ #
43
+ # Args:
44
+ # key:: string or symbol
45
+ #
46
+ # Returns the value associated with the key, or nil if the key does not exist.
47
+ def [](key)
48
+ @data[key.to_s]
49
+ end
50
+
51
+ # Inserts or updates a key in the database.
52
+ #
53
+ # Args:
54
+ # key:: string or symbol
55
+ # value:: the value to be associated with the key
56
+ #
57
+ # Returns value.
58
+ def []=(key, value)
59
+ @dirty = true
60
+ if value.nil?
61
+ @data.delete key.to_s
62
+ else
63
+ @data[key.to_s] = (value.kind_of?(Numeric) || value.kind_of?(Symbol) ||
64
+ value == true || value == false) ? value : value.dup
65
+ end
66
+ value
67
+ end
68
+
69
+ # True if the database contents has changed since the database has been open.
70
+ def dirty?
71
+ @dirty
72
+ end
73
+
74
+ # Reads a key from the database, creating a proxy for inexistent keys.
75
+ #
76
+ # Args:
77
+ # key:: string or symbol
78
+ #
79
+ # Returns the value associated with the key, or a database access proxy if the
80
+ # key doesn't exist. This makes it possible to have .-separated keys, like
81
+ # db.user.name = 'abc'.
82
+ def proxy_get(key)
83
+ value = self[key]
84
+ value.nil? ? Proxy.new(self, key + '.') : value
85
+ end
86
+
87
+ # Inserts or updates
88
+ #
89
+ # Args:
90
+ # key:: string or symbol
91
+ # value:: the value to be associated with the key
92
+ #
93
+ # Returns value.
94
+ def proxy_set(key, value)
95
+ self[key] = value
96
+ end
97
+
98
+ if defined? BasicObject
99
+ # :nodoc: superclass for 1.9+
100
+ class Proxy < BasicObject; end
101
+ else
102
+ # :nodoc: superclass for 1.8
103
+ class Proxy < Object; end
104
+ end
105
+
106
+ # Easy access to a configuration database.
107
+ class Proxy
108
+ def initialize(database, prefix)
109
+ @db = database
110
+ @prefix = prefix
111
+ @eigenclass = (class <<self; self; end)
112
+ end
113
+
114
+ # Proxy objects aren't database values, so they behave like nil.
115
+ def nil?
116
+ true
117
+ end
118
+
119
+ # Proxy objects aren't database values, so they behave like nil.
120
+ def empty?
121
+ true
122
+ end
123
+
124
+ # Pretty access
125
+ def method_missing(name, *args)
126
+ db_key = @prefix + name.to_s
127
+ do_write = false
128
+
129
+ case db_key[-1]
130
+ when ??
131
+ db_key = db_key[0...-1]
132
+ when ?=
133
+ db_key = db_key[0...-1]
134
+ do_write = true
135
+ end
136
+
137
+ if do_write
138
+ @eigenclass.class_eval <<END_DEF
139
+ def #{name}(value)
140
+ @db.proxy_set #{db_key.inspect}, value
141
+ end
142
+ END_DEF
143
+ else
144
+ @eigenclass.class_eval <<END_DEF
145
+ def #{name}
146
+ @db.proxy_get #{db_key.inspect}
147
+ end
148
+ END_DEF
149
+ end
150
+
151
+ # The method was defined, now fire it off.
152
+ send name, *args
153
+ end
154
+ end # class Pwnbus::ConfigDb::Db::Proxy
155
+
156
+ end # class Pwnbus::Configdb::Db
157
+
158
+ end # namespace Pwnbus::Configdb
159
+
160
+ end # namespace Pwnbus
@@ -0,0 +1,159 @@
1
+ require 'fileutils'
2
+
3
+ # :nodoc: namespace
4
+ module Pwnbus
5
+
6
+ # :nodoc: file-related functionality
7
+ module Configdb
8
+
9
+ # Handling procedures for database files.
10
+ module Files
11
+ # Opens the file for a configuration database. The file must exist.
12
+ #
13
+ # Returns a File instance.
14
+ def self.open_db(db_path, options)
15
+ db_path = crash_recovery_db_path db_path, options
16
+ f = File.open db_path, 'r'
17
+ f.flock options[:read] ? File::LOCK_SH : File::LOCK_EX
18
+ f
19
+ end
20
+
21
+ # Peforms crash recovery on a database.
22
+ #
23
+ # Args:
24
+ # db_path:: path to a file containing a configuration database
25
+ # options:: same as for Configdb#new
26
+ #
27
+ # Returns the path to the recovered database. Most of the time, this will be
28
+ def self.crash_recovery_db_path(db_path, options)
29
+ new_db_path = db_path + '.new'
30
+ if !File.exist?(db_path)
31
+ # Crashed during rename.
32
+ if options[:read]
33
+ # Writing to the .new copy completed. The copy will be locked.
34
+ return new_db_path
35
+ else
36
+ # Do the rename.
37
+ File.rename new_db_path, db_path
38
+ return db_path
39
+ end
40
+ end
41
+
42
+ if File.exist?(new_db_path) && !options[:read]
43
+ # Crashed during new version write. The new version is probably corrupted.
44
+ File.unlink new_db_path
45
+ end
46
+ db_path
47
+ end
48
+
49
+ # Writes a new database version atomically.
50
+ #
51
+ # Args:
52
+ # db_path:: path to a file containing a configuration database
53
+ # options:: same as for Configdb#new
54
+ #
55
+ # Returns db_path.
56
+ def self.write_db(db_path, options)
57
+ new_db_path = db_path + '.new'
58
+ File.open(new_db_path, 'w') do |f|
59
+ f.flock File::LOCK_EX
60
+ permissions = public_db_name?(File.basename(db_path)) ? 0644 : 0600
61
+ File.chmod permissions, new_db_path
62
+ yield f
63
+ end
64
+ File.unlink db_path
65
+ File.rename new_db_path, db_path
66
+ db_path
67
+ end
68
+
69
+ # True if the file is accessible with the desired Configdb#open options.
70
+ #
71
+ # Args:
72
+ # db_path:: path to a database file
73
+ # options:: same as for Configdb#new
74
+ def self.can_access_path?(db_path, options)
75
+ db_stat = File.stat(db_path)
76
+ options[:read] ? db_stat.readable? : db_stat.writable?
77
+ end
78
+
79
+ # The path to a database, or nil if the database doesn't exist.
80
+ #
81
+ # Args:
82
+ # name:: the database name
83
+ def self.find_db(name)
84
+ db_dir_paths.each do |dir_path|
85
+ db_path = File.join dir_path, name + '.yml'
86
+ return db_path if File.exist?(db_path) || File.exist?(db_path + '.new')
87
+ end
88
+ nil
89
+ end
90
+
91
+ # Creates a new database. The database must not exist.
92
+ #
93
+ # Args:
94
+ # name:: the database name
95
+ # options:: same as for Configdb#open
96
+ #
97
+ # Returns the path to the database file.
98
+ def self.create_db(name, options)
99
+ db_path = File.join ensure_db_dir_exists, name + '.yml'
100
+ File.open(db_path, 'w') do |f|
101
+ f.flock File::LOCK_EX
102
+ YAML.dump empty_db_data(name), f
103
+ end
104
+ permissions = public_db_name?(name) ? 0644 : 0600
105
+ File.chmod permissions, db_path
106
+ db_path
107
+ end
108
+
109
+ # The contents of a empty (newly created) database.
110
+ #
111
+ # Args:
112
+ # name:: the database's name
113
+ def self.empty_db_data(name)
114
+ {}
115
+ end
116
+
117
+ # Databases whose names start with . are public (global-read, author-write).
118
+ def self.public_db_name?(name)
119
+ name[0] != ?.
120
+ end
121
+
122
+ # Returns the the database directory. Creates it if it doesn't exist.
123
+ def self.ensure_db_dir_exists
124
+ dir_path = db_dir_paths.first
125
+ unless File.exist? dir_path
126
+ FileUtils.mkdir_p dir_path
127
+ File.chmod 0755, dir_path
128
+ end
129
+ dir_path
130
+ end
131
+
132
+ # Paths to the directory containing database files for the current user.
133
+ def self.db_dir_paths
134
+ if superuser?
135
+ [db_dir_global_path]
136
+ else
137
+ [db_dir_user_path, db_dir_global_path]
138
+ end
139
+ end
140
+
141
+ # Path to the directory holding per-user databases.
142
+ def self.db_dir_user_path
143
+ File.expand_path('~/.pwnbus')
144
+ end
145
+
146
+ # Path to the computer-global databases.
147
+ def self.db_dir_global_path
148
+ '/etc/pwnbus'
149
+ end
150
+
151
+ # True if running as the root user.
152
+ def self.superuser?
153
+ Process.euid == 0
154
+ end
155
+ end # namespace Pwnbus::Configdb::Files
156
+
157
+ end # namespace Pwnbus::Configdb
158
+
159
+ end # namespace Pwnbus
@@ -0,0 +1,83 @@
1
+ # Generated by jeweler
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Jeweler::Tasks in Rakefile, and run the gemspec command
4
+ # -*- encoding: utf-8 -*-
5
+
6
+ Gem::Specification.new do |s|
7
+ s.name = %q{pwnbus-configdb}
8
+ s.version = "0.1.0"
9
+
10
+ s.required_rubygems_version = Gem::Requirement.new(">= 0") if s.respond_to? :required_rubygems_version=
11
+ s.authors = ["Victor Costan"]
12
+ s.date = %q{2010-09-19}
13
+ s.description = %q{Dead-simple key-value store with atomic updates.}
14
+ s.email = %q{victor@costan.us}
15
+ s.extra_rdoc_files = [
16
+ "LICENSE",
17
+ "README.rdoc"
18
+ ]
19
+ s.files = [
20
+ ".document",
21
+ ".gitignore",
22
+ ".project",
23
+ "Gemfile",
24
+ "Gemfile.lock",
25
+ "LICENSE",
26
+ "README.rdoc",
27
+ "Rakefile",
28
+ "VERSION",
29
+ "lib/pwnbus/configdb.rb",
30
+ "lib/pwnbus/configdb/db.rb",
31
+ "lib/pwnbus/configdb/files.rb",
32
+ "pwnbus-configdb.gemspec",
33
+ "spec/.rspec",
34
+ "spec/db_object_spec.rb",
35
+ "spec/db_spec.rb",
36
+ "spec/fixtures/db.yml",
37
+ "spec/spec_helper.rb"
38
+ ]
39
+ s.homepage = %q{http://github.com/pwnall/pwnbus_configdb}
40
+ s.require_paths = ["lib"]
41
+ s.rubygems_version = %q{1.3.7}
42
+ s.summary = %q{Pure-ruby database for configuration variables.}
43
+ s.test_files = [
44
+ "spec/db_object_spec.rb",
45
+ "spec/db_spec.rb",
46
+ "spec/spec_helper.rb"
47
+ ]
48
+
49
+ if s.respond_to? :specification_version then
50
+ current_version = Gem::Specification::CURRENT_SPECIFICATION_VERSION
51
+ s.specification_version = 3
52
+
53
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
54
+ s.add_development_dependency(%q<rspec>, [">= 2.0.0.beta.20"])
55
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
56
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.0.pre3"])
57
+ s.add_development_dependency(%q<rcov>, [">= 0"])
58
+ s.add_development_dependency(%q<rspec>, [">= 2.0.0.beta.20"])
59
+ s.add_development_dependency(%q<bundler>, ["~> 1.0.0"])
60
+ s.add_development_dependency(%q<jeweler>, ["~> 1.5.0.pre3"])
61
+ s.add_development_dependency(%q<rcov>, [">= 0"])
62
+ else
63
+ s.add_dependency(%q<rspec>, [">= 2.0.0.beta.20"])
64
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
65
+ s.add_dependency(%q<jeweler>, ["~> 1.5.0.pre3"])
66
+ s.add_dependency(%q<rcov>, [">= 0"])
67
+ s.add_dependency(%q<rspec>, [">= 2.0.0.beta.20"])
68
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
69
+ s.add_dependency(%q<jeweler>, ["~> 1.5.0.pre3"])
70
+ s.add_dependency(%q<rcov>, [">= 0"])
71
+ end
72
+ else
73
+ s.add_dependency(%q<rspec>, [">= 2.0.0.beta.20"])
74
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
75
+ s.add_dependency(%q<jeweler>, ["~> 1.5.0.pre3"])
76
+ s.add_dependency(%q<rcov>, [">= 0"])
77
+ s.add_dependency(%q<rspec>, [">= 2.0.0.beta.20"])
78
+ s.add_dependency(%q<bundler>, ["~> 1.0.0"])
79
+ s.add_dependency(%q<jeweler>, ["~> 1.5.0.pre3"])
80
+ s.add_dependency(%q<rcov>, [">= 0"])
81
+ end
82
+ end
83
+
@@ -0,0 +1 @@
1
+ --color
@@ -0,0 +1,54 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+
3
+ describe "Db object" do
4
+ before do
5
+ File.open File.expand_path('../fixtures/db.yml', __FILE__) do |f|
6
+ @db = Pwnbus::Configdb::Db.new f
7
+ end
8
+ @proxy = @db.proxy
9
+ end
10
+
11
+ it "reads simple key" do
12
+ @proxy.variable.should == 'value'
13
+ end
14
+
15
+ it "reads nested key" do
16
+ @proxy.deeper.variable.should == 'deeper value'
17
+ end
18
+
19
+ it "changes simple key" do
20
+ @proxy.variable = 'new value'
21
+ @proxy.variable.should == 'new value'
22
+ end
23
+
24
+ it "changes nested key" do
25
+ @proxy.deeper.variable = 'new deeper value'
26
+ @proxy.deeper.variable.should == 'new deeper value'
27
+ end
28
+
29
+ it "returns nil? object for non-existing keys" do
30
+ @proxy.none.should be_nil
31
+ end
32
+
33
+ it "returns nil? object for non-existing nested keys" do
34
+ @proxy.none.none.should be_nil
35
+ end
36
+
37
+ it "inserts simple key" do
38
+ @proxy.new_key.should be_nil
39
+ @proxy.new_key = 'new value'
40
+ @proxy.new_key.should == 'new value'
41
+ end
42
+
43
+ it "inserts nested key" do
44
+ @proxy.newer.new_key.should be_nil
45
+ @proxy.newer.new_key = 'new deep value'
46
+ @proxy.newer.new_key.should == 'new deep value'
47
+ end
48
+
49
+ it "inserts nested key off existing prefix" do
50
+ @proxy.deeper.new_key.should be_nil
51
+ @proxy.deeper.new_key = 'new mixed value'
52
+ @proxy.deeper.new_key.should == 'new mixed value'
53
+ end
54
+ end
@@ -0,0 +1,166 @@
1
+ require File.expand_path(File.dirname(__FILE__) + '/spec_helper')
2
+ require 'fileutils'
3
+
4
+ describe "Db" do
5
+ Files = Pwnbus::Configdb::Files
6
+ Configdb = Pwnbus::Configdb
7
+
8
+ shared_examples_for "paths" do
9
+ it "should have a global path pointing to /etc" do
10
+ Files.db_dir_paths.last[0, 5].should == '/etc/'
11
+ end
12
+ end
13
+
14
+ describe "for non-root user" do
15
+ it_should_behave_like "paths"
16
+
17
+ before do
18
+ Files.should_receive(:superuser?).and_return(false)
19
+ end
20
+
21
+ it "should use a local and a global path" do
22
+ Files.db_dir_paths.should have(2).items
23
+ end
24
+ end
25
+
26
+ describe "for root user" do
27
+ before do
28
+ Files.should_receive(:superuser?).and_return(true)
29
+ end
30
+
31
+ it "should only use the global path" do
32
+ Files.db_dir_paths.should have(1).item
33
+ end
34
+ end
35
+ end
36
+
37
+ describe "Db with stubbed dir" do
38
+ before do
39
+ @tempdir = "/tmp/pwnbus-#{(Time.now.to_f * 1000).to_i}"
40
+
41
+ @global_dir = File.join @tempdir, 'etc/pwnbus'
42
+ @user_dir = File.join @tempdir, 'home', 'user', '.pwnbus'
43
+ Dir.mkdir(@tempdir)
44
+ Files.stub!(:db_dir_global_path).and_return(@global_dir)
45
+ Files.stub!(:db_dir_user_path).and_return(@user_dir)
46
+ end
47
+
48
+ after do
49
+ FileUtils.rm_r(@tempdir)
50
+ end
51
+
52
+ describe "for non-root user" do
53
+ before do
54
+ Files.should_receive(:superuser?).at_least(:once).and_return(false)
55
+ end
56
+
57
+ it "saves new databases in the local dir" do
58
+ Configdb.open('pathtest') { }
59
+ File.exist?(@global_dir + '/pathtest.yml').should be_false
60
+ File.exist?(@user_dir + '/pathtest.yml').should be_true
61
+ end
62
+ end
63
+
64
+ describe "for root user" do
65
+ before do
66
+ Files.should_receive(:superuser?).at_least(:once).and_return(true)
67
+ end
68
+
69
+ it "saves databases in /etc'" do
70
+ Configdb.open('pathtest') { }
71
+ File.exist?(@user_dir + '/pathtest.yml').should be_false
72
+ File.exist?(@global_dir + '/pathtest.yml').should be_true
73
+ end
74
+ end
75
+
76
+ describe "with saved db" do
77
+ before do
78
+ Configdb.open('persistence') do |c|
79
+ c.really.long.flag = true
80
+ c.really.long.number = 41
81
+ c.really.long.string = 'something'
82
+ end
83
+ end
84
+
85
+ it "should report variables correctly" do
86
+ Configdb.open('persistence') do |c|
87
+ c.really.long.flag.should == true
88
+ c.really.long.number.should == 41
89
+ c.really.long.string.should == 'something'
90
+ end
91
+ end
92
+
93
+ it "should make the dbfile world-readable" do
94
+ (File.stat(@user_dir + '/persistence.yml').mode & 0777).should == 0644
95
+ end
96
+
97
+ describe "after overwriting some vars" do
98
+ before do
99
+ Configdb.open('persistence') do |c|
100
+ c.really.long.number = 42
101
+ c.really.long.flag = false
102
+ end
103
+ end
104
+
105
+ it "should report changes correctly" do
106
+ Configdb.open('persistence') do |c|
107
+ c.really.long.flag.should == false
108
+ c.really.long.number.should == 42
109
+ c.really.long.string.should == 'something'
110
+ end
111
+ end
112
+ end
113
+ end
114
+
115
+ describe "with private db" do
116
+ before do
117
+ Configdb.open('.private') do |c|
118
+ c.secret.pin = '1234'
119
+ end
120
+ end
121
+
122
+ it "should not make the dbfile world-readable" do
123
+ (File.stat(@user_dir + '/.private.yml').mode & 0777).should == 0600
124
+ end
125
+
126
+ it "should be able to read back data" do
127
+ Configdb.open('.private') { |c| c.secret.pin.should == '1234' }
128
+ end
129
+ end
130
+
131
+ describe "after crash" do
132
+ before do
133
+ @crash_path = @user_dir + '/crash.yml'
134
+ @crash_new_path = @crash_path + '.new'
135
+ FileUtils.mkdir_p @user_dir
136
+ File.open(@crash_path, 'w') { |f| YAML.dump({'old' => 'yes'}, f) }
137
+ File.open(@crash_new_path, 'w') { |f| YAML.dump({'old' => 'no'}, f) }
138
+ end
139
+
140
+ describe "during writing" do
141
+ before do
142
+ @db_old = Configdb.open('crash') { |crash| crash.old }
143
+ end
144
+
145
+ it "should read the old copy" do
146
+ @db_old.should == 'yes'
147
+ end
148
+ end
149
+
150
+ describe "during rename" do
151
+ before do
152
+ File.unlink @crash_path
153
+ @db_old = Configdb.open('crash') { |crash| crash.old }
154
+ end
155
+
156
+ it "should read the new copy" do
157
+ @db_old.should == 'no'
158
+ end
159
+
160
+ it "should finish the rename" do
161
+ File.exist?(@crash_path).should be_true
162
+ File.exist?(@crash_new_path).should be_false
163
+ end
164
+ end
165
+ end
166
+ end
@@ -0,0 +1,3 @@
1
+ ---
2
+ variable: value
3
+ deeper.variable: deeper value
@@ -0,0 +1,20 @@
1
+ require 'bundler'
2
+ begin
3
+ Bundler.setup(:default, :development)
4
+ rescue Bundler::BundlerError => e
5
+ $stderr.puts e.message
6
+ $stderr.puts "Run `bundle install` to install missing gems"
7
+ exit e.status_code
8
+ end
9
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
10
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
11
+ require 'pwnbus/configdb'
12
+ require 'rspec'
13
+
14
+ # Requires supporting files with custom matchers and macros, etc,
15
+ # in ./support/ and its subdirectories.
16
+ Dir["#{File.dirname(__FILE__)}/support/**/*.rb"].each {|f| require f}
17
+
18
+ RSpec.configure do |config|
19
+
20
+ end
metadata ADDED
@@ -0,0 +1,216 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: pwnbus-configdb
3
+ version: !ruby/object:Gem::Version
4
+ hash: 27
5
+ prerelease: false
6
+ segments:
7
+ - 0
8
+ - 1
9
+ - 0
10
+ version: 0.1.0
11
+ platform: ruby
12
+ authors:
13
+ - Victor Costan
14
+ autorequire:
15
+ bindir: bin
16
+ cert_chain: []
17
+
18
+ date: 2010-09-19 00:00:00 -04:00
19
+ default_executable:
20
+ dependencies:
21
+ - !ruby/object:Gem::Dependency
22
+ prerelease: false
23
+ name: rspec
24
+ version_requirements: &id001 !ruby/object:Gem::Requirement
25
+ none: false
26
+ requirements:
27
+ - - ">="
28
+ - !ruby/object:Gem::Version
29
+ hash: 62196427
30
+ segments:
31
+ - 2
32
+ - 0
33
+ - 0
34
+ - beta
35
+ - 20
36
+ version: 2.0.0.beta.20
37
+ requirement: *id001
38
+ type: :development
39
+ - !ruby/object:Gem::Dependency
40
+ prerelease: false
41
+ name: bundler
42
+ version_requirements: &id002 !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ~>
46
+ - !ruby/object:Gem::Version
47
+ hash: 23
48
+ segments:
49
+ - 1
50
+ - 0
51
+ - 0
52
+ version: 1.0.0
53
+ requirement: *id002
54
+ type: :development
55
+ - !ruby/object:Gem::Dependency
56
+ prerelease: false
57
+ name: jeweler
58
+ version_requirements: &id003 !ruby/object:Gem::Requirement
59
+ none: false
60
+ requirements:
61
+ - - ~>
62
+ - !ruby/object:Gem::Version
63
+ hash: -1876988218
64
+ segments:
65
+ - 1
66
+ - 5
67
+ - 0
68
+ - pre3
69
+ version: 1.5.0.pre3
70
+ requirement: *id003
71
+ type: :development
72
+ - !ruby/object:Gem::Dependency
73
+ prerelease: false
74
+ name: rcov
75
+ version_requirements: &id004 !ruby/object:Gem::Requirement
76
+ none: false
77
+ requirements:
78
+ - - ">="
79
+ - !ruby/object:Gem::Version
80
+ hash: 3
81
+ segments:
82
+ - 0
83
+ version: "0"
84
+ requirement: *id004
85
+ type: :development
86
+ - !ruby/object:Gem::Dependency
87
+ prerelease: false
88
+ name: rspec
89
+ version_requirements: &id005 !ruby/object:Gem::Requirement
90
+ none: false
91
+ requirements:
92
+ - - ">="
93
+ - !ruby/object:Gem::Version
94
+ hash: 62196427
95
+ segments:
96
+ - 2
97
+ - 0
98
+ - 0
99
+ - beta
100
+ - 20
101
+ version: 2.0.0.beta.20
102
+ requirement: *id005
103
+ type: :development
104
+ - !ruby/object:Gem::Dependency
105
+ prerelease: false
106
+ name: bundler
107
+ version_requirements: &id006 !ruby/object:Gem::Requirement
108
+ none: false
109
+ requirements:
110
+ - - ~>
111
+ - !ruby/object:Gem::Version
112
+ hash: 23
113
+ segments:
114
+ - 1
115
+ - 0
116
+ - 0
117
+ version: 1.0.0
118
+ requirement: *id006
119
+ type: :development
120
+ - !ruby/object:Gem::Dependency
121
+ prerelease: false
122
+ name: jeweler
123
+ version_requirements: &id007 !ruby/object:Gem::Requirement
124
+ none: false
125
+ requirements:
126
+ - - ~>
127
+ - !ruby/object:Gem::Version
128
+ hash: -1876988218
129
+ segments:
130
+ - 1
131
+ - 5
132
+ - 0
133
+ - pre3
134
+ version: 1.5.0.pre3
135
+ requirement: *id007
136
+ type: :development
137
+ - !ruby/object:Gem::Dependency
138
+ prerelease: false
139
+ name: rcov
140
+ version_requirements: &id008 !ruby/object:Gem::Requirement
141
+ none: false
142
+ requirements:
143
+ - - ">="
144
+ - !ruby/object:Gem::Version
145
+ hash: 3
146
+ segments:
147
+ - 0
148
+ version: "0"
149
+ requirement: *id008
150
+ type: :development
151
+ description: Dead-simple key-value store with atomic updates.
152
+ email: victor@costan.us
153
+ executables: []
154
+
155
+ extensions: []
156
+
157
+ extra_rdoc_files:
158
+ - LICENSE
159
+ - README.rdoc
160
+ files:
161
+ - .document
162
+ - .gitignore
163
+ - .project
164
+ - Gemfile
165
+ - Gemfile.lock
166
+ - LICENSE
167
+ - README.rdoc
168
+ - Rakefile
169
+ - VERSION
170
+ - lib/pwnbus/configdb.rb
171
+ - lib/pwnbus/configdb/db.rb
172
+ - lib/pwnbus/configdb/files.rb
173
+ - pwnbus-configdb.gemspec
174
+ - spec/.rspec
175
+ - spec/db_object_spec.rb
176
+ - spec/db_spec.rb
177
+ - spec/fixtures/db.yml
178
+ - spec/spec_helper.rb
179
+ has_rdoc: true
180
+ homepage: http://github.com/pwnall/pwnbus_configdb
181
+ licenses: []
182
+
183
+ post_install_message:
184
+ rdoc_options: []
185
+
186
+ require_paths:
187
+ - lib
188
+ required_ruby_version: !ruby/object:Gem::Requirement
189
+ none: false
190
+ requirements:
191
+ - - ">="
192
+ - !ruby/object:Gem::Version
193
+ hash: 3
194
+ segments:
195
+ - 0
196
+ version: "0"
197
+ required_rubygems_version: !ruby/object:Gem::Requirement
198
+ none: false
199
+ requirements:
200
+ - - ">="
201
+ - !ruby/object:Gem::Version
202
+ hash: 3
203
+ segments:
204
+ - 0
205
+ version: "0"
206
+ requirements: []
207
+
208
+ rubyforge_project:
209
+ rubygems_version: 1.3.7
210
+ signing_key:
211
+ specification_version: 3
212
+ summary: Pure-ruby database for configuration variables.
213
+ test_files:
214
+ - spec/db_object_spec.rb
215
+ - spec/db_spec.rb
216
+ - spec/spec_helper.rb