fx 0.1.0 → 0.11.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 (106) hide show
  1. checksums.yaml +5 -5
  2. data/.github/scripts/check-eol +95 -0
  3. data/.github/workflows/ci.yml +61 -0
  4. data/.github/workflows/eol-check.yml +30 -0
  5. data/.rspec +1 -1
  6. data/.standard.yml +3 -0
  7. data/CHANGELOG.md +186 -0
  8. data/CONTRIBUTING.md +13 -4
  9. data/Gemfile +21 -2
  10. data/LICENSE +18 -0
  11. data/README.md +99 -14
  12. data/Rakefile +11 -1
  13. data/bin/rake +2 -3
  14. data/bin/rspec +13 -3
  15. data/bin/setup +1 -4
  16. data/bin/standardrb +27 -0
  17. data/bin/yard +13 -3
  18. data/fx.gemspec +20 -27
  19. data/lib/fx/adapters/postgres/connection.rb +6 -0
  20. data/lib/fx/adapters/postgres/functions.rb +16 -28
  21. data/lib/fx/adapters/postgres/query_executor.rb +34 -0
  22. data/lib/fx/adapters/postgres/triggers.rb +14 -28
  23. data/lib/fx/adapters/postgres.rb +30 -17
  24. data/lib/fx/command_recorder.rb +104 -13
  25. data/lib/fx/configuration.rb +10 -25
  26. data/lib/fx/definition.rb +32 -10
  27. data/lib/fx/function.rb +23 -9
  28. data/lib/fx/railtie.rb +15 -0
  29. data/lib/fx/schema_dumper.rb +33 -10
  30. data/lib/fx/statements.rb +213 -11
  31. data/lib/fx/trigger.rb +5 -3
  32. data/lib/fx/version.rb +1 -1
  33. data/lib/fx.rb +40 -0
  34. data/lib/generators/fx/function/USAGE +2 -0
  35. data/lib/generators/fx/function/function_generator.rb +54 -39
  36. data/lib/generators/fx/function/templates/db/migrate/create_function.erb +1 -1
  37. data/lib/generators/fx/function/templates/db/migrate/update_function.erb +1 -1
  38. data/lib/generators/fx/migration_helper.rb +45 -0
  39. data/lib/generators/fx/name_helper.rb +33 -0
  40. data/lib/generators/fx/trigger/USAGE +2 -0
  41. data/lib/generators/fx/trigger/templates/db/migrate/create_trigger.erb +1 -1
  42. data/lib/generators/fx/trigger/templates/db/migrate/update_trigger.erb +1 -1
  43. data/lib/generators/fx/trigger/trigger_generator.rb +46 -51
  44. data/lib/generators/fx/version_helper.rb +55 -0
  45. data/spec/acceptance/user_manages_functions_spec.rb +26 -6
  46. data/spec/acceptance/user_manages_triggers_spec.rb +11 -11
  47. data/spec/acceptance_helper.rb +17 -11
  48. data/spec/dummy/Rakefile +8 -1
  49. data/spec/dummy/bin/bundle +2 -2
  50. data/spec/dummy/bin/rails +3 -3
  51. data/spec/dummy/bin/rake +2 -2
  52. data/spec/dummy/config/application.rb +2 -0
  53. data/spec/dummy/config/database.yml +2 -0
  54. data/spec/dummy/config.ru +1 -1
  55. data/spec/features/functions/migrations_spec.rb +9 -9
  56. data/spec/features/functions/revert_spec.rb +13 -13
  57. data/spec/features/triggers/migrations_spec.rb +11 -11
  58. data/spec/features/triggers/revert_spec.rb +21 -21
  59. data/spec/fx/adapters/postgres/functions_spec.rb +40 -0
  60. data/spec/fx/adapters/postgres/query_executor_spec.rb +75 -0
  61. data/spec/fx/adapters/postgres/triggers_spec.rb +48 -0
  62. data/spec/fx/adapters/postgres_spec.rb +115 -111
  63. data/spec/fx/command_recorder_spec.rb +39 -53
  64. data/spec/fx/configuration_spec.rb +20 -9
  65. data/spec/fx/definition_spec.rb +53 -38
  66. data/spec/fx/function_spec.rb +143 -0
  67. data/spec/fx/schema_dumper_spec.rb +241 -0
  68. data/spec/fx/statements_spec.rb +219 -0
  69. data/spec/fx/trigger_spec.rb +52 -0
  70. data/spec/fx_spec.rb +28 -0
  71. data/spec/generators/fx/function/function_generator_spec.rb +22 -10
  72. data/spec/generators/fx/migration_helper_spec.rb +108 -0
  73. data/spec/generators/fx/name_helper_spec.rb +114 -0
  74. data/spec/generators/fx/trigger/trigger_generator_spec.rb +55 -20
  75. data/spec/generators/fx/version_helper_spec.rb +157 -0
  76. data/spec/spec_helper.rb +16 -4
  77. data/spec/support/database_reset.rb +24 -0
  78. data/spec/support/definition_helpers.rb +5 -9
  79. data/spec/support/generator_setup.rb +46 -5
  80. data/spec/support/migration_helpers.rb +9 -1
  81. data/spec/support/warning_helper.rb +5 -0
  82. metadata +43 -215
  83. data/.ruby-version +0 -1
  84. data/.travis.yml +0 -27
  85. data/Appraisals +0 -21
  86. data/bin/appraisal +0 -17
  87. data/gemfiles/rails40.gemfile +0 -8
  88. data/gemfiles/rails40.gemfile.lock +0 -111
  89. data/gemfiles/rails41.gemfile +0 -8
  90. data/gemfiles/rails41.gemfile.lock +0 -113
  91. data/gemfiles/rails42.gemfile +0 -8
  92. data/gemfiles/rails42.gemfile.lock +0 -130
  93. data/gemfiles/rails50.gemfile +0 -8
  94. data/gemfiles/rails50.gemfile.lock +0 -126
  95. data/lib/fx/command_recorder/arguments.rb +0 -43
  96. data/lib/fx/command_recorder/function.rb +0 -30
  97. data/lib/fx/command_recorder/trigger.rb +0 -30
  98. data/lib/fx/schema_dumper/function.rb +0 -29
  99. data/lib/fx/schema_dumper/trigger.rb +0 -29
  100. data/lib/fx/statements/function.rb +0 -105
  101. data/lib/fx/statements/trigger.rb +0 -133
  102. data/spec/fx/command_recorder/arguments_spec.rb +0 -41
  103. data/spec/fx/schema_dumper/function_spec.rb +0 -22
  104. data/spec/fx/schema_dumper/trigger_spec.rb +0 -40
  105. data/spec/fx/statements/function_spec.rb +0 -103
  106. data/spec/fx/statements/trigger_spec.rb +0 -132
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 07cc76f1310a1982698708c2022bbfae7853263a
4
- data.tar.gz: a598bd9efd102a6d8b2789379c46f865a4319f86
2
+ SHA256:
3
+ metadata.gz: a1b25361cfa43280f81e493a4eb518864862dad321878048282ce8f9daad6e1a
4
+ data.tar.gz: ce20ce6b1bb9031b420e78b83ecf23416d50ca44668cf15e0951142e32a0c75e
5
5
  SHA512:
6
- metadata.gz: b9b0b8012f86bb96300811e6e7f3eaf0ae03f437da774bb8751759cd7c3546556d5bd3ed195410e97db0a808f55e118c88deca1887d570bd6752308aabd254b0
7
- data.tar.gz: 022f693c2da994f7204e6320b4bf73b606df63d8d2c5a9f28bce6cb596d035ce949f2c39a3b6111aa6f926079a5b1b67ed73cd989b7ec1567aa5b3d2113a0e74
6
+ metadata.gz: 2e8d5db85fb283586a9d713cdffb6fc50604e85498eeb2c16ac8f63708fabe01a29e03b4157b32f84fe061d4bedb996f4e44db90f5d53d2b655c730352c7c101
7
+ data.tar.gz: 70e6670c67e267f383fa7ed3001350fa3c6548d4bf6183625ee10bf1a5c53cfa5c87cbd9bf231b196524f206d96bf638ba16f6b9020f20803f4ae85685fbf44a
@@ -0,0 +1,95 @@
1
+ #!/bin/sh
2
+ #
3
+ # Check if minimum supported dependency versions are at or approaching EOL.
4
+ # Uses the endoflife.date API. Outputs GitHub Actions annotations and opens
5
+ # an issue when action is needed.
6
+ #
7
+ # Environment:
8
+ # WARN_DAYS - days before EOL to start warning (default: 90)
9
+ # GH_TOKEN - GitHub token for creating issues (optional)
10
+
11
+ set -e
12
+
13
+ WARN_DAYS="${WARN_DAYS:-90}"
14
+
15
+ ruby_min=$(ruby -e "
16
+ spec = Gem::Specification.load('fx.gemspec')
17
+ puts spec.required_ruby_version.requirements
18
+ .select { |op, _| op == '>=' }
19
+ .map { |_, v| v.segments[0..1].join('.') }
20
+ .first
21
+ ")
22
+
23
+ rails_min=$(ruby -e "
24
+ spec = Gem::Specification.load('fx.gemspec')
25
+ dep = spec.dependencies.find { |d| d.name == 'activerecord' }
26
+ puts dep.requirement.requirements
27
+ .select { |op, _| op == '>=' }
28
+ .map { |_, v| v.segments[0..1].join('.') }
29
+ .first
30
+ ")
31
+
32
+ postgres_min=$(ruby -ryaml -e "
33
+ ci = YAML.load_file('.github/workflows/ci.yml')
34
+ versions = ci.dig('jobs', 'tests', 'strategy', 'matrix', 'postgres')
35
+ puts versions.map(&:to_i).min
36
+ ")
37
+
38
+ echo "Ruby minimum: $ruby_min"
39
+ echo "Rails minimum: $rails_min"
40
+ echo "PostgreSQL minimum: $postgres_min"
41
+
42
+ alerts=""
43
+
44
+ check_eol() {
45
+ product="$1"
46
+ version="$2"
47
+
48
+ eol_date=$(curl -sf "https://endoflife.date/api/${product}/${version}.json" \
49
+ | ruby -rjson -e '
50
+ data = JSON.parse($stdin.read)
51
+ eol = data["eol"]
52
+ if eol == true
53
+ puts "1970-01-01"
54
+ elsif eol == false
55
+ exit
56
+ else
57
+ puts eol
58
+ end
59
+ ') || return 0
60
+
61
+ if [ -z "$eol_date" ]; then
62
+ echo "$product $version: no EOL date set (still supported)"
63
+ return
64
+ fi
65
+
66
+ today=$(date -u +%Y-%m-%d)
67
+ warn_date=$(date -u -d "$today + ${WARN_DAYS} days" +%Y-%m-%d 2>/dev/null \
68
+ || date -u -v+"${WARN_DAYS}"d +%Y-%m-%d)
69
+
70
+ if [ "$eol_date" \< "$today" ] || [ "$eol_date" = "$today" ]; then
71
+ echo "::error::$product $version reached EOL on $eol_date"
72
+ alerts="$alerts- **$product $version** reached EOL on $eol_date\n"
73
+ elif [ "$eol_date" \< "$warn_date" ]; then
74
+ echo "::warning::$product $version reaches EOL on $eol_date (within ${WARN_DAYS} days)"
75
+ alerts="$alerts- **$product $version** reaches EOL on $eol_date (within ${WARN_DAYS} days)\n"
76
+ else
77
+ echo "$product $version: EOL on $eol_date (no action needed)"
78
+ fi
79
+ }
80
+
81
+ check_eol "ruby" "$ruby_min"
82
+ check_eol "rails" "$rails_min"
83
+ check_eol "postgresql" "$postgres_min"
84
+
85
+ if [ -n "$alerts" ]; then
86
+ title="Dependency EOL alert"
87
+ body=$(printf "The following minimum supported versions are at or approaching end-of-life:\n\n%b\n\nConsider bumping the minimum version in \`fx.gemspec\` and updating the CI matrix." "$alerts")
88
+
89
+ existing=$(gh issue list --state open --search "$title" --json number --jq '.[0].number' 2>/dev/null || true)
90
+ if [ -n "$existing" ]; then
91
+ echo "Issue #$existing already open, skipping creation"
92
+ else
93
+ gh issue create --title "$title" --body "$body"
94
+ fi
95
+ fi
@@ -0,0 +1,61 @@
1
+ name: CI
2
+
3
+ on:
4
+ push:
5
+ branches: master
6
+ pull_request:
7
+ branches: "*"
8
+
9
+ jobs:
10
+ tests:
11
+ name: "ruby ${{ matrix.ruby }} / rails ${{ matrix.rails }} / postgres ${{ matrix.postgres }}"
12
+ runs-on: ubuntu-latest
13
+
14
+ strategy:
15
+ fail-fast: false
16
+ matrix:
17
+ ruby: ["3.3", "4.0"]
18
+ rails: ["7.2", "8.1"]
19
+ postgres: ["14", "18"]
20
+ include:
21
+ - ruby: "4.0"
22
+ rails: "main"
23
+ postgres: "18"
24
+ allow-failure: true
25
+
26
+ continue-on-error: ${{ matrix.allow-failure || false }}
27
+
28
+ services:
29
+ postgres:
30
+ image: postgres:${{ matrix.postgres }}
31
+ env:
32
+ POSTGRES_USER: postgres
33
+ POSTGRES_HOST_AUTH_METHOD: trust
34
+ ports:
35
+ - 5432:5432
36
+ # Set health checks to wait until postgres has started
37
+ options: >-
38
+ --health-cmd pg_isready
39
+ --health-interval 10s
40
+ --health-timeout 5s
41
+ --health-retries 5
42
+
43
+ env:
44
+ POSTGRES_USER: postgres
45
+ RAILS_VERSION: ${{ matrix.rails }}
46
+
47
+ steps:
48
+ - uses: actions/checkout@v6
49
+
50
+ - name: Set up Ruby
51
+ uses: ruby/setup-ruby@v1
52
+ with:
53
+ ruby-version: ${{ matrix.ruby }}
54
+ bundler-cache: true
55
+ rubygems: latest
56
+
57
+ - name: Setup environment
58
+ run: bin/setup
59
+
60
+ - name: Run tests
61
+ run: bundle exec rake
@@ -0,0 +1,30 @@
1
+ name: EOL Check
2
+
3
+ on:
4
+ schedule:
5
+ - cron: "0 9 1 * *" # 1st of each month at 09:00 UTC
6
+ workflow_dispatch:
7
+ inputs:
8
+ warn_days:
9
+ description: "Days before EOL to start warning"
10
+ required: false
11
+ default: "90"
12
+
13
+ permissions:
14
+ issues: write
15
+
16
+ env:
17
+ WARN_DAYS: ${{ inputs.warn_days || '90' }}
18
+
19
+ jobs:
20
+ check:
21
+ name: Check dependency EOL status
22
+ runs-on: ubuntu-latest
23
+
24
+ steps:
25
+ - uses: actions/checkout@v6
26
+
27
+ - name: Check EOL status
28
+ env:
29
+ GH_TOKEN: ${{ github.token }}
30
+ run: .github/scripts/check-eol
data/.rspec CHANGED
@@ -1,2 +1,2 @@
1
- --color
1
+ --no-color
2
2
  --require spec_helper
data/.standard.yml ADDED
@@ -0,0 +1,3 @@
1
+ ignore:
2
+ - 'spec/dummy/db/schema.rb'
3
+ - '**/tmp/*'
data/CHANGELOG.md ADDED
@@ -0,0 +1,186 @@
1
+ # Changelog
2
+
3
+ The noteworthy changes for each version are included here. For a complete
4
+ changelog, see the [commits] for each version via the version links.
5
+
6
+ [commits]: https://github.com/teoljungberg/fx/commits/master
7
+
8
+ ## [0.11.0]
9
+
10
+ [0.11.0]: https://github.com/teoljungberg/fx/compare/v0.10.2...v0.11.0
11
+
12
+ - Drop EOL Ruby 3.2 (#212)
13
+ - Add `Function#signature` for PostgreSQL function identity (#207)
14
+ - Add PostgreSQL versioning policy, officially supporting PostgreSQL 14-18 (#194)
15
+ - Refactor Statements module to use explicit keyword arguments instead of `**options` hash (#186)
16
+ - Internal refactorings / improvements
17
+ - Add scheduled EOL check for Ruby, Rails, and PostgreSQL (#205)
18
+ - Add GitHub release creation to release task (#209)
19
+ - Add PostgreSQL 18 to CI test matrix (#194)
20
+ - Default `support_drop_function_without_args` to `true` (#194)
21
+ - Document adapter subclass pattern for custom schema dump ordering (#211)
22
+ - Remove duplicate command recorder specs (#202)
23
+
24
+ ## [0.10.2]
25
+
26
+ [0.10.2]: https://github.com/teoljungberg/fx/compare/v0.10.1...v0.10.2
27
+
28
+ - Remove upper bound on Rails dependency (#198)
29
+ - Internal refactorings / improvements
30
+ - Add Rails main to CI matrix
31
+
32
+ ## [0.10.1]
33
+
34
+ [0.10.1]: https://github.com/teoljungberg/fx/compare/v0.10.0...v0.10.1
35
+
36
+ - Fix spacing between functions and triggers in schema.rb (#195)
37
+ - Remove `database_cleaner` dependency (#190)
38
+ - Internal refactorings / improvements
39
+ - Improve schema dumper test coverage
40
+ - Use methods over instance variables (#192)
41
+ - Write out fully qualified path in definitions (#189)
42
+ - Improve test isolation with schema reset (#190)
43
+ - Rewrite shell command chains (#188)
44
+ - Inline `active_record_migration_class` (#187)
45
+ - Add Ruby 4.0 to CI matrix (#193)
46
+ - Minimize CI matrix to boundary versions (#196)
47
+
48
+ ## [0.10.0]
49
+
50
+ [0.10.0]: https://github.com/teoljungberg/fx/compare/v0.9.0...v0.10.0
51
+
52
+ - Scope functions and triggers to schema search path (#168)
53
+ - Ensure multi-schema dumping in Rails 8.1.0 does not dump the same objects for
54
+ all schemas (#177)
55
+ - Require Ruby >= 3.2 (#166)
56
+ - Require Ruby >= 3.1 (#162)
57
+ - New version support
58
+ - Add Rails 8.1 to the test matrix (#175)
59
+ - Add Ruby 3.4 to the test matrix (#161)
60
+ - Removed version support
61
+ - Drop Rails 7.1 due to EOL (#176)
62
+ - Drop EOL Ruby versions (3.0) (#162)
63
+ - Drop EOL Rails versions (7.0) (#162)
64
+ - Internal refactorings / improvements
65
+ - Extract validation methods in `Statements` module (#184)
66
+ - Refactor generators with helper classes and improvements (#183)
67
+ - Extract `QueryExecutor` to remove duplication (#182)
68
+ - Extract PostgreSQL version constants (#181)
69
+ - YARD documentation improvements (#179)
70
+ - Ensure fx has been loaded (#173)
71
+ - Replace ammeter (#172)
72
+ - Remove teardown phase (#171)
73
+
74
+ ## [0.9.0]
75
+
76
+ [0.9.0]: https://github.com/teoljungberg/fx/compare/v0.8.0...v0.9.0
77
+
78
+ - Drop EOL Rails versions (6.2)
79
+ - Add Ruby 3.4.0 preview's to the test matrix (#152)
80
+ - Add Rails 8.0.0 to the test matrix (#152)
81
+ - Add Rails 7.2 to the test matrix (#150)
82
+ - Fix deprecation warnings in Rails (#148)
83
+ - Mark `Fx::CommandRecorder::Arguments` as private.
84
+ - Add Ruby 3.3 to the test matrix (#144)
85
+ - Internal refactorings:
86
+ - Move development dependencies to Gemfile (#145)
87
+ - Inline `Fx::CommandRecorder::Arguments`
88
+ - Inline `Fx::{CommandRecorder,SchemaDumper,Statements}::{Function,Trigger}`
89
+ - Move configuration methods to `Fx`
90
+ - Add `Fx::Definition.{function,trigger}` (#119)
91
+ - Add Rails 7.1 to the test matrix (#136)
92
+ - Add Rubygems metadata to gemspec (#132)
93
+ - Disable RSpec's monkey patching (#121)
94
+ - Raise on warnings (#124)
95
+ - Require Ruby >= 3.0 (#128)
96
+ - Require Rails >= 6.1 (#127)
97
+
98
+ ## [0.8.0]
99
+
100
+ [0.8.0]: https://github.com/teoljungberg/fx/compare/v0.7.0...v0.8.0
101
+
102
+ - Replace Travis CI with GitHub Actions.
103
+ - Bump minimum Ruby version to 2.7.
104
+ - Ruby 2.7 will be dropped in end of March 2023, so a release to drop it will
105
+ happen afterwards.
106
+ - Bump minimum Rails version to 6.0.0
107
+ - Rails 6.0 will be dropped in June 2023, so a release to drop it will happen
108
+ afterwards
109
+ - Adopt standard.rb
110
+ - Contributing improvements
111
+ - Test-suite improvements
112
+
113
+ ## [0.7.0]
114
+
115
+ [0.7.0]: https://github.com/teoljungberg/fx/compare/v0.6.2...v0.7.0
116
+
117
+ - Support Ruby 3 (#76)
118
+ - Preserve backslashes when dumping the schema (#71)
119
+ - Add a link to F(x) SqlServer Adapter in the README (#80)
120
+
121
+ ## [0.6.2]
122
+
123
+ [0.6.2]: https://github.com/teoljungberg/fx/compare/v0.6.1...v0.6.2
124
+
125
+ - Add support for Ruby 3
126
+
127
+ ## [0.6.1]
128
+
129
+ [0.6.1]: https://github.com/teoljungberg/fx/compare/v0.6.0...v0.6.1
130
+
131
+ - Fix: Support --no-migration generator flag (#62)
132
+
133
+ ## [0.6.0]
134
+
135
+ [0.6.0]: https://github.com/teoljungberg/fx/compare/v0.5.0...v0.6.0
136
+
137
+ - Support unique functions with parameters (#27)
138
+ - Use db connection provided by Rails (#49)
139
+ - Support `--no-migration` generator flag (#60)
140
+ - Use current ActiveRecord version in migrations (#59)
141
+ - Does not include aggregates when dumping schema (#50)
142
+ - Dump functions in the beginning of the schema (#53)
143
+
144
+ ## [0.5.0]
145
+
146
+ [0.5.0]: https://github.com/teoljungberg/fx/compare/v0.4.0...v0.5.0
147
+
148
+ - Drop EOL Ruby versions.
149
+ - Drop EOL Rails versions.
150
+
151
+ ## [0.4.0]
152
+
153
+ [0.4.0]: https://github.com/teoljungberg/fx/compare/v0.3.1...v0.4.0
154
+
155
+ - Add table_name to README (#15)
156
+ - Reverse function/trigger order in README (#17)
157
+ - Split up Trigger#definition test (#19)
158
+ - Find definitions in engines (#18)
159
+
160
+ ## [0.3.1]
161
+
162
+ [0.3.1]: https://github.com/teoljungberg/fx/compare/v0.3.0...v0.3.1
163
+
164
+ - Strip shared leading whitespace from sql_definitions (#13)
165
+ - Update documentation for `drop_function`
166
+ - Document `Fx::Adapters::Postgres#initialize`
167
+ - Fix test suite issues:
168
+ - Add unit test coverage for `Fx::Adapters::Triggers`
169
+ - Add unit test coverage for `Fx::Adapters::Functions`
170
+ - Add unit test coverage for `Fx::Trigger`
171
+ - Add unit test coverage for `Fx::Function`
172
+
173
+ ## [0.3.0]
174
+
175
+ [0.3.0]: https://github.com/teoljungberg/fx/compare/v0.2.0...v0.3.0
176
+
177
+ ## [0.2.0]
178
+
179
+ [0.2.0]: https://github.com/teoljungberg/fx/compare/v0.1.0...v0.2.0
180
+
181
+ ## [0.1.0]
182
+
183
+ F(x) adds methods to `ActiveRecord::Migration` to create and manage database
184
+ functions and triggers in Rails.
185
+
186
+ [0.1.0]: https://github.com/teoljungberg/fx/compare/4ccf986643d9de82038977eff8c6b1a4a716d698...v0.1.0
data/CONTRIBUTING.md CHANGED
@@ -1,15 +1,24 @@
1
1
  # Contributing
2
2
 
3
+ We love contributions from everyone. By participating in this project, you
4
+ agree to abide by our [code of conduct].
5
+
6
+ [code of conduct]: https://thoughtbot.com/open-source-code-of-conduct
7
+
8
+ ## Contributing Code
9
+
3
10
  1. Fork the repository.
4
11
  2. Run `bin/setup`, which will install dependencies and create the dummy
5
12
  application database.
6
- 3. Run `bin/appraisal rake` to verify that the tests pass against all
7
- supported versions of Rails.
8
- 4. Make your change with new passing tests, following the existing style.
13
+ 3. Run `rake` to verify that the tests pass against the version of Rails you are
14
+ running locally.
15
+ 4. Make your change with new passing tests, following existing style.
9
16
  5. Write a [good commit message], push your fork, and submit a pull request.
17
+ 6. CI will run the test suite on all configured versions of Ruby and Rails.
18
+ Address any failures.
10
19
 
11
20
  [good commit message]: http://tbaggery.com/2008/04/19/a-note-about-git-commit-messages.html
12
21
 
13
- Others will give constructive feedback. This is a time for discussion and
22
+ Others will give constructive feedback. This is a time for discussion and
14
23
  improvements, and making the necessary changes will be required before we can
15
24
  merge the contribution.
data/Gemfile CHANGED
@@ -1,4 +1,23 @@
1
- source 'https://rubygems.org'
1
+ source "https://rubygems.org"
2
+
3
+ rails_version = ENV.fetch("RAILS_VERSION", "8.1")
2
4
 
3
- # Specify your gem's dependencies in fx.gemspec
4
5
  gemspec
6
+
7
+ if rails_version == "main"
8
+ gem "activerecord", github: "rails/rails", branch: "main"
9
+ gem "railties", github: "rails/rails", branch: "main"
10
+ else
11
+ gem "activerecord", "~> #{rails_version}.0"
12
+ gem "railties", "~> #{rails_version}.0"
13
+ end
14
+
15
+ gem "bundler", ">= 1.5"
16
+ gem "pg"
17
+ gem "pry"
18
+ gem "rake"
19
+ gem "redcarpet"
20
+ gem "rspec", ">= 3.3"
21
+ gem "standardrb"
22
+ gem "yard"
23
+ gem "warning"
data/LICENSE ADDED
@@ -0,0 +1,18 @@
1
+ Copyright 2016 Teo Ljungberg
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy of
4
+ this software and associated documentation files (the "Software"), to deal in
5
+ the Software without restriction, including without limitation the rights to
6
+ use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of
7
+ the Software, and to permit persons to whom the Software is furnished to do so,
8
+ subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in all
11
+ copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS
15
+ FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR
16
+ COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER
17
+ IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN
18
+ CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md CHANGED
@@ -1,5 +1,7 @@
1
1
  # F(x)
2
2
 
3
+ [![Build Status](https://github.com/teoljungberg/fx/actions/workflows/ci.yml/badge.svg)](https://github.com/teoljungberg/fx/actions/workflows/ci.yml)
4
+
3
5
  F(x) adds methods to `ActiveRecord::Migration` to create and manage database
4
6
  functions and triggers in Rails.
5
7
 
@@ -18,33 +20,35 @@ F(x) ships with support for PostgreSQL. The adapter is configurable (see
18
20
 
19
21
  ## Great, how do I create a trigger and a function?
20
22
 
21
- You've got this great idea for a trigger you'd like to call
23
+ You've got this great idea for a function you'd like to call
22
24
  `uppercase_users_name`. You can create the migration and the corresponding
23
25
  definition file with the following command:
24
26
 
25
27
  ```sh
26
- % rails generate fx:trigger uppercase_users_name
28
+ % rails generate fx:function uppercase_users_name
29
+ create db/functions/uppercase_users_name_v01.sql
30
+ create db/migrate/[TIMESTAMP]_create_function_uppercase_users_name.rb
31
+ ```
32
+
33
+ Edit the `db/functions/uppercase_users_name_v01.sql` file with the SQL statement
34
+ that defines your function.
35
+
36
+ Next, let's add a trigger called `uppercase_users_name` to call our new
37
+ function each time we `INSERT` on the `users` table.
38
+
39
+ ```sh
40
+ % rails generate fx:trigger uppercase_users_name table_name:users
27
41
  create db/triggers/uppercase_users_name_v01.sql
28
42
  create db/migrate/[TIMESTAMP]_create_trigger_uppercase_users_name.rb
29
43
  ```
30
44
 
31
- Edit the `db/triggers/uppercase_users_name_v01.sql` file with the SQL statement
32
- that defines your trigger. In our example, this might look something like this:
45
+ In our example, this might look something like this:
33
46
 
34
47
  ```sql
35
48
  CREATE TRIGGER uppercase_users_name
36
49
  BEFORE INSERT ON users
37
50
  FOR EACH ROW
38
- EXECUTE PROCEDURE uppercase_users_name();
39
- ```
40
-
41
- As you see, we execute a function called `uppercase_users_name` before each
42
- `INSERT` on the `users` table, which is a function we don't have yet.
43
-
44
- ```sh
45
- % rails generate fx:function uppercase_users_name
46
- create db/functions/uppercase_users_name_v01.sql
47
- create db/migrate/[TIMESTAMP]_create_function_uppercase_users_name.rb
51
+ EXECUTE FUNCTION uppercase_users_name();
48
52
  ```
49
53
 
50
54
  The generated migrations contains `create_function` and `create_trigger`
@@ -79,3 +83,84 @@ def change
79
83
  drop_function :uppercase_users_name, revert_to_version: 2
80
84
  end
81
85
  ```
86
+
87
+ ## What if I need to use a function as the default value of a column?
88
+
89
+ You need to set F(x) to dump the functions in the beginning of db/schema.rb in a
90
+ initializer:
91
+
92
+ ```ruby
93
+ # config/initializers/fx.rb
94
+ Fx.configure do |config|
95
+ config.dump_functions_at_beginning_of_schema = true
96
+ end
97
+ ```
98
+
99
+ And then you can use a lambda in your migration file:
100
+
101
+ ```ruby
102
+ create_table :my_table do |t|
103
+ t.string :my_column, default: -> { "my_function()" }
104
+ end
105
+ ```
106
+
107
+ That's how you tell Rails to use the default as a literal SQL for the default
108
+ column value instead of a plain string.
109
+
110
+ ## Customizing Schema Dump Order
111
+
112
+ By default, functions and triggers are dumped to `schema.rb` in the order
113
+ returned by the database. If you need a specific ordering (e.g., alphabetical
114
+ for deterministic diffs), subclass the adapter and override `#functions` or
115
+ `#triggers`. These methods are part of the adapter's public API and will remain
116
+ stable across releases:
117
+
118
+ ```ruby
119
+ # config/initializers/fx.rb
120
+ class SortedPostgresAdapter < Fx::Adapters::Postgres
121
+ def functions
122
+ super.sort_by(&:name)
123
+ end
124
+
125
+ def triggers
126
+ super.sort_by(&:name)
127
+ end
128
+ end
129
+
130
+ Fx.configure do |config|
131
+ config.database = SortedPostgresAdapter.new
132
+ end
133
+ ```
134
+
135
+ The same approach works for more advanced ordering. For example, if your
136
+ functions depend on each other and need to be dumped in dependency order, you
137
+ could use Ruby's built-in `TSort` to topologically sort them.
138
+
139
+ ## Plugins/Adapters
140
+
141
+ - [MySQL](https://github.com/f-mer/fx-adapters-mysql/)
142
+ - [Oracle](https://github.com/zygotecnologia/fx-oracle-adapter)
143
+ - [SQLserver](https://github.com/tarellel/fx-sqlserver-adapter)
144
+
145
+ ## Version Support
146
+
147
+ F(x) follows the maintenance policies of Ruby, Rails, and PostgreSQL, supporting
148
+ versions within their official maintenance windows.
149
+
150
+ **Ruby:** 3.3+ ([maintenance branches])
151
+
152
+ **Rails:** 7.2, 8.0, 8.1 ([maintenance policy])
153
+
154
+ **PostgreSQL:** 14, 15, 16, 17, 18 ([versioning policy])
155
+
156
+ When a version reaches end-of-life, support will be dropped in the next minor
157
+ release of F(x). Older versions may continue to work but are not tested or
158
+ guaranteed.
159
+
160
+ [maintenance branches]: https://www.ruby-lang.org/en/downloads/branches/
161
+ [maintenance policy]: https://rubyonrails.org/maintenance
162
+ [versioning policy]: https://www.postgresql.org/support/versioning/
163
+
164
+ ## Contributing
165
+
166
+ See [contributing](CONTRIBUTING.md) for more details.
data/Rakefile CHANGED
@@ -1,5 +1,15 @@
1
1
  require "bundler/gem_tasks"
2
2
  require "rspec/core/rake_task"
3
+ require "standard/rake"
4
+
5
+ Rake::Task["release"].enhance do
6
+ unless system("which gh > /dev/null 2>&1")
7
+ abort "gh CLI is not installed. Install it to create GitHub releases."
8
+ end
9
+
10
+ tag = "v#{Fx::VERSION}"
11
+ sh "gh release create #{tag} --verify-tag -t #{tag} --generate-notes"
12
+ end
3
13
 
4
14
  namespace :dummy do
5
15
  require_relative "spec/dummy/config/application"
@@ -20,4 +30,4 @@ RSpec::Core::RakeTask.new("spec:acceptance") do |task|
20
30
  end
21
31
 
22
32
  desc "Run the specs and acceptance tests"
23
- task default: %w(spec spec:acceptance)
33
+ task default: %w[spec spec:acceptance standard]
data/bin/rake CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
+
3
4
  #
4
5
  # This file was generated by Bundler.
5
6
  #
@@ -7,9 +8,7 @@
7
8
  # this file is here to facilitate running it.
8
9
  #
9
10
 
10
- require "pathname"
11
- ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
12
- Pathname.new(__FILE__).realpath)
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
13
12
 
14
13
  require "rubygems"
15
14
  require "bundler/setup"
data/bin/rspec CHANGED
@@ -1,5 +1,6 @@
1
1
  #!/usr/bin/env ruby
2
2
  # frozen_string_literal: true
3
+
3
4
  #
4
5
  # This file was generated by Bundler.
5
6
  #
@@ -7,9 +8,18 @@
7
8
  # this file is here to facilitate running it.
8
9
  #
9
10
 
10
- require "pathname"
11
- ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../../Gemfile",
12
- Pathname.new(__FILE__).realpath)
11
+ ENV["BUNDLE_GEMFILE"] ||= File.expand_path("../Gemfile", __dir__)
12
+
13
+ bundle_binstub = File.expand_path("bundle", __dir__)
14
+
15
+ if File.file?(bundle_binstub)
16
+ if File.read(bundle_binstub, 300) =~ /This file was generated by Bundler/
17
+ load(bundle_binstub)
18
+ else
19
+ abort("Your `bin/bundle` was not generated by Bundler, so this binstub cannot run.
20
+ Replace `bin/bundle` by running `bundle binstubs bundler --force`, then run this command again.")
21
+ end
22
+ end
13
23
 
14
24
  require "rubygems"
15
25
  require "bundler/setup"