hoaxdb 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: c7768d5652b3e5c33b611b48f182cd748b587e9fba398ba0fe4cc86242111f95
4
+ data.tar.gz: 3a714dd387f5ab77dcd334201921f49e237ce2ea5c551e3fce8230353a9fe488
5
+ SHA512:
6
+ metadata.gz: 18040158b426bcf35355d21eebaf5e14389bc1e67d5f41e228e10664c593c9b4f4716ca2b94a5b6fab5b9b61516434ceeaaecc827b77e349038deaea8a799e39
7
+ data.tar.gz: 2aad20d569865209d388575050034185c1f08ad0a34c8d124ec73a74af23c020b1c5ae215aa9b0a8bd81c50b1055d992e06bcca6724d72d039a5cf1a7f9e5244
data/.gitignore ADDED
@@ -0,0 +1,11 @@
1
+ /.bundle/
2
+ /.yardoc
3
+ /_yardoc/
4
+ /coverage/
5
+ /doc/
6
+ /pkg/
7
+ /spec/reports/
8
+ /tmp/
9
+ lib/hash.rb
10
+ lib/data.db
11
+ lib/e.xml
data/.travis.yml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ sudo: false
3
+ language: ruby
4
+ cache: bundler
5
+ rvm:
6
+ - 2.4.4
7
+ before_install: gem install bundler -v 1.16.3
data/Gemfile ADDED
@@ -0,0 +1,6 @@
1
+ source "https://rubygems.org"
2
+
3
+ git_source(:github) {|repo_name| "https://github.com/#{repo_name}" }
4
+
5
+ # Specify your gem's dependencies in hashdb.gemspec
6
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,21 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2018 njaneambrose
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,67 @@
1
+ # Hoaxdb
2
+
3
+ Hoaxdb is a simple implementation of a database written in 100% pure ruby. It offers an interface to insert,update,query and delete records like any other databse but does not offer complex features of a database. You set specific data types for your fields and you can also have default values on top of that it validates the records you enter. In terms of queries you can choose specific fields, limits and also sort according to a specific field which can be ascending or descending. Queries are written using ruby hence no external knowledge is needed.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ ```ruby
10
+ gem 'hoaxdb'
11
+ ```
12
+
13
+ And then execute:
14
+
15
+ $ bundle
16
+
17
+ Or install it yourself as:
18
+
19
+ $ gem install hoaxdb
20
+
21
+ ## Usage
22
+
23
+ Using the hoaxdb is very simple once you have installed it, the following are the basics:
24
+ #### Creating a table
25
+
26
+ ```ruby
27
+ require 'hoaxdb'
28
+ db = Hoaxdb.new('products.db') #This reads an existing database or creates a database
29
+ db.create_table("products",{
30
+ "name"=>{"type"=>"String","default"=>""}, #setting default prevents nil values
31
+ "price"=>{"type"=>"Float","default"=>0.0}
32
+ })
33
+ ```
34
+
35
+ #### Inserting a record
36
+
37
+ ```ruby
38
+ products.insert({"name"=>"Iphone","price"=>100.67}) #insert a record
39
+ ```
40
+
41
+ #### Updating a record
42
+
43
+ ```ruby
44
+ products.update('this["name"].eql? "Iphone"',{"quantity"=>200})
45
+ ```
46
+
47
+ #### Selecting records
48
+
49
+ ```ruby
50
+ products.select_if('this["name"].eql? "Iphone"')
51
+ ```
52
+
53
+ #### Deleting records
54
+
55
+ ```ruby
56
+ products.del_if('this["name"].eql? "Iphone"')
57
+ ```
58
+
59
+ More information is available via rdoc when the gem is installed
60
+
61
+ ## Contributing
62
+
63
+ Bug reports and pull requests are welcome on GitHub at https://github.com/njaneambrose/hoaxdb.
64
+
65
+ ## License
66
+
67
+ The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
data/Rakefile ADDED
@@ -0,0 +1,10 @@
1
+ require "bundler/gem_tasks"
2
+ require "rake/testtask"
3
+
4
+ Rake::TestTask.new(:test) do |t|
5
+ t.libs << "test"
6
+ t.libs << "lib"
7
+ t.test_files = FileList["test/**/*_test.rb"]
8
+ end
9
+
10
+ task :default => :test
data/bin/console ADDED
@@ -0,0 +1,14 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require "bundler/setup"
4
+ require "hashdb"
5
+
6
+ # You can add fixtures and/or initialization code here to make experimenting
7
+ # with your gem easier. You can also use a different console, if you like.
8
+
9
+ # (If you use this, don't forget to add pry to your Gemfile!)
10
+ # require "pry"
11
+ # Pry.start
12
+
13
+ require "irb"
14
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
data/hoaxdb.gemspec ADDED
@@ -0,0 +1,29 @@
1
+
2
+ lib = File.expand_path("../lib", __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require "hoaxdb/version"
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "hoaxdb"
8
+ spec.version = Hoaxdb::VERSION
9
+ spec.authors = ["njaneambrose"]
10
+ spec.email = ["njaneambrose@gmail.com"]
11
+
12
+ spec.summary = %q{An implementation of a NoSQl database in pure ruby}
13
+ spec.description = %q{This is simple database based on a Hash that uses JSON for storage.It offers interfaces to create,update,delete and query records with a variety of options for queries}
14
+ spec.homepage = "https://github.com/njaneambrose/hoaxdb"
15
+ spec.license = "MIT"
16
+
17
+ # Specify which files should be added to the gem when it is released.
18
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
19
+ spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
20
+ `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
21
+ end
22
+ spec.bindir = "exe"
23
+ spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ["lib"]
25
+
26
+ spec.add_development_dependency "bundler", "~> 1.16"
27
+ spec.add_development_dependency "rake", "~> 10.0"
28
+ spec.add_development_dependency "minitest", "~> 5.0"
29
+ end
data/lib/hoaxdb.rb ADDED
@@ -0,0 +1,265 @@
1
+ require "hoaxdb/version"
2
+ require "hoaxdb/table"
3
+ require "zlib"
4
+ require "json"
5
+ require "thread"
6
+
7
+ module Hoaxdb
8
+ #=Introduction
9
+ #Hoaxdb is a small implementation of a database using a Hash and JSON for storage written in 100% pure ruby. It allows you to create, update, query and delete records.
10
+ #It also allows you to set specific data types for your fields, default values and validates input. In terms of queries you can set limits, select specific fields, sort by a field and also set a limit for the query.
11
+ #==Field rules
12
+ # Use string keys like ["products"] not [:products] due to JSON nature of storage
13
+ #==Data types
14
+ #The currently supported data types are
15
+ # Hash, DateTime, Date, Boolean, Float, Integer, Array & String
16
+ #==Saving your work
17
+ # #=> Never forget to call
18
+ # db.commit
19
+ # #=> to save the current changes
20
+ #==Querying rules:
21
+ # 1. "this" is used to refrence the current row e.g: this["price"] indicates the current row price
22
+ # 2. The syntax this[.. is used for queries if not * for all rows
23
+ # 3. For equality use .eql? not ==..
24
+ #check the querying section for examples
25
+ #==Updating rules:
26
+ # 1. All rules for querying above apply
27
+ # 2. To edit the current value use $ e.g an Array do: {"array"=>"$.push('value')"}, $ refrences the current value and must be called in a string as the first character
28
+ #check the updating section for examples
29
+ #
30
+ #====CAUTION: Do not use Hoaxdb for extreme loads due to speed
31
+ #
32
+ #==Creating a table
33
+ #Creating a table is very easy as first you need to connect to a database file then:
34
+ # require 'hoaxdb'
35
+ # db = Hoaxdb.new('products.db') #This reads an existing database or creates a database
36
+ # db.create_table("products",{
37
+ # "name"=>{"type"=>"String","default"=>""}, #setting default prevents nil values
38
+ # "price"=>{"type"=>"Float","default"=>0.0},
39
+ # "quantity"=>{"type"=>"Integer","default"=>100},
40
+ # "shipped"=>{"type"=>"Date"}
41
+ # })
42
+ #This creates a simple table with three fields
43
+ #==Inserting Records
44
+ #The next step is just a simple as the one above, this is a continuation from above:
45
+ # products = db.table("products") #this connects to the products table
46
+ # products.insert({"name"=>"Iphone","price"=>100.67,"shipped"=>Date.new(2018,6,30)}) #insert a record
47
+ # products.insert({"name"=>"Cable Tvs","price"=>390.67,"shipped"=>Date.new(2018,9,12),"quantity"=>400}) #insert another record
48
+ # p products.data #=> [{"name"=>"Iphone","price"=>100.67,"quantity"=>100,shipped=>"2018-6-30"},{"name"=>"Cable Tvs","price"=>390.67,"quantity"=>400,shipped=>"2018-9-12"}]
49
+ #==Querying records
50
+ # #=>select_if('condtion * for all',[array of the fields you want * for all],limit -1 for no limit,"sort field must be in the fields array",desc=true/false)
51
+ # #=> condition * indicates all rows
52
+ # products.select_if('*',["name","price"]) #=> [{"name"=>"Iphone","price"=>100.67},{"name"=>"Cable Tvs","price"=>390.67}]
53
+ # products.select_if('*',["name","price"],-1,"price") #=> [{"name"=>"Cable Tvs","price"=>390.67},{"name"=>"Iphone","price"=>100.67}]
54
+ # products.select_if('*',["name","price"],-1,"price",true) #=> [{"name"=>"Iphone","price"=>100.67},{"name"=>"Cable Tvs","price"=>390.67}]
55
+ # products.select_if('this["price"] > 300',['name','price']) #=> [{"name"=>"Cable Tvs","price"=>390.67}]
56
+ # products.max("price") #=> 390.67
57
+ # products.min("price") #=> 100.67
58
+ # products.avg("price") #=> 245.67
59
+ # products.sum("price") #=> 491.34
60
+ #==Updating records
61
+ # #=> table.update('condition * for all',{new values})
62
+ # #=> condition * indicates all rows
63
+ # #Incase of a 15% price increase in products do this to update
64
+ # products.update('*',{"price"=>"$ = this[\"price\"]*1.15"}) #=> Here you see how the $ symbol is used to update the existing value
65
+ # products.update('this["name"].eql? "Iphone"',{"quantity"=>200})
66
+ #==Deleting records
67
+ # #=> table.del_if('condition')
68
+ # products.del_if('this["name"].eql? "Iphone"')
69
+ # p products.data #=> [{"name"=>"Cable Tvs","price"=>390.67,"quantity"=>400,shipped=>"2018-9-12"}]
70
+ #==Adding fields
71
+ # products.add_column("remaining",{"type"=>"Integer","default"=>0})
72
+ # p products.base #=> [{"name"=>{"type"=>"String","default"=>""},"price"=>{"type"=>"Float","default"=>0.0},"quantity"=>{"type"=>Integer","default"=>100}, "shipped"=>{"type"=>"Date","default"=>nil},{"remaining"=>{"type"=>"Integer","default"=>0}}]
73
+ #==Deleting fields
74
+ # products.drop_column("remaining")
75
+ # p products.base #=> [{"name"=>{"type"=>"String","default"=>""},"price"=>{"type"=>"Float","default"=>0.0},"quantity"=>{"type"=>Integer","default"=>100}, "shipped"=>{"type"=>"Date","default"=>nil}]
76
+ class Db
77
+ def initialize(a)
78
+ @file = a
79
+ @a = {}
80
+ thr = Thread.new do
81
+ if File::exists? a
82
+ begin
83
+ Zlib::GzipReader.open(@file) do |gz|
84
+ x = JSON.parse(gz.read)
85
+ x.each{|k,v| @a.store(k,v)}
86
+ end
87
+ rescue Zlib::Error
88
+ raise "We could not read any keys from #{a} the database may be corrupted"
89
+ rescue JSON::ParserError
90
+ @a = {}
91
+ end
92
+ else
93
+ Zlib::GzipWriter.open(a).close
94
+ end
95
+ end
96
+ thr.join
97
+ end
98
+ def create_table(name,base)
99
+ if @a.has_key? name
100
+ raise "Table #{name} already exists"
101
+ end
102
+ tbase = name + "_base"
103
+ @a.store(tbase,{})
104
+ x = Table.new(name,base)
105
+ @a[tbase] = base
106
+ @a.store(name,[])
107
+ end
108
+ def drop_table(name)
109
+ tbase = name + "_base"
110
+ @a.delete(name)
111
+ @a.delete(tbase)
112
+ end
113
+ def table(name)
114
+ if @a.has_key? name and !name.match(/_base$/)
115
+ tbase = name + "_base"
116
+ x = Table.new(name,@a[tbase])
117
+ x.inject(@a[tbase],@a[name])
118
+ return x
119
+ elsif name.match(/_base$/)
120
+ raise "#{name} cannot be edited directly"
121
+ else
122
+ raise "Table #{name} does not exist"
123
+ end
124
+ end
125
+ #Lists all the tables in the database
126
+ def tables
127
+ tables = []
128
+ @a.keys.each{|e| if !e.match(/_base$/) then tables.push(e) end}
129
+ tables
130
+ end
131
+ #Deletes all tables in the database
132
+ def clear
133
+ @a.clear
134
+ end
135
+ #This saves chages made in the current session
136
+ # db.commit #saves the database
137
+ def commit
138
+ thr = Thread.new do
139
+ Zlib::GzipWriter.open(@file) do |gz|
140
+ gz.write(JSON.generate(@a))
141
+ end
142
+ end
143
+ thr.join
144
+ end
145
+ #This writes your database to a file in json format
146
+ # db >> "data.json"
147
+ def >>(a)
148
+ f = File.open(a,'w')
149
+ f << @a.to_json
150
+ f.close
151
+ end
152
+ #This returns the path to your database
153
+ # db.path
154
+ def path
155
+ @file
156
+ end
157
+ #This renames your database
158
+ # db.rename("hash.db")
159
+ def rename(a)
160
+ File.rename(@file,a)
161
+ @file = a
162
+ end
163
+ #This deletes your database
164
+ # db.del
165
+ def del
166
+ File.delete(@file)
167
+ end
168
+ #This returns JSON from selected table
169
+ # db.get_json("products")
170
+ def get_json(a)
171
+ x = {}
172
+ x.store(a,@a[a])
173
+ JSON.generate(x)
174
+ end
175
+ #This returns XML from a selected table
176
+ # db.get_xml("products")
177
+ def get_xml(a)
178
+ r = ""
179
+ if ishash(@a[a])
180
+ r = self.hash_to_xml(a,@a[a])
181
+ elsif isarray(@a[a])
182
+ r = self.array_to_xml(a,@a[a])
183
+ else
184
+ r = "<#{a}>#{@a[a]}</#{a}>"
185
+ end
186
+ r
187
+ end
188
+ #This offloads the hoaxdb to a file other than the current file and can also be used for backup pourposes
189
+ # db.offload("backup.db")
190
+ def offload(a)
191
+ Zlib::GzipWriter.open(a) do |gz|
192
+ gz.write(JSON.generate(@a))
193
+ end
194
+ end
195
+ #This reloads data from the file and discards unsaved changes
196
+ def reload
197
+ @a.clear
198
+ if File::exists? @file
199
+ begin
200
+ Zlib::GzipReader.open(@file) do |gz|
201
+ x = JSON.parse(gz.read)
202
+ x.each{|k,v| @a.store(k,v)}
203
+ end
204
+ rescue Zlib::Error
205
+ raise "We could not reload #{@file} it may be corrupted"
206
+ rescue JSON::ParserError
207
+ end
208
+ else
209
+ Zlib::GzipWriter.open(a).close
210
+ end
211
+ end
212
+ def isarray(e)
213
+ if e.class == Array
214
+ return true
215
+ else
216
+ return false
217
+ end
218
+ end
219
+ def ishash(e)
220
+ if e.class == Hash
221
+ return true
222
+ else
223
+ return false
224
+ end
225
+ end
226
+ def array_to_xml(k,a)
227
+ sub = "<#{k}>"
228
+ a.each{|o|
229
+ if isarray(o)
230
+ sub << array_to_xml("array",o);
231
+ elsif ishash(o)
232
+ sub << hash_to_xml("hash",o)
233
+ else
234
+ sub << "<s#{k}>#{o}</s#{k}>"
235
+ end
236
+ }
237
+ sub << "</#{k}>"
238
+ sub
239
+ end
240
+ def hash_to_xml(x,k)
241
+ sub = "<#{x}>"
242
+ k.each do |q,v|
243
+ if isarray(v)
244
+ sub << array_to_xml(q,v);
245
+ elsif ishash(v)
246
+ sub << "<#{q}>"
247
+ v.each{|t,b|
248
+ if isarray(b)
249
+ sub << array_to_xml(t,b)
250
+ elsif ishash(b)
251
+ sub << hash_to_xml(t,b);
252
+ else
253
+ sub << "<#{t}>#{b}</#{t}>"
254
+ end
255
+ }
256
+ sub << "</#{q}>"
257
+ else
258
+ sub << "<#{q}>#{v}</#{q}>"
259
+ end
260
+ end
261
+ sub << "</#{x}>"
262
+ sub
263
+ end
264
+ end
265
+ end
@@ -0,0 +1,297 @@
1
+ require 'date'
2
+
3
+ module Hoaxdb
4
+
5
+ class Query
6
+ def parse(query)
7
+ query.strip!
8
+ if query.match(/row\[|@data|@base|[^\>\<]=/)
9
+ raise "This syntax is not allowed for querying"
10
+ else
11
+ if query.match(/this\[/)
12
+ query = query.gsub(/this\[/,"row[")
13
+ elsif query.eql? "*"
14
+ query = "true"
15
+ else
16
+ raise "Unfamiliar query used"
17
+ end
18
+ end
19
+ query
20
+ end
21
+ def parse_complex(query)
22
+ query.strip!
23
+ if query.match(/@data|@base|;|row/)
24
+ raise "This syntax is not allowed for querying"
25
+ else
26
+ query = query.gsub(/this\[/,"row[")
27
+ end
28
+ query
29
+ end
30
+ end
31
+
32
+ class Table
33
+ def initialize(name,args)
34
+ @name = name
35
+ @base = {}
36
+ if !args.class.eql? Hash
37
+ raise "Incorrect base for your database"
38
+ else
39
+ args.each{|k,v|
40
+ if self.valid_type(v["type"])
41
+ self.validate(v)
42
+ @base.store(k,v)
43
+ else
44
+ raise "Invalid data type #{v}"
45
+ end
46
+ }
47
+ end
48
+ @data = []
49
+ end
50
+ def validate(v)
51
+ if v["type"].eql? 'Date' or v["type"].eql? 'DateTime' and v["default"]
52
+ v["default"] = "#{v["default"]}"
53
+ elsif v["type"].eql? 'Boolean' and !v["default"].nil?
54
+ if v["default"].class.eql? TrueClass or v["default"].class.eql? FalseClass
55
+ else
56
+ raise "Invalid default #{v["default"]} to #{v["type"]}"
57
+ end
58
+ else
59
+ if !v["default"].nil? and !v["default"].class.eql? eval(v["type"])
60
+ raise "Invalid default #{v["default"]} to #{v["type"]}"
61
+ end
62
+ end
63
+ if v["default"].nil? then v["default"] = nil end
64
+ end
65
+ def data
66
+ @data.clone
67
+ end
68
+ def base
69
+ @base.clone
70
+ end
71
+ def fields
72
+ @base.keys
73
+ end
74
+ def valid_type(type)
75
+ types = ['String','Integer','Float','DateTime','Date','Boolean','Array','Hash']
76
+ found = false
77
+ types.each{|e| if e.eql? type then found = true;break end}
78
+ found
79
+ end
80
+ def load(data)
81
+ data.each do |row|
82
+ row.each do |k,v|
83
+ if !v.nil?
84
+ if @base[k]['type'].eql? 'Date'
85
+ row[k] = Date.parse(v)
86
+ elsif @base[k]['type'].eql? 'DateTime'
87
+ row[k] = DateTime.parse(v)
88
+ elsif !@base[k]['type'].eql? 'String' and v.class.eql? String
89
+ row[k] = eval(v)
90
+ end
91
+ end
92
+ end
93
+ end
94
+ data
95
+ end
96
+ def load_hash(data)
97
+ data.each do |k,v|
98
+ if !v.nil?
99
+ if @base[k]['type'].eql? 'Date'
100
+ data[k] = Date.parse(v)
101
+ elsif @base[k]['type'].eql? 'DateTime'
102
+ data[k] = DateTime.parse(v)
103
+ elsif !@base[k]['type'].eql? 'String' and v.class.eql? String
104
+ data[k] = eval(v)
105
+ end
106
+ end
107
+ end
108
+ end
109
+ def dump_array(data)
110
+ data.each do |row|
111
+ row.each do |k,v|
112
+ if @base[k]['type'].eql? 'Date' or @base[k]['type'].eql? 'DateTime'
113
+ row[k] = "#{v}"
114
+ end
115
+ end
116
+ end
117
+ data
118
+ end
119
+ def dump_hash(data)
120
+ data.each do |k,v|
121
+ if @base[k]['type'].eql? 'Date' or @base[k]['type'].eql? 'DateTime'
122
+ data[k] = "#{v}"
123
+ end
124
+ end
125
+ data
126
+ end
127
+ def insert(data)
128
+ data.each{|k,v|
129
+ if @base[k]
130
+ if @base[k]['type'].eql? 'Boolean'
131
+ if v.class.eql? FalseClass or v.class.eql? TrueClass
132
+ correct = true
133
+ else
134
+ raise "#{v} is not a Boolean value"
135
+ end
136
+ elsif !v.class.eql? eval(@base[k]['type'])
137
+ raise "Incorrect data type for #{v} to #{@base[k]}"
138
+ end
139
+ else
140
+ raise "This field #{k} is not in #{@name} table"
141
+ end
142
+ }
143
+ data = self.dump_hash(data)
144
+ dt = {}
145
+ @base.each_key{|k|
146
+ if !data[k] then dt.store(k,@base[k]['default'])
147
+ else
148
+ dt.store(k,data[k])
149
+ end
150
+ }
151
+ @data.push(dt)
152
+ end
153
+ def alter_default(col,default)
154
+ if default.class.eql? eval(@base[col]['type'])
155
+ @base[col]['default'] = default
156
+ else
157
+ raise "#{default} is not of data type #{@base[col]['type']}"
158
+ end
159
+ end
160
+ def add_column(col,type)
161
+ if @base.has_key? col
162
+ raise "Column #{col} already exists"
163
+ end
164
+ if valid_type(type['type'])
165
+ self.validate(type)
166
+ @base.store(col,type)
167
+ @data.each do |row|
168
+ row.store(col,@base[col]['default'])
169
+ end
170
+ end
171
+ end
172
+ def update(query,values)
173
+ @data = self.load(@data)
174
+ @data.each{|row|
175
+ if eval(Query.new.parse(query))
176
+ values.each{|k,v|
177
+ if v.class.eql? String and v[0].eql? "$"
178
+ f = Query.new.parse_complex(v.delete("$"))
179
+ temp = (row[k]).clone
180
+ eval("temp#{f}")
181
+ if temp.class.eql? eval(@base[k]['type'])
182
+ row[k] = temp
183
+ else
184
+ raise "Invalid data #{temp} to #{@base[k]['type']}"
185
+ end
186
+ else
187
+ if v.class.eql? eval(@base[k]['type'])
188
+ row[k] = v
189
+ else
190
+ raise "Invalid data #{v} to #{@base[k]['type']}"
191
+ end
192
+ end
193
+ }
194
+ end
195
+ }
196
+ @data = self.dump_array(@data)
197
+ end
198
+ def drop_column(col)
199
+ @base.delete(col)
200
+ @data.each do |row|
201
+ row.delete(col)
202
+ end
203
+ end
204
+ def rename_column(col,ncol)
205
+ if @base.has_key? col
206
+ val = @base[col]
207
+ @base.delete(col)
208
+ @base.store(ncol,val)
209
+ @data.each do |row|
210
+ val = row[col]
211
+ row.delete(col)
212
+ row.store(ncol,val)
213
+ end
214
+ else
215
+ raise "Column #{col} does not exist"
216
+ end
217
+ end
218
+ def del_if(query)
219
+ rm = []
220
+ @data = self.load(@data)
221
+ d = Query.new.parse(query)
222
+ for x in 0...@data.size
223
+ row = @data[x]
224
+ if eval(d)
225
+ rm.push(x)
226
+ end
227
+ end
228
+ rm.reverse.each{|o| @data.delete_at(o)}
229
+ @data = self.dump_array(@data)
230
+ end
231
+ def max(k)
232
+ if !@base[k]['type'].eql? 'Array' or !@base[k]['type'].eql? 'Hash'
233
+ max = @data[0][k] || 0
234
+ @data.each{|row| if row[k] > max then max = row[k] end}
235
+ return max
236
+ end
237
+ end
238
+ def min(k)
239
+ if !@base[k]['type'].eql? 'Array' or !@base[k].eql? 'Hash'
240
+ min = @data[0][k] || 0
241
+ @data.each{|row| if row[k] < min then min = row[k] end}
242
+ return min
243
+ end
244
+ end
245
+ def avg(k)
246
+ if @base[k]['type'].eql? 'Integer' or @base[k]['type'].eql? 'Float'
247
+ sum = 0
248
+ @data.each{|row| sum += row[k]}
249
+ return (sum.to_f/@data.size)
250
+ else
251
+ raise "column #{k} is not of type Float or Integer"
252
+ end
253
+ end
254
+ def sum(k)
255
+ if @base[k]['type'].eql? 'Integer' or @base[k]['type'].eql? 'Float'
256
+ sum = 0
257
+ @data.each{|row| sum += row[k]}
258
+ return sum
259
+ else
260
+ raise "column #{k} is not of type Float or Integer"
261
+ end
262
+ end
263
+ def select_if(query,cols,limit=-1,sort=false,desc=false)
264
+ result = []
265
+ @data = self.load(@data)
266
+ x = Query.new.parse(query)
267
+ res = @data.select{|row| eval(x)}
268
+ g = false
269
+ if cols[0].strip.eql? "*" then g = true end
270
+ res.each do |row|
271
+ col = {}
272
+ if !g
273
+ cols.each do |e|
274
+ col.store(e,row[e])
275
+ end
276
+ result.push(col)
277
+ else
278
+ result.push(row)
279
+ end
280
+ end
281
+ if sort
282
+ result = result.sort{|a,b| a[sort] <=> b[sort]}
283
+ end
284
+ if desc
285
+ result.reverse!
286
+ end
287
+ if limit != -1 then result = result[0...limit] end
288
+ @data = self.dump_array(@data)
289
+ result = self.load(result)
290
+ result
291
+ end
292
+ def inject(base,data) #:nodoc:#
293
+ @base = base
294
+ @data = data
295
+ end
296
+ end
297
+ end
@@ -0,0 +1,3 @@
1
+ module Hoaxdb
2
+ VERSION = "0.1.0"
3
+ end
metadata ADDED
@@ -0,0 +1,99 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: hoaxdb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - njaneambrose
8
+ autorequire:
9
+ bindir: exe
10
+ cert_chain: []
11
+ date: 2019-04-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bundler
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: '1.16'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: '1.16'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - "~>"
32
+ - !ruby/object:Gem::Version
33
+ version: '10.0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - "~>"
39
+ - !ruby/object:Gem::Version
40
+ version: '10.0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: minitest
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - "~>"
46
+ - !ruby/object:Gem::Version
47
+ version: '5.0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - "~>"
53
+ - !ruby/object:Gem::Version
54
+ version: '5.0'
55
+ description: This is simple database based on a Hash that uses JSON for storage.It
56
+ offers interfaces to create,update,delete and query records with a variety of options
57
+ for queries
58
+ email:
59
+ - njaneambrose@gmail.com
60
+ executables: []
61
+ extensions: []
62
+ extra_rdoc_files: []
63
+ files:
64
+ - ".gitignore"
65
+ - ".travis.yml"
66
+ - Gemfile
67
+ - LICENSE.txt
68
+ - README.md
69
+ - Rakefile
70
+ - bin/console
71
+ - bin/setup
72
+ - hoaxdb.gemspec
73
+ - lib/hoaxdb.rb
74
+ - lib/hoaxdb/table.rb
75
+ - lib/hoaxdb/version.rb
76
+ homepage: https://github.com/njaneambrose/hoaxdb
77
+ licenses:
78
+ - MIT
79
+ metadata: {}
80
+ post_install_message:
81
+ rdoc_options: []
82
+ require_paths:
83
+ - lib
84
+ required_ruby_version: !ruby/object:Gem::Requirement
85
+ requirements:
86
+ - - ">="
87
+ - !ruby/object:Gem::Version
88
+ version: '0'
89
+ required_rubygems_version: !ruby/object:Gem::Requirement
90
+ requirements:
91
+ - - ">="
92
+ - !ruby/object:Gem::Version
93
+ version: '0'
94
+ requirements: []
95
+ rubygems_version: 3.0.1
96
+ signing_key:
97
+ specification_version: 4
98
+ summary: An implementation of a NoSQl database in pure ruby
99
+ test_files: []