local_model 0.1.9 → 0.1.12
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 +4 -4
- data/Gemfile.lock +1 -1
- data/README.md +1 -1
- data/lib/local_model/collection.rb +41 -0
- data/lib/local_model/concerns/csv_interactable.rb +22 -5
- data/lib/local_model/csv.rb +28 -2
- data/lib/local_model/errors/record_invalid.rb +4 -0
- data/lib/local_model/errors/record_not_found.rb +3 -0
- data/lib/local_model/model.rb +91 -16
- data/lib/local_model/version.rb +1 -1
- data/lib/local_model.rb +11 -4
- metadata +6 -3
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA256:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 23ec22d2650b17792c2a03dff88d9dffbcedd78b08ebd05afd057fd124d0476a
|
4
|
+
data.tar.gz: 8c89b6eaf25891cb05c3692785d9e88979372be6078ea465d080e9fbd9a7f330
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: 6b770b1003097729755a6a63df94b909c58eb2c39ae64f398db5ff64083b2846307b173d8da3bd3533efa30ba4dce9dce2170f69ac3e6cc61d886d2b4cf08d5c
|
7
|
+
data.tar.gz: 405d6229dbb39b63e8fc5b9dd01d0e3dadaedf06907519f85a411a0d0547c7a1c12d6b1e7528d11f50a6d2e1cfe79f13526f5dcd6e919e1e91125ec9c03290b5
|
data/Gemfile.lock
CHANGED
data/README.md
CHANGED
@@ -81,7 +81,7 @@ As of now, supports:
|
|
81
81
|
- #destroy, .find, .create, .new, .where, .first, .second, .last, relationships, updating,
|
82
82
|
|
83
83
|
Does not support yet (notably):
|
84
|
-
- .build, .update, validations
|
84
|
+
- .build, .update, validations
|
85
85
|
- object equivalence if gotten from source twice
|
86
86
|
|
87
87
|
|
@@ -0,0 +1,41 @@
|
|
1
|
+
class LocalModel::Collection < Array
|
2
|
+
|
3
|
+
def self.create_from(array: , for_model: , for_collection_class:, add_to_collection_proc:)
|
4
|
+
new_obj = new(array)
|
5
|
+
new_obj.model = for_model
|
6
|
+
new_obj.collection_class = for_collection_class
|
7
|
+
new_obj.add_to_collection = add_to_collection_proc
|
8
|
+
new_obj
|
9
|
+
end
|
10
|
+
|
11
|
+
attr_accessor :model, :collection_class, :add_to_collection
|
12
|
+
|
13
|
+
def <<(arg)
|
14
|
+
self.push(arg)
|
15
|
+
end
|
16
|
+
|
17
|
+
def push(arg)
|
18
|
+
self.[]=(self.length, arg)
|
19
|
+
raise ArgumentError.new("#{arg.class} inputted, expecting #{self.collection_class}") if !arg.is_a?(self.collection_class)
|
20
|
+
add_to_collection[arg, self.model]
|
21
|
+
arg.save && self.model.save
|
22
|
+
end
|
23
|
+
|
24
|
+
def build(**args)
|
25
|
+
self.push(collection_class.create(**args))
|
26
|
+
end
|
27
|
+
|
28
|
+
def where(**args)
|
29
|
+
self.filter do |el|
|
30
|
+
found = true
|
31
|
+
args.each do |k,v|
|
32
|
+
if el.send(k.to_s) != v
|
33
|
+
found = false
|
34
|
+
break
|
35
|
+
end
|
36
|
+
end
|
37
|
+
found
|
38
|
+
end
|
39
|
+
end
|
40
|
+
|
41
|
+
end
|
@@ -18,15 +18,15 @@ module CSVInteractable
|
|
18
18
|
rand_val = @@rand.rand(1000000)
|
19
19
|
f = File.new("#{self.storage_path}-#{rand_val}.prep", 'w')
|
20
20
|
f.close
|
21
|
-
r = @@rand.rand / 10.0
|
22
|
-
sleep(r)
|
21
|
+
# r = @@rand.rand / 10.0
|
22
|
+
# sleep(r)
|
23
23
|
|
24
24
|
if File.exist?("#{self.storage_path}.bak.csv") || Dir["#{self.storage_path}-*.prep"].length > 1
|
25
25
|
File.delete("#{self.storage_path}-#{rand_val}.prep")
|
26
|
-
if @@rand.rand(
|
27
|
-
sleep(0
|
26
|
+
if @@rand.rand(5) != 1
|
27
|
+
sleep(@@rand.rand / 10.0)
|
28
28
|
end
|
29
|
-
mutate_csv
|
29
|
+
mutate_csv(option, block)
|
30
30
|
return
|
31
31
|
else
|
32
32
|
File.delete("#{self.storage_path}-#{rand_val}.prep")
|
@@ -200,6 +200,17 @@ module CSVInteractable
|
|
200
200
|
end
|
201
201
|
end
|
202
202
|
|
203
|
+
def delete_all_rows
|
204
|
+
begin
|
205
|
+
self.mutate_csv(:mutate) {}
|
206
|
+
true
|
207
|
+
rescue
|
208
|
+
File.delete("#{self.storage_path}.bak.csv")
|
209
|
+
false
|
210
|
+
end
|
211
|
+
|
212
|
+
end
|
213
|
+
|
203
214
|
end
|
204
215
|
|
205
216
|
module InstanceMethods
|
@@ -218,6 +229,12 @@ module CSVInteractable
|
|
218
229
|
end
|
219
230
|
end
|
220
231
|
|
232
|
+
def save!
|
233
|
+
if !save
|
234
|
+
raise LocalModel::RecordInvalid
|
235
|
+
end
|
236
|
+
end
|
237
|
+
|
221
238
|
end
|
222
239
|
|
223
240
|
end
|
data/lib/local_model/csv.rb
CHANGED
@@ -22,7 +22,7 @@ class LocalModel::CSV < LocalModel::Model
|
|
22
22
|
end
|
23
23
|
|
24
24
|
self.define_singleton_method :columns do
|
25
|
-
|
25
|
+
cols
|
26
26
|
end
|
27
27
|
|
28
28
|
schema_data = cols.each_with_index.reduce({}) do |mem, (key,i)|
|
@@ -52,8 +52,14 @@ class LocalModel::CSV < LocalModel::Model
|
|
52
52
|
all_instances
|
53
53
|
end
|
54
54
|
|
55
|
+
def self.count
|
56
|
+
total = 0
|
57
|
+
self.each_record{ total += 1 }
|
58
|
+
total
|
59
|
+
end
|
60
|
+
|
55
61
|
def self.destroy_all
|
56
|
-
|
62
|
+
delete_all_rows
|
57
63
|
end
|
58
64
|
|
59
65
|
def self.where(**args)
|
@@ -105,12 +111,27 @@ class LocalModel::CSV < LocalModel::Model
|
|
105
111
|
return self.find_by(id: id)
|
106
112
|
end
|
107
113
|
|
114
|
+
def self.find!(id)
|
115
|
+
found_record = find(id)
|
116
|
+
if !found_record
|
117
|
+
raise LocalModel::RecordNotFound.new
|
118
|
+
else
|
119
|
+
found_record
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
108
123
|
def self.create(**args)
|
109
124
|
inst = new(**args)
|
110
125
|
inst.save
|
111
126
|
inst
|
112
127
|
end
|
113
128
|
|
129
|
+
def self.create!(**args)
|
130
|
+
inst = new(**args)
|
131
|
+
inst.save!
|
132
|
+
inst
|
133
|
+
end
|
134
|
+
|
114
135
|
def initialize(**args)
|
115
136
|
args.each do |k,v|
|
116
137
|
self.send("#{k}=", v)
|
@@ -121,6 +142,11 @@ class LocalModel::CSV < LocalModel::Model
|
|
121
142
|
def saved?
|
122
143
|
!self.id.nil?
|
123
144
|
end
|
145
|
+
|
146
|
+
def reload
|
147
|
+
raise LocalModel::RecordNotFound if !self.id
|
148
|
+
self.class.find!(self.id)
|
149
|
+
end
|
124
150
|
|
125
151
|
def destroy
|
126
152
|
self.class.delete_row({id: self.id})
|
data/lib/local_model/model.rb
CHANGED
@@ -5,7 +5,14 @@ class LocalModel::Model
|
|
5
5
|
# yield(SchemaBuilder.new(self))
|
6
6
|
end
|
7
7
|
|
8
|
-
def self.belongs_to(association, class_name: nil, foreign_key: nil)
|
8
|
+
def self.belongs_to(association, class_name: nil, foreign_key: nil, polymorphic: false)
|
9
|
+
if foreign_key.nil?
|
10
|
+
keyname = "#{association}_id"
|
11
|
+
typename = "#{association}_type"
|
12
|
+
else
|
13
|
+
keyname = foreign_key
|
14
|
+
end
|
15
|
+
|
9
16
|
if class_name.nil?
|
10
17
|
association_class_name = LocalModel::Functions.snake_to_camel(association)
|
11
18
|
association_class_name[0] = association_class_name[0].upcase
|
@@ -14,49 +21,86 @@ class LocalModel::Model
|
|
14
21
|
association_class_name = namespace_classname(class_name)
|
15
22
|
end
|
16
23
|
|
17
|
-
if foreign_key.nil?
|
18
|
-
keyname = "#{association}_id"
|
19
|
-
else
|
20
|
-
keyname = foreign_key
|
21
|
-
end
|
22
24
|
|
23
25
|
define_method association do
|
24
|
-
|
26
|
+
if polymorphic
|
27
|
+
association_type = self.send(typename)
|
28
|
+
return nil if association_type.nil? || association_type.empty?
|
29
|
+
polymorphic_class_name = LocalModel::Functions.snake_to_camel(association_type.gsub("_type", ""))
|
30
|
+
polymorphic_class_name[0] = polymorphic_class_name[0].upcase
|
31
|
+
association_class = Object.const_get(polymorphic_class_name)
|
32
|
+
else
|
33
|
+
association_class = Object.const_get(association_class_name)
|
34
|
+
end
|
25
35
|
id = self.send(keyname)
|
26
36
|
association_class.find(id)
|
27
37
|
end
|
28
38
|
|
29
39
|
define_method "#{association}=" do |association_obj|
|
30
|
-
self.send("#{keyname}=", association_obj
|
40
|
+
self.send("#{keyname}=", association_obj&.id)
|
41
|
+
if polymorphic
|
42
|
+
if !association_obj.nil?
|
43
|
+
self.send("#{typename}=", association_obj.class.to_s)
|
44
|
+
end
|
45
|
+
end
|
31
46
|
end
|
32
47
|
end
|
33
48
|
|
34
|
-
def self.has_many(association, through: nil, class_name: nil, foreign_key: nil)
|
49
|
+
def self.has_many(association, through: nil, class_name: nil, foreign_key: nil, as: nil)
|
35
50
|
if class_name.nil?
|
36
51
|
association_classname = namespace_classname(get_classname_from_association(association))
|
37
52
|
else
|
38
53
|
association_classname = namespace_classname(class_name)
|
39
54
|
end
|
40
55
|
|
41
|
-
|
42
56
|
if through.nil?
|
43
|
-
|
57
|
+
if as.nil?
|
58
|
+
current_class_id_methodname = foreign_key || "#{LocalModel::Functions.camel_to_snake(denamespace_classname(self))}_id"
|
59
|
+
else
|
60
|
+
current_class_id_methodname = "#{as}_id"
|
61
|
+
end
|
44
62
|
belongs_to_id_sym = current_class_id_methodname.to_sym
|
63
|
+
add_to_collection = Proc.new do |arg, model|
|
64
|
+
arg.send("#{belongs_to_id_sym}=", model.id)
|
65
|
+
end
|
66
|
+
|
45
67
|
define_method association do
|
68
|
+
collection_args = {belongs_to_id_sym => self.id}
|
69
|
+
if !as.nil?
|
70
|
+
collection_args["#{as}_type".to_sym] = self.class.to_s
|
71
|
+
end
|
72
|
+
|
46
73
|
association_class = Object.const_get(association_classname)
|
47
|
-
|
74
|
+
LocalModel::Collection.create_from(
|
75
|
+
array: association_class.where(**collection_args),
|
76
|
+
for_model: self,
|
77
|
+
for_collection_class: association_class,
|
78
|
+
add_to_collection_proc: add_to_collection
|
79
|
+
)
|
48
80
|
end
|
49
81
|
else
|
50
82
|
current_class_id_methodname = foreign_key || "#{LocalModel::Functions.camel_to_snake(LocalModel::Functions.singularize(association))}_id"
|
51
83
|
belongs_to_id_sym = current_class_id_methodname.to_sym
|
84
|
+
add_to_collection = Proc.new do |arg, model|
|
85
|
+
through_collection = model.send(through)
|
86
|
+
through_classname = through_collection.collection_class
|
87
|
+
new_join = through_classname.new
|
88
|
+
new_join.send("#{belongs_to_id_sym}=", arg.id)
|
89
|
+
through_collection << new_join
|
90
|
+
end
|
52
91
|
define_method association do
|
53
92
|
association_class = Object.const_get(association_classname)
|
54
|
-
|
93
|
+
LocalModel::Collection.create_from(
|
94
|
+
array: self.send(through).map{|through_obj| association_class.find(through_obj.send(belongs_to_id_sym))},
|
95
|
+
for_model: self,
|
96
|
+
for_collection_class: association_class,
|
97
|
+
add_to_collection_proc: add_to_collection
|
98
|
+
)
|
55
99
|
end
|
56
100
|
end
|
57
101
|
end
|
58
102
|
|
59
|
-
def self.has_one(association, through: nil, class_name: nil, foreign_key: nil)
|
103
|
+
def self.has_one(association, through: nil, class_name: nil, foreign_key: nil, as: nil)
|
60
104
|
if class_name.nil?
|
61
105
|
association_classname = LocalModel::Functions.snake_to_camel(association)
|
62
106
|
association_classname[0] = association_classname[0].upcase
|
@@ -66,11 +110,19 @@ class LocalModel::Model
|
|
66
110
|
end
|
67
111
|
|
68
112
|
if through.nil?
|
69
|
-
|
113
|
+
if as.nil?
|
114
|
+
current_class_id_methodname = foreign_key || "#{LocalModel::Functions.camel_to_snake(denamespace_classname(self))}_id"
|
115
|
+
else
|
116
|
+
current_class_id_methodname = "#{as}_id"
|
117
|
+
end
|
70
118
|
belongs_to_id_sym = current_class_id_methodname.to_sym
|
71
119
|
define_method association do
|
120
|
+
collection_args = {belongs_to_id_sym => self.id}
|
121
|
+
if !as.nil?
|
122
|
+
collection_args["#{as}_type".to_sym] = self.class.to_s
|
123
|
+
end
|
72
124
|
association_class = Object.const_get(association_classname)
|
73
|
-
association_class.where(
|
125
|
+
association_class.where(**collection_args).first
|
74
126
|
end
|
75
127
|
else
|
76
128
|
current_class_id_methodname = foreign_key || "#{LocalModel::Functions.camel_to_snake(association)}_id"
|
@@ -113,6 +165,29 @@ class LocalModel::Model
|
|
113
165
|
"#{LocalModel.path}#{slash}#{self}.csv"
|
114
166
|
end
|
115
167
|
|
168
|
+
def update(**args)
|
169
|
+
args.each do |k,v|
|
170
|
+
self.send("#{k.to_s}=", v)
|
171
|
+
end
|
172
|
+
self.save
|
173
|
+
end
|
174
|
+
|
175
|
+
def update!(**args)
|
176
|
+
args.each do |k,v|
|
177
|
+
self.send("#{k.to_s}=", v)
|
178
|
+
end
|
179
|
+
self.save!
|
180
|
+
end
|
181
|
+
|
182
|
+
def reload
|
183
|
+
self.class.find(self.id)
|
184
|
+
end
|
185
|
+
|
186
|
+
def ==(other)
|
187
|
+
self.class == other.class &&
|
188
|
+
self.id == other.id
|
189
|
+
end
|
190
|
+
|
116
191
|
|
117
192
|
class SchemaBuilder
|
118
193
|
|
data/lib/local_model/version.rb
CHANGED
data/lib/local_model.rb
CHANGED
@@ -1,6 +1,9 @@
|
|
1
1
|
require_relative "./local_model/version"
|
2
2
|
require 'csv'
|
3
|
+
require_relative './local_model/errors/record_invalid'
|
4
|
+
require_relative './local_model/errors/record_not_found'
|
3
5
|
require_relative './local_model/sandbox'
|
6
|
+
require_relative './local_model/collection'
|
4
7
|
require_relative './local_model/adapters/boolean_adapter'
|
5
8
|
require_relative './local_model/adapters/datetime_adapter'
|
6
9
|
require_relative './local_model/adapters/float_adapter'
|
@@ -36,6 +39,13 @@ module LocalModel
|
|
36
39
|
@@path
|
37
40
|
end
|
38
41
|
|
42
|
+
def self.db_drop
|
43
|
+
Dir.foreach(@@path) do |f|
|
44
|
+
fn = File.join(@@path, f)
|
45
|
+
File.delete(fn) if f != '.' && f != '..'
|
46
|
+
end
|
47
|
+
end
|
48
|
+
|
39
49
|
def self.config(&block)
|
40
50
|
configuration = Configuration.new
|
41
51
|
if block_given?
|
@@ -45,10 +55,7 @@ module LocalModel
|
|
45
55
|
@@namespace = configuration.namespace
|
46
56
|
Dir.mkdir(configuration.path) unless Dir.exist?(configuration.path)
|
47
57
|
if configuration.cleanup_on_start
|
48
|
-
|
49
|
-
fn = File.join(configuration.path, f)
|
50
|
-
File.delete(fn) if f != '.' && f != '..'
|
51
|
-
end
|
58
|
+
db_drop
|
52
59
|
end
|
53
60
|
end
|
54
61
|
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: local_model
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.1.
|
4
|
+
version: 0.1.12
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Micah Shute
|
8
8
|
autorequire:
|
9
9
|
bindir: exe
|
10
10
|
cert_chain: []
|
11
|
-
date:
|
11
|
+
date: 2022-06-16 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -77,8 +77,11 @@ files:
|
|
77
77
|
- lib/local_model/adapters/float_adapter.rb
|
78
78
|
- lib/local_model/adapters/integer_adapter.rb
|
79
79
|
- lib/local_model/adapters/string_adapter.rb
|
80
|
+
- lib/local_model/collection.rb
|
80
81
|
- lib/local_model/concerns/csv_interactable.rb
|
81
82
|
- lib/local_model/csv.rb
|
83
|
+
- lib/local_model/errors/record_invalid.rb
|
84
|
+
- lib/local_model/errors/record_not_found.rb
|
82
85
|
- lib/local_model/helpers/functions.rb
|
83
86
|
- lib/local_model/helpers/pluralized_words.rb
|
84
87
|
- lib/local_model/model.rb
|
@@ -107,7 +110,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
|
|
107
110
|
- !ruby/object:Gem::Version
|
108
111
|
version: '0'
|
109
112
|
requirements: []
|
110
|
-
rubygems_version: 3.
|
113
|
+
rubygems_version: 3.3.7
|
111
114
|
signing_key:
|
112
115
|
specification_version: 4
|
113
116
|
summary: Easily set up a schema persisted with CSV but interactable with AR-like methods
|