declare_schema 0.3.0 → 0.5.0.pre.1

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
  SHA256:
3
- metadata.gz: f3ae0814480b116b32a822887c83612b33458cc910384ae0c2122e547757f141
4
- data.tar.gz: 133b7bd6d7d91dae9320a4e8002a8591e43ebb97f57013cbe266cef40a169088
3
+ metadata.gz: 3f3b9aed36bcc541fb7d929a97e3650358be4da05c33a5d27e8db732633eb8f8
4
+ data.tar.gz: 800c945108be2e29301afc2badce461a1302dadad7c393bd39c939368311e874
5
5
  SHA512:
6
- metadata.gz: 77b840eb5feb07d38c4fd96f4212d4c81f19c61a2aa2b4bbffb4f4f11594ba55c30e65ca4b77a68e7b29448f7762dba52782f1845307c5bbc02f7603f080c2e8
7
- data.tar.gz: 2f85affbd44a6973595883afa01766ede6c950a70c4e783f3064e5c2a59ec5c71a3731d7d484bab133c18c698584077e2c14a8b98c81ce024e2e55bae59dabba
6
+ metadata.gz: 821e56fe2ba4e7c9913f630a47cf685a565c19d40fdf282305c3bf0db6f0783b3174aa4e1ca535732e64cc7514cd87a598eaf743fbca0b21fa7041ec62ce82a0
7
+ data.tar.gz: c2522286a389e559fda6b635b0d85b1025bdf918a98a5eb27e40946d5eee1e079efca53a0e2ad6f24649a8b05f59d652869cee924dcca7491ff7575d8292519c
@@ -0,0 +1,14 @@
1
+ version: 2
2
+ updates:
3
+ - package-ecosystem: bundler
4
+ directory: "/"
5
+ schedule:
6
+ interval: weekly
7
+ day: friday
8
+ time: "22:00"
9
+ timezone: PST8PDT
10
+ open-pull-requests-limit: 99
11
+ versioning-strategy: lockfile-only
12
+ commit-message:
13
+ prefix: No-Jira
14
+ include: scope
@@ -4,6 +4,37 @@ 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.5.0] - Unreleased
8
+ ### Added
9
+ - Added support for configuring the character set and collation for MySQL databases
10
+ at the global, table, and field level
11
+
12
+ ## [0.4.2] - 2020-12-05
13
+ ### Fixed
14
+ - Generalize the fix below to sqlite || Rails 4.
15
+
16
+ ## [0.4.1] - 2020-12-04
17
+ ### Fixed
18
+ - Fixed a bug detecting compound primary keys in Rails 4.
19
+
20
+ ## [0.4.0] - 2020-11-20
21
+ ### Added
22
+ - Fields may be declared with `serialize: true` (any value with a valid `.to_yaml` stored as YAML),
23
+ or `serialize: <serializeable-class>`, where `<serializeable-class>`
24
+ may be `Array` (`Array` stored as YAML) or `Hash` (`Hash` stored as YAML) or `JSON` (any value with a valid `.to_json`, stored as JSON)
25
+ or any custom serializable class.
26
+ This invokes `ActiveSupport`'s `serialize` macro for that field, passing the serializable class, if given.
27
+
28
+ Note: when `serialize:` is used, any `default:` should be given in a matching Ruby type--for example, `[]` or `{}` or `{ 'currency' => 'USD' }`--in
29
+ which case the serializeable class will be used to determine the serialized default value and that will be set as the SQL default.
30
+
31
+ ### Fixed
32
+ - Sqlite now correctly infers the PRIMARY KEY so it won't attempt to add that index again.
33
+
34
+ ## [0.3.1] - 2020-11-13
35
+ ### Fixed
36
+ - When passing `belongs_to` to Rails, suppress the `optional:` option in Rails 4, since that option was added in Rails 5.
37
+
7
38
  ## [0.3.0] - 2020-11-02
8
39
  ### Added
9
40
  - Added support for `belongs_to optional:`.
@@ -38,11 +69,15 @@ using the appropriate Rails configuration attributes.
38
69
  ### Changed
39
70
  - Added travis support and created 2 specs as a starting point.
40
71
 
41
-
42
72
  ## [0.1.1] - 2020-09-24
43
73
  ### Added
44
74
  - Initial version from https://github.com/Invoca/hobo_fields v4.1.0.
45
75
 
76
+ [0.5.0]: https://github.com/Invoca/declare_schema/compare/v0.4.2...v0.5.0
77
+ [0.4.2]: https://github.com/Invoca/declare_schema/compare/v0.4.1...v0.4.2
78
+ [0.4.1]: https://github.com/Invoca/declare_schema/compare/v0.4.0...v0.4.1
79
+ [0.4.0]: https://github.com/Invoca/declare_schema/compare/v0.3.1...v0.4.0
80
+ [0.3.1]: https://github.com/Invoca/declare_schema/compare/v0.3.0...v0.3.1
46
81
  [0.3.0]: https://github.com/Invoca/declare_schema/compare/v0.2.0...v0.3.0
47
82
  [0.2.0]: https://github.com/Invoca/declare_schema/compare/v0.1.3...v0.2.0
48
83
  [0.1.3]: https://github.com/Invoca/declare_schema/compare/v0.1.2...v0.1.3
data/Gemfile CHANGED
@@ -14,6 +14,7 @@ gem 'bundler', '< 2'
14
14
  gem "climate_control", '~> 0.2'
15
15
  gem 'pry'
16
16
  gem 'pry-byebug'
17
+ gem 'mysql2'
17
18
  gem 'rails', '~> 5.2', '>= 5.2.4.3'
18
19
  gem 'responders'
19
20
  gem 'rspec'
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- declare_schema (0.3.0)
4
+ declare_schema (0.5.0.pre.1)
5
5
  rails (>= 4.2)
6
6
 
7
7
  GEM
@@ -54,7 +54,7 @@ GEM
54
54
  thor (>= 0.14.0)
55
55
  arel (9.0.0)
56
56
  ast (2.4.1)
57
- bootsnap (1.4.8)
57
+ bootsnap (1.5.1)
58
58
  msgpack (~> 1.0)
59
59
  builder (3.2.4)
60
60
  byebug (11.1.3)
@@ -69,7 +69,7 @@ GEM
69
69
  activesupport (>= 4.2.0)
70
70
  i18n (1.8.5)
71
71
  concurrent-ruby (~> 1.0)
72
- listen (3.2.1)
72
+ listen (3.3.1)
73
73
  rb-fsevent (~> 0.10, >= 0.10.3)
74
74
  rb-inotify (~> 0.9, >= 0.9.10)
75
75
  loofah (2.7.0)
@@ -85,6 +85,7 @@ GEM
85
85
  mini_portile2 (2.4.0)
86
86
  minitest (5.14.2)
87
87
  msgpack (1.3.3)
88
+ mysql2 (0.5.3)
88
89
  nio4r (2.5.4)
89
90
  nokogiri (1.10.10)
90
91
  mini_portile2 (~> 2.4.0)
@@ -134,19 +135,19 @@ GEM
134
135
  actionpack (>= 5.0)
135
136
  railties (>= 5.0)
136
137
  rexml (3.2.4)
137
- rspec (3.9.0)
138
- rspec-core (~> 3.9.0)
139
- rspec-expectations (~> 3.9.0)
140
- rspec-mocks (~> 3.9.0)
141
- rspec-core (3.9.2)
142
- rspec-support (~> 3.9.3)
143
- rspec-expectations (3.9.2)
138
+ rspec (3.10.0)
139
+ rspec-core (~> 3.10.0)
140
+ rspec-expectations (~> 3.10.0)
141
+ rspec-mocks (~> 3.10.0)
142
+ rspec-core (3.10.0)
143
+ rspec-support (~> 3.10.0)
144
+ rspec-expectations (3.10.0)
144
145
  diff-lcs (>= 1.2.0, < 2.0)
145
- rspec-support (~> 3.9.0)
146
- rspec-mocks (3.9.1)
146
+ rspec-support (~> 3.10.0)
147
+ rspec-mocks (3.10.0)
147
148
  diff-lcs (>= 1.2.0, < 2.0)
148
- rspec-support (~> 3.9.0)
149
- rspec-support (3.9.3)
149
+ rspec-support (~> 3.10.0)
150
+ rspec-support (3.10.0)
150
151
  rubocop (0.91.0)
151
152
  parallel (~> 1.10)
152
153
  parser (>= 2.7.1.1)
@@ -187,6 +188,7 @@ DEPENDENCIES
187
188
  climate_control (~> 0.2)
188
189
  declare_schema!
189
190
  listen
191
+ mysql2
190
192
  pry
191
193
  pry-byebug
192
194
  rails (~> 5.2, >= 5.2.4.3)
data/README.md CHANGED
@@ -70,6 +70,72 @@ DeclareSchema::Migration::Migrator.before_generating_migration do
70
70
  end
71
71
  ```
72
72
 
73
+ ## Declaring Character Set and Collation
74
+ _Note: This feature currently only works for MySQL database configurations._
75
+
76
+ MySQL originally supported UTF-8 in the range of 1-3 bytes (`mb3` or "multi-byte 3")
77
+ which covered the full set of Unicode code points at the time: U+0000 - U+FFFF.
78
+ But later, Unicode was extended beyond U+FFFF to make room for emojis, and with that
79
+ UTF-8 require 1-4 bytes (`mb4` or "multi-byte 4"). With this addition, there has
80
+ come a need to dynamically define the character set and collation for individual
81
+ tables and columns in the database. With `declare_schema` this can be configured
82
+ at three separate levels
83
+
84
+ ### Global Configuration
85
+ The character set and collation for all tables and fields can be set at the global level
86
+ using the `Generators::DeclareSchema::Migrator.default_charset=` and
87
+ `Generators::DeclareSchema::Migrator.default_collation=` configuration methods.
88
+
89
+ For example, adding the following to your `config/initializers` directory will
90
+ turn all tables into `utf8mb4` supporting tables:
91
+
92
+ **declare_schema.rb**
93
+ ```ruby
94
+ # frozen_string_literal: true
95
+
96
+ Generators::DeclareSchema::Migrator.default_charset = "utf8mb4"
97
+ Generators::DeclareSchema::Migrator.default_collation = "utf8mb4_general"
98
+ ```
99
+
100
+ ### Table Configuration
101
+ In order to configure a table's default character set and collation, the `charset` and
102
+ `collation` arguments can be added to the `fields` block.
103
+
104
+ For example, if you have a comments model that needs `utf8mb4` support, it would look
105
+ like the following:
106
+
107
+ **app/models/comment.rb**
108
+ ```ruby
109
+ # frozen_string_literal: true
110
+
111
+ class Comment < ActiveRecord::Base
112
+ fields charset: "utf8mb4", collation: "utf8mb4_general" do
113
+ subject :string, limit: 255
114
+ content :text, limit: 0xffff_ffff
115
+ end
116
+ end
117
+ ```
118
+
119
+ ### Field Configuration
120
+ If you're looking to only change the character set and collation for a single field
121
+ in the table, simply set the `charset` and `collation` configuration options on the
122
+ field definition itself.
123
+
124
+ For example, if you only want to support `utf8mb4` for the content of a comment, it would
125
+ look like the following:
126
+
127
+ **app/models/comment.rb**
128
+ ```ruby
129
+ # frozen_string_literal: true
130
+
131
+ class Comment < ActiveRecord::Base
132
+ fields do
133
+ subject :string, limit: 255
134
+ context :text, limit: 0xffff_ffff, charset: "utf8mb4", collation: "utf8mb4_general"
135
+ end
136
+ end
137
+ ```
138
+
73
139
  ## Installing
74
140
 
75
141
  Install the `DeclareSchema` gem directly:
@@ -7,6 +7,7 @@ gem "bundler", "< 2"
7
7
  gem "climate_control", "~> 0.2"
8
8
  gem "pry"
9
9
  gem "pry-byebug"
10
+ gem "mysql2"
10
11
  gem "rails", "~> 4.2"
11
12
  gem "responders"
12
13
  gem "rspec"
@@ -7,6 +7,7 @@ gem "bundler", "< 2"
7
7
  gem "climate_control", "~> 0.2"
8
8
  gem "pry"
9
9
  gem "pry-byebug"
10
+ gem "mysql2"
10
11
  gem "rails", "~> 5.2"
11
12
  gem "responders"
12
13
  gem "rspec"
@@ -7,6 +7,7 @@ gem "bundler", "< 2"
7
7
  gem "climate_control", "~> 0.2"
8
8
  gem "pry"
9
9
  gem "pry-byebug"
10
+ gem "mysql2"
10
11
  gem "rails", "~> 6.0"
11
12
  gem "responders"
12
13
  gem "rspec"
@@ -39,6 +39,8 @@ require 'declare_schema/extensions/active_record/fields_declaration'
39
39
  require 'declare_schema/field_declaration_dsl'
40
40
  require 'declare_schema/model'
41
41
  require 'declare_schema/model/field_spec'
42
- require 'declare_schema/model/index_spec'
42
+ require 'declare_schema/model/index_definition'
43
+ require 'declare_schema/model/foreign_key_definition'
44
+ require 'declare_schema/model/table_options_definition'
43
45
 
44
46
  require 'declare_schema/railtie' if defined?(Rails)
@@ -6,12 +6,13 @@ require 'declare_schema/field_declaration_dsl'
6
6
 
7
7
  module DeclareSchema
8
8
  module FieldsDsl
9
- def fields(&block)
9
+ def fields(table_options = {}, &block)
10
10
  # Any model that calls 'fields' gets DeclareSchema::Model behavior
11
11
  DeclareSchema::Model.mix_in(self)
12
12
 
13
13
  # @include_in_migration = false #||= options.fetch(:include_in_migration, true); options.delete(:include_in_migration)
14
14
  @include_in_migration = true
15
+ @table_options = table_options
15
16
 
16
17
  if block
17
18
  dsl = DeclareSchema::FieldDeclarationDsl.new(self, null: false)
@@ -25,11 +25,15 @@ module DeclareSchema
25
25
  # and speeds things up a little.
26
26
  inheriting_cattr_reader field_specs: HashWithIndifferentAccess.new
27
27
 
28
- # index_specs holds IndexSpec objects for all the declared indexes.
29
- inheriting_cattr_reader index_specs: []
28
+ # index_definitions holds IndexDefinition objects for all the declared indexes.
29
+ inheriting_cattr_reader index_definitions: []
30
30
  inheriting_cattr_reader ignore_indexes: []
31
31
  inheriting_cattr_reader constraint_specs: []
32
32
 
33
+ # table_options holds optional configuration for the create_table statement
34
+ # supported options include :charset and :collation
35
+ inheriting_cattr_reader table_options: HashWithIndifferentAccess.new
36
+
33
37
  # eval avoids the ruby 1.9.2 "super from singleton method ..." error
34
38
 
35
39
  eval %(
@@ -51,19 +55,19 @@ module DeclareSchema
51
55
  def index(fields, options = {})
52
56
  # don't double-index fields
53
57
  index_fields_s = Array.wrap(fields).map(&:to_s)
54
- unless index_specs.any? { |index_spec| index_spec.fields == index_fields_s }
55
- index_specs << ::DeclareSchema::Model::IndexSpec.new(self, fields, options)
58
+ unless index_definitions.any? { |index_spec| index_spec.fields == index_fields_s }
59
+ index_definitions << ::DeclareSchema::Model::IndexDefinition.new(self, fields, options)
56
60
  end
57
61
  end
58
62
 
59
63
  def primary_key_index(*fields)
60
- index(fields.flatten, unique: true, name: "PRIMARY_KEY")
64
+ index(fields.flatten, unique: true, name: ::DeclareSchema::Model::IndexDefinition::PRIMARY_KEY_NAME)
61
65
  end
62
66
 
63
67
  def constraint(fkey, options = {})
64
68
  fkey_s = fkey.to_s
65
69
  unless constraint_specs.any? { |constraint_spec| constraint_spec.foreign_key == fkey_s }
66
- constraint_specs << DeclareSchema::Model::ForeignKeySpec.new(self, fkey, options)
70
+ constraint_specs << DeclareSchema::Model::ForeignKeyDefinition.new(self, fkey, options)
67
71
  end
68
72
  end
69
73
 
@@ -79,19 +83,20 @@ module DeclareSchema
79
83
  # declarations.
80
84
  def declare_field(name, type, *args)
81
85
  options = args.extract_options!
82
- field_added(name, type, args, options) if respond_to?(:field_added)
83
- add_formatting_for_field(name, type, args)
86
+ try(:field_added, name, type, args, options)
87
+ add_serialize_for_field(name, type, options)
88
+ add_formatting_for_field(name, type)
84
89
  add_validations_for_field(name, type, args, options)
85
90
  add_index_for_field(name, args, options)
86
91
  field_specs[name] = ::DeclareSchema::Model::FieldSpec.new(self, name, type, options)
87
- attr_order << name unless name.in?(attr_order)
92
+ attr_order << name unless attr_order.include?(name)
88
93
  end
89
94
 
90
- def index_specs_with_primary_key
91
- if index_specs.any?(&:primary_key?)
92
- index_specs
95
+ def index_definitions_with_primary_key
96
+ if index_definitions.any?(&:primary_key?)
97
+ index_definitions
93
98
  else
94
- index_specs + [rails_default_primary_key]
99
+ index_definitions + [rails_default_primary_key]
95
100
  end
96
101
  end
97
102
 
@@ -102,11 +107,11 @@ module DeclareSchema
102
107
  private
103
108
 
104
109
  def rails_default_primary_key
105
- ::DeclareSchema::Model::IndexSpec.new(self, [primary_key.to_sym], unique: true, name: DeclareSchema::Model::IndexSpec::PRIMARY_KEY_NAME)
110
+ ::DeclareSchema::Model::IndexDefinition.new(self, [primary_key.to_sym], unique: true, name: DeclareSchema::Model::IndexDefinition::PRIMARY_KEY_NAME)
106
111
  end
107
112
 
108
113
  # Extend belongs_to so that it creates a FieldSpec for the foreign key
109
- def belongs_to(name, scope = nil, **options, &block)
114
+ def belongs_to(name, scope = nil, **options)
110
115
  column_options = {}
111
116
 
112
117
  column_options[:null] = if options.has_key?(:null)
@@ -128,13 +133,17 @@ module DeclareSchema
128
133
 
129
134
  fk = options[:foreign_key]&.to_s || "#{name}_id"
130
135
 
131
- if !options.has_key?(:optional) && Rails::VERSION::MAJOR >= 5
136
+ if !options.has_key?(:optional)
132
137
  options[:optional] = column_options[:null] # infer :optional from :null
133
138
  end
134
139
 
135
140
  fk_options[:dependent] = options.delete(:far_end_dependent) if options.has_key?(:far_end_dependent)
136
141
 
137
- super(name, scope, options)
142
+ if Rails::VERSION::MAJOR >= 5
143
+ super
144
+ else
145
+ super(name, scope, options.except(:optional))
146
+ end
138
147
 
139
148
  refl = reflections[name.to_s] or raise "Couldn't find reflection #{name} in #{reflections.keys}"
140
149
  fkey = refl.foreign_key or raise "Couldn't find foreign_key for #{name} in #{refl.inspect}"
@@ -145,7 +154,6 @@ module DeclareSchema
145
154
  index([foreign_type, fkey], index_options) if index_options[:name] != false
146
155
  else
147
156
  index(fkey, index_options) if index_options[:name] != false
148
- options[:constraint_name] = options
149
157
  constraint(fkey, fk_options) if fk_options[:constraint_name] != false
150
158
  end
151
159
  end
@@ -163,9 +171,8 @@ module DeclareSchema
163
171
  # does not effect the attribute in any way - it just records the
164
172
  # metadata.
165
173
  def declare_attr_type(name, type, options = {})
166
- klass = DeclareSchema.to_class(type)
167
- attr_types[name] = DeclareSchema.to_class(type)
168
- klass.declared(self, name, options) if klass.respond_to?(:declared)
174
+ attr_types[name] = klass = DeclareSchema.to_class(type)
175
+ klass.try(:declared, self, name, options)
169
176
  end
170
177
 
171
178
  # Add field validations according to arguments in the
@@ -189,7 +196,37 @@ module DeclareSchema
189
196
  end
190
197
  end
191
198
 
192
- def add_formatting_for_field(name, type, _args)
199
+ def add_serialize_for_field(name, type, options)
200
+ if (serialize_class = options.delete(:serialize))
201
+ type == :string || type == :text or raise ArgumentError, "serialize field type must be :string or :text"
202
+ serialize_args = Array((serialize_class unless serialize_class == true))
203
+ serialize(name, *serialize_args)
204
+ if options.has_key?(:default)
205
+ options[:default] = serialized_default(name, serialize_class == true ? Object : serialize_class, options[:default])
206
+ end
207
+ end
208
+ end
209
+
210
+ def serialized_default(attr_name, class_name_or_coder, default)
211
+ # copied from https://github.com/rails/rails/blob/7d6cb950e7c0e31c2faaed08c81743439156c9f5/activerecord/lib/active_record/attribute_methods/serialization.rb#L70-L76
212
+ coder = if class_name_or_coder == ::JSON
213
+ ActiveRecord::Coders::JSON
214
+ elsif [:load, :dump].all? { |x| class_name_or_coder.respond_to?(x) }
215
+ class_name_or_coder
216
+ elsif Rails::VERSION::MAJOR >= 5
217
+ ActiveRecord::Coders::YAMLColumn.new(attr_name, class_name_or_coder)
218
+ else
219
+ ActiveRecord::Coders::YAMLColumn.new(class_name_or_coder)
220
+ end
221
+
222
+ if default == coder.load(nil)
223
+ nil # handle Array default: [] or Hash default: {}
224
+ else
225
+ coder.dump(default)
226
+ end
227
+ end
228
+
229
+ def add_formatting_for_field(name, type)
193
230
  if (type_class = DeclareSchema.to_class(type))
194
231
  if "format".in?(type_class.instance_methods)
195
232
  before_validation do |record|