{"id":17044781,"url":"https://github.com/keredson/ruby-db-evolve","last_synced_at":"2025-09-06T14:40:16.858Z","repository":{"id":30502541,"uuid":"34056888","full_name":"keredson/ruby-db-evolve","owner":"keredson","description":"DB::Evolve is to Ruby Migrations as React is to jQuery.","archived":false,"fork":false,"pushed_at":"2016-06-10T14:46:08.000Z","size":69,"stargazers_count":10,"open_issues_count":1,"forks_count":2,"subscribers_count":3,"default_branch":"master","last_synced_at":"2025-06-21T10:49:33.344Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"gpl-2.0","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/keredson.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2015-04-16T13:19:52.000Z","updated_at":"2018-06-07T17:20:38.000Z","dependencies_parsed_at":"2022-08-17T20:30:09.073Z","dependency_job_id":null,"html_url":"https://github.com/keredson/ruby-db-evolve","commit_stats":null,"previous_names":[],"tags_count":16,"template":false,"template_full_name":null,"purl":"pkg:github/keredson/ruby-db-evolve","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/keredson%2Fruby-db-evolve","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/keredson%2Fruby-db-evolve/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/keredson%2Fruby-db-evolve/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/keredson%2Fruby-db-evolve/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/keredson","download_url":"https://codeload.github.com/keredson/ruby-db-evolve/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/keredson%2Fruby-db-evolve/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":266263058,"owners_count":23901355,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2024-10-14T09:35:30.139Z","updated_at":"2025-07-21T08:03:08.313Z","avatar_url":"https://github.com/keredson.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"\nRuby DB::Evolve\n===============\n\nIntroduction\n----------------\n\n**TL;DR:** *DB::Evolve is to Ruby Migrations as React is to jQuery.*\n\nIf I'm defining some structured data (like a schema), I want to simply define it outright.  Rather than writing a bunch of diffs/deltas/migrations and letting a tool rebuild the structure I want to write the structure and have a tool build the diffs/deltas/migrations.  It's more efficient and less error prone, which makes it cheaper.  And if it's cheaper I'm more likely to do it, which means I'm less likely to work around bad schemas, which leads to better schemas overall.  *Win.*\n\nSo I wrote [such a tool](https://github.com/keredson/deseb) for Python/Django back in 2006 (for Google's Summer of Code).  I still use it today.\n\nI'm now working in Ruby, and boy do I miss it.  So I ported it.\n\nPrereqs:\n\n1.  A schema DSL (hey look, `schema.rb`)\n2. DB schema extraction (already built into the connectoin object)\n\nWhat it should do:\n\n1. Diff `schema.rb` and the live database.\n2. Generate CREATE/ALTER TABLE/COLUMN statements to bring to database in line with `schema.rb`.\n\nHow it should work in practice:\n\n1. I want a new column.\n2. I add a new line to `schema.db`.\n3. I run `rake db:evolve`.\n4. An `ALTER TABLE ADD COLUMN` statement is generated, I preview it, and say `yes` to run it.\n\nInstall\n---------\n\n1. Add `gem 'db-evolve'` to your `Gemfile`.\n2. `$ bundle install`\n\nQuick Start\n---------------------\n\nLet's say your `schema.rb` looks like this:\n\n```ruby\nActiveRecord::Schema.define(:version =\u003e 20150402195918) do\n  create_table \"comments\" do |t|\n    t.integer  \"post_id\",       :null =\u003e false\n    t.integer  \"author_id\"\n    t.datetime \"created_at\",    :null =\u003e false\n    t.datetime \"updated_at\",    :null =\u003e false\n    t.text     \"text\"\n  end\nend\n```\n\nTo enable schema evolution change this:\n\n```ruby\nActiveRecord::Schema.define(:version =\u003e 20150402195918) do\n```\n\nto this:\n\n```ruby\nDB::Evolve::Schema.define do\n```\n\nAnd then run this:\n\n```\n$ rake db:evolve\n```\n\nIt'll do nothing (assuming your database is up to date).  YAY!\n\nNow tweak a column:\n\n```ruby\n  create_table \"user_comments\", :aka =\u003e \"comments\" do |t|\n```\n\nOh look, we renamed a column.  Let's run again:\n\n```sql\n$ rake db:evolve\n\nBEGIN TRANSACTION;\n\n-- rename tables\nALTER TABLE \"comments\" RENAME TO \"user_comments\";\n\nCOMMIT;\n```\n\nHoly bejeezus!\n\nLet's try another:\n\n```ruby\n    t.text     \"body\", :aka =\u003e \"text\"\n```\n\nRun again...\n\n```sql\n$ rake db:evolve\n\nBEGIN TRANSACTION;\n\n-- column changes for user_comments\nALTER TABLE \"user_comments\" RENAME COLUMN \"text\" TO \"body\";\n\nCOMMIT;\n```\n\nYou get the idea.  Stop writing your own diffs to apply to your schema.  Just write your schema!\n\n\nForeign Keys\n------------\nRails 4 (!!!) added support for foreign keys, but the syntax is wanting.  We support it, but I don't recommend it:\n\n```ruby \n  create_table \"posts\" do |t|\n  end\n  create_table \"comments\" do |t|\n    t.integer  \"post_id\"\n  end\n  add_foreign_key :comments, :posts\n```\n\nWe also support our own syntax for defining a foreign key:\n```ruby \n  create_table \"posts\" do |t|\n  end\n  create_table \"comments\" do |t|\n    t.integer  \"post_id\", :fk =\u003e \"posts\"\n  end\n```\n\nBoth work, but ours is a lot clearer / less verbose once you get past the base case.  Compare:\n\n```ruby \n  create_table \"users\" do |t|\n  end\n  create_table \"messages\" do |t|\n    t.integer  \"from_user_id\", :fk =\u003e \"users\"\n    t.integer  \"to_user_id\", :fk =\u003e \"users\"\n  end\n```\n\nWith:\n\n```ruby \n  create_table \"users\" do |t|\n  end\n  create_table \"messages\" do |t|\n    t.integer  \"from_user_id\", :fk =\u003e \"users\"\n    t.integer  \"to_user_id\", :fk =\u003e \"users\"\n  end\n  add_foreign_key :messages, :users, column: :from_user_id\n  add_foreign_key :messages, :users, column: :to_user_id\n```\n\nOur foreign key support works with Rails `\u003e=3.2`.\n\n\nDatabase Permission Management\n------------------------------\nTo manage table level permissions db:evolve has several directives available.  To grant everything, use `grant`:\n\n```ruby\nDB::Evolve::Schema.define do\n  grant :all\n  create_table \"whatever\" do |t|\n    [...]\n```\n\nAcceptable options are `:insert`, `:select`, `:update`, `:delete`, `:truncate`, `:references`, `:trigger` and `:all`.\n\nTo grant everything except `:references` and `:trigger`:\n```ruby\nDB::Evolve::Schema.define do\n  grant :all\n  revoke :references, :trigger\n  create_table \"whatever\" do |t|\n    [...]\n```\n\nOrder is important, including between `create_table` statements.  Grants/revokes only effect the tables defined after them.\n\nIf you want to grant or revoke permissions on a specific table:\n\n```ruby\n  create_table \"whatever\" do |t|\n    t.string   \"some_sort_of_log_info\"\n    t.revoke :update, :delete\n  end\n```\n\nGrant and revoke by default apply to the current db user (as defined by your env in `database.yml`), but you can mange other users as well:\n\n```ruby\n  create_table \"whatever\" do |t|\n    t.string   \"some_sort_of_log_info\"\n    t.revoke :update, :delete, from: \"normal_user\"\n    t.grant :update, :delete, to: \"admin_user\"\n  end\n```\n\nDB::Evolve will only update permissions for users specifically mentioned (via `from` or `to`, or in `database.yml` for the given environment), not every user in the database.\n\nSchema Change Permissions\n-------------------------\nNormally, in production, you don't want your web server's database account to have permissions to modify your schema.  To have db:evolve run the schema changes as a different user than the user for your environment, create a second db env with the same name, but with `_dbevolve` appended.  For example, if my `datebase.yml` looks like:\n\n```\nproduction:\n  adapter: postgresql\n  database: mysite_prod\n  host: localhost\n  username: www\n  password: passw0rd\n```\n\nAdding this would work:\n\n```\nproduction_dbevolve:\n  adapter: postgresql\n  database: mysite_prod\n  host: localhost\n  username: postgres\n  password: 5uper5ecret\n```\n\nOr more simply this:\n```\nproduction_dbevolve:\n  adapter: postgresql\n  database: mysite_prod\n  # not specifying the host, username and password makes postgres use local auth,\n  # assuming your local user has permissions to modify the schema.\n```\n\nStatus\n--------\n\n* PostgreSQL only ATM.\n* Feature complete (ADD/DROP/RENAME tables, columns and indexes, ALTER types, limits, nullable, precision, scale).\n* GRAS (Generally Recognized as Safe).  It prompts yes/no before running any SQL.  And switching back to migrations is just as easy as switching to DB::Evolve.\n\n\nFAQ\n------\n\n*Q: But the file `schema.rb` is autogenerated and shouldn't be manually altered?!?*\n\nA: Now it will NOT be auto-genned, and you will manipulate it like the authoritative data-source that it claims to be.  It's a good schema DSL, and everyone already has one, so using it makes sense.\n\n\n\nDerek's S#!tty Version Control System\n--------------------------------------------------\n\n*AKA the \"why did you do this?\" section.*\n\nAllow me to introduce DSVCS (Derek's S#!tty Version Control System).  It works a little different from how you'd expect.\nRather than write your code in a file and let a tool figure out the history, DSVCS gives you full control of the history\ntracking progress.  It's a revolutionary new coding process.  Watch how easy it is!\n\nTo start a new file, simply create a new directory `my_file.rb`.  The directory name, by convention, becomes your final file name.\n\nIn that directory, create a new file `1.diff`:\n\n\u003cpre\u003e\n0a1,2\n\u003e class MyClass\n\u003e end\n\u003c/pre\u003e\n\nIf you recognize this as an industry standard diff file, congrats!  YOU'RE GETTING IT!\n\nNow when you want to use your file, just build it first.  DSVCS has a built in tool to re-generate your final file from whatever series of diffs you've defined.  Just run:\n\n```\n$ dsvcs-generate my_file.rb \u003e build/my_file.rb\n```\n\nThis will open the directory `my_file.rb`, run each diff in sequence and write the results to `build/my_file.rb`.  It couldn't be easier!\n\nNow let's make it do something by adding `2.diff`:\n\n\u003cpre\u003e\n1a2,4\n\u003e   def hi()\n\u003e     puts \"Helo World!\"\n\u003e   end\n\u003c/pre\u003e\n\nNow run:\n\n```\n$ dsvcs-generate my_file.rb \u003e build/my_file.rb\n```\n\nOh oh, I think I made a typo.  No matter, just write `3.diff`:\n\n\u003cpre\u003e\n3c3\n\u003c     puts \"Helo World!\"\n---\n\u003e     puts \"Hello World!\"\n\u003c/pre\u003e\n\nAnd you're done!\n\nCan't you just feel the power of DSVCS?  No, well then you clearly **Just Don't Get It\u003csup\u003eTM\u003c/sup\u003e**!\n\n-- Derek\n\nI'm a relative novice in the Ruby world.  But this spoofed VCS should give you a fair approximation how crazy the [official active record migratoin documentation](http://guides.rubyonrails.org/active_record_migrations.html) reads to me.\n\n\n\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkeredson%2Fruby-db-evolve","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fkeredson%2Fruby-db-evolve","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fkeredson%2Fruby-db-evolve/lists"}