miguel 0.1.0.pre2 → 0.1.0.pre3

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 6cc20233657ee51f12faa39ae0c81c55184b09cb
4
- data.tar.gz: 983d7e268d3cd5f9448b128b56f84f5a2f64cdea
3
+ metadata.gz: be756726f39332cdc296e5a236f934748da5676d
4
+ data.tar.gz: bac00b2ce0a29b8e020f78c5bb76bf41b4185ac7
5
5
  SHA512:
6
- metadata.gz: 61288b386f01bf54e2fedda5f0988e70ab0aaf3eb2133279b43ebc69f277a7cff39c57170eb92c5209fd418dd0a1613ef9ef43a87d75f2d6112ebbcfa96c8b66
7
- data.tar.gz: 43f256c47f62d61d58f2031d5ec0cbcf6c8d1ce89c0e583e272c7701d9e618e8d51590010412ba4fb37bfca506c49e08f625ac15004ae6541f58f2d7d2107bfe
6
+ metadata.gz: d7ff9bb4c91af68d0902e63f3bf4616ffece4be5015ed3517cf5915f86632f1dd60a8d3f37b7c1271803795c79f4de9d9ab70d1ff5194002562bdd06897b6aa4
7
+ data.tar.gz: 2d1ca527b7bf924d5fb571c1a320d86030e9a004c4bd13c106c41fd795f571e079c1359ead9f4dd11e7e301a6ba534244a7b8a9f53a0fc680a7036e53533d258
data/.travis.gemfile CHANGED
@@ -5,5 +5,6 @@ source 'https://rubygems.org'
5
5
  gem 'rake'
6
6
  gem 'bacon', '~> 1.2'
7
7
  gem 'sequel', '~> 4.0'
8
+ gem 'sqlite3', '~> 1.3'
8
9
 
9
10
  # EOF #
data/README.md CHANGED
@@ -1,6 +1,6 @@
1
1
  # Miguel
2
2
 
3
- [![Gem Version](https://img.shields.io/gem/v/miguel.svg)](http://rubygems.org/gems/miguel) [![Build Status](https://img.shields.io/travis/raxoft/miguel.svg?branch=master)](http://travis-ci.org/raxoft/miguel) [![Dependency Status](https://img.shields.io/gemnasium/raxoft/miguel.svg)](https://gemnasium.com/raxoft/miguel) [![Code Climate](https://img.shields.io/codeclimate/github/raxoft/miguel.svg)](https://codeclimate.com/github/raxoft/miguel) [![Coverage](https://img.shields.io/codeclimate/coverage/github/raxoft/miguel.svg)](https://codeclimate.com/github/raxoft/miguel)
3
+ [![Gem Version](https://img.shields.io/gem/v/miguel.svg)](http://rubygems.org/gems/miguel) [![Build Status](https://travis-ci.org/raxoft/miguel.svg?branch=master)](http://travis-ci.org/raxoft/miguel) [![Dependency Status](https://img.shields.io/gemnasium/raxoft/miguel.svg)](https://gemnasium.com/raxoft/miguel) [![Code Climate](https://img.shields.io/codeclimate/github/raxoft/miguel.svg)](https://codeclimate.com/github/raxoft/miguel) [![Coverage](https://img.shields.io/codeclimate/coverage/github/raxoft/miguel.svg)](https://codeclimate.com/github/raxoft/miguel) [![Donate](https://img.shields.io/badge/support-donate-green.svg)](https://www.paypal.com/cgi-bin/webscr?cmd=_xclick&business=paypal%40raxoft%2ecz&item_name=Miguel%20%2d%20Database%20migration%20tool&no_shipping=1&return=https%3a%2f%2fgithub%2ecom%2fraxoft%2fmiguel&cancel_return=https%3a%2f%2fgithub%2ecom%2fraxoft%2fmiguel&cn=Optional%20Feedback&tax=0&currency_code=EUR&bn=PP%2dDonationsBF&charset=UTF%2d8)
4
4
 
5
5
  Miguel is a tool for sane management of database schemas. It aims to help with these goals:
6
6
 
@@ -10,7 +10,7 @@ Miguel is a tool for sane management of database schemas. It aims to help with t
10
10
 
11
11
  To achieve this, it provides the following features:
12
12
 
13
- * [Sequel](http://sequel.jeremyevans.net/)-like DSL for schema description with some enhancements.
13
+ * [Sequel][]-like DSL for schema description with some enhancements.
14
14
  * Load schema from given description file or from given database.
15
15
  * Show changes necessary to turn one schema into another.
16
16
  * Render those changes as Sequel's change or up/down migrations.
@@ -110,22 +110,30 @@ See documentation of the `set_defaults` method for details.
110
110
  The preset defaults are like this:
111
111
 
112
112
  ``` ruby
113
- set_defaults :global, :null => false
114
- set_defaults :primary_key, :type => :integer, :unsigned => true
115
- set_defaults :foreign_key, :key => :id, :type => :integer, :unsigned => true
116
- set_defaults :unique, :index, :unique => true
113
+ set_defaults :global, null: false
114
+
117
115
  set_defaults :Bool, :TrueClass
118
- set_defaults :True, :TrueClass, :default => true
119
- set_defaults :False, :TrueClass, :default => false
120
- set_defaults :Signed, :integer, :unsigned => false
121
- set_defaults :Unsigned, :integer, :unsigned => true
122
- set_defaults :Text, :String, :text => true
123
- set_defaults :Time, :timestamp, :default => 0
124
- set_defaults :Time?, :timestamp, :default => nil
116
+ set_defaults :True, :TrueClass, default: true
117
+ set_defaults :False, :TrueClass, default: false
118
+ set_defaults :Signed, :integer, unsigned: false
119
+ set_defaults :Unsigned, :integer, unsigned: true
120
+ set_defaults :Text, :String, text: true
121
+ set_defaults :Time, :timestamp, default: '0000-00-00 00:00:00'
122
+ set_defaults :Time?, :timestamp, default: nil
123
+
124
+ set_defaults :unique, :index, unique: true
125
+
126
+ set_defaults :primary_key, type: :integer, unsigned: false
127
+ set_defaults :foreign_key, key: :id, type: :integer, unsigned: false
125
128
  ```
126
129
 
127
- Finally, the `timestamp` helper can be used to create the
130
+ If you prefer unsigned keys instead and your database engine supports it,
131
+ you can pass the `unsigned_keys: true` option to `define` to make it happen.
132
+
133
+ Finally, the `timestamps` helper can be used to create the
128
134
  `create_time` and `update_time` timestamps for you.
135
+ If you pass the `mysql_timestamps: true` option to `define`,
136
+ the `update_time` timestamp will have the MySQL auto-update feature enabled.
129
137
 
130
138
  ## Using the command
131
139
 
@@ -145,10 +153,13 @@ the command will always ask for a confirmation before changing anything in the d
145
153
  (unless you use the `--force` option).
146
154
 
147
155
  Databases can be specified either by their Sequel URL like
156
+ `sqlite://test.db`
157
+ or
148
158
  `mysql2://user:password@localhost/main`,
149
159
  or by the common database `.yml` config file:
150
160
 
151
161
  ``` yaml
162
+ # Example db.yml.
152
163
  adapter: mysql2
153
164
  user: jim
154
165
  password: sup3rsecr3t
@@ -176,12 +187,15 @@ leaving dozens of piecewise migration files finally behind.
176
187
 
177
188
  ## Limitations
178
189
 
179
- The type support is geared towards the MySQL, especially the timestamp types.
190
+ The database specific type support is geared towards [MySQL][] and [SQLite][].
180
191
  Generic types should work with any database, but your mileage may vary.
181
192
 
182
- It is currently not possible to describe renaming of columns.
193
+ Changing the type of primary keys can be as problematic as with normal Sequel migrations,
194
+ so it's best to set them once and stick with them.
195
+
196
+ It is currently not possible to describe renaming of columns or tables.
183
197
  If you need that,
184
- simply rename the columns directly in the database or by using standard Sequel migration,
198
+ simply rename them directly in the database or by using standard Sequel migration,
185
199
  and adjust the schema description accordingly.
186
200
 
187
201
  ## Credits
@@ -189,3 +203,7 @@ and adjust the schema description accordingly.
189
203
  Copyright © 2015 Patrik Rak
190
204
 
191
205
  Miguel is released under the MIT license.
206
+
207
+ [Sequel]: http://sequel.jeremyevans.net/
208
+ [MySQL]: https://www.mysql.com/
209
+ [SQLite]: https://www.sqlite.org/
data/examples/schema.rb CHANGED
@@ -126,7 +126,7 @@ Miguel::Schema.define do
126
126
 
127
127
  # User's followers.
128
128
 
129
- join_table :user_id, :user, :follower_id, :user, :user_followers
129
+ join_table :user_id, :users, :follower_id, :users, :user_followers
130
130
 
131
131
  end
132
132
 
@@ -163,19 +163,27 @@ module Miguel
163
163
  db = if name.nil? or name.empty?
164
164
  fail "Missing database name."
165
165
  elsif File.exist?( name )
166
- require 'yaml'
167
- config = YAML.load_file( name )
168
- env = self.env || "development"
169
- config = config[ env ] || config[ env.to_sym ] || config
170
- config.keys.each{ |k| config[ k.to_sym ] = config.delete( k ) }
166
+ config = load_db_config( name )
171
167
  Sequel.connect( config )
172
- else
168
+ elsif name =~ /:\/\//
173
169
  Sequel.connect( name )
170
+ else
171
+ fail "Database config #{name} not found."
174
172
  end
175
173
  db.loggers = loggers
176
174
  db
177
175
  end
178
176
 
177
+ # Load database config from given file.
178
+ def load_db_config( name )
179
+ require 'yaml'
180
+ config = YAML.load_file( name )
181
+ env = self.env || "development"
182
+ config = config[ env ] || config[ env.to_sym ] || config
183
+ config.keys.each{ |k| config[ k.to_sym ] = config.delete( k ) }
184
+ config
185
+ end
186
+
179
187
  # Show changes between the two schemas.
180
188
  def show_changes( from, to )
181
189
  m = Migrator.new
@@ -17,7 +17,7 @@ module Miguel
17
17
 
18
18
  private
19
19
 
20
- # Which characrets we convert when parsing enum values.
20
+ # Which characters we convert when parsing enum values.
21
21
  # Quite likely not exhaustive, but sufficient for our purposes.
22
22
  ESCAPED_CHARS = {
23
23
  "''" => "'",
@@ -56,19 +56,24 @@ module Miguel
56
56
  when /\A(enum|set)\((.*)\)\z/
57
57
  return $1.to_sym, :elements => parse_elements( $2 )
58
58
  end
59
+ when :sqlite
60
+ case type
61
+ when /\Ainteger UNSIGNED\z/
62
+ return :integer, :unsigned => true
63
+ end
59
64
  end
60
65
 
61
66
  case type
62
67
  when /\Avarchar/
63
- return :string, :default_size => 255
68
+ return :String, :default_size => 255
64
69
  when /\Achar/
65
- return :string, :fixed => true, :default_size => 255
70
+ return :String, :fixed => true, :default_size => 255
66
71
  when /\Atext\z/
67
- return :string, :text => true
72
+ return :String, :text => true
68
73
  when /\A(\w+)\([\s\d,]+\)\z/
69
74
  return $1.to_sym
70
75
  when /\A\w+\z/
71
- return type
76
+ return type.to_sym
72
77
  end
73
78
 
74
79
  ruby_type
@@ -98,6 +103,28 @@ module Miguel
98
103
  [ type, opts ]
99
104
  end
100
105
 
106
+ # Types we support for default values. Anything else is converted to String.
107
+ DEFAULT_TYPES = [ String, Numeric, TrueClass, FalseClass ]
108
+
109
+ # Convert given database default of given type to default used by our schema definitions.
110
+ def revert_default( type, default, ruby_default )
111
+ default = ruby_default unless ruby_default.nil?
112
+ return if default.nil?
113
+
114
+ default = default.to_s unless DEFAULT_TYPES.any?{ |x| default.is_a?( x ) }
115
+
116
+ if type.to_s =~ /date|time/
117
+ case default
118
+ when /\A'([-: \d]+)'\z/
119
+ default = $1
120
+ when 'CURRENT_TIMESTAMP'
121
+ # This matches our use of MySQL timestamps in schema definitions.
122
+ default = Sequel.lit('CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP')
123
+ end
124
+ end
125
+ default
126
+ end
127
+
101
128
  # Import indexes of given table.
102
129
  def import_indexes( table )
103
130
  # Foreign keys also automatically create indexes, which we must exclude when importing.
@@ -122,12 +149,6 @@ module Miguel
122
149
  end
123
150
  end
124
151
 
125
- # Our custom mapping of some database defaults into the values we use in our schemas.
126
- DEFAULT_CONSTANTS = {
127
- "0000-00-00 00:00:00" => 0,
128
- "CURRENT_TIMESTAMP" => Sequel.lit("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP"),
129
- }
130
-
131
152
  # Options which are ignored for columns.
132
153
  # These are usually just schema hints which the user normally doesn't specify.
133
154
  IGNORED_OPTS = [ :max_length ]
@@ -169,11 +190,9 @@ module Miguel
169
190
  default = opts.delete( :default )
170
191
  ruby_default = opts.delete( :ruby_default )
171
192
 
172
- default = DEFAULT_CONSTANTS[ default ] || ( ruby_default.nil? ? default : ruby_default )
193
+ default = revert_default( type, default, ruby_default )
173
194
 
174
- unless default.nil?
175
- opts[ :default ] = default
176
- end
195
+ opts[ :default ] = default unless default.nil?
177
196
 
178
197
  # Deal with primary keys, which is a bit obscure because of the auto-increment handling.
179
198
 
data/lib/miguel/schema.rb CHANGED
@@ -17,9 +17,9 @@ module Miguel
17
17
  def out_value( value )
18
18
  case value
19
19
  when Hash
20
- "{" << ( value.map{ |k,v| "#{out_value( k )} => #{out_value( v )}" }.join( ', ' ) ) << "}"
20
+ "{#{ value.map{ |k,v| "#{out_value( k )} => #{out_value( v )}" }.join( ', ' ) }}"
21
21
  when Array
22
- "[" << ( value.map{ |v| out_value( v ) }.join( ', ' ) ) << "]"
22
+ "[#{ value.map{ |v| out_value( v ) }.join( ', ' ) }]"
23
23
  when Sequel::LiteralString
24
24
  "Sequel.lit(#{value.to_s.inspect})"
25
25
  else
@@ -131,12 +131,22 @@ module Miguel
131
131
  CANONIC_TYPES[ t ] || t
132
132
  end
133
133
 
134
+ # Convert given size into its canonic form.
135
+ def canonic_size( size )
136
+ if canonic_type == :decimal && size.is_a?( Integer )
137
+ [ size, 0 ]
138
+ else
139
+ size
140
+ end
141
+ end
142
+
134
143
  # Default options implied for certain types.
135
144
  DEFAULT_OPTS = {
136
145
  :string => { :size => 255 },
137
146
  :bigint => { :size => 20 },
138
147
  :decimal => { :size => [ 10, 0 ] },
139
148
  :integer => { :unsigned => false },
149
+ :primary_key => { :unsigned => false, :type => :integer },
140
150
  }
141
151
 
142
152
  # Options which are ignored for columns.
@@ -149,6 +159,7 @@ module Miguel
149
159
  o = { :type => canonic_type, :default => default }
150
160
  o.merge!( DEFAULT_OPTS[ canonic_type ] || {} )
151
161
  o.merge!( opts )
162
+ o[ :size ] = canonic_size( o[ :size ] )
152
163
  o.delete_if{ |key, value| IGNORED_OPTS.include? key }
153
164
  end
154
165
 
@@ -229,7 +240,9 @@ module Miguel
229
240
 
230
241
  # Get the foreign key options, in a canonic way.
231
242
  def canonic_opts
232
- opts.reject{ |key, value| IGNORED_OPTS.include? key }
243
+ o = { :on_update => :no_action, :on_delete => :no_action }
244
+ o.merge!( opts )
245
+ o.delete_if{ |key, value| IGNORED_OPTS.include? key }
233
246
  end
234
247
 
235
248
  # Compare one foreign key with another one.
@@ -274,15 +287,21 @@ module Miguel
274
287
 
275
288
  # Create the default timestamp fields.
276
289
  def timestamps
277
- # Unfortunately, MySQL allows only either automatic create timestamp
278
- # (DEFAULT CURRENT_TIMESTAMP) or automatic update timestamp (DEFAULT
279
- # CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP), but not both - one
280
- # has to be updated manually anyway. So we choose to have the update timestamp
281
- # automatically updated, and let the create one to be set manually.
282
- # Also, Sequel doesn't currently honor :on_update for column definitions,
283
- # so we have to use default literal to make it work. Sigh.
284
- timestamp :create_time, :null => false, :default => 0
285
- timestamp :update_time, :null => false, :default => Sequel.lit( 'CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP' )
290
+ opts = @table.schema.opts
291
+ if opts[ :mysql_timestamps ]
292
+ # Unfortunately, MySQL allows only either automatic create timestamp
293
+ # (DEFAULT CURRENT_TIMESTAMP) or automatic update timestamp (DEFAULT
294
+ # CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP), but not both - one
295
+ # has to be updated manually anyway. So we choose to have the update timestamp
296
+ # automatically updated, and let the create one to be set manually.
297
+ # Also, Sequel doesn't currently honor :on_update for column definitions,
298
+ # so we have to use default literal to make it work. Sigh.
299
+ timestamp :create_time, :null => false, :default => '0000-00-00 00:00:00'
300
+ timestamp :update_time, :null => false, :default => Sequel.lit( 'CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP' )
301
+ else
302
+ Time :create_time
303
+ Time :update_time
304
+ end
286
305
  end
287
306
  end
288
307
 
@@ -373,12 +392,16 @@ module Miguel
373
392
 
374
393
  end
375
394
 
395
+ # Schema options.
396
+ attr_reader :opts
397
+
376
398
  # Create new schema.
377
- def initialize
399
+ def initialize( opts = {} )
378
400
  @tables = {}
379
401
  @aliases = {}
380
402
  @defaults = {}
381
403
  @callbacks = {}
404
+ @opts = self.class.default_options.merge(opts)
382
405
  end
383
406
 
384
407
  # Get all tables.
@@ -452,6 +475,15 @@ module Miguel
452
475
  @defaults[ name ] = args.pop if args.last.is_a? Hash
453
476
  @callbacks[ name ] = block
454
477
  fail( ArgumentError, "invalid defaults for #{name}" ) unless args.empty?
478
+ self
479
+ end
480
+
481
+ # Clear defaults and aliases for given statement.
482
+ def clear_defaults( name )
483
+ @aliases.delete( name )
484
+ @defaults.delete( name )
485
+ @callbacks.delete( name )
486
+ self
455
487
  end
456
488
 
457
489
  # Get default options for given statement.
@@ -464,31 +496,41 @@ module Miguel
464
496
  # The current set of defaults is as follows:
465
497
  #
466
498
  # :global, :null => false
467
- # :primary_key, :type => :integer, :unsigned => true
468
- # :foreign_key, :key => :id, :type => :integer, :unsigned => true
469
- # :unique, :index, :unique => true
499
+ #
470
500
  # :Bool, :TrueClass
471
501
  # :True, :TrueClass, :default => true
472
502
  # :False, :TrueClass, :default => false
473
503
  # :Signed, :integer, :unsigned => false
474
504
  # :Unsigned, :integer, :unsigned => true
475
505
  # :Text, :String, :text => true
476
- # :Time, :timestamp, :default => 0
506
+ # :Time, :timestamp, :default => '0000-00-00 00:00:00'
477
507
  # :Time?, :timestamp, :default => nil
478
- def set_standard_defaults
508
+ #
509
+ # :unique, :index, :unique => true
510
+ #
511
+ # :primary_key, :type => :integer, :unsigned => false
512
+ # :foreign_key, :key => :id, :type => :integer, :unsigned => false
513
+ #
514
+ # If the +unsigned_keys+ option is set to true, the keys
515
+ # are set up to use unsigned integers instead.
516
+ def set_standard_defaults( opts = self.opts )
479
517
 
480
518
  # We set NOT NULL on everything by default, but note the ?
481
519
  # syntax (like Text?) which declares the column as NULL.
482
- # We also like our keys unsigned, so we make that a default, too.
520
+
521
+ set_defaults :global, :null => false
522
+
523
+ # We also like our keys unsigned, so we allow setting that, too.
483
524
  # Unfortunately, :unsigned currently works only with :integer,
484
525
  # not the default :Integer, and :integer can't be specified for compound keys,
485
526
  # so we have to use the callback to set the type only at correct times.
486
527
 
487
- set_defaults :global, :null => false
488
- set_defaults :primary_key, :unsigned => true do |opts,args,table|
528
+ unsigned_keys = !! opts[ :unsigned_keys ]
529
+
530
+ set_defaults :primary_key, :unsigned => unsigned_keys do |opts,args,table|
489
531
  opts[ :type ] ||= :integer unless args.first.is_a? Array
490
532
  end
491
- set_defaults :foreign_key, :key => :id, :unsigned => true do |opts,args,table|
533
+ set_defaults :foreign_key, :key => :id, :unsigned => unsigned_keys do |opts,args,table|
492
534
  opts[ :type ] ||= :integer unless args.first.is_a? Array
493
535
  end
494
536
 
@@ -511,7 +553,7 @@ module Miguel
511
553
  # we have to be careful to turn off the MySQL autoupdate behavior.
512
554
  # That's why we have to set defaults explicitly.
513
555
 
514
- set_defaults :Time, :timestamp, :default => 0
556
+ set_defaults :Time, :timestamp, :default => '0000-00-00 00:00:00'
515
557
  set_defaults :Time?, :timestamp, :default => nil
516
558
 
517
559
  self
@@ -551,7 +593,7 @@ module Miguel
551
593
  end
552
594
 
553
595
  # Define schema with the provided block.
554
- def define( opts = {}, &block )
596
+ def define( &block )
555
597
  fail( ArgumentError, "missing schema block" ) unless block
556
598
  set_standard_defaults unless opts[ :use_defaults ] == false
557
599
  instance_eval &block
@@ -560,22 +602,69 @@ module Miguel
560
602
 
561
603
  class << self
562
604
 
563
- # The most recent schema defined by Schema.define.
564
- attr_reader :schema
605
+ # Mutex protecting access to thread sensitive variables.
606
+ LOCK = Mutex.new
607
+
608
+ # Get default schema options.
609
+ def default_options
610
+ sync{ @opts || {} }
611
+ end
612
+
613
+ # Set default schema options.
614
+ def default_options=( opts )
615
+ sync{ @opts = opts.nil? ? nil : opts.dup }
616
+ end
617
+ alias set_default_options default_options=
565
618
 
566
619
  # Define schema with provided block.
567
620
  def define( opts = {}, &block )
568
- @schema = new.define( opts, &block )
621
+ set_schema( new( opts ).define( &block ) )
569
622
  end
570
623
 
571
624
  # Load schema from given file.
572
- def load( name )
573
- @schema = nil
625
+ def load( name, opts = {} )
574
626
  name = File.expand_path( name )
575
- Kernel.load( name )
627
+ sync do
628
+ get_schema do
629
+ with_options( opts ) do
630
+ Kernel.load( name )
631
+ end
632
+ end
633
+ end
634
+ end
635
+
636
+ private
637
+
638
+ # Serialize access to thread sensitive variables.
639
+ def sync( &block )
640
+ LOCK.synchronize &block
641
+ rescue ThreadError
642
+ yield
643
+ end
644
+
645
+ # Store given schema for later if requested.
646
+ def set_schema( schema )
647
+ sync{ @schema = schema if @schema == self }
576
648
  schema
577
649
  end
578
650
 
651
+ # Execute given block and return stored schema.
652
+ def get_schema
653
+ @schema = self
654
+ yield
655
+ @schema unless @schema == self
656
+ ensure
657
+ @schema = nil
658
+ end
659
+
660
+ # Execute block with given options.
661
+ def with_options( opts )
662
+ saved, @opts = @opts, default_options.merge(opts)
663
+ yield
664
+ ensure
665
+ @opts = saved
666
+ end
667
+
579
668
  end
580
669
 
581
670
  end
data/miguel.gemspec CHANGED
@@ -4,7 +4,7 @@ require File.expand_path( '../lib/miguel/version', __FILE__ )
4
4
 
5
5
  Gem::Specification.new do |s|
6
6
  s.name = 'miguel'
7
- s.version = Miguel::VERSION + '.pre2'
7
+ s.version = Miguel::VERSION + '.pre3'
8
8
  s.summary = 'Database migrator and migration generator for Sequel.'
9
9
  s.description = <<EOT
10
10
  This gem makes it easy to create and maintain an up-to-date database schema
@@ -0,0 +1,100 @@
1
+ # Test schema.
2
+
3
+ Miguel::Schema.define do
4
+
5
+ table :sequel_types do
6
+ Integer :a0 # integer
7
+ String :a1 # varchar(255)
8
+ String :a2, :size=>50 # varchar(50)
9
+ String :a3, :fixed=>true # char(255)
10
+ String :a4, :fixed=>true, :size=>50 # char(50)
11
+ String :a5, :text=>true # text
12
+ File :b # blob
13
+ Fixnum :c # integer
14
+ Bignum :d # bigint
15
+ Float :e # double precision
16
+ BigDecimal :f # numeric
17
+ BigDecimal :f2, :size=>10 # numeric(10)
18
+ BigDecimal :f3, :size=>[10, 2] # numeric(10, 2)
19
+ Date :g # date
20
+ DateTime :h # timestamp
21
+ Time :i # timestamp
22
+ Numeric :j # numeric
23
+ TrueClass :k # boolean
24
+ FalseClass :l # boolean
25
+ end
26
+
27
+ table :miguel_types do
28
+ String :string
29
+ Text :text
30
+ File :blob
31
+ Integer :int
32
+ Signed :signed
33
+ Unsigned :unsigned
34
+ Float :float
35
+ Bool :bool
36
+ True :true
37
+ False :false
38
+ Time :t
39
+ end
40
+
41
+ table :timestamps do
42
+ Time :t1
43
+ Time? :t2
44
+ timestamps
45
+ end
46
+
47
+ table :users do
48
+ primary_key :id
49
+ String :name
50
+ unique :name
51
+ end
52
+
53
+ table :simple do
54
+ primary_key :id
55
+ foreign_key :user_id, :users
56
+ index :user_id
57
+ end
58
+
59
+ table :reuse do
60
+ primary_key :user_id
61
+ foreign_key [:user_id], :users
62
+ end
63
+
64
+ table :compound do
65
+ Unsigned :a
66
+ Unsigned :b
67
+ primary_key [:a, :b]
68
+ foreign_key [:b, :a], :compound, key: [:a, :b]
69
+ String :c
70
+ Signed :d
71
+ unique [:a, :c]
72
+ index [:b, :c, :d]
73
+ index [:b, :a]
74
+ end
75
+
76
+ table :null do
77
+ String? :string
78
+ Text? :text
79
+ File? :blob
80
+ Integer? :int
81
+ Signed? :signed
82
+ Unsigned? :unsigned
83
+ Float? :float
84
+ Bool? :bool
85
+ True? :true
86
+ False? :false
87
+ Time? :t
88
+ foreign_key? :user_id, :users
89
+ end
90
+
91
+ join_table :user_id, :users, :simple_id, :simple
92
+
93
+ join_table :left_id, :users, :right_id, :users, :self_join do
94
+ timestamps
95
+ index :create_time
96
+ end
97
+
98
+ end
99
+
100
+ # EOF #
@@ -0,0 +1,100 @@
1
+ table :sequel_types do
2
+ Integer :a0, :null => false
3
+ String :a1, :null => false
4
+ String :a2, :null => false, :size => 50
5
+ String :a3, :null => false, :fixed => true
6
+ String :a4, :null => false, :fixed => true, :size => 50
7
+ String :a5, :null => false, :text => true
8
+ File :b, :null => false
9
+ Fixnum :c, :null => false
10
+ Bignum :d, :null => false
11
+ Float :e, :null => false
12
+ BigDecimal :f, :null => false
13
+ BigDecimal :f2, :null => false, :size => 10
14
+ BigDecimal :f3, :null => false, :size => [10, 2]
15
+ Date :g, :null => false
16
+ DateTime :h, :null => false
17
+ timestamp :i, :null => false, :default => "0000-00-00 00:00:00"
18
+ Numeric :j, :null => false
19
+ TrueClass :k, :null => false
20
+ FalseClass :l, :null => false
21
+ end
22
+ table :miguel_types do
23
+ String :string, :null => false
24
+ String :text, :null => false, :text => true
25
+ File :blob, :null => false
26
+ Integer :int, :null => false
27
+ integer :signed, :null => false, :unsigned => false
28
+ integer :unsigned, :null => false, :unsigned => true
29
+ Float :float, :null => false
30
+ TrueClass :bool, :null => false
31
+ TrueClass :true, :null => false, :default => true
32
+ TrueClass :false, :null => false, :default => false
33
+ timestamp :t, :null => false, :default => "0000-00-00 00:00:00"
34
+ end
35
+ table :timestamps do
36
+ timestamp :t1, :null => false, :default => "0000-00-00 00:00:00"
37
+ timestamp :t2, :null => true, :default => nil
38
+ timestamp :create_time, :null => false, :default => "0000-00-00 00:00:00"
39
+ timestamp :update_time, :null => false, :default => "0000-00-00 00:00:00"
40
+ end
41
+ table :users do
42
+ primary_key :id, :null => false, :unsigned => false, :type => :integer
43
+ String :name, :null => false
44
+ index [:name], :null => false, :unique => true
45
+ end
46
+ table :simple do
47
+ primary_key :id, :null => false, :unsigned => false, :type => :integer
48
+ integer :user_id, :null => false, :key => [:id], :unsigned => false, :type => :integer
49
+ index [:user_id], :null => false
50
+ foreign_key [:user_id], :users, :null => false, :key => [:id], :unsigned => false, :type => :integer
51
+ end
52
+ table :reuse do
53
+ primary_key :user_id, :null => false, :unsigned => false, :type => :integer
54
+ foreign_key [:user_id], :users, :null => false, :key => [:id], :unsigned => false
55
+ end
56
+ table :compound do
57
+ integer :a, :null => false, :unsigned => true
58
+ integer :b, :null => false, :unsigned => true
59
+ primary_key [:a, :b], :null => false, :unsigned => false
60
+ String :c, :null => false
61
+ integer :d, :null => false, :unsigned => false
62
+ index [:a, :c], :null => false, :unique => true
63
+ index [:b, :c, :d], :null => false
64
+ index [:b, :a], :null => false
65
+ foreign_key [:b, :a], :compound, :null => false, :key => [:a, :b], :unsigned => false
66
+ end
67
+ table :null do
68
+ String :string, :null => true
69
+ String :text, :null => true, :text => true
70
+ File :blob, :null => true
71
+ Integer :int, :null => true
72
+ integer :signed, :null => true, :unsigned => false
73
+ integer :unsigned, :null => true, :unsigned => true
74
+ Float :float, :null => true
75
+ TrueClass :bool, :null => true
76
+ TrueClass :true, :null => true, :default => true
77
+ TrueClass :false, :null => true, :default => false
78
+ timestamp :t, :null => true, :default => nil
79
+ integer :user_id, :null => true, :key => [:id], :unsigned => false, :type => :integer
80
+ foreign_key [:user_id], :users, :null => true, :key => [:id], :unsigned => false, :type => :integer
81
+ end
82
+ table :simple_users do
83
+ integer :user_id, :null => false, :key => [:id], :unsigned => false, :type => :integer
84
+ integer :simple_id, :null => false, :key => [:id], :unsigned => false, :type => :integer
85
+ primary_key [:user_id, :simple_id], :null => false, :unsigned => false
86
+ index [:simple_id, :user_id], :null => false, :unique => true
87
+ foreign_key [:user_id], :users, :null => false, :key => [:id], :unsigned => false, :type => :integer
88
+ foreign_key [:simple_id], :simple, :null => false, :key => [:id], :unsigned => false, :type => :integer
89
+ end
90
+ table :self_join do
91
+ integer :left_id, :null => false, :key => [:id], :unsigned => false, :type => :integer
92
+ integer :right_id, :null => false, :key => [:id], :unsigned => false, :type => :integer
93
+ primary_key [:left_id, :right_id], :null => false, :unsigned => false
94
+ timestamp :create_time, :null => false, :default => "0000-00-00 00:00:00"
95
+ timestamp :update_time, :null => false, :default => "0000-00-00 00:00:00"
96
+ index [:right_id, :left_id], :null => false, :unique => true
97
+ index [:create_time], :null => false
98
+ foreign_key [:left_id], :users, :null => false, :key => [:id], :unsigned => false, :type => :integer
99
+ foreign_key [:right_id], :users, :null => false, :key => [:id], :unsigned => false, :type => :integer
100
+ end
@@ -0,0 +1,16 @@
1
+ # Simple schema.
2
+
3
+ Miguel::Schema.define do
4
+
5
+ table :items do
6
+ primary_key :id
7
+ String :name
8
+ foreign_key :parent_id, :items
9
+ timestamps
10
+ unique :name
11
+ index :parent_id
12
+ end
13
+
14
+ end
15
+
16
+ # EOF #
@@ -0,0 +1,10 @@
1
+ table :items do
2
+ primary_key :id, :null => false, :unsigned => false, :type => :integer
3
+ String :name, :null => false
4
+ integer :parent_id, :null => false, :key => [:id], :unsigned => false, :type => :integer
5
+ timestamp :create_time, :null => false, :default => "0000-00-00 00:00:00"
6
+ timestamp :update_time, :null => false, :default => "0000-00-00 00:00:00"
7
+ index [:name], :null => false, :unique => true
8
+ index [:parent_id], :null => false
9
+ foreign_key [:parent_id], :items, :null => false, :key => [:id], :unsigned => false, :type => :integer
10
+ end
@@ -0,0 +1,10 @@
1
+ table :items do
2
+ primary_key :id, :null => false, :unsigned => true, :type => :integer
3
+ String :name, :null => false
4
+ integer :parent_id, :null => false, :key => [:id], :unsigned => true, :type => :integer
5
+ timestamp :create_time, :null => false, :default => "0000-00-00 00:00:00"
6
+ timestamp :update_time, :null => false, :default => Sequel.lit("CURRENT_TIMESTAMP ON UPDATE CURRENT_TIMESTAMP")
7
+ index [:name], :null => false, :unique => true
8
+ index [:parent_id], :null => false
9
+ foreign_key [:parent_id], :items, :null => false, :key => [:id], :unsigned => true, :type => :integer
10
+ end
@@ -0,0 +1,47 @@
1
+ # Test Schema.
2
+
3
+ require 'miguel/schema'
4
+
5
+ describe Miguel::Schema do
6
+
7
+ DATA_DIR = File.expand_path( "#{__FILE__}/../data" )
8
+
9
+ def data( name )
10
+ "#{DATA_DIR}/#{name}"
11
+ end
12
+
13
+ should 'load and dump schema properly' do
14
+ schema = Miguel::Schema.load( data( 'schema.rb' ) )
15
+ schema.dump.to_s.should == File.read( data( 'schema.txt' ) )
16
+ end
17
+
18
+ should 'allow changing default schema options temporarily' do
19
+ schema = Miguel::Schema.load( data( 'simple.rb' ), unsigned_keys: true, mysql_timestamps: true )
20
+ schema.dump.to_s.should == File.read( data( 'simple_mysql.txt' ) )
21
+ Miguel::Schema.new.opts.should.be.empty
22
+
23
+ schema = Miguel::Schema.load( data( 'simple.rb' ) )
24
+ schema.dump.to_s.should == File.read( data( 'simple.txt' ) )
25
+ end
26
+
27
+ should 'allow changing default schema options permanently' do
28
+ Miguel::Schema.default_options.should == {}
29
+
30
+ Miguel::Schema.set_default_options( unsigned_keys: true, mysql_timestamps: true )
31
+ Miguel::Schema.new( test: true ).opts.should == { unsigned_keys: true, mysql_timestamps: true, test: true }
32
+ Miguel::Schema.default_options.should == { unsigned_keys: true, mysql_timestamps: true }
33
+
34
+ schema = Miguel::Schema.load( data( 'simple.rb' ) )
35
+ schema.dump.to_s.should == File.read( data( 'simple_mysql.txt' ) )
36
+
37
+ Miguel::Schema.default_options = nil
38
+ Miguel::Schema.default_options.should == {}
39
+ Miguel::Schema.new.opts.should.be.empty
40
+
41
+ schema = Miguel::Schema.load( data( 'simple.rb' ) )
42
+ schema.dump.to_s.should == File.read( data( 'simple.txt' ) )
43
+ end
44
+
45
+ end
46
+
47
+ # EOF #
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: miguel
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.1.0.pre2
4
+ version: 0.1.0.pre3
5
5
  platform: ruby
6
6
  authors:
7
7
  - Patrik Rak
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2015-09-17 00:00:00.000000000 Z
11
+ date: 2015-09-18 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: sequel
@@ -64,7 +64,13 @@ files:
64
64
  - lib/miguel/schema.rb
65
65
  - lib/miguel/version.rb
66
66
  - miguel.gemspec
67
+ - test/data/schema.rb
68
+ - test/data/schema.txt
69
+ - test/data/simple.rb
70
+ - test/data/simple.txt
71
+ - test/data/simple_mysql.txt
67
72
  - test/test_dumper.rb
73
+ - test/test_schema.rb
68
74
  homepage: http://rubygems.org/gems/miguel
69
75
  licenses:
70
76
  - MIT