jsondb 0.0.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.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: f12ca944e1c7a4a054b787da398a96998aa78f37
4
+ data.tar.gz: 29b3f64e40cfd3cf4355b3fe3e9ce255105c7b86
5
+ SHA512:
6
+ metadata.gz: e01e62d7f226e0c81cd006294f54b5b628ba830d2fed14c1870eff0fd9484497817900e11ee4087bca5449131b9a613eeab385d5c75b73d83c3380774888cd15
7
+ data.tar.gz: 6a7324ba2fffb7c7cc5e9232dbe247b5e5e8bf30c4fba2daf81212161a7295ddb843e443fe89c0cafbe2b5223bb4a94bedd2696645523dcdb7c38b89b4c025d6
data/lib/jsondb.rb ADDED
@@ -0,0 +1,11 @@
1
+ #it depends on json_pure
2
+ require 'json/pure'
3
+
4
+ this_dir = File.dirname(__FILE__)
5
+ require File.join(this_dir, 'jsondb/file_ops')
6
+ require File.join(this_dir, 'jsondb/db')
7
+ require File.join(this_dir, 'jsondb/validation')
8
+ require File.join(this_dir, 'jsondb/table')
9
+ require File.join(this_dir, 'jsondb/field')
10
+ require File.join(this_dir, 'jsondb/record')
11
+ require File.join(this_dir, 'jsondb/resultset')
data/lib/jsondb/db.rb ADDED
@@ -0,0 +1,56 @@
1
+ #db.rb
2
+ class Db
3
+
4
+ attr_reader :root, :tables, :writeable
5
+
6
+ def initialize(root, writeable = true)
7
+ @root = root
8
+ @writeable = writeable
9
+ @file = FileOps.new(@root, '_db.json', 'db', @writeable)
10
+ load_tables
11
+ end
12
+
13
+ def table(name)
14
+ @tables[name] ||= Table.new(name, @root, Time.now.to_i, @writeable)
15
+ end
16
+
17
+ def table_add(name)
18
+ if @tables.keys.include?(name)
19
+ raise "Table '#{name}' already defined."
20
+ else
21
+ @tables[name] = Table.new(name, @root, Time.now.to_i, @writeable)
22
+ end
23
+ end
24
+
25
+ def table_drop(name)
26
+ @tables[name].drop
27
+ @tables.delete(name)
28
+ end
29
+
30
+ def table_names
31
+ @tables.keys
32
+ end
33
+
34
+ def persist
35
+ @file.contents['tables'] = {}
36
+ @tables.each do |name, table|
37
+ if table.persisted == false
38
+ table.updated_at = Time.now.to_i
39
+ table.persist
40
+ end
41
+ @file.contents['tables'][name] = { "name" => name, "updated_at" => table.updated_at }
42
+ end
43
+ return @file.save
44
+ end
45
+
46
+ private
47
+
48
+ def load_tables
49
+ @tables_in_file = @file.contents['tables'] || Hash.new({})
50
+ @tables = Hash.new
51
+ @tables_in_file.each do |name, values|
52
+ @tables[name] = Table.new(name, @root, values['updated_at'], @writeable)
53
+ end
54
+ end
55
+
56
+ end
@@ -0,0 +1,47 @@
1
+ class Field
2
+
3
+ include Validation
4
+
5
+ attr_reader :name, :type, :nullable, :default
6
+
7
+ def initialize(name)
8
+ allowed_name?(name)
9
+
10
+ @name = name
11
+ @nullable = true
12
+ @default = nil
13
+ @type = "String"
14
+ end
15
+
16
+ def type=(_class)
17
+ @type = allowed?(_class)
18
+ end
19
+
20
+ def type
21
+ @type
22
+ end
23
+
24
+ def nullable=(value)
25
+ @nullable = validate_type("Bool", value)
26
+ end
27
+
28
+ def nullable
29
+ @nullable
30
+ end
31
+
32
+ def default=(value)
33
+ @default = validate_type(@type, value)
34
+ end
35
+
36
+ def default
37
+ @default
38
+ end
39
+
40
+ def to_hash
41
+ data = Hash.new
42
+ data['type'] = @type
43
+ data['nullable'] = @nullable
44
+ data['default'] = @default
45
+ data
46
+ end
47
+ end
@@ -0,0 +1,71 @@
1
+ class FileOps
2
+
3
+ attr_reader :writeable, :filename, :folder, :raw, :new_file
4
+
5
+ def initialize(folder, filename, filetype, writeable = true)
6
+ @folder = folder
7
+ @writeable = writeable
8
+ @filename = File.join(@folder, filename)
9
+ @filetype = filetype
10
+ if exists?(@folder)
11
+ load
12
+ else
13
+ raise "Folder '#{@folder}' does not exists."
14
+ end
15
+ end
16
+
17
+ def contents=(contents)
18
+ @contents = contents
19
+ end
20
+
21
+ def contents
22
+ @contents
23
+ end
24
+
25
+ def save
26
+ return false if @writeable == false
27
+ @file = File.open(@filename, 'w')
28
+ @raw = JSON.pretty_generate(@contents)
29
+ @file.write(@raw)
30
+ @file.close
31
+ @new_file = false
32
+ return true
33
+ end
34
+
35
+ def destroy
36
+ File.delete(@filename) if exists?(@filename)
37
+ end
38
+
39
+ def close
40
+ @file.close if @file
41
+ end
42
+
43
+ private
44
+
45
+ def exists?(file)
46
+ File.exists?(File.expand_path(file))
47
+ end
48
+
49
+ def load
50
+ @new_file = !exists?(@filename)
51
+ if exists?(@filename)
52
+ @file = File.open(@filename, 'r')
53
+ @raw = @file.read
54
+ else
55
+ @raw = default_content
56
+ end
57
+ @contents = JSON.parse(@raw)
58
+ @file.close if @file
59
+ end
60
+
61
+ def default_content
62
+ case @filetype
63
+ when 'db'
64
+ '{ "tables": {} }'
65
+ when 'table_structure'
66
+ '{ "last_id" : 0, "fields": {} }'
67
+ when 'table_records'
68
+ '{ }'
69
+ end
70
+ end
71
+ end
@@ -0,0 +1,90 @@
1
+ class Record
2
+
3
+ include Validation
4
+
5
+ def initialize(fields, data = nil)
6
+ @fields = fields
7
+ if data.nil?
8
+ @record = Hash.new
9
+ @new = true
10
+ self.id = 0
11
+ else
12
+ @record = data
13
+ @new = false
14
+ end
15
+ @persisted = false
16
+ set_defaults # we need to apply again the defaults
17
+ end
18
+
19
+ def method_missing(name, *args)
20
+ attribute = name.to_s
21
+
22
+ if @fields.to_hash.keys.include?(attribute.sub('=', ''))
23
+ if attribute =~ /=$/
24
+ if @record[attribute] != args[0]
25
+ @record[attribute.sub('=', '')] = args[0]
26
+ @persisted = true
27
+ end
28
+ else
29
+ @record[attribute]
30
+ end
31
+ else
32
+ raise "Column '#{attribute.sub('=', '')}' do not exists"
33
+ end
34
+ end
35
+
36
+ def save_with_id(id)
37
+ self.created_at = Time.now.to_i
38
+ self.updated_at = Time.now.to_i
39
+ self.id = id
40
+ @new = false
41
+ @persisted = false
42
+ end
43
+
44
+ def update
45
+ self.updated_at = Time.now.to_i
46
+ @new = false
47
+ @persisted = false
48
+ end
49
+
50
+ def to_hash
51
+ @record
52
+ end
53
+
54
+ def new?
55
+ @new
56
+ end
57
+
58
+ def persisted?
59
+ @persisted
60
+ end
61
+
62
+ def created_at
63
+ Time.at(@record['created_at'])
64
+ end
65
+
66
+ def persisted_at
67
+ Time.at(@record['updated_at'])
68
+ end
69
+
70
+ private
71
+
72
+ def set_defaults
73
+ @fields.each do |name, values|
74
+ if values.default and @record[name].nil?
75
+ @record[name] = values.default
76
+ end
77
+ end
78
+ end
79
+
80
+ # # TODO : Add Validation
81
+
82
+ # def save!
83
+ # raiseif(save, false, "Error saving the record.")
84
+ # end
85
+
86
+ # def save
87
+ # return @table_class.save
88
+ # end
89
+
90
+ end
@@ -0,0 +1,107 @@
1
+ class ResultSet
2
+
3
+ include Validation
4
+
5
+ def initialize(table_class, array_fields = nil)
6
+ @fields = array_fields || table_class.fields.to_hash.keys
7
+ # actually it deletes the record!
8
+ @result = Hash.new()
9
+ table_class.records.each do |k, v|
10
+ @result[k] = v.dup
11
+ end
12
+
13
+ @equal = Hash.new
14
+ @min = Hash.new
15
+ @max = Hash.new
16
+ @like = Hash.new
17
+ end
18
+
19
+ def equal(field, value)
20
+ @equal[field] = value
21
+ return self
22
+ end
23
+
24
+ def min(field, value)
25
+ @min[field] = value
26
+ return self
27
+ end
28
+
29
+ def max(field, value)
30
+ @max[field] = value
31
+ return self
32
+ end
33
+
34
+ def like(field, value)
35
+ @like[field] = value
36
+ return self
37
+ end
38
+
39
+ def result
40
+ result_equal if @equal.keys.count != 0
41
+ result_min if @min.keys.count != 0
42
+ result_max if @max.keys.count != 0
43
+ result_like if @like.keys.count != 0
44
+ return @result
45
+ end
46
+
47
+ private
48
+
49
+ def result_equal
50
+ @equal.each do |col_name, col_value|
51
+ this_result = Array.new
52
+ @result.each do |id, record|
53
+ r = record.send col_name.to_sym
54
+ if r == col_value
55
+ this_result << id
56
+ end
57
+ end
58
+ remove_if_key_not_in(this_result)
59
+ end
60
+ end
61
+
62
+ def result_min
63
+ @min.each do |col_name, col_value|
64
+ this_result = Array.new
65
+ @result.each do |id, record|
66
+ r = record.send col_name.to_sym
67
+ if r >= col_value
68
+ this_result << id
69
+ end
70
+ end
71
+ remove_if_key_not_in(this_result)
72
+ end
73
+ end
74
+
75
+ def result_max
76
+ @max.each do |col_name, col_value|
77
+ this_result = Array.new
78
+ @result.each do |id, record|
79
+ r = record.send col_name.to_sym
80
+ if r <= col_value
81
+ this_result << id
82
+ end
83
+ end
84
+ remove_if_key_not_in(this_result)
85
+ end
86
+ end
87
+
88
+ def result_like
89
+ @like.each do |col_name, col_value|
90
+ this_result = Array.new
91
+ @result.each do |id, record|
92
+ r = record.send col_name.to_sym
93
+ if r =~ Regexp.new(col_value)
94
+ this_result << id
95
+ end
96
+ end
97
+ remove_if_key_not_in(this_result)
98
+ end
99
+ end
100
+
101
+ def remove_if_key_not_in(arr)
102
+ @result.keys.each do |key|
103
+ @result.delete(key) if arr.include?(key) == false
104
+ end
105
+ end
106
+
107
+ end
@@ -0,0 +1,143 @@
1
+ class Table
2
+
3
+ attr_accessor :updated_at
4
+ attr_reader :fields, :records, :last_id, :persisted
5
+
6
+ include Validation
7
+
8
+ def initialize(name, folder, updated_at, writeable)
9
+ allowed_name?(name)
10
+
11
+ @name = name
12
+ @folder = folder
13
+ @writeable = writeable
14
+
15
+ @structure_file = FileOps.new(@folder, "#{@name}_structure.json", 'table_structure', @writeable)
16
+ @records_file = FileOps.new(@folder, "#{@name}_records.json", 'table_records', @writeable)
17
+
18
+ # last_id
19
+ @last_id = @structure_file.contents["last_id"]
20
+
21
+ # Fields
22
+ @fields = Hash.new
23
+
24
+ if @structure_file.contents["fields"].keys.count
25
+ @structure_file.contents["fields"].each do |name, field|
26
+ @fields[name] ||= Field.new(name)
27
+ end
28
+ end
29
+ # add id and timestamps fields if not exists...
30
+ add_main_fields
31
+
32
+ # Fills records hash
33
+ @records_contents = @records_file.contents || {}
34
+ @records = Hash.new
35
+ @records_contents.each do |id, record|
36
+ @records[id] = Record.new(@fields, record)
37
+ end
38
+
39
+ @updated_at = Time.now.to_i if @structure_file.new_file
40
+ @persisted = !@structure_file.new_file
41
+
42
+ end
43
+
44
+ def field(name)
45
+ return @fields[name] ||= Field.new(@name)
46
+ end
47
+
48
+ def records
49
+ return @records
50
+ end
51
+
52
+ def new_record
53
+ @persisted = false
54
+ @updated_at = Time.now.to_i
55
+ return Record.new(@fields)
56
+ end
57
+
58
+ def query(array_fields = nil)
59
+ return select(array_fields)
60
+ end
61
+
62
+ def select(array_fields = nil)
63
+ return ResultSet.new(self, array_fields)
64
+ end
65
+
66
+ def insert(record)
67
+ if record.id != 0
68
+ raise "Record already exists."
69
+ else
70
+ id = new_id
71
+ record.save_with_id(id)
72
+ @records[id] = record
73
+ @persisted = false
74
+ return id
75
+ end
76
+ end
77
+
78
+ def update(record)
79
+ @persisted = false
80
+ @records[record.id] = record.update
81
+ end
82
+
83
+ def delete(record)
84
+ @persisted = false
85
+ @records.delete(record.id)
86
+ end
87
+
88
+ def drop
89
+ @structure_file.destroy
90
+ @records_file.destroy
91
+ end
92
+
93
+ def persist
94
+ @structure_file.contents['last_id'] = @last_id
95
+ @structure_file.contents['fields'] = @fields.to_hash
96
+ @structure_file.save
97
+
98
+ @records_file.contents = {}
99
+ @records.each do |id, record|
100
+ @records_file.contents[id] = record.to_hash
101
+ end
102
+ @records_file.save
103
+ @updated_at = Time.now.to_i
104
+ @persisted = true
105
+ end
106
+
107
+ def to_hash
108
+ return { "name" => @name, "updated_at" => @updated_at }
109
+ end
110
+
111
+ def updated_at_time
112
+ Time.at(@updated_at)
113
+ end
114
+
115
+ private
116
+
117
+ def new_id
118
+ @last_id = @last_id + 1
119
+ return @last_id
120
+ end
121
+
122
+ def add_main_fields
123
+ if @fields["id"].nil?
124
+ id_field = self.field('id')
125
+ id_field.type = "Fixnum"
126
+ id_field.nullable = false
127
+ id_field.default = 0
128
+ end
129
+ if @fields["created_at"].nil?
130
+ created_field = self.field('created_at')
131
+ created_field.type = "Fixnum"
132
+ created_field.nullable = false
133
+ created_field.default = 0
134
+ end
135
+ if @fields["updated_at"].nil?
136
+ updated_field = self.field('updated_at')
137
+ updated_field.type = "Fixnum"
138
+ updated_field.nullable = false
139
+ updated_field.default = 0
140
+ end
141
+ end
142
+
143
+ end
@@ -0,0 +1,86 @@
1
+ module Validation
2
+
3
+ def allowed_types
4
+ ["String", "Fixnum", "Integer", "Float", "Time", "Bool"]
5
+ end
6
+
7
+ def allowed_name?(name)
8
+ if (name =~ /^[a-zA-Z0-9_]+$/).nil?
9
+ raise "Name not allowed. /^[a-zA-Z0-9_]+$/"
10
+ else
11
+ return true
12
+ end
13
+ end
14
+
15
+ def allowed?(type)
16
+ if allowed_types.include?(type.to_s)
17
+ return type.to_s
18
+ else
19
+ raise "Validation: Type '#{type.to_s}' not allowed"
20
+ end
21
+ end
22
+
23
+ def validate_type(_class, _value)
24
+ case _class
25
+ when "Bool"
26
+ bool?(_class, _value)
27
+ when "String"
28
+ string?(_class, _value)
29
+ when "Fixnum", "Integer", "Float"
30
+ numeric?(_class, _value)
31
+ when "Time"
32
+ time?(_class, _value)
33
+ else
34
+ raise "Validation: '#{_class}' type not allowed"
35
+ end
36
+ end
37
+
38
+ private
39
+
40
+ def bool?(_class, _value)
41
+ case _value
42
+ when true, false
43
+ return _value
44
+ else
45
+ raise "Validation: Bool: Not a Bool type"
46
+ end
47
+ end
48
+
49
+ def string?(_class, _value)
50
+ if _class == _value.class.to_s
51
+ return _value
52
+ else
53
+ raise "Validation: #{_class}: Value '#{_value}' not allowed for '#{_class}' type"
54
+ end
55
+ end
56
+
57
+ def numeric?(_class, _value)
58
+ case _class
59
+ when "Fixnum", "Integer"
60
+ if _value.class.to_s == "Fixnum"
61
+ return _value
62
+ else
63
+ raise "Validation: '#{_value}' is not Integer"
64
+ end
65
+ when "Float"
66
+ case eval(_value).class.to_s
67
+ when "Float"
68
+ return _value
69
+ when "Fixnum"
70
+ return _value.to_f
71
+ else
72
+ raise "Validation: '#{_value} is not a Float type"
73
+ end
74
+ else
75
+ raise "Validation: '#{_value} is not a Numeric type"
76
+ end
77
+ end
78
+
79
+ def time?(_class, _value)
80
+ if eval(_value).class.to_s != "Time"
81
+ raise "Validation: '#{_value} is not a Time type"
82
+ end
83
+ end
84
+
85
+ end
86
+
metadata ADDED
@@ -0,0 +1,65 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: jsondb
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Antonio Fernandez
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2014-09-25 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: json_pure
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - '='
18
+ - !ruby/object:Gem::Version
19
+ version: 1.8.1
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - '='
25
+ - !ruby/object:Gem::Version
26
+ version: 1.8.1
27
+ description: Local Database using JSON files
28
+ email: antoniofernandezvara@gmail.com
29
+ executables: []
30
+ extensions: []
31
+ extra_rdoc_files: []
32
+ files:
33
+ - lib/jsondb.rb
34
+ - lib/jsondb/db.rb
35
+ - lib/jsondb/field.rb
36
+ - lib/jsondb/file_ops.rb
37
+ - lib/jsondb/record.rb
38
+ - lib/jsondb/resultset.rb
39
+ - lib/jsondb/table.rb
40
+ - lib/jsondb/validation.rb
41
+ homepage: http://fernandezvara.github.io/jsondb/
42
+ licenses:
43
+ - MIT
44
+ metadata: {}
45
+ post_install_message:
46
+ rdoc_options: []
47
+ require_paths:
48
+ - lib
49
+ required_ruby_version: !ruby/object:Gem::Requirement
50
+ requirements:
51
+ - - ">="
52
+ - !ruby/object:Gem::Version
53
+ version: '0'
54
+ required_rubygems_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: '0'
59
+ requirements: []
60
+ rubyforge_project:
61
+ rubygems_version: 2.2.2
62
+ signing_key:
63
+ specification_version: 4
64
+ summary: lib to manage tiny databases in plain JSON files
65
+ test_files: []