db_schema 0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +9 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +4 -0
  5. data/Gemfile +4 -0
  6. data/Guardfile +17 -0
  7. data/LICENSE.txt +21 -0
  8. data/README.md +522 -0
  9. data/Rakefile +6 -0
  10. data/bin/console +10 -0
  11. data/bin/setup +8 -0
  12. data/db_schema.gemspec +35 -0
  13. data/lib/db_schema.rb +125 -0
  14. data/lib/db_schema/awesome_print.rb +246 -0
  15. data/lib/db_schema/changes.rb +396 -0
  16. data/lib/db_schema/configuration.rb +29 -0
  17. data/lib/db_schema/definitions.rb +122 -0
  18. data/lib/db_schema/definitions/field.rb +38 -0
  19. data/lib/db_schema/definitions/field/array.rb +19 -0
  20. data/lib/db_schema/definitions/field/base.rb +90 -0
  21. data/lib/db_schema/definitions/field/binary.rb +9 -0
  22. data/lib/db_schema/definitions/field/bit_string.rb +15 -0
  23. data/lib/db_schema/definitions/field/boolean.rb +9 -0
  24. data/lib/db_schema/definitions/field/character.rb +19 -0
  25. data/lib/db_schema/definitions/field/custom.rb +22 -0
  26. data/lib/db_schema/definitions/field/datetime.rb +30 -0
  27. data/lib/db_schema/definitions/field/geometric.rb +33 -0
  28. data/lib/db_schema/definitions/field/json.rb +13 -0
  29. data/lib/db_schema/definitions/field/monetary.rb +9 -0
  30. data/lib/db_schema/definitions/field/network.rb +17 -0
  31. data/lib/db_schema/definitions/field/numeric.rb +30 -0
  32. data/lib/db_schema/definitions/field/range.rb +29 -0
  33. data/lib/db_schema/definitions/field/text_search.rb +13 -0
  34. data/lib/db_schema/definitions/field/uuid.rb +9 -0
  35. data/lib/db_schema/dsl.rb +145 -0
  36. data/lib/db_schema/reader.rb +270 -0
  37. data/lib/db_schema/runner.rb +220 -0
  38. data/lib/db_schema/utils.rb +50 -0
  39. data/lib/db_schema/validator.rb +89 -0
  40. data/lib/db_schema/version.rb +3 -0
  41. metadata +239 -0
@@ -0,0 +1,29 @@
1
+ module DbSchema
2
+ class Configuration
3
+ attr_reader :adapter, :host, :port, :database, :user, :password
4
+
5
+ def initialize(adapter: 'postgres', host: 'localhost', port: 5432, database:, user: nil, password: '', log_changes: true, dry_run: false, post_check: true)
6
+ @adapter = adapter
7
+ @host = host
8
+ @port = port
9
+ @database = database
10
+ @user = user
11
+ @password = password
12
+ @log_changes = log_changes
13
+ @dry_run = dry_run
14
+ @post_check = post_check
15
+ end
16
+
17
+ def log_changes?
18
+ @log_changes
19
+ end
20
+
21
+ def dry_run?
22
+ @dry_run
23
+ end
24
+
25
+ def post_check_enabled?
26
+ @post_check
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,122 @@
1
+ require 'dry/equalizer'
2
+
3
+ module DbSchema
4
+ module Definitions
5
+ class Index
6
+ include Dry::Equalizer(:name, :fields, :unique?, :type, :condition)
7
+ attr_reader :name, :fields, :type, :condition
8
+
9
+ def initialize(name:, fields:, unique: false, type: :btree, condition: nil)
10
+ @name = name.to_sym
11
+ @fields = fields
12
+ @unique = unique
13
+ @type = type
14
+ @condition = condition
15
+ end
16
+
17
+ def unique?
18
+ @unique
19
+ end
20
+
21
+ def btree?
22
+ type == :btree
23
+ end
24
+
25
+ class Field
26
+ include Dry::Equalizer(:name, :order, :nulls)
27
+ attr_reader :name, :order, :nulls
28
+
29
+ def initialize(name, order: :asc, nulls: order == :asc ? :last : :first)
30
+ @name = name
31
+ @order = order
32
+ @nulls = nulls
33
+ end
34
+
35
+ def asc?
36
+ @order == :asc
37
+ end
38
+
39
+ def desc?
40
+ @order == :desc
41
+ end
42
+
43
+ def to_sequel
44
+ if asc?
45
+ Sequel.asc(name, nulls: nulls)
46
+ else
47
+ Sequel.desc(name, nulls: nulls)
48
+ end
49
+ end
50
+ end
51
+ end
52
+
53
+ class ForeignKey
54
+ include Dry::Equalizer(:name, :fields, :table, :keys, :on_update, :on_delete, :deferrable?)
55
+ attr_reader :name, :fields, :table, :keys, :on_update, :on_delete
56
+
57
+ def initialize(name:, fields:, table:, keys: [], on_update: :no_action, on_delete: :no_action, deferrable: false)
58
+ @name = name
59
+ @fields = fields
60
+ @table = table
61
+ @keys = keys
62
+ @on_update = on_update
63
+ @on_delete = on_delete
64
+ @deferrable = deferrable
65
+ end
66
+
67
+ def references_primary_key?
68
+ keys.empty?
69
+ end
70
+
71
+ def deferrable?
72
+ @deferrable
73
+ end
74
+
75
+ def options
76
+ {
77
+ deferrable: deferrable?,
78
+ name: name,
79
+ on_delete: on_delete,
80
+ on_update: on_update
81
+ }.tap do |options|
82
+ options[:key] = keys unless references_primary_key?
83
+ end
84
+ end
85
+ end
86
+
87
+ class CheckConstraint
88
+ include Dry::Equalizer(:name, :condition)
89
+ attr_reader :name, :condition
90
+
91
+ def initialize(name:, condition:)
92
+ @name = name
93
+ @condition = condition
94
+ end
95
+ end
96
+
97
+ class Table
98
+ include Dry::Equalizer(:name, :fields, :indices, :checks, :foreign_keys)
99
+ attr_reader :name, :fields, :indices, :checks, :foreign_keys
100
+
101
+ def initialize(name, fields: [], indices: [], checks: [], foreign_keys: [])
102
+ @name = name.to_sym
103
+ @fields = fields
104
+ @indices = indices
105
+ @checks = checks
106
+ @foreign_keys = foreign_keys
107
+ end
108
+ end
109
+
110
+ class Enum
111
+ include Dry::Equalizer(:name, :values)
112
+ attr_reader :name, :values
113
+
114
+ def initialize(name, values)
115
+ @name = name
116
+ @values = values
117
+ end
118
+ end
119
+ end
120
+ end
121
+
122
+ require_relative 'definitions/field'
@@ -0,0 +1,38 @@
1
+ module DbSchema
2
+ module Definitions
3
+ module Field
4
+ class << self
5
+ def build(name, type, **options)
6
+ type_class_for(type).new(name, **options)
7
+ end
8
+
9
+ def type_class_for(type)
10
+ registry.fetch(type) do |type|
11
+ raise ArgumentError, "#{type.inspect} type is not supported."
12
+ end
13
+ end
14
+
15
+ def registry
16
+ @registry ||= {}
17
+ end
18
+ end
19
+ end
20
+ end
21
+ end
22
+
23
+ require_relative 'field/base'
24
+ require_relative 'field/numeric'
25
+ require_relative 'field/monetary'
26
+ require_relative 'field/character'
27
+ require_relative 'field/binary'
28
+ require_relative 'field/datetime'
29
+ require_relative 'field/boolean'
30
+ require_relative 'field/geometric'
31
+ require_relative 'field/network'
32
+ require_relative 'field/bit_string'
33
+ require_relative 'field/text_search'
34
+ require_relative 'field/uuid'
35
+ require_relative 'field/json'
36
+ require_relative 'field/array'
37
+ require_relative 'field/range'
38
+ require_relative 'field/custom'
@@ -0,0 +1,19 @@
1
+ module DbSchema
2
+ module Definitions
3
+ module Field
4
+ class Array < Base
5
+ register :array
6
+ attr_reader :element_type
7
+
8
+ def initialize(name, of:, **options)
9
+ super(name, **options)
10
+ @element_type = Field.type_class_for(of)
11
+ end
12
+
13
+ def attributes
14
+ super.merge(element_type: element_type.type)
15
+ end
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,90 @@
1
+ module DbSchema
2
+ module Definitions
3
+ module Field
4
+ class Base
5
+ include Dry::Equalizer(:name, :type, :primary_key?, :options)
6
+ attr_reader :name, :default
7
+
8
+ def initialize(name, primary_key: false, null: true, default: nil, **attributes)
9
+ @name = name
10
+ @primary_key = primary_key
11
+ @null = null
12
+ @default = process_default(default)
13
+ @attributes = attributes
14
+ end
15
+
16
+ def primary_key?
17
+ @primary_key
18
+ end
19
+
20
+ def null?
21
+ !primary_key? && @null
22
+ end
23
+
24
+ def options
25
+ attributes.tap do |options|
26
+ options[:null] = false unless null?
27
+ options[:default] = default unless default.nil?
28
+ end
29
+ end
30
+
31
+ def attributes
32
+ self.class.valid_attributes.reduce({}) do |hash, attr_name|
33
+ if attr_value = @attributes[attr_name]
34
+ hash.merge(attr_name => attr_value)
35
+ elsif default_value = self.class.default_attribute_values[attr_name]
36
+ hash.merge(attr_name => default_value)
37
+ else
38
+ hash
39
+ end
40
+ end
41
+ end
42
+
43
+ def custom_type?
44
+ false
45
+ end
46
+
47
+ def type
48
+ self.class.type
49
+ end
50
+
51
+ def process_default(default)
52
+ if default.is_a?(Symbol)
53
+ Sequel.function(default)
54
+ else
55
+ default
56
+ end
57
+ end
58
+
59
+ class << self
60
+ def register(*types)
61
+ types.each do |type|
62
+ Field.registry[type] = self
63
+ end
64
+ end
65
+
66
+ def attributes(*attr_names, **attributes_with_defaults)
67
+ valid_attributes.push(*attr_names)
68
+
69
+ attributes_with_defaults.each do |attr_name, default_value|
70
+ valid_attributes.push(attr_name)
71
+ default_attribute_values[attr_name] = default_value
72
+ end
73
+ end
74
+
75
+ def valid_attributes
76
+ @valid_attributes ||= []
77
+ end
78
+
79
+ def default_attribute_values
80
+ @default_attribute_values ||= {}
81
+ end
82
+
83
+ def type
84
+ Field.registry.key(self)
85
+ end
86
+ end
87
+ end
88
+ end
89
+ end
90
+ end
@@ -0,0 +1,9 @@
1
+ module DbSchema
2
+ module Definitions
3
+ module Field
4
+ class Bytea < Base
5
+ register :bytea
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,15 @@
1
+ module DbSchema
2
+ module Definitions
3
+ module Field
4
+ class Bit < Base
5
+ register :bit
6
+ attributes length: 1
7
+ end
8
+
9
+ class Varbit < Base
10
+ register :varbit, :'bit varying'
11
+ attributes :length
12
+ end
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ module DbSchema
2
+ module Definitions
3
+ module Field
4
+ class Boolean < Base
5
+ register :boolean
6
+ end
7
+ end
8
+ end
9
+ end
@@ -0,0 +1,19 @@
1
+ module DbSchema
2
+ module Definitions
3
+ module Field
4
+ class Char < Base
5
+ register :char, :character
6
+ attributes length: 1
7
+ end
8
+
9
+ class Varchar < Base
10
+ register :varchar, :'character varying'
11
+ attributes :length
12
+ end
13
+
14
+ class Text < Base
15
+ register :text
16
+ end
17
+ end
18
+ end
19
+ end
@@ -0,0 +1,22 @@
1
+ module DbSchema
2
+ module Definitions
3
+ module Field
4
+ class Custom < Base
5
+ attr_reader :type_name
6
+
7
+ def initialize(name, type_name:, **options)
8
+ super(name, **options)
9
+ @type_name = type_name
10
+ end
11
+
12
+ def custom_type?
13
+ true
14
+ end
15
+
16
+ def type
17
+ type_name
18
+ end
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,30 @@
1
+ module DbSchema
2
+ module Definitions
3
+ module Field
4
+ class Timestamp < Base
5
+ register :timestamp, :'timestamp without time zone'
6
+ end
7
+
8
+ class Timestamptz < Base
9
+ register :timestamptz, :'timestamp with time zone'
10
+ end
11
+
12
+ class Date < Base
13
+ register :date
14
+ end
15
+
16
+ class Time < Base
17
+ register :time, :'time without time zone'
18
+ end
19
+
20
+ class Timetz < Base
21
+ register :timetz, :'time with time zone'
22
+ end
23
+
24
+ class Interval < Base
25
+ register :interval
26
+ attributes :fields, :precision
27
+ end
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,33 @@
1
+ module DbSchema
2
+ module Definitions
3
+ module Field
4
+ class Point < Base
5
+ register :point
6
+ end
7
+
8
+ class Line < Base
9
+ register :line
10
+ end
11
+
12
+ class Lseg < Base
13
+ register :lseg
14
+ end
15
+
16
+ class Box < Base
17
+ register :box
18
+ end
19
+
20
+ class Path < Base
21
+ register :path
22
+ end
23
+
24
+ class Polygon < Base
25
+ register :polygon
26
+ end
27
+
28
+ class Circle < Base
29
+ register :circle
30
+ end
31
+ end
32
+ end
33
+ end