local_model 0.1.10 → 0.1.13

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 CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA256:
3
- metadata.gz: 8851a543d6b05aff962d00d160c7e38c5d770e880b38131bb7f417e51c7e1a35
4
- data.tar.gz: 20e8e5b027a36a7e4a99d142a21a07797563ca024ec3e24be1c55e8b14b663e2
3
+ metadata.gz: 5e778e1ced2f3d028e36ac5363e110343ee1f290fe97f23fcf2005ee3a38cfdb
4
+ data.tar.gz: b5d80e4c968830806dfc33e23f7bd72000c1382c59bb22dc96b952cf9a949766
5
5
  SHA512:
6
- metadata.gz: c75fb02836ac60ec9f916fe545a8d1af7ba95895f94f217379ef9086e2140f7489b4ad879b5efcf7a183be666e2a0ec4beb38854556fff96e87eeb2423d79101
7
- data.tar.gz: c9c72999f0f71fc98ba75563cbacf4114678dc4e9343cdbe49327db35564c2324633f1fc08edd348790e2df795fb7c3dbaaed41c876d49dcd2b9ccb73a1be9ba
6
+ metadata.gz: cc73cfe46da4e2fd03918604b0a80dba92d678a7ab8101671b777855b31a16fc07c20469fa7a5d01b9acc179f07c99448467d58193193ee7088d92c105999d70
7
+ data.tar.gz: 470ed0d894f193ea64e63cb2bea0a06949769080712a91454470315cceae74d9172f5fe3944daf4ac4186de563b05ac4acdc78f556d1f74baba7ec41314ee38e
data/Gemfile CHANGED
@@ -9,3 +9,5 @@ gemspec
9
9
  gem "pry", "~> 0.14.1", group: :development
10
10
 
11
11
  gem "irb", "~> 1.3"
12
+
13
+ gem "thor", "~> 1.2"
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- local_model (0.1.9)
4
+ local_model (0.1.12)
5
5
 
6
6
  GEM
7
7
  remote: https://rubygems.org/
@@ -31,6 +31,7 @@ GEM
31
31
  diff-lcs (>= 1.2.0, < 2.0)
32
32
  rspec-support (~> 3.10.0)
33
33
  rspec-support (3.10.2)
34
+ thor (1.2.1)
34
35
 
35
36
  PLATFORMS
36
37
  x86_64-linux
@@ -42,6 +43,7 @@ DEPENDENCIES
42
43
  pry (~> 0.14.1)
43
44
  rake (~> 13.0)
44
45
  rspec (~> 3.10)
46
+ thor (~> 1.2)
45
47
 
46
48
  BUNDLED WITH
47
- 2.2.17
49
+ 2.3.14
data/Rakefile CHANGED
@@ -5,9 +5,12 @@ RSpec::Core::RakeTask.new(:spec)
5
5
 
6
6
  task :default => :spec
7
7
 
8
- desc "run a console"
9
- task :console do
10
- require 'pry'
8
+ desc "environment"
9
+ task :environment do
11
10
  require_relative './lib/local_model.rb'
12
- binding.pry
11
+ end
12
+
13
+ desc "run file generator"
14
+ task :generate => :environment do
15
+ LocalModel::Generator.new.invoke_all
13
16
  end
data/bin/local_model ADDED
@@ -0,0 +1,5 @@
1
+ #!/usr/bin/env ruby
2
+
3
+ require 'local_model'
4
+
5
+ LocalModel::Generators::Initialize.start
@@ -21,8 +21,21 @@ class LocalModel::Collection < Array
21
21
  arg.save && self.model.save
22
22
  end
23
23
 
24
- def build(arg)
25
- self.push(arg)
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
26
39
  end
27
40
 
28
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(2) == 1
27
- sleep(0.5)
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
@@ -22,7 +22,7 @@ class LocalModel::CSV < LocalModel::Model
22
22
  end
23
23
 
24
24
  self.define_singleton_method :columns do
25
- cols
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
- self.all.each{ |obj| obj.destroy }
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})
@@ -0,0 +1,4 @@
1
+ class LocalModel::RecordInvalid < StandardError
2
+
3
+
4
+ end
@@ -0,0 +1,3 @@
1
+ class LocalModel::RecordNotFound < StandardError
2
+
3
+ end
@@ -0,0 +1,32 @@
1
+ require 'thor'
2
+ require 'thor/group'
3
+
4
+ module LocalModel
5
+ module Generators
6
+ class Initialize < Thor::Group
7
+ include Thor::Actions
8
+
9
+ class_option :namespace, default: "InMemory"
10
+
11
+ desc 'Create required files'
12
+
13
+ def self.source_root
14
+ File.dirname(__FILE__) + '/../'
15
+ end
16
+
17
+ def create_initializer
18
+ @namespace_classname = options[:namespace]
19
+ template('templates/initializer.erb', 'config/initializers/local_model.rb')
20
+ end
21
+
22
+ def create_data_accessor
23
+ template('templates/data_accessor.erb', 'lib/data_accessor.rb')
24
+ end
25
+
26
+ def create_model_generator
27
+ @class_namespace_snake = LocalModel::Functions.camel_to_snake(@namespace_classname)
28
+ template('templates/generate_model.erb', 'lib/tasks/local_model.rake')
29
+ end
30
+ end
31
+ end
32
+ end
@@ -24,7 +24,6 @@ class LocalModel::PluralizedWords
24
24
  "moves" => "move",
25
25
  "groves" => "grove",
26
26
  "stoves" => "stove",
27
- "beliefs" => "belief"
28
27
  }
29
28
 
30
29
  IRREGULAR_PLURALIZED_WORDS = {
@@ -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,24 +21,32 @@ 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
- association_class = Object.const_get(association_class_name)
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
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
@@ -39,15 +54,25 @@ class LocalModel::Model
39
54
  end
40
55
 
41
56
  if through.nil?
42
- current_class_id_methodname = foreign_key || "#{LocalModel::Functions.camel_to_snake(denamespace_classname(self))}_id"
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
43
62
  belongs_to_id_sym = current_class_id_methodname.to_sym
44
63
  add_to_collection = Proc.new do |arg, model|
45
64
  arg.send("#{belongs_to_id_sym}=", model.id)
46
65
  end
66
+
47
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
+
48
73
  association_class = Object.const_get(association_classname)
49
74
  LocalModel::Collection.create_from(
50
- array: association_class.where(belongs_to_id_sym => self.id),
75
+ array: association_class.where(**collection_args),
51
76
  for_model: self,
52
77
  for_collection_class: association_class,
53
78
  add_to_collection_proc: add_to_collection
@@ -75,7 +100,7 @@ class LocalModel::Model
75
100
  end
76
101
  end
77
102
 
78
- 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)
79
104
  if class_name.nil?
80
105
  association_classname = LocalModel::Functions.snake_to_camel(association)
81
106
  association_classname[0] = association_classname[0].upcase
@@ -85,11 +110,19 @@ class LocalModel::Model
85
110
  end
86
111
 
87
112
  if through.nil?
88
- current_class_id_methodname = foreign_key || "#{LocalModel::Functions.camel_to_snake(denamespace_classname(self))}_id"
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
89
118
  belongs_to_id_sym = current_class_id_methodname.to_sym
90
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
91
124
  association_class = Object.const_get(association_classname)
92
- association_class.where(belongs_to_id_sym => self.id).first
125
+ association_class.where(**collection_args).first
93
126
  end
94
127
  else
95
128
  current_class_id_methodname = foreign_key || "#{LocalModel::Functions.camel_to_snake(association)}_id"
@@ -132,6 +165,29 @@ class LocalModel::Model
132
165
  "#{LocalModel.path}#{slash}#{self}.csv"
133
166
  end
134
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
+
135
191
 
136
192
  class SchemaBuilder
137
193
 
@@ -0,0 +1,61 @@
1
+ module DataAccessor
2
+ class Base
3
+ class Accessors
4
+ LOCAL = :local
5
+ ACTIVE_RECORD = :active_record
6
+
7
+ NAMESPACES = {
8
+ LOCAL => <%= @namespace_classname %>,
9
+ ACTIVE_RECORD => Object
10
+ }
11
+
12
+ end
13
+
14
+ @@strategy = ENV["USE_LOCAL_MODELS"] ? Accessors::LOCAL : Accessors::ACTIVE_RECORD
15
+ @@exceptions = Set.new([])
16
+
17
+ def self.set_strategy(strat)
18
+ if strat.to_sym == :local
19
+ @@strategy = Accessors::LOCAL
20
+ else
21
+ @@strategy = Accessors::ACTIVE_RECORD
22
+ end
23
+ end
24
+
25
+ def self.klass(klass_name)
26
+ namespace = get_namespace_for(klass_name)
27
+ namespaced_klass_name = "#{namespace}::#{klass_name.to_s}"
28
+ namespaced_klass = Object.const_get(namespaced_klass_name)
29
+ end
30
+ private_class_method :klass
31
+
32
+ def self.get_namespace
33
+ Accessors::NAMESPACES[@@strategy]
34
+ end
35
+ private_class_method :get_namespace
36
+
37
+ def self.get_namespace_for(klass)
38
+ exception_strategy = @@strategy == Accessors::ACTIVE_RECORD ? Accessors::LOCAL : Accessors::ACTIVE_RECORD
39
+ exception_namespace = Accessors::NAMESPACES[exception_strategy]
40
+ if @@exceptions.include?(klass.to_s.to_sym)
41
+ exception_namespace
42
+ else
43
+ get_namespace
44
+ end
45
+ end
46
+ private_class_method :get_namespace_for
47
+
48
+ def self.set_exceptions(exceptions)
49
+ @@exceptions = Set.new(exceptions.map(&:to_sym))
50
+ end
51
+
52
+ # Example - find user
53
+ # def self.find_user(id)
54
+ # ref = klass(:User)
55
+ # ref.find(id)
56
+ # end
57
+ # private_class_method :find_user
58
+
59
+ # MARK: PUBLIC GETTERS
60
+ end
61
+ end
@@ -0,0 +1,20 @@
1
+
2
+ namespace :local_model do
3
+
4
+ desc "Generate LocalModel model"
5
+ task :create_model, [:klass_name] => :environment do |_t, args|
6
+ klass_name = args[:klass_name]
7
+ model_code = <<~RUBY
8
+ class <%= @namespace_classname %>::#{klass_name} < LocalModel::CSV
9
+ schema do |t|
10
+
11
+ end
12
+ end
13
+ RUBY
14
+
15
+ dir = Rails.root.join('lib', '<%= @class_namespace_snake%>')
16
+ Dir.mkdir(dir) unless File.exists?(dir)
17
+ filename = "#{dir}/#{klass_name.underscore}.rb"
18
+ File.open(filename, 'w') { |f| f.write(model_code) }
19
+ end
20
+ end
@@ -0,0 +1,7 @@
1
+ module <%= @namespace_classname %>
2
+ LocalModel.config do |c|
3
+ c.path = "./tmp/#{ENV['RAILS_ENV'] || 'development'}"
4
+ c.namespace = "<%= @namespace_classname %>"
5
+ c.cleanup_on_start = false
6
+ end
7
+ end
@@ -1,3 +1,3 @@
1
1
  module LocalModel
2
- VERSION = "0.1.10"
2
+ VERSION = "0.1.13"
3
3
  end
data/lib/local_model.rb CHANGED
@@ -1,5 +1,7 @@
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'
4
6
  require_relative './local_model/collection'
5
7
  require_relative './local_model/adapters/boolean_adapter'
@@ -12,6 +14,7 @@ require_relative './local_model/helpers/functions'
12
14
  require_relative './local_model/helpers/pluralized_words'
13
15
  require_relative './local_model/model'
14
16
  require_relative './local_model/csv'
17
+ require_relative './local_model/generators/initialize'
15
18
 
16
19
  module LocalModel
17
20
  class Error < StandardError; end
@@ -37,6 +40,13 @@ module LocalModel
37
40
  @@path
38
41
  end
39
42
 
43
+ def self.db_drop
44
+ Dir.foreach(@@path) do |f|
45
+ fn = File.join(@@path, f)
46
+ File.delete(fn) if f != '.' && f != '..'
47
+ end
48
+ end
49
+
40
50
  def self.config(&block)
41
51
  configuration = Configuration.new
42
52
  if block_given?
@@ -46,10 +56,7 @@ module LocalModel
46
56
  @@namespace = configuration.namespace
47
57
  Dir.mkdir(configuration.path) unless Dir.exist?(configuration.path)
48
58
  if configuration.cleanup_on_start
49
- Dir.foreach(configuration.path) do |f|
50
- fn = File.join(configuration.path, f)
51
- File.delete(fn) if f != '.' && f != '..'
52
- end
59
+ db_drop
53
60
  end
54
61
  end
55
62
 
data/local_model.gemspec CHANGED
@@ -30,8 +30,11 @@ Gem::Specification.new do |spec|
30
30
  spec.files = Dir.chdir(File.expand_path('..', __FILE__)) do
31
31
  `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
32
32
  end
33
- spec.bindir = "exe"
34
- spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
33
+ # spec.bindir = "exe"
34
+ spec.bindir = "bin"
35
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
36
+ spec.executables << 'local_model'
37
+ # spec.executables = spec.files.grep(%r{^exe/}) { |f| File.basename(f) }
35
38
  spec.require_paths = ["lib"]
36
39
 
37
40
  spec.add_development_dependency "bundler", "~> 2.2"
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.10
4
+ version: 0.1.13
5
5
  platform: ruby
6
6
  authors:
7
7
  - Micah Shute
8
8
  autorequire:
9
- bindir: exe
9
+ bindir: bin
10
10
  cert_chain: []
11
- date: 2021-06-01 00:00:00.000000000 Z
11
+ date: 2022-06-17 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: bundler
@@ -56,7 +56,10 @@ description: This is meant to help facilitate an API-first strategy when creatin
56
56
  web apps with RoR
57
57
  email:
58
58
  - micah.shute@gmail.com
59
- executables: []
59
+ executables:
60
+ - console
61
+ - local_model
62
+ - setup
60
63
  extensions: []
61
64
  extra_rdoc_files: []
62
65
  files:
@@ -70,6 +73,7 @@ files:
70
73
  - README.md
71
74
  - Rakefile
72
75
  - bin/console
76
+ - bin/local_model
73
77
  - bin/setup
74
78
  - lib/local_model.rb
75
79
  - lib/local_model/adapters/boolean_adapter.rb
@@ -80,10 +84,16 @@ files:
80
84
  - lib/local_model/collection.rb
81
85
  - lib/local_model/concerns/csv_interactable.rb
82
86
  - lib/local_model/csv.rb
87
+ - lib/local_model/errors/record_invalid.rb
88
+ - lib/local_model/errors/record_not_found.rb
89
+ - lib/local_model/generators/initialize.rb
83
90
  - lib/local_model/helpers/functions.rb
84
91
  - lib/local_model/helpers/pluralized_words.rb
85
92
  - lib/local_model/model.rb
86
93
  - lib/local_model/sandbox.rb
94
+ - lib/local_model/templates/data_accessor.erb
95
+ - lib/local_model/templates/generate_model.erb
96
+ - lib/local_model/templates/initializer.erb
87
97
  - lib/local_model/version.rb
88
98
  - local_model.gemspec
89
99
  homepage: https://github.com/micahshute/local_model
@@ -108,7 +118,7 @@ required_rubygems_version: !ruby/object:Gem::Requirement
108
118
  - !ruby/object:Gem::Version
109
119
  version: '0'
110
120
  requirements: []
111
- rubygems_version: 3.2.3
121
+ rubygems_version: 3.3.7
112
122
  signing_key:
113
123
  specification_version: 4
114
124
  summary: Easily set up a schema persisted with CSV but interactable with AR-like methods