declare_schema 0.12.0.pre.2 → 0.13.0

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: e55426213e7d15730b0531d3dba4036a4b18f3967b73a7619e60f27d15fc5b55
4
- data.tar.gz: 294eef4ce49c912df24403ab59998e61641c681ea1e98fa6c9fa00d66a4c4502
3
+ metadata.gz: dc0328d501bd76426598e121bc75283089a2a1b7c72781ce9b29ae4e60f8dff2
4
+ data.tar.gz: a734efaadde7dae50ee1cf512a52858eabc7c7315e6b5dbf3a8f6660eda32da4
5
5
  SHA512:
6
- metadata.gz: 3a3ebaa029176ff322a396baf65aaca878f631ae3ac0acad2a2290fc73275c56c312a00e1423219a30055f39ca17f1873a55f0d8be8a4dfeb38f821718c57128
7
- data.tar.gz: 25d48b29e96b5508b52c07ca869221d506f05370a38af76994a33bbfcb67153f379b9605a60e9169876bebb63e6db5bf9acbf1f09aab900d03fc05be404cd643
6
+ metadata.gz: 697da828c1b2fe88048a4c66cccda205adc809d449361294fc4e0e0c69d229bb97ed8b861979ab85eed6c8bd30d5dd942ed2791b8ded632eb371f084215e56c8
7
+ data.tar.gz: 5ff7eb9bc51daba2f0544e093baf38e2b181b2631683d3e2548d2054c4da70036ed98cdc7223658c93f5cb9423a79081c67e600629e0441dd505b09b82039bc1
data/CHANGELOG.md CHANGED
@@ -4,7 +4,16 @@ Inspired by [Keep a Changelog](https://keepachangelog.com/en/1.0.0/).
4
4
 
5
5
  Note: this project adheres to [Semantic Versioning](https://semver.org/spec/v2.0.0.html).
6
6
 
7
- ## [0.12.0] - Unreleased
7
+ ## [0.13.0] - 2020-06-11
8
+ ### Added
9
+ - Added support for `default_schema` block to apply a default schema to every model, unless disabled for that model with `default_schema: false`.
10
+
11
+ ## [0.12.1] - 2021-05-10
12
+ ### Fixed
13
+ - When an `enum` type field is declared, there is now enforcement that its `limit:` must be an array of 1 or more Symbols,
14
+ and its `default:`--if given--must be a Symbol or `nil`.
15
+
16
+ ## [0.12.0] - 2021-04-28
8
17
  ### Added
9
18
  - `belongs_to` now always infers the `limit:` of the foreign key to match that of the primary key it points to.
10
19
  Note: this isn't possible for polymorphic foreign keys, so it assumes `limit: 8` there...unless the schema
@@ -174,6 +183,8 @@ using the appropriate Rails configuration attributes.
174
183
  ### Added
175
184
  - Initial version from https://github.com/Invoca/hobo_fields v4.1.0.
176
185
 
186
+ [0.13.0]: https://github.com/Invoca/declare_schema/compare/v0.12.1...v0.13.0
187
+ [0.12.1]: https://github.com/Invoca/declare_schema/compare/v0.12.0...v0.12.1
177
188
  [0.12.0]: https://github.com/Invoca/declare_schema/compare/v0.11.1...v0.12.0
178
189
  [0.11.1]: https://github.com/Invoca/declare_schema/compare/v0.11.0...v0.11.1
179
190
  [0.11.0]: https://github.com/Invoca/declare_schema/compare/v0.10.1...v0.11.0
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- declare_schema (0.12.0.pre.2)
4
+ declare_schema (0.13.0)
5
5
  rails (>= 4.2)
6
6
 
7
7
  GEM
data/README.md CHANGED
@@ -62,7 +62,7 @@ trigger the `eager_load!` on the `Rails` application and all `Rails::Engine`s lo
62
62
  into scope. If you need to generate migrations for models that aren't automatically loaded by `eager_load!`,
63
63
  load them in the `before_generating_migration` block.
64
64
 
65
- **Example Configuration**
65
+ For example:
66
66
 
67
67
  ```ruby
68
68
  DeclareSchema::Migration::Migrator.before_generating_migration do
@@ -70,6 +70,32 @@ DeclareSchema::Migration::Migrator.before_generating_migration do
70
70
  end
71
71
  ```
72
72
 
73
+ ### default_schema
74
+ If there are default columns you would like in the schema for every model, you can define them in a block that is registered with
75
+ `DeclareSchema.default_schema`. For example:
76
+
77
+ ```ruby
78
+ DeclareSchema.default_schema do
79
+ timestamps
80
+ optimistic_lock
81
+ end
82
+ ```
83
+ This will add these fields to the schema of each model (if not already there).
84
+ If you have a model where you don't want the defaults applied, that can be set with the `default_schema:` boolean option to `declare_schema` (the default value is true). For example:
85
+ ```ruby
86
+ class User < ActiveRecord::Base
87
+ declare_schema default_schema: false do
88
+ ...
89
+ end
90
+ end
91
+ ```
92
+
93
+ ### clear_default_schema
94
+ This method clears out any previously declared `default_schema`.
95
+ ```ruby
96
+ DeclareSchema.clear_default_schema
97
+ ```
98
+
73
99
  ### Global Configuration
74
100
  Configurations can be set at the global level to customize default declaration for the following values:
75
101
 
@@ -85,6 +85,19 @@ module DeclareSchema
85
85
  @default_generate_indexing = generate_indexing
86
86
  end
87
87
 
88
+ def default_schema(&block)
89
+ if block.nil?
90
+ @default_schema # equivalent to attr_reader :default_schema
91
+ else
92
+ block.respond_to?(:call) or raise "default_schema must be passed a block that responds to call"
93
+ @default_schema = block
94
+ end
95
+ end
96
+
97
+ def clear_default_schema
98
+ @default_schema = nil
99
+ end
100
+
88
101
  def db_migrate_command=(db_migrate_command)
89
102
  db_migrate_command.is_a?(String) or raise ArgumentError, "db_migrate_command must be a string (got #{db_migrate_command.inspect})"
90
103
  @db_migrate_command = db_migrate_command
@@ -7,7 +7,7 @@ module DeclareSchema
7
7
  include ::Kernel # but we need the basic class methods
8
8
 
9
9
  instance_methods.each do |m|
10
- unless m.to_s.starts_with?('__') || m.in?([:object_id, :instance_eval])
10
+ unless m.to_s.starts_with?('__') || m.in?([:object_id, :instance_eval, :instance_exec])
11
11
  undef_method(m)
12
12
  end
13
13
  end
@@ -32,8 +32,11 @@ module DeclareSchema
32
32
  @model.declare_field(name, type, *(args + [@options.merge(options)]))
33
33
  end
34
34
 
35
- def method_missing(type, name, *args)
36
- field(name, type, *args)
35
+ # TODO: make [:required] just another option. Either 'required: true] or 'optional: false'?
36
+ def method_missing(*args, **options)
37
+ args.count(&:itself) >= 2 or raise ::ArgumentError, "fields in declare_schema block must be declared as: type name, [:required], options (got #{args.inspect}, #{options.inspect})"
38
+ type, name, *required = args
39
+ field(name, type, *required, options)
37
40
  end
38
41
  end
39
42
  end
@@ -27,7 +27,7 @@ module DeclareSchema
27
27
  end
28
28
  deprecate :fields, deprecator: ActiveSupport::Deprecation.new('1.0', 'DeclareSchema')
29
29
 
30
- def declare_schema(table_options = {}, &block)
30
+ def declare_schema(default_schema: true, **table_options, &block)
31
31
  # Any model that calls 'fields' gets DeclareSchema::Model behavior
32
32
  DeclareSchema::Model.mix_in(self)
33
33
 
@@ -37,10 +37,9 @@ module DeclareSchema
37
37
 
38
38
  if block
39
39
  dsl = DeclareSchema::Dsl.new(self, null: false)
40
- if block.arity == 1
41
- yield dsl
42
- else
43
- dsl.instance_eval(&block)
40
+ dsl.instance_eval(&block)
41
+ if default_schema && DeclareSchema.default_schema
42
+ dsl.instance_exec(&DeclareSchema.default_schema)
44
43
  end
45
44
  end
46
45
  end
@@ -34,19 +34,16 @@ module DeclareSchema
34
34
  # supported options include :charset and :collation
35
35
  inheriting_cattr_reader table_options: HashWithIndifferentAccess.new
36
36
 
37
- # eval avoids the ruby 1.9.2 "super from singleton method ..." error
38
-
39
- eval <<~EOS
40
- def self.inherited(klass)
41
- unless klass.field_specs.has_key?(inheritance_column)
42
- declare_schema do |f|
43
- f.field(inheritance_column, :string, limit: 255, null: true)
44
- end
45
- index(inheritance_column)
37
+ def self.inherited(klass)
38
+ unless klass.field_specs.has_key?(inheritance_column)
39
+ ic = inheritance_column
40
+ declare_schema do
41
+ field(ic, :string, limit: 255, null: true)
46
42
  end
47
- super
43
+ index(ic)
48
44
  end
49
- EOS
45
+ super
46
+ end
50
47
  end
51
48
  end
52
49
  end
@@ -76,12 +76,19 @@ module DeclareSchema
76
76
  when :bigint
77
77
  @type = :integer
78
78
  @options[:limit] = 8
79
+ when :enum
80
+ @options[:default].nil? || @options[:default].is_a?(Symbol) or
81
+ raise ArgumentError, "enum default: must be nil or a Symbol; got #{@options[:default].inspect}"
82
+ @options[:limit].is_a?(Array) && @options[:limit].size >= 1 && @options[:limit].all? { |value| value.is_a?(Symbol) } or
83
+ raise ArgumentError, "enum limit: must be an array of 1 or more Symbols; got #{@options[:limit].inspect}"
79
84
  end
80
85
 
81
86
  Column.native_type?(@type) or raise UnknownTypeError, "#{@type.inspect} not found in #{Column.native_types.inspect} for adapter #{ActiveRecord::Base.connection.class.name}"
82
87
 
83
- if @type.in?([:string, :text, :binary, :varbinary, :integer, :enum])
88
+ if @type.in?([:string, :text, :binary, :varbinary, :integer])
84
89
  @options[:limit] ||= Column.native_types.dig(@type, :limit)
90
+ elsif @type.in?([:enum])
91
+ # nothing to do
85
92
  else
86
93
  @type != :decimal && @options.has_key?(:limit) and warn("unsupported limit: for SQL type #{@type} in field #{model}##{@name}")
87
94
  @options.delete(:limit)
@@ -1,5 +1,5 @@
1
1
  # frozen_string_literal: true
2
2
 
3
3
  module DeclareSchema
4
- VERSION = "0.12.0.pre.2"
4
+ VERSION = "0.13.0"
5
5
  end
@@ -343,16 +343,15 @@ module Generators
343
343
 
344
344
  db_columns = model.connection.columns(current_table_name).index_by(&:name)
345
345
  if (pk = model._declared_primary_key.presence)
346
- key_was_in_db_columns = db_columns.delete(pk)
346
+ pk_was_in_db_columns = db_columns.delete(pk)
347
347
  end
348
348
 
349
349
  model_column_names = model.field_specs.keys.map(&:to_s)
350
350
  db_column_names = db_columns.keys.map(&:to_s)
351
351
 
352
352
  to_add = model_column_names - db_column_names
353
- to_add << pk if pk && !key_was_in_db_columns
353
+ to_add << pk if pk && !pk_was_in_db_columns
354
354
  to_remove = db_column_names - model_column_names
355
- to_remove -= [pk.to_sym] if pk # TODO: The .to_sym here means this code is always a no-op, right? -Colin
356
355
 
357
356
  to_rename = extract_column_renames!(to_add, to_remove, new_table_name)
358
357
 
@@ -40,8 +40,6 @@ RSpec.describe 'DeclareSchema API' do
40
40
  load_models
41
41
 
42
42
  if ActiveSupport::VERSION::MAJOR == 5
43
- # TODO: get this to work on Travis for Rails 6
44
- # TODO: uncomment since we're not on Travis any more? -Colin
45
43
  generate_migrations '-n', '-m'
46
44
  end
47
45
 
@@ -50,8 +48,6 @@ RSpec.describe 'DeclareSchema API' do
50
48
 
51
49
  ## The Basics
52
50
 
53
- # The main feature of DeclareSchema, aside from the migration generator, is the ability to declare rich types for your fields. For example, you can declare that a field is an email address, and the field will be automatically validated for correct email address syntax.
54
-
55
51
  ### Field Types
56
52
 
57
53
  # Field values are returned as the type you specify.
@@ -115,6 +115,48 @@ RSpec.describe DeclareSchema::Model::FieldSpec do
115
115
  end
116
116
  end
117
117
 
118
+ describe 'enum' do
119
+ before do
120
+ allow(::DeclareSchema::Model::Column).to receive(:native_types).and_wrap_original do |m, *_args|
121
+ result = m.call
122
+ if result.has_key?(:enum)
123
+ result
124
+ else
125
+ result.merge(enum: { name: "enum" })
126
+ end
127
+ end
128
+ end
129
+
130
+ describe 'default' do
131
+ it 'allows default of nil or a Symbol' do
132
+ [nil, :first].each do |default|
133
+ expect do
134
+ subject = described_class.new(model, :status, :enum, limit: [:first, :second, :third], default: default, null: false, position: 2)
135
+ expect(subject.default).to eq(default)
136
+ end.to_not raise_exception
137
+ end
138
+ end
139
+
140
+ it 'raises ArgumentError if default is not nil or a Symbol' do
141
+ ["first", 1].each do |default|
142
+ expect do
143
+ described_class.new(model, :status, :enum, limit: [:first, :second, :third], default: default, null: false, position: 2)
144
+ end.to raise_exception(ArgumentError, /enum default: must be nil or a Symbol; got #{Regexp.escape(default.inspect)}/)
145
+ end
146
+ end
147
+ end
148
+
149
+ describe 'limit' do
150
+ it 'raises ArgumentError if any of the limit values are not Symbols' do
151
+ [['first', 'second', 'third'], [1, 2, 3], nil, []].each do |limit|
152
+ expect do
153
+ described_class.new(model, :status, :enum, limit: limit, null: false, position: 2)
154
+ end.to raise_exception(ArgumentError, /enum limit: must be an array of 1 or more Symbols; got #{Regexp.escape(limit.inspect)}/)
155
+ end
156
+ end
157
+ end
158
+ end
159
+
118
160
  if defined?(Mysql2)
119
161
  describe 'varbinary' do # TODO: :varbinary is an Invoca addition to Rails; make it a configurable option
120
162
  it 'is supported' do
@@ -1860,6 +1860,8 @@ RSpec.describe 'DeclareSchema Migration Generator' do
1860
1860
  class SuperFancyAdvert < FancyAdvert
1861
1861
  end
1862
1862
 
1863
+ expect(Generators::DeclareSchema::Migration::Migrator.run.first).to be_present
1864
+
1863
1865
  up, _ = Generators::DeclareSchema::Migration::Migrator.run do |migrations|
1864
1866
  expect(migrations).to(
1865
1867
  migrate_up(<<~EOS.strip)
@@ -2491,5 +2493,78 @@ RSpec.describe 'DeclareSchema Migration Generator' do
2491
2493
  end
2492
2494
  end
2493
2495
  end
2496
+
2497
+ context 'default_schema' do
2498
+ let(:default_schema_block) { nil }
2499
+ let(:declare_model) do
2500
+ -> do
2501
+ class Advert < active_record_base_class.constantize
2502
+ declare_schema do
2503
+ integer :price, limit: 8
2504
+ end
2505
+ end
2506
+ end
2507
+ end
2508
+
2509
+ before do
2510
+ DeclareSchema.default_schema(&default_schema_block)
2511
+ end
2512
+
2513
+ after do
2514
+ DeclareSchema.clear_default_schema
2515
+ end
2516
+
2517
+ context 'when unset' do
2518
+ it 'adds nothing' do
2519
+ declare_model.call
2520
+
2521
+ expect(Advert.field_specs.keys).to eq(['price'])
2522
+ end
2523
+ end
2524
+
2525
+ context 'when set to a block' do
2526
+ let(:default_schema_block) do
2527
+ -> do
2528
+ timestamps
2529
+ field :lock_version, :integer, default: 1
2530
+ end
2531
+ end
2532
+
2533
+ it 'adds the fields in that block' do
2534
+ declare_model.call
2535
+
2536
+ expect(Advert.field_specs.keys).to eq(['price', 'created_at', 'updated_at', 'lock_version'])
2537
+ end
2538
+
2539
+ context 'and the model sets default_schema: false' do
2540
+ before do
2541
+ class Advert < active_record_base_class.constantize
2542
+ declare_schema default_schema: false do
2543
+ integer :price, limit: 8
2544
+ end
2545
+ end
2546
+ end
2547
+
2548
+ it 'does not add the default schema fields' do
2549
+ expect(Advert.field_specs.keys).to eq(['price'])
2550
+ end
2551
+ end
2552
+
2553
+ context 'and the block has redundant fields' do
2554
+ before do
2555
+ class Advert < active_record_base_class.constantize
2556
+ declare_schema do
2557
+ integer :price, limit: 8
2558
+ timestamps
2559
+ end
2560
+ end
2561
+ end
2562
+
2563
+ it 'is a no-op' do
2564
+ expect(Advert.field_specs.keys).to eq(['price', 'created_at', 'updated_at', 'lock_version'])
2565
+ end
2566
+ end
2567
+ end
2568
+ end
2494
2569
  end
2495
2570
  end
metadata CHANGED
@@ -1,14 +1,14 @@
1
1
  --- !ruby/object:Gem::Specification
2
2
  name: declare_schema
3
3
  version: !ruby/object:Gem::Version
4
- version: 0.12.0.pre.2
4
+ version: 0.13.0
5
5
  platform: ruby
6
6
  authors:
7
7
  - Invoca Development adapted from hobo_fields by Tom Locke
8
8
  autorequire:
9
9
  bindir: bin
10
10
  cert_chain: []
11
- date: 2021-04-27 00:00:00.000000000 Z
11
+ date: 2021-06-11 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails