Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Introduce edgestitch #61

Merged
merged 33 commits into from
Dec 27, 2022
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
33 commits
Select commit Hold shift + click to select a range
cb7d063
Introduce edgestitch
xjunior Dec 20, 2022
0b4149a
Add edgestitch workflow
xjunior Dec 21, 2022
1c59f3d
Remove Gemfile.lock to support multiple rails versions
xjunior Dec 21, 2022
fda4f38
Disable unused frameworks
xjunior Dec 21, 2022
9981c3e
Start mysql on edgestitch workflow
xjunior Dec 21, 2022
0600d02
Eager load in test to avoid eager loading every where
xjunior Dec 21, 2022
79b6a97
Expect inclusion to be able to match different rails versions
xjunior Dec 21, 2022
c43855c
Allow define_create and define_self return the created task
xjunior Dec 21, 2022
f0d1005
Reenable tasks so they can run on a subsequent spec
xjunior Dec 21, 2022
52fce8e
Always run tests in RAILS_ENV test
xjunior Dec 21, 2022
e463efd
Prefer configuration_hash to avoid DEPRECATION warning
xjunior Dec 21, 2022
a06259f
Support psych 4 on ruby 3.1
xjunior Dec 21, 2022
91a9467
Inherit dependency decisions from oss-guide
xjunior Dec 21, 2022
fb14309
Ignore generated structure.sql
xjunior Dec 21, 2022
1587fc3
Replace acts_as_taggable by table without a model on example
xjunior Dec 21, 2022
2208a97
Lower migrations to oldest supported rails
xjunior Dec 21, 2022
44027ac
Load defaults for oldest supported rails
xjunior Dec 21, 2022
e57e6a9
Drop support for rails 5.2 on edgestitch
xjunior Dec 21, 2022
d6aeeed
Rename patchwork spec dir to edgestitch
xjunior Dec 21, 2022
0dbe367
Avoid empty file while writting new structure-self.sql
xjunior Dec 21, 2022
8fb38d4
Add YARD documentation
xjunior Dec 22, 2022
b42b83d
Rename renderer to stitcher
xjunior Dec 22, 2022
5a415d1
Rename task definition methods
xjunior Dec 22, 2022
9740649
Update edgestitch.gemspec with the correct info
xjunior Dec 22, 2022
567f3a7
Add README info
xjunior Dec 22, 2022
1db129f
Add portal documentation
xjunior Dec 22, 2022
8244d13
Fix rubocop violation
xjunior Dec 22, 2022
068a7a8
Merge branch 'main' into edgestitch
xjunior Dec 22, 2022
ac135ee
Drop 5.2 for good
xjunior Dec 22, 2022
51c0186
Remove unnecessary rails generated files
xjunior Dec 22, 2022
b5af16a
Read instance vairables with attr_reader
xjunior Dec 22, 2022
364d834
Apply suggestions from code review
xjunior Dec 22, 2022
29d24aa
Make first release 0.1.0
xjunior Dec 27, 2022
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions .github/workflows/_ruby-package.yml
Original file line number Diff line number Diff line change
Expand Up @@ -53,6 +53,7 @@ jobs:
bundler: '1'
env:
RAILS_VERSION: '~> ${{ matrix.rails }}'
RAILS_ENV: test
steps:
- uses: actions/checkout@v3
- name: Set up Ruby
Expand Down
14 changes: 14 additions & 0 deletions .github/workflows/edgestitch.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,14 @@
name: edgestitch

on:
push:

jobs:
ruby:
uses: ./.github/workflows/_ruby-package.yml
with:
package: ${{ github.workflow }}
workdir: 'packages/${{ github.workflow }}'
before_build: sudo /etc/init.d/mysql start
rails: "['6.0','6.1','7.0']"
secrets: inherit
4 changes: 4 additions & 0 deletions docs/README.md
Original file line number Diff line number Diff line change
Expand Up @@ -42,6 +42,10 @@ Helping ruby developers implement easy patterns.

A shared layout so that your suite of applications can have the same look and feel.

[edgestitch](https://github.com/powerhome/power-tools/blob/main/packages/edgestitch/docs/README.md) 💎

Edgestitch allows engines to define partial structure-self.sql files to be stitched into a single structure.sql file by the umbrella application.

## Installation 🛠

These packages are all meant to install inside of an application and aren't intended to stand alone; currently, they are all published to [RubyGems](https://rubygems.org/) and you can use standard Bundler methods to install them.
Expand Down
1 change: 1 addition & 0 deletions packages/consent/mkdocs.yml
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
site_name: Consent
nav:
- "Home": "README.md"
- "Changelog": "CHANGELOG.md"
plugins:
- techdocs-core
9 changes: 9 additions & 0 deletions packages/edgestitch/.gitignore
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
.bundle/
log/*.log
pkg/
spec/dummy/db/structure.sql
spec/dummy/log/*.log
spec/dummy/storage/
spec/dummy/tmp/
coverage
Gemfile.lock
2 changes: 2 additions & 0 deletions packages/edgestitch/.rubocop.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
require:
- rubocop-powerhome
9 changes: 9 additions & 0 deletions packages/edgestitch/Gemfile
Original file line number Diff line number Diff line change
@@ -0,0 +1,9 @@
# frozen_string_literal: true

source "https://rubygems.org"

gemspec

rails_version = ENV.fetch("RAILS_VERSION", ">= 6.0.6")

gem "rails", rails_version
12 changes: 12 additions & 0 deletions packages/edgestitch/Rakefile
Original file line number Diff line number Diff line change
@@ -0,0 +1,12 @@
# frozen_string_literal: true

require "rspec/core/rake_task"
RSpec::Core::RakeTask.new(:spec)

require "rubocop/rake_task"
RuboCop::RakeTask.new(:rubocop)

APP_RAKEFILE = File.expand_path("spec/dummy/Rakefile", __dir__)
load "rails/tasks/engine.rake"

task default: %i[rubocop app:db:prepare spec]
3 changes: 3 additions & 0 deletions packages/edgestitch/doc/dependency_decisions.yml
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
---
- - :inherit_from
- https://raw.githubusercontent.com/powerhome/oss-guide/master/license_rules.yml
4 changes: 4 additions & 0 deletions packages/edgestitch/docs/CHANGELOG.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,4 @@
## [0.1.0] - 2022-12-27

- First release of Edgestitch, extracted from NitroMysql.
- Supports all rails >= 6.0 and all ruby >= 2.7
99 changes: 99 additions & 0 deletions packages/edgestitch/docs/README.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,99 @@
# Edgestitch

Edgestitch allows engines to define partial structure-self.sql files to be stitched into a single structure.sql file by the umbrella application. This allows big teams to develop large [Cobra](https://cbra.info) [Applications](https://github.com/powerhome/cobra_commander) without much conflict happening in a single structure.sql file. Instead, each team will more likely update one component or two structure-self.sql files while other teams concerned with other areas of the application will be changing different files.

## Installation

### Umbrella App

Add this line to your application's Gemfile:

```ruby
# Gemfile

gem "edgestitch", require: false
```

And then execute:

$ bundle install

Then require the railtie in the umbrella's `config/application.rb`:

```ruby
# config/application.rb

require "edgestitch/railtie"
```

If your umbrella app also has migrations or even models, you'll have to also install the engine task to your `Rakefile`:

```ruby
# Rakefile

Edgestitch.define_engine(::My::Application)
```

### Engines

Each internal engine will also have a development dependency on `edgestitch`, and can install it the same way:

```ruby
# crazy.gemspec

spec.add_development_dependency "edgestitch"
```

And then execute:

$ bundle install

And install the helper tasks:

```ruby
Edgestitch.define_engine(::Crazy::Engine)
```

You'll also have to add the `railtie` to the dummy app (as they're dummy umbrella apps), in case the engine has external database dependencies.

```ruby
# spec/dummy/config/application.rb

require "edgestitch/railtie"
```

## Usage

Edgestitch will enhance the default rails tasks, so nothing special has to be done in order to use it. Once edgestitch is correctly installed things should Just Work™️ as explained in the rails manual, but it will be generating `structure-self.sql` along with `structure.sql` files. **Important**: It's recommended that `structure.sql` files are added to `.gitignore`.

# How does it work

Edgestitch works based on table and migrations ownership. Based on these ownerships, Edgestitch can export a `structure-self.sql` file defining the DDL owned by a specific engine.

The stitching process then takes all loaded engines (assuming the dependency between engines is defined correctly) and assembles a structure.sql file.
that-jill marked this conversation as resolved.
Show resolved Hide resolved

## Table and Migration Ownerships

A model is owned by an engine when it inherits from the Engine's ApplicationModel, and thus the table is owned by that engine. A migration is owned by the engine if it is defined within its `db/migrate` directory.

## Extra tables

When an external dependency brings in extra tables (i.e. acts_as_taggable) that are not defined in any `structure-self.sql`. To be part of the ecosystem, the new tables should be owned by the engine adding them. That can be done by adding these tables to `<engine>/db/extra_tables`. It's a simple text file with a list of table names that do not inherit from `<Engine>::AplicationModel`, but are owned by that engine and should be included in its structure-self.sql.

## External Gems

An external gem can also define a `structure-self.sql` file to be adapted in this ecosystem. That can be done using the same approach specified in Installation / Engines.

## Development

After checking out the repo, run `bin/setup` to install dependencies. Then, run `rake spec` to run the tests. You can also run `bin/console` for an interactive prompt that will allow you to experiment.

To install this gem onto your local machine, run `bundle exec rake install`.

## Contributing

Bug reports and pull requests are welcome on GitHub at https://github.com/powerhome/power-tools.

## License

The gem is available as open source under the terms of the [MIT License](https://opensource.org/licenses/MIT).
xjunior marked this conversation as resolved.
Show resolved Hide resolved
46 changes: 46 additions & 0 deletions packages/edgestitch/edgestitch.gemspec
Original file line number Diff line number Diff line change
@@ -0,0 +1,46 @@
# frozen_string_literal: true

$LOAD_PATH.push File.expand_path("lib", __dir__)

require "edgestitch/version"

Gem::Specification.new do |spec|
spec.name = "edgestitch"
spec.version = Edgestitch::VERSION
spec.authors = ["Carlos Palhares"]
spec.email = ["chjunior@gmail.com"]

spec.description = spec.summary = "Edgestitch allows engines to define partial structure-self.sql files to be " \
"stitched into a single structure.sql file by the umbrella application."
spec.homepage = "https://github.com/powerhome/power-tools"
spec.license = "MIT"
spec.required_ruby_version = ">= 2.7"

spec.metadata["rubygems_mfa_required"] = "true"
spec.metadata["homepage_uri"] = spec.homepage
spec.metadata["source_code_uri"] = spec.homepage
spec.metadata["changelog_uri"] = "#{spec.homepage}/blob/main/packages/edgestitch/docs/CHANGELOG.md"

# Specify which files should be added to the gem when it is released.
# The `git ls-files -z` loads the files in the RubyGem that have been added into git.
spec.files = Dir.chdir(__dir__) do
`git ls-files -z`.split("\x0").reject do |f|
(f == __FILE__) || f.match(%r{\A(?:(?:bin|test|spec|features)/|\.(?:git|travis|circleci)|appveyor)})
end
end
spec.bindir = "exe"
spec.executables = spec.files.grep(%r{\Aexe/}) { |f| File.basename(f) }
spec.require_paths = ["lib"]

spec.add_development_dependency "bundler", "~> 2.1"
spec.add_development_dependency "license_finder", ">= 7.0"
spec.add_development_dependency "mysql2", "0.5.3"
spec.add_development_dependency "pry-byebug", "3.9.0"
spec.add_development_dependency "rails", ">= 5.2.8.1"
spec.add_development_dependency "rake", "~> 13.0"
spec.add_development_dependency "rspec", "~> 3.0"
spec.add_development_dependency "rspec-rails", "~> 5.1.2"
spec.add_development_dependency "rubocop-powerhome", "0.5.0"
spec.add_development_dependency "simplecov", "0.15.1"
spec.add_development_dependency "yard", "0.9.21"
end
26 changes: 26 additions & 0 deletions packages/edgestitch/lib/edgestitch.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
# frozen_string_literal: true

require "edgestitch/exporter"
require "edgestitch/stitcher"
require "edgestitch/tasks"
require "edgestitch/version"

require "edgestitch/mysql/dump"

# Facade module to access public Edgestitch functions
#
module Edgestitch
module_function

# Define a db:stitch task
# @see Edgestitch::Tasks
def define_stitch(...)
::Edgestitch::Tasks.define_stitch(...)
end

# Define a db:stitch:<engine_name> task
# @see Edgestitch::Tasks
def define_engine(...)
::Edgestitch::Tasks.define_engine(...)
end
end
74 changes: 74 additions & 0 deletions packages/edgestitch/lib/edgestitch/exporter.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,74 @@
# frozen_string_literal: true

module Edgestitch
# @private
#
# This class is responsible for exporting an engine's owned tables and
# migrations to a SQL file.
#
class Exporter
# Exports an engine using a dump helper (@see Edgestitch::Mysql::Dump)
#
# @param engine [Class<Rails::Engine>] the engine to export
# @param dump [<#export_tables,#export_migrations>] the dump helper
#
def self.export(engine, dump)
new(engine).export(dump)
end

attr_reader :engine

def initialize(engine)
@engine = engine
@database_directory_path = engine.root.join("db")
xjunior marked this conversation as resolved.
Show resolved Hide resolved
@extra_tables_path = database_directory_path.join("extra_tables")
@structure_file_path = database_directory_path.join("structure-self.sql")
end

def export(dump, to: structure_file_path)
StringIO.open do |buffer|
buffer.puts dump.export_tables(tables)
buffer.puts
buffer.puts dump.export_migrations(migrations)
File.write to, "#{buffer.string.strip}\n"
end
end

def migrations
@migrations ||= begin
migrations_glob = database_directory_path.join("{migrate,migrate.archive}/*.rb")
Dir[migrations_glob]
.map { |filename| File.basename(filename).to_i }
.sort
end
end

def tables
component_tables + extra_tables
end

private

attr_reader :database_directory_path, :extra_tables_path, :structure_file_path

def extra_tables
@extra_tables ||= extra_tables_path.exist? ? extra_tables_path.readlines.map(&:strip) : []
end

def component_tables
@component_tables ||= models.each_with_object(Set.new) do |model, tables|
tables << model.table_name if model.table_exists?
end.sort
end

def models
@models ||= application_record&.descendants || []
end

def application_record
@application_record ||= engine.railtie_namespace&.const_get(:ApplicationRecord, false)
rescue LoadError, NameError
nil
that-jill marked this conversation as resolved.
Show resolved Hide resolved
end
end
end
Loading