volt-sql 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.
@@ -0,0 +1,22 @@
1
+ module Volt
2
+ module Spec
3
+ module Helpers
4
+
5
+ def reconcile!
6
+ # trigger the reconcile
7
+ db_adaptor.db
8
+ end
9
+
10
+ def remove_model(klass)
11
+ klass_name = klass.name.to_sym
12
+ Volt::RootModels.remove_model_class(klass)
13
+ Object.send(:remove_const, klass_name)
14
+ end
15
+
16
+ def indexes(table_name)
17
+ Volt::Sql::Helper.normalized_indexes_from_table(db, table_name)
18
+ end
19
+
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,88 @@
1
+ require 'spec_helper'
2
+ require_relative 'helpers'
3
+
4
+ describe Volt::Sql::IndexUpdater do
5
+ let(:db_adaptor) { Volt::DataStore.fetch(volt_app) }
6
+ let(:db) { db_adaptor.db }
7
+ before do
8
+ # Access store, so we trigger cleanup after
9
+ store
10
+ end
11
+
12
+ include Volt::Spec::Helpers
13
+
14
+ it 'should add indexes' do
15
+ class SampleModelIndexes1 < Volt::Model
16
+ temporary
17
+ field :name, String
18
+
19
+ index :name
20
+ end
21
+
22
+ reconcile!
23
+
24
+ expect(indexes(:sample_model_indexes1s)).to eq(
25
+ {:sample_model_indexes1s_name_index=>{:columns=>[:name], :unique=>false}}
26
+ )
27
+ end
28
+
29
+ it 'should drop a removed index' do
30
+ class SampleModelIndexes2 < Volt::Model
31
+ temporary
32
+ field :name, String
33
+
34
+ index :name
35
+ end
36
+
37
+ reconcile!
38
+
39
+ expect(indexes(:sample_model_indexes2s)).to eq(
40
+ {:sample_model_indexes2s_name_index=>{:columns=>[:name], :unique=>false}}
41
+ )
42
+
43
+ remove_model(SampleModelIndexes2)
44
+
45
+ class SampleModelIndexes2 < Volt::Model
46
+ temporary
47
+ field :name, String
48
+ end
49
+
50
+ reconcile!
51
+
52
+ expect(indexes(:sample_model_indexes2s)).to eq({})
53
+ end
54
+
55
+ it 'should rename an index' do
56
+ class SampleModelIndexes3 < Volt::Model
57
+ temporary
58
+ field :name, String
59
+
60
+ index :name, name: :index_for_name
61
+ end
62
+
63
+ reconcile!
64
+
65
+ expect(indexes(:sample_model_indexes3s)).to eq(
66
+ {:index_for_name=>{:columns=>[:name], :unique=>false}}
67
+ )
68
+
69
+ remove_model(SampleModelIndexes3)
70
+
71
+ class SampleModelIndexes3 < Volt::Model
72
+ temporary
73
+ field :name, String
74
+
75
+ index :name, name: :name_index_for_name, unique: true
76
+ end
77
+
78
+ expect(SampleModelIndexes3.indexes).to eq(
79
+ {:name_index_for_name=>{:unique=>true, :columns=>[:name]}}
80
+ )
81
+
82
+ reconcile!
83
+
84
+ expect(indexes(:sample_model_indexes3s)).to eq(
85
+ {:name_index_for_name=>{:columns=>[:name], :unique=>true}}
86
+ )
87
+ end
88
+ end
@@ -0,0 +1,59 @@
1
+ require 'spec_helper'
2
+
3
+ describe Volt::Sql::WhereCall do
4
+ it 'should replay the ast' do
5
+ ast = ["c", "ident", "name"]
6
+
7
+ name_double = double('name')
8
+ ident = double('ident')
9
+ expect(ident).to receive(:name).and_return(name_double)
10
+ result = Volt::Sql::WhereCall.new(ident).call(ast)
11
+
12
+ expect(result).to eq(name_double)
13
+ end
14
+
15
+ it 'should replay the ast with nested' do
16
+ ast = ["c", ["c", "ident", "lat"], ">", 80]
17
+
18
+ ident = double('ident')
19
+ lat = double('lat')
20
+ expect(ident).to receive(:lat).and_return(lat)
21
+
22
+ final = double('final')
23
+ expect(lat).to receive(:>).with(80).and_return(final)
24
+
25
+ result = Volt::Sql::WhereCall.new(ident).call(ast)
26
+
27
+ expect(result).to eq(final)
28
+ end
29
+
30
+ it 'should replay with nested and logical operators' do
31
+ ast = [
32
+ "c",
33
+ ["c", ["c", "ident", "lat"], ">", 80],
34
+ '&',
35
+ ["c", ["c", "ident", "lng"], "<", 50]
36
+ ]
37
+
38
+ ident = double('ident')
39
+ lat = double('lat')
40
+ expect(ident).to receive(:lat).and_return(lat)
41
+
42
+ lng = double('lng')
43
+ expect(ident).to receive(:lng).and_return(lng)
44
+
45
+ left = double('left')
46
+ expect(lat).to receive(:>).with(80).and_return(left)
47
+
48
+ right = double('right')
49
+ expect(lng).to receive(:<).with(50).and_return(right)
50
+
51
+ final = double('final')
52
+ expect(left).to receive(:&).with(right).and_return(final)
53
+
54
+ result = Volt::Sql::WhereCall.new(ident).call(ast)
55
+
56
+ expect(result).to eq(final)
57
+
58
+ end
59
+ end
@@ -0,0 +1,178 @@
1
+ require 'spec_helper'
2
+ require_relative 'helpers'
3
+
4
+ describe Volt::Sql::TableReconcile do
5
+ let(:db_adaptor) { Volt::DataStore.fetch(volt_app) }
6
+ let(:db) { db_adaptor.db }
7
+ before do
8
+ # Access store, so we trigger cleanup after
9
+ store
10
+ end
11
+
12
+ include Volt::Spec::Helpers
13
+
14
+ it 'should create a table when a new model is defined' do
15
+ expect(db.tables).to_not include(:sample_model1)
16
+
17
+ class SampleModel1 < Volt::Model
18
+ temporary
19
+ end
20
+
21
+ reconcile!
22
+
23
+ expect(db.tables).to include(:sample_model1s)
24
+ end
25
+
26
+ it 'should assign the right types to fields' do
27
+ class SampleModel2 < Volt::Model
28
+ temporary
29
+
30
+ field :name, String
31
+ field :title, String, size: 50
32
+ field :place, String, nil: false
33
+ field :created_at, VoltTime
34
+ field :count, Fixnum
35
+ field :cash, Float
36
+ field :is_admin, Volt::Boolean
37
+ end
38
+
39
+ adaptor_name = db_adaptor.adaptor_name
40
+
41
+ reconcile = Volt::Sql::TableReconcile.new(db_adaptor, db, SampleModel2)
42
+ reconcile.run
43
+ db_fields = reconcile.db_fields_for_table(:sample_model2s)
44
+
45
+ schema = {
46
+ :id=>{:db_type=>"text", :default=>nil, :allow_null=>false, :primary_key=>true, :type=>:string, :auto_increment=>false, :ruby_default=>nil},
47
+ :extra=>{:db_type=>"json", :default=>nil, :allow_null=>true, :primary_key=>false, :type=>:json, :ruby_default=>nil},
48
+ :created_at=>{:db_type=>"timestamp without time zone", :default=>nil, :allow_null=>true, :primary_key=>false, :type=>:datetime, :ruby_default=>nil},
49
+ :count=>{:db_type=>"integer", :default=>nil, :allow_null=>true, :primary_key=>false, :type=>:integer, :ruby_default=>nil},
50
+ :cash=>{:db_type=>"double precision", :default=>nil, :allow_null=>true, :primary_key=>false, :type=>:float, :ruby_default=>nil},
51
+ :title => {:db_type=>"character varying(50)", :default=>nil, :allow_null=>true, :primary_key=>false, :type=>:string, :ruby_default=>nil, :max_length=>50},
52
+ :is_admin => {:db_type=>"boolean", :default=>nil, :allow_null=>true, :primary_key=>false, :type=>:boolean, :ruby_default=>nil},
53
+ :place => {:db_type=>"text", :default=>nil, :allow_null=>false, :primary_key=>false, :type=>:string, :ruby_default=>nil}
54
+ }
55
+
56
+ if adaptor_name == 'sqlite'
57
+ schema[:name] = {:db_type=>"text", :default=>nil, :allow_null=>true, :primary_key=>false, :type=>:string, :ruby_default=>nil}
58
+ else
59
+ schema[:name] = {:db_type=>"text", :default=>nil, :allow_null=>true, :primary_key=>false, :type=>:string, :ruby_default=>nil}
60
+ end
61
+
62
+ schema.each_pair do |field_name, attrs|
63
+ expect(db_fields[field_name].without(:oid)).to eq(attrs)
64
+ end
65
+
66
+ # Also check if volt can decode these back to volt field declarations
67
+ klass_and_db_opts = {
68
+ created_at: [[VoltTime, NilClass], {}],
69
+ title: [[String, NilClass], size: 50],
70
+ place: [[String], {}],
71
+ count: [[Fixnum, NilClass], {}],
72
+ cash: [[Float, NilClass], {}],
73
+ is_admin: [[Volt::Boolean, NilClass], {}]
74
+ }
75
+
76
+ klass_and_db_opts.each_pair do |field_name, klasses_and_db_opts|
77
+ expect_klasses, expect_db_opts = klasses_and_db_opts
78
+
79
+ klasses, db_opts = Volt::Sql::Helper.klasses_and_options_from_db(db_fields[field_name])
80
+
81
+ expect(expect_klasses).to eq(klasses)
82
+ expect(expect_db_opts).to eq(db_opts)
83
+ end
84
+
85
+ end
86
+
87
+ it 'should create migrations when a field changes' do
88
+ class SampleModel3 < Volt::Model
89
+ temporary
90
+ field :some_num, Fixnum
91
+ end
92
+
93
+ reconcile!
94
+
95
+ expect(db.tables).to include(:sample_model3s)
96
+
97
+ remove_model(SampleModel3)
98
+
99
+ db_adaptor.skip_reconcile do
100
+ class SampleModel3 < Volt::Model
101
+ temporary
102
+ field :some_num, String
103
+ end
104
+ end
105
+
106
+ reconcile = Volt::Sql::TableReconcile.new(db_adaptor, db, SampleModel3)
107
+
108
+ allow(reconcile.field_updater).to receive(:generate_and_run)
109
+ .with(
110
+ "column_change_sample_model3s_some_num",
111
+ "set_column_type :sample_model3s, :some_num, String, {:allow_null=>true, :text=>true}",
112
+ "set_column_type :sample_model3s, :some_num, Fixnum, {:allow_null=>true}"
113
+ ).and_return(nil)
114
+
115
+ reconcile.run
116
+ end
117
+
118
+ it 'should create a migration when a field is removed' do
119
+ class SampleModel4 < Volt::Model
120
+ temporary
121
+ field :some_num, Fixnum
122
+ end
123
+
124
+ reconcile!
125
+
126
+ expect(db.tables).to include(:sample_model4s)
127
+
128
+ remove_model(SampleModel4)
129
+
130
+ db_adaptor.skip_reconcile do
131
+ class SampleModel4 < Volt::Model
132
+ temporary
133
+ end
134
+ end
135
+
136
+ reconcile = Volt::Sql::TableReconcile.new(db_adaptor, db, SampleModel4)
137
+
138
+ expect(reconcile.field_updater).to receive(:generate_and_run)
139
+ .with(
140
+ "remove_sample_model4s_some_num",
141
+ "drop_column :sample_model4s, :some_num",
142
+ "add_column :sample_model4s, :some_num, Fixnum, {:allow_null=>true}"
143
+ )
144
+
145
+ reconcile.run
146
+ end
147
+
148
+ it 'should create a migration to change the allow null status' do
149
+ class SampleModel5 < Volt::Model
150
+ temporary
151
+ field :some_num, Fixnum
152
+ end
153
+
154
+ reconcile!
155
+
156
+ expect(db.tables).to include(:sample_model5s)
157
+
158
+ remove_model(SampleModel5)
159
+
160
+ db_adaptor.skip_reconcile do
161
+ class SampleModel5 < Volt::Model
162
+ temporary
163
+ field :some_num, Fixnum, nil: false
164
+ end
165
+ end
166
+
167
+ reconcile = Volt::Sql::TableReconcile.new(db_adaptor, db, SampleModel5)
168
+
169
+ expect(reconcile.field_updater).to receive(:generate_and_run)
170
+ .with(
171
+ "column_change_sample_model5s_some_num",
172
+ "set_column_not_null :sample_model5s, :some_num",
173
+ "set_column_allow_null :sample_model5s, :some_num"
174
+ )
175
+
176
+ reconcile.run
177
+ end
178
+ end
@@ -0,0 +1,36 @@
1
+ require 'volt'
2
+
3
+ if ENV['DB'] = 'pg'
4
+ Volt.configure do |config|
5
+ config.db.uri = 'postgres://ryanstout:@localhost:5432/volt_sql_test'
6
+ end
7
+ end
8
+
9
+ require 'volt/spec/setup'
10
+
11
+ unless RUBY_PLATFORM == 'opal'
12
+ begin
13
+ require 'pry-byebug'
14
+ rescue LoadError => e
15
+ # Ignore if not installed
16
+ end
17
+ end
18
+
19
+ # Specs are run against the kitchen sink app
20
+ kitchen_sink_path = File.expand_path(File.join(File.dirname(__FILE__), 'apps/kitchen_sink'))
21
+ Volt.spec_setup(kitchen_sink_path)
22
+
23
+ unless RUBY_PLATFORM == 'opal'
24
+ RSpec.configure do |config|
25
+ config.run_all_when_everything_filtered = true
26
+ config.filter_run :focus
27
+
28
+ # Run specs in random order to surface order dependencies. If you find an
29
+ # order dependency and want to debug it, you can fix the order by providing
30
+ # the seed, which is printed after each run.
31
+ # --seed 1234
32
+ config.order = 'random'
33
+ config.seed = '10780'
34
+ end
35
+
36
+ end
data/volt-sql.gemspec ADDED
@@ -0,0 +1,23 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'volt/sql/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = "volt-sql"
8
+ spec.version = Volt::Sql::VERSION
9
+ spec.authors = ["Ryan Stout"]
10
+ spec.email = ["ryan@agileproductions.com"]
11
+ spec.summary = %q{sql database drivers for volt}
12
+ spec.homepage = ""
13
+ spec.license = "MIT"
14
+
15
+ spec.files = `git ls-files -z`.split("\x0")
16
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
17
+ spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
18
+ spec.require_paths = ["lib"]
19
+
20
+ spec.add_dependency 'sequel', '~> 4.23.0'
21
+ spec.add_development_dependency "rake"
22
+ spec.add_development_dependency "volt"
23
+ end
metadata ADDED
@@ -0,0 +1,122 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: volt-sql
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ platform: ruby
6
+ authors:
7
+ - Ryan Stout
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-11-03 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: sequel
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - "~>"
18
+ - !ruby/object:Gem::Version
19
+ version: 4.23.0
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - "~>"
25
+ - !ruby/object:Gem::Version
26
+ version: 4.23.0
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ - !ruby/object:Gem::Dependency
42
+ name: volt
43
+ requirement: !ruby/object:Gem::Requirement
44
+ requirements:
45
+ - - ">="
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ type: :development
49
+ prerelease: false
50
+ version_requirements: !ruby/object:Gem::Requirement
51
+ requirements:
52
+ - - ">="
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ description:
56
+ email:
57
+ - ryan@agileproductions.com
58
+ executables: []
59
+ extensions: []
60
+ extra_rdoc_files: []
61
+ files:
62
+ - ".gitignore"
63
+ - Gemfile
64
+ - README.md
65
+ - Rakefile
66
+ - app/sql/config/dependencies.rb
67
+ - app/sql/config/initializers/boot.rb
68
+ - app/sql/config/routes.rb
69
+ - app/sql/controllers/main_controller.rb
70
+ - app/sql/lib/field_updater.rb
71
+ - app/sql/lib/helper.rb
72
+ - app/sql/lib/index_updater.rb
73
+ - app/sql/lib/migration.rb
74
+ - app/sql/lib/migration_generator.rb
75
+ - app/sql/lib/reconcile.rb
76
+ - app/sql/lib/sql_adaptor_client.rb
77
+ - app/sql/lib/sql_adaptor_server.rb
78
+ - app/sql/lib/sql_logger.rb
79
+ - app/sql/lib/store_persistor.rb
80
+ - app/sql/lib/table_reconcile.rb
81
+ - app/sql/lib/where_call.rb
82
+ - app/sql/views/main/index.html
83
+ - config/db/development.db
84
+ - config/db/test.db
85
+ - lib/volt/sql.rb
86
+ - lib/volt/sql/version.rb
87
+ - spec/postgres/lib/helpers.rb
88
+ - spec/postgres/lib/index_updater_spec.rb
89
+ - spec/postgres/lib/postgres_where_call_spec.rb
90
+ - spec/postgres/lib/table_reconcile_spec.rb
91
+ - spec/spec_helper.rb
92
+ - volt-sql.gemspec
93
+ homepage: ''
94
+ licenses:
95
+ - MIT
96
+ metadata: {}
97
+ post_install_message:
98
+ rdoc_options: []
99
+ require_paths:
100
+ - lib
101
+ required_ruby_version: !ruby/object:Gem::Requirement
102
+ requirements:
103
+ - - ">="
104
+ - !ruby/object:Gem::Version
105
+ version: '0'
106
+ required_rubygems_version: !ruby/object:Gem::Requirement
107
+ requirements:
108
+ - - ">="
109
+ - !ruby/object:Gem::Version
110
+ version: '0'
111
+ requirements: []
112
+ rubyforge_project:
113
+ rubygems_version: 2.4.5
114
+ signing_key:
115
+ specification_version: 4
116
+ summary: sql database drivers for volt
117
+ test_files:
118
+ - spec/postgres/lib/helpers.rb
119
+ - spec/postgres/lib/index_updater_spec.rb
120
+ - spec/postgres/lib/postgres_where_call_spec.rb
121
+ - spec/postgres/lib/table_reconcile_spec.rb
122
+ - spec/spec_helper.rb