rndb 0.1.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.
@@ -0,0 +1,116 @@
1
+ # frozen_string_literal: true
2
+
3
+ module RnDB
4
+ class Thicket
5
+ include Enumerable
6
+
7
+ # A sorted array of disjoint ranges, optionally initialised with a default range.
8
+ def initialize(range=nil)
9
+ @ids = []
10
+ @ids << Slice.new(range.min, range.max) unless range.nil?
11
+ end
12
+
13
+ # Append a range, throwing an error if it overlaps with an existing range,
14
+ # and keeping the resulting array sorted so we can iterate over values.
15
+ def <<(range)
16
+ slice = Slice.new(range.min, range.max)
17
+ raise "slices in thicket must be disjoint" unless @ids.all? { |id| (id & slice).nil? }
18
+ @ids << Slice.new(range.min, range.max)
19
+ @ids.sort!
20
+ self
21
+ end
22
+
23
+ # Intersect two Thickets, useful when performing queries.
24
+ def &(other)
25
+ retval = self.class.new
26
+ slices.each do |slice|
27
+ other.slices.each do |other_slice|
28
+ next unless (intersection = slice & other_slice)
29
+ retval << intersection
30
+ end
31
+ end
32
+ retval
33
+ end
34
+
35
+ # Merge two Thickets, which we need during migrations.
36
+ def |(other)
37
+ retval = self.class.new
38
+ slices.each { |slice| retval << slice }
39
+ other.slices.each { |slice| retval << slice }
40
+ retval
41
+ end
42
+
43
+ # Subdivide a Thicket with a probability range, also used for migrations.
44
+ def *(other)
45
+ slices.each_with_object(self.class.new) do |slice, thicket|
46
+ min = slice.min + (slice.count * other.min).round
47
+ max = slice.min + (slice.count * other.max).round - 1
48
+ thicket << (min..max) unless max < min
49
+ end
50
+ end
51
+
52
+ # Sum the counts of the Slices in the Thicket.
53
+ def count
54
+ @ids.map(&:count).sum
55
+ end
56
+
57
+ # Return the ID corresponding to the supplied index.
58
+ def [](index)
59
+ index += count while index.negative?
60
+ @ids.each do |slice|
61
+ return index + slice.min if index < slice.count
62
+ index -= slice.count
63
+ end
64
+ nil
65
+ end
66
+
67
+ # Return the index corresponding to the supplied ID.
68
+ def index(id)
69
+ start = 0
70
+ @ids.each do |slice|
71
+ return start + id - slice.min if slice.include?(id)
72
+ start += slice.count
73
+ end
74
+ nil
75
+ end
76
+
77
+ # Test whether the specified ID exists in this Thicket.
78
+ def include?(id)
79
+ @ids.any? { |slice| slice.include?(id) }
80
+ end
81
+
82
+ # Implemented to be consistent with #first, which we get by magic.
83
+ def last
84
+ self[-1] unless count.zero?
85
+ end
86
+
87
+ # Iterate over each slice in the Thicket in turn.
88
+ def each(&block)
89
+ @ids.each { |slice| slice.each(&block) }
90
+ end
91
+
92
+ # Return a Thicket that contains a sampling of IDs.
93
+ def sample(limit=1, prng=Random)
94
+ ids = Set.new
95
+ limit = [limit, count].min
96
+ ids << self[prng.rand(count)] while ids.count < limit
97
+ ids.sort.each_with_object(self.class.new) do |id, thicket|
98
+ thicket << Slice.new(id, id)
99
+ end
100
+ end
101
+
102
+ # We display the internal slices.
103
+ def to_s
104
+ slices.to_s
105
+ end
106
+
107
+ alias min first
108
+ alias max last
109
+
110
+ protected
111
+
112
+ def slices
113
+ @ids
114
+ end
115
+ end
116
+ end
data/rndb.gemspec ADDED
@@ -0,0 +1,91 @@
1
+ # Generated by juwelier
2
+ # DO NOT EDIT THIS FILE DIRECTLY
3
+ # Instead, edit Juwelier::Tasks in Rakefile, and run 'rake gemspec'
4
+ # -*- encoding: utf-8 -*-
5
+ # stub: rndb 0.1.1 ruby lib
6
+
7
+ Gem::Specification.new do |s|
8
+ s.name = "rndb".freeze
9
+ s.version = "0.1.1"
10
+
11
+ s.required_rubygems_version = Gem::Requirement.new(">= 0".freeze) if s.respond_to? :required_rubygems_version=
12
+ s.require_paths = ["lib".freeze]
13
+ s.authors = ["Lloyd Kranzky".freeze]
14
+ s.date = "2021-02-25"
15
+ s.description = "".freeze
16
+ s.email = "lloyd@kranzky.com".freeze
17
+ s.extra_rdoc_files = [
18
+ "LICENSE",
19
+ "README.md"
20
+ ]
21
+ s.files = [
22
+ ".document",
23
+ ".editorconfig",
24
+ ".github/workflows/ship.yml",
25
+ ".github/workflows/test.yml",
26
+ ".rdoc_options",
27
+ ".rgignore",
28
+ ".rspec",
29
+ ".rubocop.yml",
30
+ ".ruby-gemset",
31
+ ".ruby-version",
32
+ "Gemfile",
33
+ "Gemfile.lock",
34
+ "LICENSE",
35
+ "README.md",
36
+ "Rakefile",
37
+ "VERSION",
38
+ "lib/rndb.rb",
39
+ "lib/rndb/database.rb",
40
+ "lib/rndb/query.rb",
41
+ "lib/rndb/slice.rb",
42
+ "lib/rndb/table.rb",
43
+ "lib/rndb/thicket.rb",
44
+ "rndb.gemspec",
45
+ "spec/rn_db/database_spec.rb",
46
+ "spec/rn_db/query_spec.rb",
47
+ "spec/rn_db/slice_spec.rb",
48
+ "spec/rn_db/table_spec.rb",
49
+ "spec/rn_db/thicket_spec.rb",
50
+ "spec/spec_helper.rb"
51
+ ]
52
+ s.homepage = "https://github.com/kranzky/rndb".freeze
53
+ s.licenses = ["Unlicense".freeze]
54
+ s.required_ruby_version = Gem::Requirement.new(">= 2.1".freeze)
55
+ s.rubygems_version = "2.7.6.2".freeze
56
+ s.summary = "RnDB is an procedurally-generated mock database.".freeze
57
+
58
+ if s.respond_to? :specification_version then
59
+ s.specification_version = 4
60
+
61
+ if Gem::Version.new(Gem::VERSION) >= Gem::Version.new('1.2.0') then
62
+ s.add_runtime_dependency(%q<rdoc>.freeze, ["~> 6.3"])
63
+ s.add_runtime_dependency(%q<yard>.freeze, ["~> 0.9"])
64
+ s.add_development_dependency(%q<bundler>.freeze, ["~> 1.17"])
65
+ s.add_development_dependency(%q<gemfile_updater>.freeze, ["~> 0.1"])
66
+ s.add_development_dependency(%q<juwelier>.freeze, ["~> 2.4"])
67
+ s.add_development_dependency(%q<byebug>.freeze, ["~> 11.1"])
68
+ s.add_development_dependency(%q<crypt-isaac>.freeze, ["~> 1.2"])
69
+ s.add_development_dependency(%q<faker>.freeze, ["~> 2.16"])
70
+ else
71
+ s.add_dependency(%q<rdoc>.freeze, ["~> 6.3"])
72
+ s.add_dependency(%q<yard>.freeze, ["~> 0.9"])
73
+ s.add_dependency(%q<bundler>.freeze, ["~> 1.17"])
74
+ s.add_dependency(%q<gemfile_updater>.freeze, ["~> 0.1"])
75
+ s.add_dependency(%q<juwelier>.freeze, ["~> 2.4"])
76
+ s.add_dependency(%q<byebug>.freeze, ["~> 11.1"])
77
+ s.add_dependency(%q<crypt-isaac>.freeze, ["~> 1.2"])
78
+ s.add_dependency(%q<faker>.freeze, ["~> 2.16"])
79
+ end
80
+ else
81
+ s.add_dependency(%q<rdoc>.freeze, ["~> 6.3"])
82
+ s.add_dependency(%q<yard>.freeze, ["~> 0.9"])
83
+ s.add_dependency(%q<bundler>.freeze, ["~> 1.17"])
84
+ s.add_dependency(%q<gemfile_updater>.freeze, ["~> 0.1"])
85
+ s.add_dependency(%q<juwelier>.freeze, ["~> 2.4"])
86
+ s.add_dependency(%q<byebug>.freeze, ["~> 11.1"])
87
+ s.add_dependency(%q<crypt-isaac>.freeze, ["~> 1.2"])
88
+ s.add_dependency(%q<faker>.freeze, ["~> 2.16"])
89
+ end
90
+ end
91
+
@@ -0,0 +1,57 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe RnDB::Database do
4
+ before(:all) do # rubocop:disable RSpec/BeforeAfterAll
5
+ described_class.new(137).add_table(Ball, 1_000_000)
6
+ end
7
+
8
+ it "can be counted" do
9
+ expect(Ball.count).to eq(1_000_000)
10
+ end
11
+
12
+ it "has a first element" do
13
+ expect(Ball.first.id).to eq(0)
14
+ end
15
+
16
+ it "has a last element" do
17
+ expect(Ball.last.id).to eq(999_999)
18
+ end
19
+
20
+ it "can find things" do
21
+ ball = Ball.find { |b| b.location =~ /island/i }
22
+ expect(ball.location).to match(/Island/)
23
+ end
24
+
25
+ it "can take random samples" do
26
+ ids = Ball.sample(10).pluck(:id)
27
+ expect(ids.sort.uniq.size).to eq(10)
28
+ end
29
+
30
+ context "when running a query" do
31
+ let(:query) do
32
+ Ball.where(colour: [:red, :blue], material: :wood)
33
+ end
34
+
35
+ it "can be counted" do
36
+ expect(query.count).to eq(240_000)
37
+ end
38
+
39
+ it "has a first element" do
40
+ expect(query.first.id).to eq(5400)
41
+ end
42
+
43
+ it "has a last element" do
44
+ expect(query.last.id).to eq(905_499)
45
+ end
46
+
47
+ it "can find things" do
48
+ ball = query.find { |b| !b.transparent }
49
+ expect(ball.transparent).to be(false)
50
+ end
51
+
52
+ it "can take random samples" do
53
+ ids = query.sample(10).pluck(:id)
54
+ expect(ids.sort.uniq.size).to eq(10)
55
+ end
56
+ end
57
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe RnDB::Query do
4
+ context 'when no tests have been written' do
5
+ it 'displays "not yet implemented"'
6
+ end
7
+ end
@@ -0,0 +1,20 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe RnDB::Slice do
4
+ it "has a count" do
5
+ slice = described_class.new(5, 13)
6
+ expect(slice.count).to eq(slice.to_a.length)
7
+ end
8
+
9
+ it "can find the intersection of two slices" do
10
+ left = described_class.new(5, 13)
11
+ right = described_class.new(10, 17)
12
+ expect(left & right).to eq(described_class.new(10, 13))
13
+ end
14
+
15
+ it "returns nil if the intersection is empty" do
16
+ left = described_class.new(5, 10)
17
+ right = described_class.new(12, 17)
18
+ expect(left & right).to be_nil
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe RnDB::Table do
4
+ context 'when no tests have been written' do
5
+ it 'displays "not yet implemented"'
6
+ end
7
+ end
@@ -0,0 +1,53 @@
1
+ # frozen_string_literal: true
2
+
3
+ describe RnDB::Thicket do
4
+ it "is empty by default" do
5
+ thicket = described_class.new
6
+ expect(thicket.count).to be_zero
7
+ end
8
+
9
+ it "can be initialized with a value" do
10
+ thicket = described_class.new(2..21)
11
+ expect(thicket.count).to eq((2..21).size)
12
+ end
13
+
14
+ it "can contain multiple disjoint slices" do
15
+ thicket = described_class.new
16
+ thicket << (11..16)
17
+ thicket << (4..9)
18
+ expect(thicket.to_a).to eq((4..9).to_a | (11..16).to_a)
19
+ end
20
+
21
+ it "can contain large values" do
22
+ thicket = described_class.new(7..1e15)
23
+ expect((thicket.min..thicket.max)).to eq((7..1e15.to_i))
24
+ end
25
+
26
+ it "can return the id by index" do
27
+ thicket = described_class.new
28
+ thicket << (11..16)
29
+ thicket << (4..9)
30
+ expect(thicket[7]).to eq(12)
31
+ end
32
+
33
+ it "returns a nil value when the index is out of range" do
34
+ thicket = described_class.new
35
+ thicket << (11..16)
36
+ thicket << (4..9)
37
+ expect(thicket[16]).to be_nil
38
+ end
39
+
40
+ it "can get the index of an id" do
41
+ thicket = described_class.new
42
+ thicket << (11..16)
43
+ thicket << (4..9)
44
+ expect(thicket.index(12)).to eq(7)
45
+ end
46
+
47
+ it "returns a nil index when the id does not exist" do
48
+ thicket = described_class.new
49
+ thicket << (11..16)
50
+ thicket << (4..9)
51
+ expect(thicket.index(10)).to be_nil
52
+ end
53
+ end
@@ -0,0 +1,46 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'simplecov'
4
+ require 'coveralls'
5
+ SimpleCov.start do
6
+ add_filter 'spec'
7
+ end
8
+ Coveralls.wear!
9
+
10
+ require 'faker'
11
+ require_relative '../lib/rndb'
12
+
13
+ class Ball < RnDB::Table
14
+ column :colour, {
15
+ red: 0.3,
16
+ green: 0.1,
17
+ brown: 0.01,
18
+ blue: 0.5,
19
+ orange: 0.09
20
+ }
21
+ column :transparent, {
22
+ true => 0.1,
23
+ false => 0.9
24
+ }
25
+ column :weight, {
26
+ light: 0.3,
27
+ medium: 0.6,
28
+ heavy: 0.1
29
+ }, lambda { |value|
30
+ lookup = {
31
+ light: (0.1..3.0),
32
+ medium: (3.0..6.0),
33
+ heavy: (6.0..9.9)
34
+ }
35
+ self.rand(lookup[value])
36
+ }
37
+ column :material, {
38
+ leather: 0.2,
39
+ steel: 0.4,
40
+ wood: 0.3,
41
+ fluff: 0.1
42
+ }
43
+ column :name, -> { Faker::Games::Pokemon.name }
44
+ column :location, -> { Faker::Games::Pokemon.location }
45
+ column :move, -> { Faker::Games::Pokemon.move }
46
+ end
metadata ADDED
@@ -0,0 +1,186 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rndb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.1
5
+ platform: ruby
6
+ authors:
7
+ - Lloyd Kranzky
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2021-02-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: rdoc
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '6.3'
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '6.3'
27
+ - !ruby/object:Gem::Dependency
28
+ name: yard
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '0.9'
34
+ type: :runtime
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '0.9'
41
+ - !ruby/object:Gem::Dependency
42
+ name: bundler
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '1.17'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '1.17'
55
+ - !ruby/object:Gem::Dependency
56
+ name: gemfile_updater
57
+ requirement: !ruby/object:Gem::Requirement
58
+ requirements:
59
+ - - "~>"
60
+ - !ruby/object:Gem::Version
61
+ version: '0.1'
62
+ type: :development
63
+ prerelease: false
64
+ version_requirements: !ruby/object:Gem::Requirement
65
+ requirements:
66
+ - - "~>"
67
+ - !ruby/object:Gem::Version
68
+ version: '0.1'
69
+ - !ruby/object:Gem::Dependency
70
+ name: juwelier
71
+ requirement: !ruby/object:Gem::Requirement
72
+ requirements:
73
+ - - "~>"
74
+ - !ruby/object:Gem::Version
75
+ version: '2.4'
76
+ type: :development
77
+ prerelease: false
78
+ version_requirements: !ruby/object:Gem::Requirement
79
+ requirements:
80
+ - - "~>"
81
+ - !ruby/object:Gem::Version
82
+ version: '2.4'
83
+ - !ruby/object:Gem::Dependency
84
+ name: byebug
85
+ requirement: !ruby/object:Gem::Requirement
86
+ requirements:
87
+ - - "~>"
88
+ - !ruby/object:Gem::Version
89
+ version: '11.1'
90
+ type: :development
91
+ prerelease: false
92
+ version_requirements: !ruby/object:Gem::Requirement
93
+ requirements:
94
+ - - "~>"
95
+ - !ruby/object:Gem::Version
96
+ version: '11.1'
97
+ - !ruby/object:Gem::Dependency
98
+ name: crypt-isaac
99
+ requirement: !ruby/object:Gem::Requirement
100
+ requirements:
101
+ - - "~>"
102
+ - !ruby/object:Gem::Version
103
+ version: '1.2'
104
+ type: :development
105
+ prerelease: false
106
+ version_requirements: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - "~>"
109
+ - !ruby/object:Gem::Version
110
+ version: '1.2'
111
+ - !ruby/object:Gem::Dependency
112
+ name: faker
113
+ requirement: !ruby/object:Gem::Requirement
114
+ requirements:
115
+ - - "~>"
116
+ - !ruby/object:Gem::Version
117
+ version: '2.16'
118
+ type: :development
119
+ prerelease: false
120
+ version_requirements: !ruby/object:Gem::Requirement
121
+ requirements:
122
+ - - "~>"
123
+ - !ruby/object:Gem::Version
124
+ version: '2.16'
125
+ description: ''
126
+ email: lloyd@kranzky.com
127
+ executables: []
128
+ extensions: []
129
+ extra_rdoc_files:
130
+ - LICENSE
131
+ - README.md
132
+ files:
133
+ - ".document"
134
+ - ".editorconfig"
135
+ - ".github/workflows/ship.yml"
136
+ - ".github/workflows/test.yml"
137
+ - ".rdoc_options"
138
+ - ".rgignore"
139
+ - ".rspec"
140
+ - ".rubocop.yml"
141
+ - ".ruby-gemset"
142
+ - ".ruby-version"
143
+ - Gemfile
144
+ - Gemfile.lock
145
+ - LICENSE
146
+ - README.md
147
+ - Rakefile
148
+ - VERSION
149
+ - lib/rndb.rb
150
+ - lib/rndb/database.rb
151
+ - lib/rndb/query.rb
152
+ - lib/rndb/slice.rb
153
+ - lib/rndb/table.rb
154
+ - lib/rndb/thicket.rb
155
+ - rndb.gemspec
156
+ - spec/rn_db/database_spec.rb
157
+ - spec/rn_db/query_spec.rb
158
+ - spec/rn_db/slice_spec.rb
159
+ - spec/rn_db/table_spec.rb
160
+ - spec/rn_db/thicket_spec.rb
161
+ - spec/spec_helper.rb
162
+ homepage: https://github.com/kranzky/rndb
163
+ licenses:
164
+ - Unlicense
165
+ metadata: {}
166
+ post_install_message:
167
+ rdoc_options: []
168
+ require_paths:
169
+ - lib
170
+ required_ruby_version: !ruby/object:Gem::Requirement
171
+ requirements:
172
+ - - ">="
173
+ - !ruby/object:Gem::Version
174
+ version: '2.1'
175
+ required_rubygems_version: !ruby/object:Gem::Requirement
176
+ requirements:
177
+ - - ">="
178
+ - !ruby/object:Gem::Version
179
+ version: '0'
180
+ requirements: []
181
+ rubyforge_project:
182
+ rubygems_version: 2.7.6.2
183
+ signing_key:
184
+ specification_version: 4
185
+ summary: RnDB is an procedurally-generated mock database.
186
+ test_files: []