BETA

【Sinatra】Active Recordでつまずいた!~解決編

投稿日:2019-08-23
最終更新:2019-08-29

こちらの記事の続きです。

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 でマイグレーションを実行し、完了です。

まとめ

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

技術ブログをはじめよう Qrunch(クランチ)は、プログラマの技術アプトプットに特化したブログサービスです
駆け出しエンジニアからエキスパートまで全ての方々のアウトプットを歓迎しております!
or 外部アカウントで 登録 / ログイン する
クランチについてもっと詳しく

この記事が掲載されているブログ

初学者の成長が垣間見れます

よく一緒に読まれる記事

0件のコメント

ブログ開設 or ログイン してコメントを送ってみよう