relaxo 1.2.0 → 1.6.1

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 0001153bcb2843832dc1f6ad29e49885db98729d
4
- data.tar.gz: 0e065a359e1ae3f0ff21ecc1848b2189f3f5345e
2
+ SHA256:
3
+ metadata.gz: ee389a32f25fdf49c2a4ce9e347b8f75f865e99027836fe960029a97b614886c
4
+ data.tar.gz: 7fb8ea8ad158e7cd244dba5ab7ef5b0b94b02a4207a5be4090a131ccf4b0a51b
5
5
  SHA512:
6
- metadata.gz: 768562aeadc873bdd50df1a49ef3a455f64d9a6bc56d2edb4fcf5fe0876e46b6a83f137a677df3faac8cac46aa3da8dc4b6eff91157fc152f0478e621e93922b
7
- data.tar.gz: f88b84ef0e018606ef3696688755315d6fd0092fb201de2e76186b5cd1606b8162f72cadf32650dad2513767850e36eadd8af7028722b74aef5ae4d5c9c98ec4
6
+ metadata.gz: 549af3e6849d9a99a86415201e2b6126f0a76f2fa8f7b27ebc7e2af0a9b1750be40efb24b14fdc8fa04fb4a49a1daaa5f6b9dbcb395e960d18fc0c3308a1c6ff
7
+ data.tar.gz: 50ab5e3e2f2fde0643c276729e9954da0ade6027e597490e950f3fdc95849d9c208b32da1139fb218c49198444b6147311e6e9c7a8b0937dc7e142a6fe92e293
Binary file
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2012 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
1
+ # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the "Software"), to deal
@@ -20,18 +20,35 @@
20
20
 
21
21
  require 'relaxo/database'
22
22
 
23
- require 'pry'
23
+ require 'etc'
24
+ require 'socket'
24
25
 
25
26
  module Relaxo
26
- def self.connect(path, metadata = {})
27
+ MASTER = 'master'.freeze
28
+
29
+ def self.connect(path, branch: nil, sync: nil, **metadata)
27
30
  unless File.exist?(path)
28
31
  repository = Rugged::Repository.init_at(path, true)
29
32
 
30
- if metadata[:sync] || ENV['RELAXO_SYNC']
33
+ if sync || ENV['RELAXO_SYNC']
31
34
  repository.config['core.fsyncObjectFiles'] = true
32
35
  end
33
36
  end
34
37
 
35
- return Database.new(path, metadata)
38
+ branch ||= MASTER
39
+
40
+ database = Database.new(path, branch, metadata)
41
+
42
+ if config = database.config
43
+ unless config['user.name']
44
+ login = Etc.getlogin
45
+ hostname = Socket.gethostname
46
+
47
+ config['user.name'] = login
48
+ config['user.email'] = "#{login}@#{hostname}"
49
+ end
50
+ end
51
+
52
+ return database
36
53
  end
37
54
  end
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2012 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
1
+ # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the "Software"), to deal
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2012 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
1
+ # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the "Software"), to deal
@@ -19,8 +19,8 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  require 'rugged'
22
- require 'logger'
23
22
 
23
+ require_relative 'logger'
24
24
  require_relative 'dataset'
25
25
  require_relative 'changeset'
26
26
 
@@ -28,29 +28,48 @@ module Relaxo
28
28
  HEAD = 'HEAD'.freeze
29
29
 
30
30
  class Database
31
- def initialize(path, metadata = {})
31
+ def initialize(path, branch, metadata = {})
32
32
  @path = path
33
33
  @metadata = metadata
34
34
 
35
- @logger = metadata[:logger] || Logger.new($stderr).tap{|logger| logger.level = Logger::INFO}
36
-
37
35
  @repository = Rugged::Repository.new(path)
36
+ # @repository.config['core.fsyncObjectFiles'] = fsync
37
+
38
+ @branch = branch
39
+ end
40
+
41
+ def config
42
+ @repository.config
38
43
  end
39
44
 
40
45
  attr :path
41
46
  attr :metadata
42
47
  attr :repository
43
48
 
49
+ # Completely clear out the database.
50
+ def clear!
51
+ if head = @repository.branches[@branch]
52
+ @repository.references.delete(head)
53
+ end
54
+ end
55
+
44
56
  def empty?
45
57
  @repository.empty?
46
58
  end
47
59
 
60
+ def head
61
+ @repository.branches[@branch]
62
+ end
63
+
48
64
  def [] key
49
65
  @metadata[key]
50
66
  end
51
67
 
52
68
  # During the execution of the block, changes don't get stored immediately, so reading from the dataset (from outside the block) will continue to return the values that were stored in the configuration when the transaction was started.
69
+ # @return the result of the block.
53
70
  def commit(**options)
71
+ result = nil
72
+
54
73
  track_time(options[:message]) do
55
74
  catch(:abort) do
56
75
  begin
@@ -58,10 +77,12 @@ module Relaxo
58
77
 
59
78
  changeset = Changeset.new(@repository, tree)
60
79
 
61
- yield changeset
80
+ result = yield changeset
62
81
  end until apply(parent, changeset, **options)
63
82
  end
64
83
  end
84
+
85
+ return result
65
86
  end
66
87
 
67
88
  # Efficient point-in-time read-only access.
@@ -113,7 +134,7 @@ module Relaxo
113
134
  end_time = Time.now
114
135
  elapsed_time = end_time - start_time
115
136
 
116
- @logger.debug("time") {"#{message.inspect}: %0.3fs" % elapsed_time}
137
+ Relaxo.logger.debug(self) {"#{message.inspect}: %0.3fs" % elapsed_time}
117
138
  end
118
139
 
119
140
  def apply(parent, changeset, **options)
@@ -121,7 +142,7 @@ module Relaxo
121
142
 
122
143
  options[:tree] = changeset.write_tree
123
144
  options[:parents] ||= [parent]
124
- options[:update_ref] ||= HEAD
145
+ options[:update_ref] ||= "refs/heads/#{@branch}"
125
146
 
126
147
  begin
127
148
  Rugged::Commit.create(@repository, options)
@@ -131,13 +152,11 @@ module Relaxo
131
152
  end
132
153
 
133
154
  def latest_commit
134
- if head = @repository.head
155
+ if head = self.head
135
156
  return head.target, head.target.tree
136
157
  else
137
158
  return nil, empty_tree
138
159
  end
139
- rescue Rugged::ReferenceError
140
- return nil, empty_tree
141
160
  end
142
161
 
143
162
  def empty_tree
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2012 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
1
+ # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the "Software"), to deal
@@ -1,4 +1,4 @@
1
- # Copyright, 2016, by Samuel G. D. Williams. <http://www.codeotaku.com>
1
+ # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the "Software"), to deal
@@ -18,32 +18,8 @@
18
18
  # OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
19
  # THE SOFTWARE.
20
20
 
21
- require 'yaml'
21
+ require 'console'
22
22
 
23
23
  module Relaxo
24
- class Schema
25
- def initialize(document)
26
- @document = document
27
- end
28
-
29
- def self.load_file(schema_path)
30
- self.new(YAML.load_file(schema_path))
31
- end
32
-
33
- def id
34
- @document[ID]
35
- end
36
-
37
- def migrate(db)
38
- if db.id?(self.id)
39
- if existing_document = GeoZone::DB.get(self.id)
40
- @document[Relaxo::REV] = existing_document[Relaxo::REV]
41
- end
42
- end
43
-
44
- if existing_document != @document
45
- db.save(@document)
46
- end
47
- end
48
- end
24
+ extend Console
49
25
  end
@@ -1,4 +1,4 @@
1
- # Copyright (c) 2012 Samuel G. D. Williams. <http://www.oriontransfer.co.nz>
1
+ # Copyright, 2012, by Samuel G. D. Williams. <http://www.codeotaku.com>
2
2
  #
3
3
  # Permission is hereby granted, free of charge, to any person obtaining a copy
4
4
  # of this software and associated documentation files (the "Software"), to deal
@@ -19,5 +19,5 @@
19
19
  # THE SOFTWARE.
20
20
 
21
21
  module Relaxo
22
- VERSION = "1.2.0"
22
+ VERSION = "1.6.1"
23
23
  end
metadata CHANGED
@@ -1,15 +1,29 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: relaxo
3
3
  version: !ruby/object:Gem::Version
4
- version: 1.2.0
4
+ version: 1.6.1
5
5
  platform: ruby
6
6
  authors:
7
7
  - Samuel Williams
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2017-08-04 00:00:00.000000000 Z
11
+ date: 2020-06-02 00:00:00.000000000 Z
12
12
  dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: console
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
13
27
  - !ruby/object:Gem::Dependency
14
28
  name: rugged
15
29
  requirement: !ruby/object:Gem::Requirement
@@ -25,35 +39,63 @@ dependencies:
25
39
  - !ruby/object:Gem::Version
26
40
  version: '0'
27
41
  - !ruby/object:Gem::Dependency
28
- name: rspec
42
+ name: bake
29
43
  requirement: !ruby/object:Gem::Requirement
30
44
  requirements:
31
- - - "~>"
45
+ - - ">="
32
46
  - !ruby/object:Gem::Version
33
- version: '3.6'
47
+ version: '0'
34
48
  type: :development
35
49
  prerelease: false
36
50
  version_requirements: !ruby/object:Gem::Requirement
37
51
  requirements:
38
- - - "~>"
52
+ - - ">="
39
53
  - !ruby/object:Gem::Version
40
- version: '3.6'
54
+ version: '0'
55
+ - !ruby/object:Gem::Dependency
56
+ name: bake-bundler
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - ">="
60
+ - !ruby/object:Gem::Version
61
+ version: '0'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - ">="
67
+ - !ruby/object:Gem::Version
68
+ version: '0'
69
+ - !ruby/object:Gem::Dependency
70
+ name: bake-modernize
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - ">="
74
+ - !ruby/object:Gem::Version
75
+ version: '0'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - ">="
81
+ - !ruby/object:Gem::Version
82
+ version: '0'
41
83
  - !ruby/object:Gem::Dependency
42
84
  name: bundler
43
85
  requirement: !ruby/object:Gem::Requirement
44
86
  requirements:
45
- - - "~>"
87
+ - - ">="
46
88
  - !ruby/object:Gem::Version
47
- version: '1.3'
89
+ version: '0'
48
90
  type: :development
49
91
  prerelease: false
50
92
  version_requirements: !ruby/object:Gem::Requirement
51
93
  requirements:
52
- - - "~>"
94
+ - - ">="
53
95
  - !ruby/object:Gem::Version
54
- version: '1.3'
96
+ version: '0'
55
97
  - !ruby/object:Gem::Dependency
56
- name: rake
98
+ name: covered
57
99
  requirement: !ruby/object:Gem::Requirement
58
100
  requirements:
59
101
  - - ">="
@@ -66,44 +108,53 @@ dependencies:
66
108
  - - ">="
67
109
  - !ruby/object:Gem::Version
68
110
  version: '0'
69
- description: "\t\tRelaxo provides a set of tools and interfaces for interacting with
70
- CouchDB.\n\t\tIt aims to be as simple and efficient as possible while still improving
71
- the\n\t\tusability of various CouchDB features.\n"
72
- email:
73
- - samuel.williams@oriontransfer.co.nz
74
- executables:
75
- - relaxo
76
- - relaxo-admin
111
+ - !ruby/object:Gem::Dependency
112
+ name: msgpack
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - ">="
116
+ - !ruby/object:Gem::Version
117
+ version: '0'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - ">="
123
+ - !ruby/object:Gem::Version
124
+ version: '0'
125
+ - !ruby/object:Gem::Dependency
126
+ name: rspec
127
+ requirement: !ruby/object:Gem::Requirement
128
+ requirements:
129
+ - - "~>"
130
+ - !ruby/object:Gem::Version
131
+ version: '3.6'
132
+ type: :development
133
+ prerelease: false
134
+ version_requirements: !ruby/object:Gem::Requirement
135
+ requirements:
136
+ - - "~>"
137
+ - !ruby/object:Gem::Version
138
+ version: '3.6'
139
+ description:
140
+ email:
141
+ executables: []
77
142
  extensions: []
78
143
  extra_rdoc_files: []
79
144
  files:
80
- - ".gitignore"
81
- - ".rspec"
82
- - ".travis.yml"
83
- - Gemfile
84
- - README.md
85
- - Rakefile
86
- - bin/relaxo
87
- - bin/relaxo-admin
145
+ - lib/.DS_Store
88
146
  - lib/relaxo.rb
89
147
  - lib/relaxo/changeset.rb
90
148
  - lib/relaxo/database.rb
91
149
  - lib/relaxo/dataset.rb
92
150
  - lib/relaxo/directory.rb
93
- - lib/relaxo/schema.rb
151
+ - lib/relaxo/logger.rb
94
152
  - lib/relaxo/version.rb
95
- - relaxo.gemspec
96
- - spec/relaxo/changeset_spec.rb
97
- - spec/relaxo/concurrency_spec.rb
98
- - spec/relaxo/database_spec.rb
99
- - spec/relaxo/enumeration_spec.rb
100
- - spec/relaxo/performance_spec.rb
101
- - spec/relaxo/test_records.rb
102
- - spec/spec_helper.rb
103
- homepage: ''
153
+ homepage: https://github.com/ioquatix/relaxo
104
154
  licenses:
105
155
  - MIT
106
- metadata: {}
156
+ metadata:
157
+ funding_uri: https://github.com/sponsors/ioquatix/
107
158
  post_install_message:
108
159
  rdoc_options: []
109
160
  require_paths:
@@ -112,23 +163,15 @@ required_ruby_version: !ruby/object:Gem::Requirement
112
163
  requirements:
113
164
  - - ">="
114
165
  - !ruby/object:Gem::Version
115
- version: '0'
166
+ version: '2.5'
116
167
  required_rubygems_version: !ruby/object:Gem::Requirement
117
168
  requirements:
118
169
  - - ">="
119
170
  - !ruby/object:Gem::Version
120
171
  version: '0'
121
172
  requirements: []
122
- rubyforge_project:
123
- rubygems_version: 2.6.12
173
+ rubygems_version: 3.1.2
124
174
  signing_key:
125
175
  specification_version: 4
126
- summary: Relaxo is a helper for loading and working with CouchDB.
127
- test_files:
128
- - spec/relaxo/changeset_spec.rb
129
- - spec/relaxo/concurrency_spec.rb
130
- - spec/relaxo/database_spec.rb
131
- - spec/relaxo/enumeration_spec.rb
132
- - spec/relaxo/performance_spec.rb
133
- - spec/relaxo/test_records.rb
134
- - spec/spec_helper.rb
176
+ summary: Relaxo is versioned document database built on top of git.
177
+ test_files: []
data/.gitignore DELETED
@@ -1,18 +0,0 @@
1
- *.gem
2
- *.rbc
3
- .bundle
4
- .config
5
- .yardoc
6
- Gemfile.lock
7
- InstalledFiles
8
- _yardoc
9
- coverage
10
- doc/
11
- lib/bundler/man
12
- pkg
13
- rdoc
14
- spec/reports
15
- test/tmp
16
- test/version_tmp
17
- tmp
18
- spec/relaxo/test
data/.rspec DELETED
@@ -1,4 +0,0 @@
1
- --format documentation
2
- --backtrace
3
- --warnings
4
- --require spec_helper
@@ -1,15 +0,0 @@
1
- language: ruby
2
- sudo: false
3
- dist: trusty
4
- cache: bundler
5
- rvm:
6
- - 2.1
7
- - 2.2
8
- - 2.3
9
- - 2.4
10
- - ruby-head
11
- - jruby-head
12
- matrix:
13
- allow_failures:
14
- - rvm: "ruby-head"
15
- - rvm: "jruby-head"
data/Gemfile DELETED
@@ -1,20 +0,0 @@
1
- source 'https://rubygems.org'
2
-
3
- # Specify your gem's dependencies in relaxo.gemspec
4
- gemspec
5
-
6
- gem 'rugged', git: 'https://github.com/libgit2/rugged.git', submodules: true
7
-
8
- group :development do
9
- gem "pry"
10
- gem "msgpack"
11
- end
12
-
13
- group :test do
14
- gem 'benchmark-ips'
15
- gem 'ruby-prof'
16
-
17
- gem 'rack-test'
18
- gem 'simplecov'
19
- gem 'coveralls', require: false
20
- end
data/README.md DELETED
@@ -1,150 +0,0 @@
1
- # Relaxo
2
-
3
- Relaxo is a transactional database built on top of git. It's aim is to provide a robust interface for document storage and sorted indexes.
4
-
5
- [![Build Status](https://secure.travis-ci.org/ioquatix/relaxo.svg)](http://travis-ci.org/ioquatix/relaxo)
6
- [![Code Climate](https://codeclimate.com/github/ioquatix/relaxo.svg)](https://codeclimate.com/github/ioquatix/relaxo)
7
- [![Coverage Status](https://coveralls.io/repos/ioquatix/relaxo/badge.svg)](https://coveralls.io/r/ioquatix/relaxo)
8
-
9
- ## Installation
10
-
11
- Add this line to your application's Gemfile:
12
-
13
- gem 'relaxo'
14
-
15
- And then execute:
16
-
17
- $ bundle
18
-
19
- Or install it yourself as:
20
-
21
- $ gem install relaxo
22
-
23
- ## Usage
24
-
25
- Connect to a local database and manipulate some documents.
26
-
27
- require 'relaxo'
28
- require 'msgpack'
29
-
30
- DB = Relaxo.connect("test")
31
-
32
- DB.commit(message: "Create test data") do |dataset|
33
- object = dataset.append(MessagePack.dump({bob: 'dole'}))
34
- dataset.write("doc1.json", object)
35
- end
36
-
37
- DB.commit(message: "Update test data") do |dataset|
38
- doc = MessagePack.load dataset.read('doc1.json').data
39
- doc[:foo] = 'bar'
40
-
41
- object = dataset.append(MessagePack.dump(doc))
42
- dataset.write("doc2.json", object)
43
- end
44
-
45
- doc = MessagePack.load DB.current['doc2.json'].data
46
- puts doc
47
- # => {"bob"=>"dole", "foo"=>"bar"}
48
-
49
- ### Document Storage
50
-
51
- Relaxo uses the git persistent data structure for storing documents. This data structure exposes a file-system like interface, which stores any kind of data. This means that you are free to use JSON, or BSON, or MessagePack, or JPEG, or XML, or any combination of those.
52
-
53
- Relaxo has a transactional model for both reading and writing.
54
-
55
- #### Reading Files
56
-
57
- path = "path/to/document"
58
-
59
- DB.current do |dataset|
60
- object = dataset.read(path)
61
-
62
- puts "The object id: #{object.oid}"
63
- puts "The object data size: #{object.size}"
64
- puts "The object data: #{object.data.inspect}"
65
- end
66
-
67
- #### Writing Files
68
-
69
- path = "path/to/document"
70
- data = MessagePack.dump(document)
71
-
72
- DB.commit(message: "Adding document") do |changeset|
73
- object = changeset.append(data)
74
- changeset.write(path, object)
75
- end
76
-
77
- ### Datasets and Transactions
78
-
79
- `Dataset`s and `Changeset`s are important concepts. Relaxo doesn't allow arbitrary access to data, but instead exposes the git persistent model for both reading and writing. The implications of this are that when reading or writing, you always see a consistent snapshot of the data store.
80
-
81
- ### Suitability
82
-
83
- Relaxo is designed to scale to the hundreds of thousands of documents. It's designed around the git persistent data store, and therefore has some performance and concurrency limitations due to the underlying implementation.
84
-
85
- Because it maintains a full history of all changes, the repository would continue to grow over time by default, but there are mechanisms to deal with that.
86
-
87
- #### Performance
88
-
89
- Relaxo can do anywhere from 1000-10,000 inserts per second depending on how you structure the workload.
90
-
91
- Relaxo Performance
92
- Warming up --------------------------------------
93
- single 129.000 i/100ms
94
- Calculating -------------------------------------
95
- single 6.224k (±14.7%) i/s - 114.036k in 20.000025s
96
- single transaction should be fast
97
- Warming up --------------------------------------
98
- multiple 152.000 i/100ms
99
- Calculating -------------------------------------
100
- multiple 1.452k (±15.2%) i/s - 28.120k in 20.101831s
101
- multiple transactions should be fast
102
-
103
- Reading data is lighting fast as it's loaded directly from disk and cached.
104
-
105
- ### Loading Data
106
-
107
- As Relaxo is unapologetically based on git, you can use git directly with a non-bare working directory to add any files you like. You can even point Relaxo at an existing git repository.
108
-
109
- ### Durability
110
-
111
- Relaxo is based on `libgit2` and asserts that it is a transactional database. We base this assertion on:
112
-
113
- - All writes into the object store using `libgit2` are atomic and synchronized to disk.
114
- - All updates to refs are atomic and synchronized to disk.
115
-
116
- Provided these two invariants are maintained, the operation of Relaxo will be safe, even if there are unexpected interruptions to the program.
117
-
118
- The durability guarantees of Relaxo depend on [`libgit2` calling `fsync`](https://github.com/libgit2/libgit2/pull/4030), and [this being respected by the underlying hardware](http://www.evanjones.ca/intel-ssd-durability.html). Otherwise, durability cannot be guaranteed.
119
-
120
- ## Contributing
121
-
122
- 1. Fork it
123
- 2. Create your feature branch (`git checkout -b my-new-feature`)
124
- 3. Commit your changes (`git commit -am 'Add some feature'`)
125
- 4. Push to the branch (`git push origin my-new-feature`)
126
- 5. Create new Pull Request
127
-
128
- ## License
129
-
130
- Released under the MIT license.
131
-
132
- Copyright, 2015, by [Samuel G. D. Williams](http://www.codeotaku.com/samuel-williams).
133
-
134
- Permission is hereby granted, free of charge, to any person obtaining a copy
135
- of this software and associated documentation files (the "Software"), to deal
136
- in the Software without restriction, including without limitation the rights
137
- to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
138
- copies of the Software, and to permit persons to whom the Software is
139
- furnished to do so, subject to the following conditions:
140
-
141
- The above copyright notice and this permission notice shall be included in
142
- all copies or substantial portions of the Software.
143
-
144
- THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
145
- IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
146
- FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
147
- AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
148
- LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
149
- OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
150
- THE SOFTWARE.
data/Rakefile DELETED
@@ -1,20 +0,0 @@
1
- require "bundler/gem_tasks"
2
- require "rspec/core/rake_task"
3
-
4
- RSpec::Core::RakeTask.new(:spec) do |task|
5
- task.rspec_opts = ["--require", "simplecov"] if ENV['COVERAGE']
6
- end
7
-
8
- task :default => :spec
9
-
10
- task :console do
11
- require 'pry'
12
- require 'msgpack'
13
- require 'securerandom'
14
-
15
- require_relative 'lib/relaxo'
16
-
17
- DB = Relaxo.connect(File.join(__dir__, '/tmp/relaxo-test-db'))
18
-
19
- Pry.start
20
- end
data/bin/relaxo DELETED
@@ -1,142 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'relaxo'
4
- require 'relaxo/version'
5
- require 'optparse'
6
- require 'yaml'
7
- require 'csv'
8
-
9
- OPTIONS = {
10
- :existing => :update, # :merge, :replace
11
- :format => :yaml,
12
- :transaction => true,
13
- }
14
-
15
- module Formats
16
- def self.yaml(io, &block)
17
- documents = YAML::load(io)
18
-
19
- unless Array === documents
20
- documents = [documents]
21
- end
22
-
23
- documents.each &block
24
- end
25
-
26
- def self.csv(io, &block)
27
- csv = CSV.new(io, :headers => :first_row)
28
-
29
- csv.each do |row|
30
- yield row.to_hash
31
- end
32
- end
33
- end
34
-
35
- ARGV.options do |o|
36
- script_name = File.basename($0)
37
-
38
- o.banner = "Usage: #{script_name} [options] [server-url] [files]"
39
- o.define_head "This script can be used to import data to CouchDB."
40
-
41
- o.separator ""
42
- o.separator "Document creation:"
43
-
44
- o.on("--existing [mode]", "Control whether to 'update (new document attributes takes priority), 'merge' (existing document attributes takes priority) or replace (old document attributes discarded) existing documents.") do |mode|
45
- OPTIONS[:existing] = mode.to_sym
46
- end
47
-
48
- o.on("--format [type]", "Control the input format. 'yaml' files are imported as a single document or array of documents. 'csv' files are imported as records using the first row as attribute keys.") do |format|
49
- OPTIONS[:format] = format.to_sym
50
- end
51
-
52
- o.on("--[no-]transaction", "Controls whether data is saved using the batch save operation. Not suitable for huge amounts of data.")
53
-
54
- o.separator ""
55
- o.separator "Help and Copyright information:"
56
-
57
- o.on_tail("--copy", "Display copyright and warranty information") do
58
- $stderr.puts "#{script_name} v#{Relaxo::VERSION}. Copyright (c) 2012 Samuel Williams."
59
- $stderr.puts "This software is released under the MIT license and comes with ABSOLUTELY NO WARRANTY."
60
- exit
61
- end
62
-
63
- o.on_tail("-h", "--help", "Show this help message.") do
64
- $stderr.puts o
65
- exit
66
- end
67
- end.parse!
68
-
69
- url = ARGV.shift
70
-
71
- $stderr.puts "Connecting to #{url}..."
72
- database = Relaxo.connect(url)
73
-
74
- begin
75
- info = database.info
76
-
77
- $stderr.puts "Connected to #{info['db_name']} which contains #{info['doc_count']} document(s)."
78
- rescue StandardError => error
79
- $stderr.puts "Could not fetch info from database server!"
80
-
81
- throw
82
- end
83
-
84
-
85
- format = Formats.method(OPTIONS[:format])
86
-
87
- @stats = {
88
- :saved => 0,
89
- :errors => 0,
90
- :total => 0
91
- }
92
-
93
- def process_document(database, document)
94
- begin
95
- existing_document = nil
96
-
97
- if document && document[Relaxo::ID]
98
- $stderr.puts "Loading #{document[Relaxo::ID]}"
99
- existing_document = database.get(document[Relaxo::ID]) rescue nil
100
- end
101
-
102
- if existing_document
103
- if OPTIONS[:existing] == :replace
104
- $stderr.puts "Replacing existing document..."
105
- # The document is replaced entirely with the incoming data.
106
- document[Relaxo::REV] = existing_document[Relaxo::REV]
107
- elsif OPTIONS[:existing] == :update
108
- $stderr.puts "Updating existing document..."
109
- # Values in `existing_document` take priority
110
- document.update(existing_document)
111
- elsif OPTIONS[:existing] == :merge
112
- $stderr.puts "Merging existing document..."
113
- # Values in `document` take priority, except for `_rev`.
114
- document.update(existing_document) do |key, oldval, newval|
115
- key == Relaxo::REV ? newval : oldval
116
- end
117
- end
118
- end
119
-
120
- $stderr.puts "Saving #{document.inspect}"
121
- result = database.save(document)
122
-
123
- @stats[:saved] += 1
124
- rescue RestClient::BadRequest => ex
125
- $stderr.puts ex.inspect
126
-
127
- @stats[:errors] += 1
128
- end
129
-
130
- @stats[:total] += 1
131
- end
132
-
133
- begin
134
- format.call(ARGF) do |document|
135
- process_document(database, document)
136
- end
137
- ensure
138
- $stderr.puts "#{@stats[:saved]} document(s) saved out of #{@stats[:total]}."
139
- if @stats[:errors] > 0
140
- $stderr.puts "#{@stats[:errors]} errors occurred!"
141
- end
142
- end
@@ -1,61 +0,0 @@
1
- #!/usr/bin/env ruby
2
-
3
- require 'relaxo'
4
- require 'relaxo/version'
5
- require 'optparse'
6
- require 'digest/sha1'
7
-
8
- $url = ARGV.shift unless ARGV[0] =~ /^-/
9
- $connection = Relaxo::Connection.new($url)
10
-
11
- OPTIONS = {
12
- :roles => [],
13
- :password => nil
14
- }
15
-
16
- ARGV.options do |o|
17
- script_name = File.basename($0)
18
-
19
- o.banner = "Usage: #{script_name} [server-url] [options]"
20
- o.define_head "This script can be used to import data to CouchDB."
21
-
22
- o.on("--password password", "Specify the password for a new user") do |password|
23
- OPTIONS[:password] = password
24
- end
25
-
26
- o.on("--roles reader,writer", Array, "Specify the roles for a new user") do |roles|
27
- OPTIONS[:roles] = roles
28
- end
29
-
30
- o.on("--create-user name", "Create a new (non-admin) user") do |name|
31
- password = OPTIONS[:password]
32
-
33
- salt = Digest::SHA1.hexdigest(rand.to_s + name + Time.now.to_s + password)
34
-
35
- user = {
36
- Relaxo::ID => "org.couchdb.user:#{name}",
37
- :type => "user",
38
- :name => name,
39
- :roles => OPTIONS[:roles],
40
- :salt => salt,
41
- :password_sha => Digest::SHA1.hexdigest(password + salt)
42
- }
43
-
44
- database = Relaxo::Database.new($connection, "_users")
45
- puts database.save(user).inspect
46
- end
47
-
48
- o.separator ""
49
- o.separator "Help and Copyright information:"
50
-
51
- o.on_tail("--copy", "Display copyright and warranty information") do
52
- $stderr.puts "#{script_name} v#{Relaxo::VERSION}. Copyright (c) 2012 Samuel Williams."
53
- $stderr.puts "This software is released under the MIT license and comes with ABSOLUTELY NO WARRANTY."
54
- exit
55
- end
56
-
57
- o.on_tail("-h", "--help", "Show this help message.") do
58
- $stderr.puts o
59
- exit
60
- end
61
- end.parse!
@@ -1,30 +0,0 @@
1
- # coding: utf-8
2
- lib = File.expand_path('../lib', __FILE__)
3
- $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
- require 'relaxo/version'
5
-
6
- Gem::Specification.new do |spec|
7
- spec.name = "relaxo"
8
- spec.version = Relaxo::VERSION
9
- spec.authors = ["Samuel Williams"]
10
- spec.email = ["samuel.williams@oriontransfer.co.nz"]
11
- spec.description = <<-EOF
12
- Relaxo provides a set of tools and interfaces for interacting with CouchDB.
13
- It aims to be as simple and efficient as possible while still improving the
14
- usability of various CouchDB features.
15
- EOF
16
- spec.summary = %q{Relaxo is a helper for loading and working with CouchDB.}
17
- spec.homepage = ""
18
- spec.license = "MIT"
19
-
20
- spec.files = `git ls-files`.split($/)
21
- spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
22
- spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
23
- spec.require_paths = ["lib"]
24
-
25
- spec.add_dependency "rugged"
26
-
27
- spec.add_development_dependency "rspec", "~> 3.6"
28
- spec.add_development_dependency "bundler", "~> 1.3"
29
- spec.add_development_dependency "rake"
30
- end
@@ -1,39 +0,0 @@
1
-
2
- require_relative 'test_records'
3
-
4
- RSpec.describe Relaxo::Changeset do
5
- include_context "test records"
6
-
7
- it "should enumerate all documents including writes" do
8
- records = []
9
-
10
- database.commit(message: "Testing Enumeration") do |dataset|
11
- 5.times do |i|
12
- object = dataset.append("extra-#{i}")
13
- dataset.write("#{prefix}/extra-#{i}", object)
14
- end
15
-
16
- expect(dataset.exist?("#{prefix}/extra-0")).to be_truthy
17
-
18
- records = dataset.each(prefix).to_a
19
- end
20
-
21
- expect(records.count).to be 25
22
- end
23
-
24
- it "should enumerate all documents excluding deletes" do
25
- records = []
26
-
27
- database.commit(message: "Testing Enumeration") do |dataset|
28
- 5.times do |i|
29
- dataset.delete("#{prefix}/#{i}")
30
- end
31
-
32
- expect(dataset.exist?("#{prefix}/0")).to be_falsey
33
-
34
- records = dataset.each(prefix).to_a
35
- end
36
-
37
- expect(records.count).to be 15
38
- end
39
- end
@@ -1,39 +0,0 @@
1
-
2
- require_relative 'test_records'
3
-
4
- RSpec.describe Relaxo::Changeset do
5
- include_context "test records"
6
-
7
- it "should detect conflicts" do
8
- events = []
9
-
10
- alice = Fiber.new do
11
- database.commit(message: "Alice Data") do |changeset|
12
- events << :alice
13
-
14
- object = changeset.append("sample-data-1")
15
- changeset.write("conflict-path", object)
16
-
17
- Fiber.yield
18
- end
19
- end
20
-
21
- bob = Fiber.new do
22
- database.commit(message: "Bob Data") do |changeset|
23
- events << :bob
24
-
25
- object = changeset.append("sample-data-1")
26
- changeset.write("conflict-path", object)
27
-
28
- Fiber.yield
29
- end
30
- end
31
-
32
- alice.resume
33
- bob.resume
34
- alice.resume
35
- bob.resume
36
-
37
- expect(events).to be == [:alice, :bob, :bob]
38
- end
39
- end
@@ -1,103 +0,0 @@
1
-
2
- require 'relaxo'
3
-
4
- RSpec.describe Relaxo::Database do
5
- let(:database_path) {File.join(__dir__, 'test')}
6
-
7
- let(:database) {Relaxo.connect(database_path, test_key: "test_value")}
8
-
9
- let(:document_path) {'test/document.json'}
10
- let(:sample_json) {'[1, 2, 3]'}
11
-
12
- before(:each) {FileUtils.rm_rf(database_path)}
13
-
14
- it "should be initially empty" do
15
- expect(database).to be_empty
16
- end
17
-
18
- it "should not be empty with one document" do
19
- database.commit(message: "Create test document") do |dataset|
20
- oid = dataset.append(sample_json)
21
- dataset.write(document_path, oid)
22
- end
23
-
24
- expect(database).to_not be_empty
25
- end
26
-
27
- it "should have metadata" do
28
- expect(database[:test_key]).to be == "test_value"
29
- end
30
-
31
- it "should create a document" do
32
- database.commit(message: "Create test document") do |dataset|
33
- oid = dataset.append(sample_json)
34
- dataset.write(document_path, oid)
35
- end
36
-
37
- database.current do |dataset|
38
- expect(dataset[document_path].data).to be == sample_json
39
- end
40
- end
41
-
42
- it "should erase a document" do
43
- database.commit(message: "Create test document") do |dataset|
44
- oid = dataset.append(sample_json)
45
- dataset.write(document_path, oid)
46
- end
47
-
48
- database.commit(message: "Delete test document") do |dataset|
49
- dataset.delete(document_path)
50
- end
51
-
52
- database.current do |dataset|
53
- expect(dataset[document_path]).to be nil
54
- end
55
- end
56
-
57
- it "should create multiple documents" do
58
- database.commit(message: "Create first document") do |dataset|
59
- oid = dataset.append(sample_json)
60
- dataset.write(document_path, oid)
61
- end
62
-
63
- database.commit(message: "Create second document") do |dataset|
64
- oid = dataset.append(sample_json)
65
- dataset.write(document_path + '2', oid)
66
- end
67
-
68
- database.current do |dataset|
69
- expect(dataset[document_path].data).to be == sample_json
70
- expect(dataset[document_path + '2'].data).to be == sample_json
71
- end
72
- end
73
-
74
- it "can enumerate documents" do
75
- database.commit(message: "Create first document") do |dataset|
76
- oid = dataset.append(sample_json)
77
-
78
- 10.times do |id|
79
- dataset.write(document_path + "-#{id}", oid)
80
- end
81
- end
82
-
83
- database.current do |dataset|
84
- expect(dataset.each('test').count).to be == 10
85
- end
86
- end
87
-
88
- it "can enumerate commit history of a document" do
89
- 10.times do |id|
90
- database.commit(message: "revising the document #{id}") do |changeset|
91
- oid = changeset.append("revision \##{id} of this document")
92
- changeset.write('test/doot.txt', oid)
93
- end
94
- end
95
-
96
- database.commit(message: "unrelated commit") do |changeset|
97
- oid = changeset.append("unrelated document")
98
- changeset.write('test/unrelated.txt', oid)
99
- end
100
-
101
- expect(database.history('test/doot.txt').count).to be == 10
102
- end
103
- end
@@ -1,30 +0,0 @@
1
-
2
- require_relative 'test_records'
3
-
4
- RSpec.describe Relaxo::Dataset do
5
- include_context "test records"
6
-
7
- it "should enumerate all documents" do
8
- records = []
9
-
10
- database.current do |dataset|
11
- records = dataset.each(prefix).to_a
12
- end
13
-
14
- expect(records.count).to be 20
15
- end
16
- end
17
-
18
- RSpec.describe Relaxo::Changeset do
19
- include_context "test records"
20
-
21
- it "should enumerate all documents" do
22
- records = []
23
-
24
- database.commit(message: "Testing Enumeration") do |dataset|
25
- records = dataset.each(prefix).to_a
26
- end
27
-
28
- expect(records.count).to be 20
29
- end
30
- end
@@ -1,78 +0,0 @@
1
-
2
- require 'benchmark/ips' if ENV['BENCHMARK']
3
- require 'ruby-prof' if ENV['PROFILE']
4
- require 'flamegraph' if ENV['FLAMEGRAPH']
5
-
6
- RSpec.describe "Relaxo Performance" do
7
- let(:database_path) {File.join(__dir__, 'test')}
8
- let(:database) {Relaxo.connect(database_path)}
9
-
10
- if defined? Benchmark
11
- def benchmark(name = nil)
12
- Benchmark.ips do |benchmark|
13
- # Collect more data for benchmark:
14
- benchmark.time = 20
15
- benchmark.warmup = 10
16
-
17
- benchmark.report(name) do |i|
18
- yield i
19
- end
20
-
21
- benchmark.compare!
22
- end
23
- end
24
- elsif defined? RubyProf
25
- def benchmark(name)
26
- result = RubyProf.profile do
27
- yield 1000
28
- end
29
-
30
- #result.eliminate_methods!([/^((?!Utopia).)*$/])
31
- printer = RubyProf::FlatPrinter.new(result)
32
- printer.print($stderr, min_percent: 1.0)
33
-
34
- printer = RubyProf::GraphHtmlPrinter.new(result)
35
- filename = name.gsub('/', '_') + '.html'
36
- File.open(filename, "w") do |file|
37
- printer.print(file)
38
- end
39
- end
40
- elsif defined? Flamegraph
41
- def benchmark(name)
42
- filename = name.gsub('/', '_') + '.html'
43
- Flamegraph.generate(filename) do
44
- yield 1
45
- end
46
- end
47
- else
48
- def benchmark(name)
49
- yield 1
50
- end
51
- end
52
-
53
- before(:each) do
54
- FileUtils.rm_rf(database_path)
55
- end
56
-
57
- it "single transaction should be fast" do
58
- benchmark("single") do |iterations|
59
- database.commit(message: "Some Documents") do |dataset|
60
- iterations.times do |i|
61
- object = dataset.append("good-#{i}")
62
- dataset.write("#{i%100}/#{i}", object)
63
- end
64
- end
65
- end
66
- end
67
-
68
- it "multiple transactions should be fast" do
69
- benchmark("multiple") do |iterations|
70
- iterations.times do |i|
71
- database.commit(message: "Some Documents") do |dataset|
72
- object = dataset.append("good-#{i}")
73
- dataset.write("#{i%100}/#{i}", object)
74
- end
75
- end
76
- end
77
- end
78
- end
@@ -1,25 +0,0 @@
1
-
2
- require 'relaxo'
3
-
4
- RSpec.shared_context "test records" do
5
- let(:database_path) {File.join(__dir__, 'test')}
6
- let(:database) {Relaxo.connect(database_path)}
7
-
8
- let(:prefix) {"records"}
9
-
10
- before(:each) do
11
- FileUtils.rm_rf(database_path)
12
-
13
- database.commit(message: "Create Sample Data") do |dataset|
14
- 20.times do |i|
15
- object = dataset.append("good-#{i}")
16
- dataset.write("#{prefix}/#{i}", object)
17
- end
18
-
19
- 10.times do |i|
20
- object = dataset.append("bad-#{i}")
21
- dataset.write("#{prefix}/subdirectory/#{i}", object)
22
- end
23
- end
24
- end
25
- end
@@ -1,29 +0,0 @@
1
-
2
- if ENV['COVERAGE'] || ENV['TRAVIS']
3
- begin
4
- require 'simplecov'
5
-
6
- SimpleCov.start do
7
- add_filter "/spec/"
8
- end
9
-
10
- if ENV['TRAVIS']
11
- require 'coveralls'
12
- Coveralls.wear!
13
- end
14
- rescue LoadError
15
- warn "Could not load simplecov: #{$!}"
16
- end
17
- end
18
-
19
- require "bundler/setup"
20
- require "relaxo"
21
-
22
- RSpec.configure do |config|
23
- # Enable flags like --only-failures and --next-failure
24
- config.example_status_persistence_file_path = ".rspec_status"
25
-
26
- config.expect_with :rspec do |c|
27
- c.syntax = :expect
28
- end
29
- end