michel 0.1.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.
Files changed (77) hide show
  1. checksums.yaml +7 -0
  2. data/.github/workflows/dynamic-security.yml +19 -0
  3. data/.github/workflows/main.yml +92 -0
  4. data/.gitignore +14 -0
  5. data/.rspec +3 -0
  6. data/.ruby-version +1 -0
  7. data/.standard.yml +5 -0
  8. data/CHANGELOG.md +5 -0
  9. data/CODE_OF_CONDUCT.md +6 -0
  10. data/CONTRIBUTING.md +38 -0
  11. data/Gemfile +17 -0
  12. data/Gemfile.lock +325 -0
  13. data/LICENSE +19 -0
  14. data/README.md +76 -0
  15. data/RELEASING.md +43 -0
  16. data/Rakefile +10 -0
  17. data/SECURITY.md +2 -0
  18. data/bin/console +11 -0
  19. data/bin/setup +8 -0
  20. data/lib/generators/michel/install/install_generator.rb +13 -0
  21. data/lib/generators/michel/install/templates/michel.rb +5 -0
  22. data/lib/generators/michel/view/templates/belongs_to_associations.erb +6 -0
  23. data/lib/generators/michel/view/templates/has_many_associations.erb +2 -0
  24. data/lib/generators/michel/view/templates/index_migration.erb +15 -0
  25. data/lib/generators/michel/view/templates/view.erb +95 -0
  26. data/lib/generators/michel/view/templates/view_migration.rb +8 -0
  27. data/lib/generators/michel/view/view_generator.rb +59 -0
  28. data/lib/michel/version.rb +5 -0
  29. data/lib/michel.rb +60 -0
  30. data/michel.gemspec +33 -0
  31. data/sig/michel.rbs +4 -0
  32. data/spec/example-app/.ruby-version +1 -0
  33. data/spec/example-app/Rakefile +6 -0
  34. data/spec/example-app/app/controllers/application_controller.rb +2 -0
  35. data/spec/example-app/app/controllers/concerns/.keep +0 -0
  36. data/spec/example-app/app/models/application_record.rb +3 -0
  37. data/spec/example-app/app/models/appointment.rb +3 -0
  38. data/spec/example-app/app/models/concerns/.keep +0 -0
  39. data/spec/example-app/app/models/physician.rb +4 -0
  40. data/spec/example-app/app/models/physician_availability.rb +3 -0
  41. data/spec/example-app/bin/bundle +109 -0
  42. data/spec/example-app/bin/dev +2 -0
  43. data/spec/example-app/bin/rails +4 -0
  44. data/spec/example-app/bin/rake +4 -0
  45. data/spec/example-app/bin/setup +34 -0
  46. data/spec/example-app/config/application.rb +44 -0
  47. data/spec/example-app/config/boot.rb +3 -0
  48. data/spec/example-app/config/credentials.yml.enc +1 -0
  49. data/spec/example-app/config/database.yml +85 -0
  50. data/spec/example-app/config/environment.rb +5 -0
  51. data/spec/example-app/config/environments/development.rb +49 -0
  52. data/spec/example-app/config/environments/production.rb +64 -0
  53. data/spec/example-app/config/environments/test.rb +42 -0
  54. data/spec/example-app/config/initializers/cors.rb +16 -0
  55. data/spec/example-app/config/initializers/filter_parameter_logging.rb +8 -0
  56. data/spec/example-app/config/initializers/inflections.rb +16 -0
  57. data/spec/example-app/config/locales/en.yml +31 -0
  58. data/spec/example-app/config/puma.rb +38 -0
  59. data/spec/example-app/config/routes.rb +10 -0
  60. data/spec/example-app/config.ru +6 -0
  61. data/spec/example-app/db/migrate/20250829205200_create_physicians.rb +8 -0
  62. data/spec/example-app/db/migrate/20250829205205_create_appointments.rb +10 -0
  63. data/spec/example-app/db/migrate/20250829205257_create_availabilities.rb +12 -0
  64. data/spec/example-app/db/schema.rb +42 -0
  65. data/spec/example-app/db/seeds.rb +9 -0
  66. data/spec/example-app/lib/tasks/.keep +0 -0
  67. data/spec/example-app/log/.keep +0 -0
  68. data/spec/example-app/public/robots.txt +1 -0
  69. data/spec/example-app/script/.keep +0 -0
  70. data/spec/example-app/tmp/.keep +0 -0
  71. data/spec/example-app/tmp/pids/.keep +0 -0
  72. data/spec/example-app/vendor/.keep +0 -0
  73. data/spec/generators/michel/install/install_generator_spec.rb +18 -0
  74. data/spec/generators/michel/view/view_generator_spec.rb +63 -0
  75. data/spec/lib/michel_spec.rb +60 -0
  76. data/spec/spec_helper.rb +27 -0
  77. metadata +158 -0
data/SECURITY.md ADDED
@@ -0,0 +1,2 @@
1
+ <!-- START /templates/security.md -->
2
+ <!-- END /templates/security.md -->
data/bin/console ADDED
@@ -0,0 +1,11 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ require "bundler/setup"
5
+ require "michel"
6
+
7
+ # You can add fixtures and/or initialization code here to make experimenting
8
+ # with your gem easier. You can also use a different console, if you like.
9
+
10
+ require "irb"
11
+ IRB.start(__FILE__)
data/bin/setup ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+ set -vx
5
+
6
+ bundle install
7
+
8
+ # Do any other automated setup that you need to do here
@@ -0,0 +1,13 @@
1
+ require "rails/generators/base"
2
+ require "scenic"
3
+ module Michel
4
+ module Generators
5
+ class InstallGenerator < Rails::Generators::Base
6
+ source_root File.expand_path("../templates", __FILE__)
7
+
8
+ def create_initializer
9
+ copy_file "michel.rb", "config/initializers/michel.rb"
10
+ end
11
+ end
12
+ end
13
+ end
@@ -0,0 +1,5 @@
1
+ Michel.setup do |config|
2
+ config.resource_class_name = "Resource"
3
+ config.booking_class_name = "Booking"
4
+ config.availability_class_name = "Availability"
5
+ end
@@ -0,0 +1,6 @@
1
+ belongs_to :<%=Michel.availability_class_symbol%>
2
+ belongs_to :<%=Michel.resource_class_symbol%>
3
+
4
+ def self.refresh
5
+ Scenic.database.refresh_materialized_view(table_name, concurrently: false, cascade: false)
6
+ end
@@ -0,0 +1,2 @@
1
+
2
+ has_many :available_time_slots
@@ -0,0 +1,15 @@
1
+
2
+ reversible do |direction|
3
+ direction.up do
4
+ execute <<-SQL
5
+ CREATE EXTENSION IF NOT EXISTS btree_gist;
6
+ CREATE INDEX on <%=Michel.booking_class_table_name%> using gist (<%=Michel.resource_class_foreign_id%>, tsrange(start_time, start_time + interval '1 minute' * duration, '()'));
7
+ SQL
8
+ end
9
+ direction.down do
10
+ execute <<-SQL
11
+ DROP INDEX <%=Michel.booking_class_table_name%>_<%=Michel.resource_class_foreign_id%>_tsrange_idx;
12
+ DROP EXTENSION IF EXISTS btree_gist;
13
+ SQL
14
+ end
15
+ end
@@ -0,0 +1,95 @@
1
+ WITH RECURSIVE time_slots AS (
2
+ -- Generate a series of dates for the next year, starting from tomorrow
3
+ SELECT
4
+ p.id AS <%=Michel.availability_class_foreign_id%>,
5
+ p.<%=Michel.resource_class_foreign_id%>,
6
+ -- what is DATE_TRUNC?
7
+ -- DATE_TRUNC('week', CURRENT_DATE) returns the first day of the week
8
+ (DATE_TRUNC('week', CURRENT_DATE) + (p.weekday - 1 || ' days')::interval) AS start_date,
9
+ CURRENT_DATE + INTERVAL '6 months' AS end_date,
10
+ p.start_time,
11
+ p.end_time,
12
+ p.timezone
13
+ FROM
14
+ <%=Michel.availability_class_table_name%> p
15
+ UNION ALL
16
+ -- Recursively generate dates for all instances of the given weekday
17
+ SELECT
18
+ ts.<%=Michel.availability_class_foreign_id%>,
19
+ ts.<%=Michel.resource_class_foreign_id%>,
20
+ ts.start_date + INTERVAL '1 week' AS start_date, -- Explicitly alias the column
21
+ ts.end_date,
22
+ ts.start_time,
23
+ ts.end_time,
24
+ ts.timezone
25
+ FROM
26
+ time_slots ts
27
+ WHERE
28
+ ts.start_date + INTERVAL '1 week' <= ts.end_date
29
+ ),
30
+ -- Generate time slots for each day, every 15 minutes
31
+ slot_intervals AS (
32
+ SELECT
33
+ ts.<%=Michel.availability_class_foreign_id%>,
34
+ ts.<%=Michel.resource_class_foreign_id%>,
35
+ ts.start_date,
36
+
37
+ -- Create proper timestamps for start and end times with timezone handling
38
+ (make_timestamptz(
39
+ date_part('year', ts.start_date)::integer,
40
+ date_part('month', ts.start_date)::integer,
41
+ date_part('day', ts.start_date)::integer,
42
+ split_part(ts.start_time, ':', 1)::int,
43
+ split_part(ts.start_time, ':', 2)::int,
44
+ 0,
45
+ ts.timezone
46
+ )) AT TIME ZONE 'UTC' AS slot_start_time,
47
+
48
+ (make_timestamptz(
49
+ date_part('year', ts.start_date)::integer,
50
+ date_part('month', ts.start_date)::integer,
51
+ date_part('day', ts.start_date)::integer,
52
+ split_part(ts.end_time, ':', 1)::int,
53
+ split_part(ts.end_time, ':', 2)::int,
54
+ 0,
55
+ ts.timezone
56
+ )) AT TIME ZONE 'UTC' AS slot_end_time
57
+ FROM
58
+ time_slots ts
59
+ ),
60
+ time_slots_every_15_min AS (
61
+ SELECT
62
+ si.<%=Michel.availability_class_foreign_id%>,
63
+ si.<%=Michel.resource_class_foreign_id%>,
64
+ si.start_date,
65
+
66
+ -- Generate the series for time slots every 15 minutes
67
+ generate_series(
68
+ GREATEST(si.slot_start_time, CURRENT_DATE + INTERVAL '1 day'),
69
+ -- Ensure it starts from tomorrow
70
+ si.slot_end_time - INTERVAL '30 minutes',
71
+ INTERVAL '15 minutes'
72
+ ) AS slot_start_time
73
+ FROM
74
+ slot_intervals si
75
+ )
76
+ SELECT
77
+ concat(<%=Michel.availability_class_foreign_id%>, slot_start_time) AS id,
78
+ <%=Michel.availability_class_foreign_id%>,
79
+ <%=Michel.resource_class_foreign_id%>,
80
+ slot_start_time as start_time,
81
+ slot_start_time + INTERVAL '30 minutes' AS end_time
82
+ FROM
83
+ time_slots_every_15_min
84
+ EXCEPT
85
+ SELECT
86
+ concat(<%=Michel.availability_class_foreign_id%>, slot_start_time) AS id,
87
+ time_slots_every_15_min.<%=Michel.availability_class_foreign_id%>,
88
+ time_slots_every_15_min.<%=Michel.resource_class_foreign_id%>,
89
+ time_slots_every_15_min.slot_start_time as start_time,
90
+ time_slots_every_15_min.slot_start_time + INTERVAL '30 minutes' AS end_time
91
+ FROM time_slots_every_15_min
92
+ CROSS JOIN <%=Michel.booking_class_table_name%>
93
+ WHERE tsrange(<%=Michel.booking_class_table_name%>.start_time, <%=Michel.booking_class_table_name%>.start_time + interval '1 minute' * <%=Michel.booking_class_table_name%>.duration, '()')
94
+ && tsrange(time_slots_every_15_min.slot_start_time, time_slots_every_15_min.slot_start_time + INTERVAL '30 minutes', '()')
95
+ AND <%=Michel.booking_class_table_name%>.<%=Michel.resource_class_foreign_id%> = time_slots_every_15_min.<%=Michel.resource_class_foreign_id%>
@@ -0,0 +1,8 @@
1
+ unless defined?(Scenic)
2
+ raise "Scenic gem must be included in the Gemfile to run this migration"
3
+ end
4
+ def down
5
+ drop_view :available_time_slots, materialized: true
6
+ end
7
+
8
+
@@ -0,0 +1,59 @@
1
+ require "rails/generators"
2
+ require "scenic"
3
+ module Michel
4
+ module Generators
5
+ class ViewGenerator < Rails::Generators::Base
6
+ source_root File.expand_path("templates", __dir__)
7
+ include Scenic
8
+
9
+ def create_index_in_migration
10
+ self.destination_root = Rails.root
11
+
12
+ puts "Generating #{Michel.booking_class_table_name} index migration"
13
+ invoke "migration", ["add_index_to_#{Michel.booking_class_table_name}"]
14
+ index_migration_content = template_content("index_migration.erb")
15
+ Dir.glob(Rails.root.join("db/migrate/*add_index_to_#{Michel.booking_class_table_name}.rb")).each do |file|
16
+ inject_into_file file, index_migration_content, after: "def change"
17
+ end
18
+ end
19
+
20
+ def create_scenic_model
21
+ self.destination_root = Rails.root
22
+ puts "Creating scenic model available_time_slot"
23
+
24
+ invoke "scenic:model", ["available_time_slots"], {"materialized" => true, "test_framework" => false}
25
+
26
+ Dir.glob(Rails.root.join("db/migrate/*create_available_time_slots.rb")).each do |file|
27
+ gsub_file file, /change/, "up"
28
+ inject_into_class file, "CreateAvailableTimeSlots", template_content("view_migration.rb")
29
+ end
30
+ end
31
+
32
+ def create_sql_file
33
+ puts "Creating materialized view SQL at db/views/available_time_slots_v01.sql"
34
+ template "view.erb", "db/views/available_time_slots_v01.sql", {force: true}
35
+ end
36
+
37
+ def add_associations_to_models
38
+ has_many_associations = template_content("has_many_associations.erb")
39
+
40
+ inject_into_class "app/models/#{Michel.availability_class_underscore}.rb", Michel.availability_class_name,
41
+ has_many_associations
42
+ inject_into_class "app/models/#{Michel.resource_class_underscore}.rb", Michel.resource_class_name,
43
+ has_many_associations
44
+ belongs_to_associations = template_content("belongs_to_associations.erb")
45
+ case behavior
46
+ when :invoke
47
+ inject_into_class "app/models/available_time_slot.rb", "AvailableTimeSlot", belongs_to_associations
48
+ end
49
+ end
50
+
51
+ private
52
+
53
+ def template_content(filename)
54
+ template = File.read(File.join(self.class.source_root, filename))
55
+ ERB.new(template).result(binding)
56
+ end
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,5 @@
1
+ # frozen_string_literal: true
2
+
3
+ module Michel
4
+ VERSION = "0.1.0"
5
+ end
data/lib/michel.rb ADDED
@@ -0,0 +1,60 @@
1
+ require "zeitwerk"
2
+
3
+ loader = Zeitwerk::Loader.for_gem
4
+ loader.ignore("#{__dir__}/generators")
5
+ loader.setup
6
+ module Michel
7
+ class Error < StandardError; end
8
+
9
+ mattr_accessor :resource_class_name
10
+ mattr_accessor :booking_class_name
11
+ mattr_accessor :availability_class_name
12
+
13
+ def self.setup
14
+ yield self
15
+ end
16
+
17
+ def self.resource_class_symbol
18
+ @@resource_class_name.underscore.to_sym
19
+ end
20
+
21
+ def self.resource_class_foreign_id
22
+ @@resource_class_name.foreign_key
23
+ end
24
+
25
+ def self.resource_class_underscore
26
+ @@resource_class_name.underscore
27
+ end
28
+
29
+ def self.booking_class_symbol
30
+ @@booking_class_name.underscore.to_sym
31
+ end
32
+
33
+ def self.booking_class_table_name
34
+ @@booking_class_name.tableize
35
+ end
36
+
37
+ def self.booking_class_foreign_id
38
+ @@booking_class_name.foreign_key
39
+ end
40
+
41
+ def self.booking_class_underscore
42
+ @@booking_class_name.underscore
43
+ end
44
+
45
+ def self.availability_class_symbol
46
+ @@availability_class_name.underscore.to_sym
47
+ end
48
+
49
+ def self.availability_class_table_name
50
+ @@availability_class_name.tableize
51
+ end
52
+
53
+ def self.availability_class_foreign_id
54
+ @@availability_class_name.foreign_key
55
+ end
56
+
57
+ def self.availability_class_underscore
58
+ @@availability_class_name.underscore
59
+ end
60
+ end
data/michel.gemspec ADDED
@@ -0,0 +1,33 @@
1
+ # frozen_string_literal: true
2
+
3
+ require_relative "lib/michel/version"
4
+
5
+ Gem::Specification.new do |spec|
6
+ spec.name = "michel"
7
+ spec.version = Michel::VERSION
8
+ spec.authors = ["Sally Hall", "Aji Slater"]
9
+ spec.email = ["sally@thoughtbot.com", "aji.slater@gmail.com"]
10
+
11
+ spec.summary = "Generator to help with appointment scheduling"
12
+ spec.homepage = "https://github.com/thoughtbot/michel/"
13
+ spec.required_ruby_version = ">= 3.0.0"
14
+
15
+ spec.metadata["homepage_uri"] = spec.homepage
16
+ # spec.metadata["changelog_uri"] = "TODO: Put your gem's CHANGELOG.md URL here."
17
+
18
+ # Specify which files should be added to the gem when it is released.
19
+ # The `git ls-files -z` loads the files in the RubyGem that have been added into git.
20
+
21
+ spec.files = `git ls-files`.split("\n")
22
+ spec.bindir = "exe"
23
+ spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
24
+ spec.require_paths = ["lib"]
25
+
26
+ # Uncomment to register a new dependency of your gem
27
+ spec.add_dependency "activerecord", ">= 7.0.0"
28
+ spec.add_dependency "pg", "~> 1.0"
29
+ spec.add_dependency "scenic", "~>1.9"
30
+
31
+ # For more information and examples about making a new gem, check out our
32
+ # guide at: https://bundler.io/guides/creating_gem.html
33
+ end
data/sig/michel.rbs ADDED
@@ -0,0 +1,4 @@
1
+ module Michel
2
+ VERSION: String
3
+ # See the writing guide of rbs: https://github.com/ruby/rbs#guides
4
+ end
@@ -0,0 +1 @@
1
+ ruby-3.3.5
@@ -0,0 +1,6 @@
1
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
2
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
3
+
4
+ require_relative "config/application"
5
+
6
+ Rails.application.load_tasks
@@ -0,0 +1,2 @@
1
+ class ApplicationController < ActionController::API
2
+ end
File without changes
@@ -0,0 +1,3 @@
1
+ class ApplicationRecord < ActiveRecord::Base
2
+ primary_abstract_class
3
+ end
@@ -0,0 +1,3 @@
1
+ class Appointment < ApplicationRecord
2
+ belongs_to :physician
3
+ end
File without changes
@@ -0,0 +1,4 @@
1
+ class Physician < ApplicationRecord
2
+ has_many :physician_availabilities
3
+ has_many :appointments
4
+ end
@@ -0,0 +1,3 @@
1
+ class PhysicianAvailability < ApplicationRecord
2
+ belongs_to :physician
3
+ end
@@ -0,0 +1,109 @@
1
+ #!/usr/bin/env ruby
2
+ # frozen_string_literal: true
3
+
4
+ #
5
+ # This file was generated by Bundler.
6
+ #
7
+ # The application 'bundle' is installed as part of a gem, and
8
+ # this file is here to facilitate running it.
9
+ #
10
+
11
+ require "rubygems"
12
+
13
+ m = Module.new do
14
+ module_function
15
+
16
+ def invoked_as_script?
17
+ File.expand_path($0) == File.expand_path(__FILE__)
18
+ end
19
+
20
+ def env_var_version
21
+ ENV["BUNDLER_VERSION"]
22
+ end
23
+
24
+ def cli_arg_version
25
+ return unless invoked_as_script? # don't want to hijack other binstubs
26
+ return unless "update".start_with?(ARGV.first || " ") # must be running `bundle update`
27
+ bundler_version = nil
28
+ update_index = nil
29
+ ARGV.each_with_index do |a, i|
30
+ if update_index && update_index.succ == i && a.match?(Gem::Version::ANCHORED_VERSION_PATTERN)
31
+ bundler_version = a
32
+ end
33
+ next unless a =~ /\A--bundler(?:[= ](#{Gem::Version::VERSION_PATTERN}))?\z/o
34
+ bundler_version = $1
35
+ update_index = i
36
+ end
37
+ bundler_version
38
+ end
39
+
40
+ def gemfile
41
+ gemfile = ENV["BUNDLE_GEMFILE"]
42
+ return gemfile if gemfile && !gemfile.empty?
43
+
44
+ File.expand_path("../../../Gemfile", __dir__)
45
+ end
46
+
47
+ def lockfile
48
+ lockfile =
49
+ case File.basename(gemfile)
50
+ when "gems.rb" then gemfile.sub(/\.rb$/, ".locked")
51
+ else "#{gemfile}.lock"
52
+ end
53
+ File.expand_path(lockfile)
54
+ end
55
+
56
+ def lockfile_version
57
+ return unless File.file?(lockfile)
58
+ lockfile_contents = File.read(lockfile)
59
+ return unless lockfile_contents =~ /\n\nBUNDLED WITH\n\s{2,}(#{Gem::Version::VERSION_PATTERN})\n/o
60
+ Regexp.last_match(1)
61
+ end
62
+
63
+ def bundler_requirement
64
+ @bundler_requirement ||=
65
+ env_var_version ||
66
+ cli_arg_version ||
67
+ bundler_requirement_for(lockfile_version)
68
+ end
69
+
70
+ def bundler_requirement_for(version)
71
+ return "#{Gem::Requirement.default}.a" unless version
72
+
73
+ bundler_gem_version = Gem::Version.new(version)
74
+
75
+ bundler_gem_version.approximate_recommendation
76
+ end
77
+
78
+ def load_bundler!
79
+ ENV["BUNDLE_GEMFILE"] ||= gemfile
80
+
81
+ activate_bundler
82
+ end
83
+
84
+ def activate_bundler
85
+ gem_error = activation_error_handling do
86
+ gem "bundler", bundler_requirement
87
+ end
88
+ return if gem_error.nil?
89
+ require_error = activation_error_handling do
90
+ require "bundler/version"
91
+ end
92
+ return if require_error.nil? && Gem::Requirement.new(bundler_requirement).satisfied_by?(Gem::Version.new(Bundler::VERSION))
93
+ warn "Activating bundler (#{bundler_requirement}) failed:\n#{gem_error.message}\n\nTo install the version of bundler this project requires, run `gem install bundler -v '#{bundler_requirement}'`"
94
+ exit 42
95
+ end
96
+
97
+ def activation_error_handling
98
+ yield
99
+ nil
100
+ rescue StandardError, LoadError => e
101
+ e
102
+ end
103
+ end
104
+
105
+ m.load_bundler!
106
+
107
+ if m.invoked_as_script?
108
+ load Gem.bin_path("bundler", "bundle")
109
+ end
@@ -0,0 +1,2 @@
1
+ #!/usr/bin/env ruby
2
+ exec "./bin/rails", "server", *ARGV
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ APP_PATH = File.expand_path("../config/application", __dir__)
3
+ require_relative "../config/boot"
4
+ require "rails/commands"
@@ -0,0 +1,4 @@
1
+ #!/usr/bin/env ruby
2
+ require_relative "../config/boot"
3
+ require "rake"
4
+ Rake.application.run
@@ -0,0 +1,34 @@
1
+ #!/usr/bin/env ruby
2
+ require "fileutils"
3
+
4
+ APP_ROOT = File.expand_path("..", __dir__)
5
+
6
+ def system!(*args)
7
+ system(*args, exception: true)
8
+ end
9
+
10
+ FileUtils.chdir APP_ROOT do
11
+ # This script is a way to set up or update your development environment automatically.
12
+ # This script is idempotent, so that you can run it at any time and get an expectable outcome.
13
+ # Add necessary setup steps to this file.
14
+
15
+ puts "== Installing dependencies =="
16
+ system("bundle check") || system!("bundle install")
17
+
18
+ # puts "\n== Copying sample files =="
19
+ # unless File.exist?("config/database.yml")
20
+ # FileUtils.cp "config/database.yml.sample", "config/database.yml"
21
+ # end
22
+
23
+ puts "\n== Preparing database =="
24
+ system! "bin/rails db:prepare"
25
+
26
+ puts "\n== Removing old logs and tempfiles =="
27
+ system! "bin/rails log:clear tmp:clear"
28
+
29
+ unless ARGV.include?("--skip-server")
30
+ puts "\n== Starting development server =="
31
+ $stdout.flush # flush the output before exec(2) so that it displays
32
+ exec "bin/dev"
33
+ end
34
+ end
@@ -0,0 +1,44 @@
1
+ require_relative "boot"
2
+
3
+ require "rails"
4
+ # Pick the frameworks you want:
5
+ require "active_model/railtie"
6
+ # require "active_job/railtie"
7
+ require "active_record/railtie"
8
+ # require "active_storage/engine"
9
+ require "action_controller/railtie"
10
+ # require "action_mailer/railtie"
11
+ # require "action_mailbox/engine"
12
+ # require "action_text/engine"
13
+ require "action_view/railtie"
14
+ # require "action_cable/engine"
15
+ # require "rails/test_unit/railtie"
16
+
17
+ # Require the gems listed in Gemfile, including any gems
18
+ # you've limited to :test, :development, or :production.
19
+ Bundler.require(*Rails.groups)
20
+
21
+ module ExampleApp
22
+ class Application < Rails::Application
23
+ # Initialize configuration defaults for originally generated Rails version.
24
+ config.load_defaults 8.0
25
+
26
+ # Please, add to the `ignore` list any other `lib` subdirectories that do
27
+ # not contain `.rb` files, or that should not be reloaded or eager loaded.
28
+ # Common ones are `templates`, `generators`, or `middleware`, for example.
29
+ config.autoload_lib(ignore: %w[assets tasks])
30
+
31
+ # Configuration for the application, engines, and railties goes here.
32
+ #
33
+ # These settings can be overridden in specific environments using the files
34
+ # in config/environments, which are processed later.
35
+ #
36
+ # config.time_zone = "Central Time (US & Canada)"
37
+ # config.eager_load_paths << Rails.root.join("extras")
38
+
39
+ # Only loads a smaller set of middleware suitable for API only apps.
40
+ # Middleware like session, flash, cookies can be added back manually.
41
+ # Skip views, helpers and assets when generating a new resource.
42
+ config.api_only = true
43
+ end
44
+ end
@@ -0,0 +1,3 @@
1
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../../Gemfile", __dir__)
2
+
3
+ require "bundler/setup" # Set up gems listed in the Gemfile.
@@ -0,0 +1 @@
1
+ bPcLj+RUYOcXjiYTK/tVFqiUjCdvnCrBcVyybZ8age2+5F5tTftPfpHy+As4XBK2AiVooOQHs8lcm2PNAsRUOoTOMG9Y+edynBb1Af0TsYfMq0vKJwJz1D/1TT6Ddf+MCvbxUqA6NvJQpQy4x5pNP2tAX29YeWKr2XsggtvwZJ4C78DMhoL8fqgich+iIsBF86QXU7XBj64L2cEsVIpm/s4KFqeig0s6XGl0m0/euy8NKysMj3jBw4LrokvYbG7BsN3meBjVupFkeI+BGxpH8+2LXeevVtTDKq+bx+rfe8n5FMXH7DPMwQMZt/6ggRlMFvW/JUxGwZt8l24Rmw6yXZelw7pGqG0MxbPOe6MIL5gxAjM7Oyk+Sb3Km6ilW0D2ZKAnXUKw5rj3Xo8l4xn2ZUf51P6/JhnYy53rFiIP1ByMUEBq6ON1GqQ+pkGJYAHCCcDifLZUuIaNULcVkizrfJKOOtz0h0y5NZWLB/yT+EkfmyjiYrD5uS/o--IUEzH9/T2+Rx416h--1A4772sIfBvMfSKf9NL7wQ==