declare_schema 0.1.0 → 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (37) hide show
  1. checksums.yaml +4 -4
  2. data/.travis.yml +37 -0
  3. data/CHANGELOG.md +28 -4
  4. data/Gemfile +0 -2
  5. data/Gemfile.lock +1 -4
  6. data/README.md +59 -2
  7. data/Rakefile +13 -20
  8. data/gemfiles/rails_4.gemfile +4 -7
  9. data/gemfiles/rails_5.gemfile +4 -7
  10. data/gemfiles/rails_6.gemfile +4 -7
  11. data/lib/declare_schema/model.rb +0 -1
  12. data/lib/declare_schema/model/field_spec.rb +4 -14
  13. data/lib/declare_schema/version.rb +1 -1
  14. data/lib/generators/declare_schema/migration/migration_generator.rb +20 -13
  15. data/lib/generators/declare_schema/migration/migrator.rb +58 -38
  16. data/lib/generators/declare_schema/migration/templates/migration.rb.erb +1 -1
  17. data/lib/generators/declare_schema/support/eval_template.rb +12 -3
  18. data/lib/generators/declare_schema/support/model.rb +77 -2
  19. data/spec/lib/declare_schema/api_spec.rb +125 -0
  20. data/spec/lib/declare_schema/field_declaration_dsl_spec.rb +8 -4
  21. data/spec/lib/declare_schema/generator_spec.rb +57 -0
  22. data/spec/lib/declare_schema/interactive_primary_key_spec.rb +51 -0
  23. data/spec/lib/declare_schema/migration_generator_spec.rb +686 -0
  24. data/spec/lib/declare_schema/prepare_testapp.rb +29 -0
  25. data/spec/lib/generators/declare_schema/migration/migrator_spec.rb +42 -0
  26. data/spec/spec_helper.rb +26 -0
  27. metadata +9 -12
  28. data/.jenkins/Jenkinsfile +0 -72
  29. data/.jenkins/ruby_build_pod.yml +0 -19
  30. data/lib/generators/declare_schema/model/templates/model_injection.rb.erb +0 -25
  31. data/test/api.rdoctest +0 -136
  32. data/test/doc-only.rdoctest +0 -76
  33. data/test/generators.rdoctest +0 -60
  34. data/test/interactive_primary_key.rdoctest +0 -56
  35. data/test/migration_generator.rdoctest +0 -846
  36. data/test/migration_generator_comments.rdoctestDISABLED +0 -74
  37. data/test/prepare_testapp.rb +0 -15
@@ -0,0 +1,29 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'fileutils'
4
+ require 'tmpdir'
5
+
6
+ TESTAPP_PATH = ENV['TESTAPP_PATH'] || File.join(Dir.tmpdir, 'declare_schema_testapp') unless defined?(TESTAPP_PATH)
7
+ FileUtils.chdir(TESTAPP_PATH)
8
+
9
+ system "rm -rf app/models/ad* app/models/alpha*"
10
+ system "rm -rf test/models/ad* test/models/alpha*"
11
+ system "rm -rf test/fixtures/ad* test/fixtures/alpha*"
12
+ system "rm -rf db/migrate/*"
13
+ system "mkdir -p #{TESTAPP_PATH}/app/assets/config"
14
+ system "echo '' >> #{TESTAPP_PATH}/app/assets/config/manifest.js"
15
+
16
+ require "#{TESTAPP_PATH}/config/environment"
17
+
18
+ require 'rails/generators'
19
+ Rails::Generators.configure!(Rails.application.config.generators)
20
+
21
+ (ActiveRecord::Base.connection.tables - Generators::DeclareSchema::Migration::Migrator.always_ignore_tables).each do |table|
22
+ ActiveRecord::Base.connection.execute("DROP TABLE #{ActiveRecord::Base.connection.quote_table_name(table)}")
23
+ end
24
+
25
+ ActiveRecord::Base.send(:descendants).each do |model|
26
+ unless model.name['Active'] || model.name['Application']
27
+ nuke_model_class(model)
28
+ end
29
+ end
@@ -0,0 +1,42 @@
1
+ # frozen_string_literal: true
2
+
3
+ require 'rails'
4
+ require 'rails/generators'
5
+
6
+ module Generators
7
+ module DeclareSchema
8
+ module Migration
9
+ RSpec.describe Migrator do
10
+ before do
11
+ ActiveRecord::Base.connection.tables
12
+ end
13
+
14
+ subject { described_class.new }
15
+
16
+ describe 'format_options' do
17
+ let(:mysql_longtext_limit) { 0xffff_ffff }
18
+
19
+ context 'MySQL' do
20
+ before do
21
+ expect(::DeclareSchema::Model::FieldSpec).to receive(:mysql_text_limits?).and_return(true)
22
+ end
23
+
24
+ it 'returns text limits' do
25
+ expect(subject.format_options({ limit: mysql_longtext_limit }, :text)).to eq(["limit: #{mysql_longtext_limit}"])
26
+ end
27
+ end
28
+
29
+ context 'non-MySQL' do
30
+ before do
31
+ expect(::DeclareSchema::Model::FieldSpec).to receive(:mysql_text_limits?).and_return(false)
32
+ end
33
+
34
+ it 'returns text limits' do
35
+ expect(subject.format_options({ limit: mysql_longtext_limit }, :text)).to eq([])
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
42
+ end
@@ -22,6 +22,32 @@ RSpec.configure do |config|
22
22
 
23
23
  RSpec::Support::ObjectFormatter.default_instance.max_formatted_output_length = 2_000
24
24
 
25
+ def active_record_base_class
26
+ if Rails::VERSION::MAJOR == 4
27
+ 'ActiveRecord::Base'
28
+ else
29
+ 'ApplicationRecord'
30
+ end
31
+ end
32
+
33
+ def migrate(renames = {})
34
+ up, down = Generators::DeclareSchema::Migration::Migrator.run(renames)
35
+ ActiveRecord::Migration.class_eval(up)
36
+ ActiveRecord::Base.send(:descendants).each { |model| model.reset_column_information }
37
+ [up, down]
38
+ end
39
+
40
+ def nuke_model_class(klass)
41
+ ActiveSupport::DescendantsTracker.instance_eval do
42
+ direct_descendants = class_variable_get('@@direct_descendants')
43
+ direct_descendants[ActiveRecord::Base] = direct_descendants[ActiveRecord::Base].to_a.reject { |descendant| descendant == klass }
44
+ if defined?(ApplicationRecord)
45
+ direct_descendants[ApplicationRecord] = direct_descendants[ApplicationRecord].to_a.reject { |descendant| descendant == klass }
46
+ end
47
+ end
48
+ Object.instance_eval { remove_const(klass.name.to_sym) rescue nil }
49
+ end
50
+
25
51
  def with_modified_env(options, &block)
26
52
  ClimateControl.modify(options, &block)
27
53
  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.1.0
4
+ version: 0.2.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: 2020-09-23 00:00:00.000000000 Z
11
+ date: 2020-10-26 00:00:00.000000000 Z
12
12
  dependencies:
13
13
  - !ruby/object:Gem::Dependency
14
14
  name: rails
@@ -35,11 +35,10 @@ files:
35
35
  - ".dependabot/config.yml"
36
36
  - ".github/workflows/gem_release.yml"
37
37
  - ".gitignore"
38
- - ".jenkins/Jenkinsfile"
39
- - ".jenkins/ruby_build_pod.yml"
40
38
  - ".rspec"
41
39
  - ".rubocop.yml"
42
40
  - ".ruby-version"
41
+ - ".travis.yml"
43
42
  - Appraisals
44
43
  - CHANGELOG.md
45
44
  - Gemfile
@@ -69,19 +68,17 @@ files:
69
68
  - lib/generators/declare_schema/migration/templates/migration.rb.erb
70
69
  - lib/generators/declare_schema/model/USAGE
71
70
  - lib/generators/declare_schema/model/model_generator.rb
72
- - lib/generators/declare_schema/model/templates/model_injection.rb.erb
73
71
  - lib/generators/declare_schema/support/eval_template.rb
74
72
  - lib/generators/declare_schema/support/model.rb
75
73
  - lib/generators/declare_schema/support/thor_shell.rb
74
+ - spec/lib/declare_schema/api_spec.rb
76
75
  - spec/lib/declare_schema/field_declaration_dsl_spec.rb
76
+ - spec/lib/declare_schema/generator_spec.rb
77
+ - spec/lib/declare_schema/interactive_primary_key_spec.rb
78
+ - spec/lib/declare_schema/migration_generator_spec.rb
79
+ - spec/lib/declare_schema/prepare_testapp.rb
80
+ - spec/lib/generators/declare_schema/migration/migrator_spec.rb
77
81
  - spec/spec_helper.rb
78
- - test/api.rdoctest
79
- - test/doc-only.rdoctest
80
- - test/generators.rdoctest
81
- - test/interactive_primary_key.rdoctest
82
- - test/migration_generator.rdoctest
83
- - test/migration_generator_comments.rdoctestDISABLED
84
- - test/prepare_testapp.rb
85
82
  - test_responses.txt
86
83
  homepage: https://github.com/Invoca/declare_schema
87
84
  licenses: []
@@ -1,72 +0,0 @@
1
- #!/usr/bin/groovy
2
- @Library('jenkins-pipeline@v0.5.1')
3
- import com.invoca.ci.*;
4
-
5
- pipeline {
6
- agent {
7
- kubernetes {
8
- defaultContainer 'ruby'
9
- yamlFile '.jenkins/ruby_build_pod.yml'
10
- }
11
- }
12
-
13
- environment {
14
- GITHUB_TOKEN = credentials('github_token')
15
- BUNDLE_GEM__FURY__IO = credentials('gemfury_deploy_token')
16
- }
17
-
18
- stages {
19
- stage('Setup') {
20
- steps {
21
- updateGitHubStatus('clean-build', 'pending', "Running unit tests")
22
- sh 'bundle install'
23
- sh 'bundle exec appraisal install'
24
- }
25
- }
26
-
27
- stage("Current Unit Tests") {
28
- steps {
29
- sh 'bundle exec rake test:prepare_testapp[force]'
30
- sh 'bundle exec rake test:all < test_responses.txt'
31
- }
32
- }
33
-
34
- stage("Rails 4 Appraisal") {
35
- steps {
36
- sh 'bundle exec appraisal rails-4 rake test:prepare_testapp[force]'
37
- sh 'bundle exec appraisal rails-4 rake test:all < test_responses.txt'
38
- }
39
- }
40
-
41
- stage("Rails 5 Appraisal") {
42
- steps {
43
- sh 'bundle exec appraisal rails-5 rake test:prepare_testapp[force]'
44
- sh 'bundle exec appraisal rails-5 rake test:all < test_responses.txt'
45
- }
46
- }
47
-
48
- stage("Rails 6 Appraisal") {
49
- steps {
50
- sh 'bundle exec appraisal rails-6 rake test:prepare_testapp[force]'
51
- sh 'bundle exec appraisal rails-6 rake test:all < test_responses.txt'
52
- }
53
- }
54
- }
55
-
56
- post {
57
- success { updateGitHubStatus('clean-build', 'success', "Unit tests passed") }
58
- failure { updateGitHubStatus('clean-build', 'failure', "Unit tests failed") }
59
- }
60
- }
61
-
62
- void updateGitHubStatus(String context, String status, String description) {
63
- gitHubStatus([
64
- repoSlug: 'Invoca/declare_schema',
65
- sha: env.GIT_COMMIT,
66
- description: description,
67
- context: context,
68
- targetURL: env.RUN_DISPLAY_URL,
69
- token: env.GITHUB_TOKEN,
70
- status: status
71
- ])
72
- }
@@ -1,19 +0,0 @@
1
-
2
- ---
3
- apiVersion: v1
4
- kind: Pod
5
- metadata:
6
- labels:
7
- jenkins/declare_schema: 'true'
8
- namespace: jenkins
9
- name: declare_schema
10
- spec:
11
- containers:
12
- - name: ruby
13
- image: ruby:2.6.5
14
- tty: true
15
- resources:
16
- requests:
17
- memory: "100Mi"
18
- command:
19
- - cat
@@ -1,25 +0,0 @@
1
-
2
- fields do
3
- <% for attribute in field_attributes -%>
4
- <%= "%-#{max_attribute_length}s" % attribute.name %> :<%= attribute.type %><%=
5
- case attribute.type.to_s
6
- when 'string'
7
- ', limit: 255'
8
- else
9
- ''
10
- end
11
- %>
12
- <% end -%>
13
- <% if options[:timestamps] -%>
14
- timestamps
15
- <% end -%>
16
- end
17
-
18
- <% for bt in bts -%>
19
- belongs_to :<%= bt %>
20
- <% end -%>
21
- <%= "\n" unless bts.empty? -%>
22
- <% for hm in hms -%>
23
- has_many :<%= hm %>, dependent: :destroy
24
- <% end -%>
25
- <%= "\n" unless hms.empty? -%>
@@ -1,136 +0,0 @@
1
- # DeclareSchema API
2
-
3
- In order for the API examples to run we need to load the rails generators of our testapp:
4
- {.hidden}
5
-
6
- doctest: prepare testapp environment
7
- doctest_require: 'prepare_testapp'
8
- {.hidden}
9
-
10
- ## Example Models
11
-
12
- Let's define some example models that we can use to demonstrate the API. With DeclareSchema we can use the 'declare_schema:model' generator like so:
13
-
14
- $ rails generate declare_schema:model advert title:string body:text
15
-
16
- This will generate the test, fixture and a model file like this:
17
-
18
- >> Rails::Generators.invoke 'declare_schema:model', %w(advert title:string body:text)
19
- {.hidden}
20
-
21
- class Advert < ActiveRecord::Base
22
- fields do
23
- title :string
24
- body :text, limit: 0xffff, null: true
25
- end
26
- end
27
-
28
- The migration generator uses this information to create a migration. The following creates and runs the migration so we're ready to go.
29
-
30
- $ rails generate declare_schema:migration -n -m
31
-
32
- We're now ready to start demonstrating the API
33
-
34
- >> require_relative "#{Rails.root}/app/models/advert.rb" if Rails::VERSION::MAJOR > 5
35
- >> Rails::Generators.invoke 'declare_schema:migration', %w(-n -m)
36
- >> Rails::Generators.invoke 'declare_schema:migration', %w(-n -m)
37
- {.hidden}
38
-
39
- ## The Basics
40
-
41
- 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.
42
-
43
- ### Field Types
44
-
45
- Field values are returned as the type you specify.
46
-
47
- >> a = Advert.new :body => "This is the body", id: 1, title: "title"
48
- >> a.body.class
49
- => String
50
-
51
- This also works after a round-trip to the database
52
-
53
- >> a.save
54
- >> b = Advert.find(a.id)
55
- >> b.body.class
56
- => String
57
-
58
- ## Names vs. Classes
59
-
60
- The full set of available symbolic names is
61
-
62
- * `:integer`
63
- * `:float`
64
- * `:decimal`
65
- * `:string`
66
- * `:text`
67
- * `:boolean`
68
- * `:date`
69
- * `:datetime`
70
- * `:html`
71
- * `:textile`
72
- * `:markdown`
73
- * `:password`
74
-
75
- You can add your own types too. More on that later.
76
-
77
-
78
- ## Model extensions
79
-
80
- DeclareSchema adds a few features to your models.
81
-
82
- ### `Model.attr_type`
83
-
84
- Returns the type (i.e. class) declared for a given field or attribute
85
-
86
- >> Advert.connection.schema_cache.clear!
87
- >> Advert.reset_column_information
88
- >> Advert.attr_type :title
89
- => String
90
- >> Advert.attr_type :body
91
- => String
92
-
93
- ## Field validations
94
-
95
- DeclareSchema gives you some shorthands for declaring some common validations right in the field declaration
96
-
97
- ### Required fields
98
-
99
- The `:required` argument to a field gives a `validates_presence_of`:
100
-
101
- >>
102
- class Advert
103
- fields do
104
- title :string, :required, limit: 255
105
- end
106
- end
107
- >> a = Advert.new
108
- >> a.valid?
109
- => false
110
- >> a.errors.full_messages
111
- => ["Title can't be blank"]
112
- >> a.id = 2
113
- >> a.body = "hello"
114
- >> a.title = "Jimbo"
115
- >> a.save
116
- => true
117
-
118
-
119
- ### Unique fields
120
-
121
- The `:unique` argument in a field declaration gives `validates_uniqueness_of`:
122
-
123
- >>
124
- class Advert
125
- fields do
126
- title :string, :unique, limit: 255
127
- end
128
- end
129
- >> a = Advert.new :title => "Jimbo", id: 3, body: "hello"
130
- >> a.valid?
131
- => false
132
- >> a.errors.full_messages
133
- => ["Title has already been taken"]
134
- >> a.title = "Sambo"
135
- >> a.save
136
- => true
@@ -1,76 +0,0 @@
1
- # DeclareSchema
2
-
3
- ## Introduction
4
-
5
- Welcome to DeclareSchema -- a spin-off from HoboFields part of the Hobo project (Hobo not required!).
6
-
7
- **DeclareSchema writes your Rails migrations for you! Your migration writing days are over!**
8
-
9
- All we ask is that you declare your fields in the model. It's still perfectly DRY because you're not having to repeat that in the migration -- DeclareSchema does that for you. In fact, you'll come to love having them there.
10
-
11
- It still has all the benefits of writing your own migrations, for example if you want to add some special code to migrate your old data, you can just edit the generated migration.
12
-
13
- ## Example
14
-
15
- First off, pass the `--skip-migration` option when generating your models:
16
-
17
- $ rails generate model blog_post --skip-migration
18
-
19
- Now edit your model as follows:
20
-
21
- class BlogPost < ActiveRecord::Base
22
- fields do
23
- title :string
24
- body :text
25
- timestamps
26
- end
27
- end
28
- {: .ruby}
29
-
30
-
31
- Then, simply run
32
-
33
- $ rails generate declare_schema:migration
34
-
35
- And voila
36
-
37
- ---------- Up Migration ----------
38
- create_table :blog_posts do |t|
39
- t.string :title
40
- t.text :body
41
- t.datetime :created_at
42
- t.datetime :updated_at
43
- end
44
- ----------------------------------
45
-
46
- ---------- Down Migration --------
47
- drop_table :blog_posts
48
- ----------------------------------
49
- {: .ruby}
50
-
51
- The migration generator has created a migration to change from the schema that is currently in your database, to the schema that your models need. That's really all there is to it. You are now free to simply hack away on your app and run the migration generator every time the database needs to play catch-up.
52
-
53
- Note that the migration generator is interactive -- it can't tell the difference between renaming something vs. adding one thing and removing another, so sometimes it will ask you to clarify. It's a bit picky about what it makes you type in response, because we really don't want you to lose data when someone's amazing twitter distracts you at the wrong moment.
54
-
55
- ## Installing
56
-
57
- The simplest and recommended way to install DeclareSchema is as a gem:
58
-
59
- $ gem install declare_schema
60
-
61
- ## API
62
-
63
- ## Migration Generator Details
64
-
65
- The migration generator doctests provide a lot more detail. They're not really that great as documentation because doctests run in a single irb session, and that doesn't fit well with the concept of a generator. Skip these unless you're really keen to see the details of the migration generator in action
66
-
67
- - [Migration Generator Details](/manual/declare_schema/migration_generator)
68
-
69
- ## About Doctests
70
-
71
- DeclareSchema is documented and tested using *doctests*. This is an idea that comes from Python that we've been experimenting with for Hobo. Whenever you see code-blocks that start "`>>`", read them as IRB sessions. The `rdoctest` tool extracts these and runs them to verify they behave as advertised.
72
-
73
- Doctests are a great way to get both documentation and tests from the same source. We're still experimenting with exactly how this all works though, so if the docs seem strange in places, please bear with us!
74
-
75
- You may download rubydoctest via [github](http://www.github.com/tablatom/rubydoctest)
76
-