【Sinatra】Active Recordでつまずいた！～解決編

[こちら](https://sunnydayservice.qrunch.io/entries/4tSuMYF5mFoDzUG0)の記事の続きです。

Sinatraでrakeファイルに記述したActive Recordを使用し、```bundle exec rake～```でrakeファイル記述した内容を実行しようとした際に出たエラーの内容に関する記事です。

エラー内容は
```cannot load such file -- sinatra/activerecord```
という内容で、ログに表現されている内容はとても簡単で、```sinatra/activerecordなんてねーよ```って意味です。けっこうな量の記事通りにsinatraのアプリを組んでも、このエラーが出るので、自分のPCの環境がよくないのかなとも思っています。ちなみにWin10を使用しています。

*参考
https://railsgirls-bne.github.io/sinatra-app-guide
https://github.com/janko/sinatra-activerecord

# Gemfileの作成

Gemfile

```
source 'https://rubygems.org'

gem 'sqlite3'
gem 'rake'
gem 'sinatra'
gem 'sinatra-activerecord', path: ".."
gem 'activerecord'
gem 'pry'
gem 'pry-byebug'
```

```bundle install``` しましょう。


# Rakefileの作成

Rakefile

```
require 'sinatra/activerecord' 
require 'sinatra/activerecord/rake'

namespace :db do
    task :load_config do
      require "./app"
    end
  end
```

Rakefileの内容です。



# sinatraフォルダの作成

```sinatra``` フォルダを作りましょう！


その配下に```activerecord.rb``` ファイルを作成します。





```sinatra/activerecord.rb
require 'sinatra/base'
require 'active_record'
require 'active_support/core_ext/hash/keys'

require 'logger'
require 'pathname'
require 'yaml'
require 'erb'

module Sinatra
  module ActiveRecordHelper
    def database
      settings.database
    end
  end

  module ActiveRecordExtension
    def self.registered(app)
      if ENV['DATABASE_URL']
        app.set :database, ENV['DATABASE_URL']
      elsif File.exist?("#{Dir.pwd}/config/database.yml")
        app.set :database_file, "#{Dir.pwd}/config/database.yml"
      end

      unless defined?(Rake) || [:test, :production].include?(app.settings.environment)
        ActiveRecord::Base.logger = Logger.new(STDOUT)
      end

      app.helpers ActiveRecordHelper

      # re-connect if database connection dropped (Rails 3 only)
      app.before { ActiveRecord::Base.verify_active_connections! if ActiveRecord::Base.respond_to?(:verify_active_connections!) }
      app.after { ActiveRecord::Base.clear_active_connections! }
    end

    def database_file=(path)
      path = File.join(root, path) if Pathname(path).relative? and root
      spec = YAML.load(ERB.new(File.read(path)).result) || {}
      set :database, spec
    end

    def database=(spec)
      if spec.is_a?(Hash) and spec.symbolize_keys[environment.to_sym]
        ActiveRecord::Base.configurations = spec.stringify_keys
        ActiveRecord::Base.establish_connection(environment.to_sym)
      elsif spec.is_a?(Hash)
        ActiveRecord::Base.configurations[environment.to_s] = spec.stringify_keys
        ActiveRecord::Base.establish_connection(spec.stringify_keys)
      else
        ActiveRecord::Base.establish_connection(spec)
        ActiveRecord::Base.configurations ||= {}
        ActiveRecord::Base.configurations[environment.to_s] = ActiveRecord::ConnectionAdapters::ConnectionSpecification::ConnectionUrlResolver.new(spec).to_hash
      end
    end

    def database
      ActiveRecord::Base
    end
  end

  register ActiveRecordExtension
end
```


そして、```sinatra``` フォルダ配下に```activerecord``` フォルダを作成します。

```sinatra/activerecord``` の配下に```rake.rb``` と、```tasks.rake``` を作成します。



```rake.rb
load "active_record/railties/databases.rake"
require "sinatra/activerecord/rake/activerecord_#{ActiveRecord::VERSION::MAJOR}"

load "sinatra/activerecord/tasks.rake"

ActiveRecord::Base.logger = nil
```


```tasks.rake
require "active_support/core_ext/string/strip"
require "pathname"
require "fileutils"

namespace :db do
  desc "Create a migration (parameters: NAME, VERSION)"
  task :create_migration do
    unless ENV["NAME"]
      puts "No NAME specified. Example usage: `rake db:create_migration NAME=create_users`"
      exit
    end

    name    = ENV["NAME"]
    version = ENV["VERSION"] || Time.now.utc.strftime("%Y%m%d%H%M%S")

    ActiveRecord::Migrator.migrations_paths.each do |directory|
      next unless File.exist?(directory)
      migration_files = Pathname(directory).children
      if duplicate = migration_files.find { |path| path.basename.to_s.include?(name) }
        puts "Another migration is already named \"#{name}\": #{duplicate}."
        exit
      end
    end

    filename = "#{version}_#{name}.rb"
    dirname  = ActiveRecord::Migrator.migrations_paths.first
    path     = File.join(dirname, filename)
    ar_maj   = ActiveRecord::VERSION::MAJOR
    ar_min   = ActiveRecord::VERSION::MINOR
    base     = "ActiveRecord::Migration"
    base    += "[#{ar_maj}.#{ar_min}]" if ar_maj >= 5

    FileUtils.mkdir_p(dirname)
    File.write path, <<-MIGRATION.strip_heredoc
      class #{name.camelize} < #{base}
        def change
        end
      end
    MIGRATION

    puts path
  end
end

# The `db:create` and `db:drop` command won't work with a DATABASE_URL because
# the `db:load_config` command tries to connect to the DATABASE_URL, which either
# doesn't exist or isn't able to drop the database. Ignore loading the configs for
# these tasks if a `DATABASE_URL` is present.
if ENV.has_key? "DATABASE_URL"
  Rake::Task["db:create"].prerequisites.delete("load_config")
  Rake::Task["db:drop"].prerequisites.delete("load_config")
end

```


```sinatra/activerecord``` の配下に、```rake``` フォルダを作成します。

```rake``` フォルダの配下に```activerecord_"Active Recordのver".rb``` を作成します。
私の場合はActive Record6なので、```activerecord_6.rb``` になります。


```activerecord_6.rb
seed_loader = Class.new do
  def load_seed
    load "#{ActiveRecord::Tasks::DatabaseTasks.db_dir}/seeds.rb"
  end
end

ActiveRecord::Tasks::DatabaseTasks.tap do |config|
  config.root                   = Rake.application.original_dir
  config.env                    = ENV["RACK_ENV"] || "development"
  config.db_dir                 = "db"
  config.migrations_paths       = ["db/migrate"]
  config.fixtures_path          = "test/fixtures"
  config.seed_loader            = seed_loader.new
  config.database_configuration = ActiveRecord::Base.configurations
end

# db:load_config can be overriden manually
Rake::Task["db:seed"].enhance(["db:load_config"])
Rake::Task["db:load_config"].clear

# define Rails' tasks as no-op
Rake::Task.define_task("db:environment")
Rake::Task["db:test:deprecated"].clear if Rake::Task.task_defined?("db:test:deprecated")
```

これで```sinatra``` フォルダの内容は出来上がりました。


# databaseの設定

```config``` フォルダを作成し、その配下に```database.yml``` を作成します。



```config/database.yml
development:
  adapter: sqlite3
  database: db/development.sqlite3

test:
  adapter: sqlite3
  database: db/test.sqlite3
```

これで基本的な設定は完成です。

```bundle exec rake -T``` コマンドで、使用できるタスクが表示されるはずです。

```
rake db:create              # Creates the database from DATABASE_URL or con...
rake db:create_migration    # Create a migration (parameters: NAME, VERSION)
rake db:drop                # Drops the database from DATABASE_URL or confi...
rake db:environment:set     # Set the environment value for the database
rake db:fixtures:load       # Loads fixtures into the current environment's...
rake db:migrate             # Migrate the database (options: VERSION=x, VER...
rake db:migrate:status      # Display status of migrations
rake db:prepare             # Runs setup if database does not exist, or run...
rake db:rollback            # Rolls the schema back to the previous version...
rake db:schema:cache:clear  # Clears a db/schema_cache.yml file
rake db:schema:cache:dump   # Creates a db/schema_cache.yml file
rake db:schema:dump         # Creates a db/schema.rb file that is portable ...
rake db:schema:load         # Loads a schema.rb file into the database
rake db:seed                # Loads the seed data from db/seeds.rb
rake db:seed:replant        # Truncates tables of each database for current...
rake db:setup               # Creates the database, loads the schema, and i...
rake db:structure:dump      # Dumps the database structure to db/structure.sql
rake db:structure:load      # Recreates the databases from the structure.sq...
rake db:version             # Retrieves the current schema version number
```

# マイグレーションファイルの作成

```bundle exec rake db:create_migration NAME=create_ideas``` で、マイグレーションファイルの作成を行います。

```20190823054856_create_ideas.rb``` のように、日付入りのファイルが出来上がると思います。

```
class CreateIdeas < ActiveRecord::Migration[6.0]
  def change
    create_table :ideas do |t|
      t.string :name
      t.text :description
      t.string :picture

      t.timestamps null: false
    end
  end
end
```

出来上がったマイグレーションファイルに修正を加えます。
特に、```class CreateIdeas < ActiveRecord::Migration[6.0]``` の```[6.0]``` を忘れないようにしましょう！

最後に```bundle exec rake db:migrate``` でマイグレーションを実行し、完了です。



# まとめ
ここまで調べるのに長くかかりました。。。
でも、調べ方が上手くなったのでよかったです！