Lチカ開発ブログ

https://l-chika.com/の開発ブログ

symbol not found in flat namespace '_mysql_affected_rows' が発生した時

前提

現象

bundle install は正常に実行できたが、./bin/rails s などのRailsコマンドを実行すると以下のようなエラーとなる。

※抜粋

$ ./bin/rails s
/Users/foo/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/bootsnap-1.11.1/lib/bootsnap/load_path_cache/core_ext/kernel_require.rb:30:in `require': dlopen(/Users/foo/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/mysql2-0.5.4/lib/mysql2/mysql2.bundle, 0x0009): symbol not found in flat namespace '_mysql_affected_rows' - /Users/foo/.rbenv/versions/3.1.1/lib/ruby/gems/3.1.0/gems/mysql2-0.5.4/lib/mysql2/mysql2.bundle (LoadError)

おそらく、homebrewでinstallしたmysqlのパスがmysql2のデフォルトと異なるため?

解決方法

$ gem uninstall mysql2
$ gem install mysql2 --with-mysql-dir=/opt/homebrew/Cellar/mysql/8.0.29

これで解消した。

RubyでSNSのシェア数を取得する方法

social_sharesというgemを利用する。はてブにも対応している github.com

:000 > require 'social_shares'
 => true

:000 > url = 'http://www.apple.com/'
 => "http://www.apple.com/"

:000 > SocialShares.hatebu url
 => 506

古い記事やgemとかだと、

なので、このgemは便利。

ruby書籍

rails generate model のいろいろ

rails generate model で出来る事のメモ(オプション以外の)

referencesとpolymorphic

rails generate model product supplier:references{polymorphic}

db/migrate/XXX_create_products.rb

class CreateProducts < ActiveRecord::Migration[5.2]
  def change
    create_table :products do |t|
      t.references :supplier, polymorphic: true

      t.timestamps
    end
  end
end

limit

整数、文字列、テキスト、およびバイナリフィールドの場合、中括弧で囲まれた整数は制限として設定できる

rails generate model user pseudo:string{30}

小数の場合は、中カッコでコンマで区切られた2つの整数が使用できる

rails generate model product 'price:decimal{10,2}'

index

rails generate model user pseudo:string:uniq

rails generate model user pseudo:string:index

limitとuniqインデックス

rails generate model user username:string{30}:uniq

db/migrate/XXX_create_users.rb

class CreateUsers < ActiveRecord::Migration[5.2]
  def change
    create_table :users do |t|
      t.string :username, limit: 30

      t.timestamps
    end
    add_index :users, :username, unique: true
  end
end

password_digest

rails generate model user password:digest

db/migrate/XXX_create_users.rb

class CreateUsers < ActiveRecord::Migration[5.2]
  def change
    create_table :users do |t|
      t.string :password_digest

      t.timestamps
    end
  end
end

auth_token

rails generate model user auth_token:token

db/migrate/XXX_create_users.rb

class CreateUsers < ActiveRecord::Migration[5.2]
  def change
    create_table :users do |t|
      t.string :auth_token

      t.timestamps
    end
    add_index :users, :auth_token, unique: true
  end
end

関連

Ruby on Rails 5アプリケーションプログラミング

Ruby on Rails 5アプリケーションプログラミング

Ruby on Rails 5 超入門

Ruby on Rails 5 超入門

Webpacker + images + Spectre.css

前提

以下をインストール済み

  • Webpacker 3.2.0
  • spectre.css 0.5.0

l-chika.hatenablog.com

参考

step

app/javascript の構成

$ tree app/javascript/
app/javascript/
├── images
│   └── logo.png
├── packs
│   └── application.js
└── styles
    ├── app-style.sass
    └── base.scss

app/javascript/packs/application.js

  • 各stylesheetのimport
  • require.contextでimages配下の画像を読み込み(個別にimportをしない)
import 'styles/app-style'
import 'styles/base'
require.context('images', true, /\.(png|jpg|jpeg|svg)$/)

app/javascript/styles/app-style.sass

  • icon も利用するtame spectre-icons もimport
@import '~spectre.css/dist/spectre'
@import '~spectre.css/dist/spectre-icons'

app/javascript/styles/base.scss

独自のcss

...
.logo {
  width: 110px;
}
...

テンプレート

<%= image_tag(asset_pack_path('images/logo.png'), class: 'logo') %>

参考

Ruby on Rails 5アプリケーションプログラミング

Ruby on Rails 5アプリケーションプログラミング

Docker + Rails + Webpacker + Spectre.css

以下のつづき。

l-chika.hatenablog.com

前提

DockerでRailsが起動する

概要

  • Webpacker のgemインストール
  • Spectre.css をyarnでインストール

参考

step

Yarn install setting

$ touch yarn.lock

Dockerfile

以下の設定を追加 - apt-get install -y yarn - ADD package.json - ADD yarn.lock - RUN yarn install

$ vim Dockerfile
FROM ruby:2.4.2
ENV TZ=Asia/Tokyo
ENV LANG C.UTF-8

# NOTE: Yarn requires Node.js 4.0 or higher to be installed.
RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - && apt-get install -y nodejs

RUN apt-get update -qq && apt-get install -y build-essential libmysqlclient-dev vim

# NOTE: yarn
RUN apt-get update && apt-get install -y curl apt-transport-https wget && \
    curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
    echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
    apt-get update && apt-get install -y yarn

ARG APP_DIR="/myapp"
RUN mkdir $APP_DIR
WORKDIR $APP_DIR

ADD Gemfile ${APP_DIR}/Gemfile
ADD Gemfile.lock ${APP_DIR}/Gemfile.lock
ENV BUNDLE_GEMFILE=${APP_DIR}/Gemfile \
  BUNDLE_JOBS=2 \
  BUNDLE_PATH=/bundle
RUN gem install bundler & bundle install

ADD package.json ${APP_DIR}/package.json
ADD yarn.lock ${APP_DIR}/yarn.lock
RUN yarn install

ADD . $APP_DIR

diff

diff --git a/Dockerfile b/Dockerfile
index cf2e811..113df83 100644
--- a/Dockerfile
+++ b/Dockerfile
@@ -7,6 +7,12 @@ RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - && apt-get install -y

 RUN apt-get update -qq && apt-get install -y build-essential libmysqlclient-dev vim

+# NOTE: yarn
+RUN apt-get update && apt-get install -y curl apt-transport-https wget && \
+    curl -sS https://dl.yarnpkg.com/debian/pubkey.gpg | apt-key add - && \
+    echo "deb https://dl.yarnpkg.com/debian/ stable main" | tee /etc/apt/sources.list.d/yarn.list && \
+    apt-get update && apt-get install -y yarn
+
 ARG APP_DIR="/myapp"
 RUN mkdir $APP_DIR
 WORKDIR $APP_DIR
@@ -19,4 +25,8 @@ ENV BUNDLE_GEMFILE=${APP_DIR}/Gemfile \
   BUNDLE_PATH=/bundle
 RUN gem install bundler & bundle install

+ADD package.json ${APP_DIR}/package.json
+ADD yarn.lock ${APP_DIR}/yarn.lock
+RUN yarn install
+
 ADD . $APP_DIR
diff --git a/docker-compose.yml b/docker-compose.yml
index d78c9de..a277fdd 100644

docker-compose.yml

  • volumes/node_modulesを追加
version: '3'
services:
  db:
    image: mysql:5.7
    command: "mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci"
    environment:
      MYSQL_ROOT_PASSWORD: password
    volumes:
      - mysql-db:/var/lib/mysql
    ports:
      - "3306:3306"
  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    environment:
      EDITOR: vim
    volumes:
      - .:/myapp
      - /myapp/node_modules
      - bundle:/bundle
    ports:
      - "3000:3000"
    depends_on:
      - db
volumes:
  mysql-db:
    driver: local
  bundle:
    driver: local

diff

--- a/docker-compose.yml
+++ b/docker-compose.yml
@@ -17,6 +17,7 @@ services:
       EDITOR: vim
     volumes:
       - .:/myapp
+      - /myapp/node_modules
       - bundle:/bundle
     ports:
       - "3000:3000"
$ docker-compose build

確認

$ docker-compose run --rm web yarn -v
1.3.2

Gemfile

$ vim Gemfile
gem 'webpacker'
$ docker-compose run --rm web bundle install
$ docker-compose run --rm web bin/rails -T | grep webpacker
rails webpacker                          # Lists all available tasks in Web...
rails webpacker:check_binstubs           # Verifies that webpack & webpack-...
rails webpacker:check_node               # Verifies if Node.js is installed
rails webpacker:check_yarn               # Verifies if Yarn is installed
rails webpacker:clobber                  # Remove the webpack compiled outp...
rails webpacker:compile                  # Compile JavaScript packs using w...
rails webpacker:install                  # Install Webpacker in this applic...
rails webpacker:install:angular          # Install everything needed for An...
rails webpacker:install:elm              # Install everything needed for Elm
rails webpacker:install:react            # Install everything needed for React
rails webpacker:install:vue              # Install everything needed for Vue
rails webpacker:verify_install           # Verifies if Webpacker is installed
rails webpacker:yarn_install[arg1,arg2]  # Support for older Rails versions
$ docker-compose run --rm web bin/rails webpacker:install
$ docker-compose build

確認

$ docker-compose run web yarn check --integrity
Starting myapp_db_1 ... done
yarn check v1.3.2
success Folder in sync.
Done in 0.13s.
node_modulesの確認
$ docker-compose down
$ docker-compose up
$ docker exec -it myapp_web_1 /bin/bash
# ls node_modules/

spectre.css追加

$ docker-compose run --rm web yarn add spectre.css
$ docker-compose build

css適用

app-style.sass

$ vim app/javascript/app-style.sass
@import '~spectre.css/dist/spectre'

application.js

$ vim app/javascript/packs/application.js
import '../app-style'

application.html.erb

$ vim app/views/layouts/application.html.erb
<%= javascript_pack_tag 'application' %>
<%= stylesheet_pack_tag 'application' %>

compile

$ docker-compose run --rm web bin/rails webpacker:compile

確認

  • 上手くdocker-compose upできない時は docker-compose downを実行してみる
$ docker-compose run --rm web bin/rails g controller pages top --no-helper --no-assets --skip-test-framework
$ docker-compose up

ブラウザ

http://0.0.0.0:3000/pages/top

binstub

./bin/webpack/bin/webpack-dev-server を追加

$ docker-compose run --rm web bundle binstubs webpacker --force --path=bin/
$ ls -1 bin/
bundle*
rails*
rake*
setup*
update*
webpack*
webpack-dev-server*
yarn* 
$ docker exec -it myapp_web_1 /bin/bash
# ./bin/webpack-dev-server

関連

Ruby on Rails 5アプリケーションプログラミング

Ruby on Rails 5アプリケーションプログラミング

Docker

Docker

Docker + Rails5.2.0.beta2

概要

DockerでRails環境構築 - DBはMySQL

参考

構築環境

  • Ruby 2.4.2
  • Rails 5.2.0.beta2
  • Docker 17.09.1
  • MySQL 5.7
  • Node 8.9.3
  • Yarn 1.3.2

step

$ mkdir {APP_DIR} && cd $_

Dockerfile

$ vim Dockerfile
FROM ruby:2.4.2
ENV TZ=Asia/Tokyo
ENV LANG C.UTF-8

# NOTE: Yarn requires Node.js 4.0 or higher to be installed.
RUN curl -sL https://deb.nodesource.com/setup_8.x | bash - && apt-get install -y nodejs

RUN apt-get update -qq && apt-get install -y build-essential libmysqlclient-dev vim

ARG APP_DIR="/myapp"
RUN mkdir $APP_DIR
WORKDIR $APP_DIR

ADD Gemfile ${APP_DIR}/Gemfile
ADD Gemfile.lock ${APP_DIR}/Gemfile.lock

ENV BUNDLE_GEMFILE=${APP_DIR}/Gemfile \
  BUNDLE_JOBS=2 \
  BUNDLE_PATH=/bundle
RUN gem install bundler & bundle install

ADD . $APP_DIR

Gemfile

$ touch Gemfile.lock
$ vim Gemfile
ruby '2.4.2'

source 'https://rubygems.org'
gem 'rails', '~> 5.2.0.beta2'

docker-compose

$ vim docker-compose.yml

docker-compose.yml

  • volumes/myappを環境に合わせて適宜変更
  • MySQL
    • 外部からも接続できるようにする
    • データも永続化する
    • 文字コードを指定
version: '3'
services:
  db:
    image: mysql:5.7
    command: "mysqld --character-set-server=utf8mb4 --collation-server=utf8mb4_unicode_ci"
    environment:
      MYSQL_ROOT_PASSWORD: password
    volumes:
      - mysql-db:/var/lib/mysql
    ports:
      - "3306:3306"
  web:
    build: .
    command: bundle exec rails s -p 3000 -b '0.0.0.0'
    environment:
      EDITOR: vim
    volumes:
      - .:/myapp
      - bundle:/bundle
    ports:
      - "3000:3000"
    depends_on:
      - db
volumes:
  mysql-db:
    driver: local
  bundle:
    driver: local

rails new

  • bin/rails active_storage:installをskipする場合には --skip-bundleをしてから別途 bundle install
$ docker-compose run web rails new . --force -d mysql --skip-bundle
$ docker-compose run --rm web bundle install

確認

$ ls -U -1
Dockerfile
Gemfile
Gemfile.lock
README.md
Rakefile
app/
bin/
config/
config.ru
db/
docker-compose.yml
lib/
log/
package.json
public/
storage/
test/
tmp/
vendor/

$ docker volume ls
DRIVER              VOLUME NAME
local               myapp_bundle
local               myapp_mysql-db

database.yml

  • hostdocker-composeで定義したMySQLのservcieに設定
  • passwordを設定
$ vim config/database.yml
default: &default
  adapter: mysql2
  encoding: utf8mb4
  pool: <%= ENV.fetch("RAILS_MAX_THREADS") { 5 } %>
  username: root
  password: <%= ENV['DB_PASSWORD'] || 'password' %>
  host: db

development:
  <<: *default
  database: myapp_development

test:
  <<: *default
  database: myapp_test

実行

$ docker-compose up

初回は --build オプションもあり

$ docker-compose up --build

確認

ブラウザ

http://0000:3000

コンテナ

  • Containerが myapp_web_1 の場合
$ docker exec -it myapp_web_1 /bin/bash

DB

$ docker-compose run --rm web rake db:create

-ActiveRecord::PendingMigrationErrorが発生する場合は、db:migrate で回避。(active_storage_blobs, active_storage_attachments を作成)

$ docker-compose run --rm -e RAILS_ENV=development web bin/rails db:migrate

確認

$ docker-compose up
$ mysql -h 127.0.0.1 -D myapp_development -uroot -p

git clone 後

$ docker-compose up --build
$ docker-compose run --rm web rake db:create

関連本

Ruby on Rails 5アプリケーションプログラミング

Ruby on Rails 5アプリケーションプログラミング

Docker

Docker

Goでgoqueryを利用してスクレイピング

Goでスクレイピング

やりたい事

  1. 検索結果へアクセス。
  2. 検索結果の一覧から、各詳細ページのリンク取得
  3. 詳細ページのコンテンツにアクセス
  4. 検索結果の次のページのリンクを取得。1へ戻る。

github.com

実装

パッケージをインストール

$ go get github.com/PuerkitoBio/goquery

goqueryで実装

$ vim scraping.go
package main

import (
    "github.com/PuerkitoBio/goquery"
    "log"
    "fmt"
    "time"
)

const (
    BASE_URL = "https://hoge"
)

func main() {
    url := nextUrl(BASE_URL + "/foo")
    contentsLinks(BASE_URL + url)
    
    for {
        url = nextUrl(BASE_URL + url)
        if url == "" {
            break
        }
        contentsLinks(BASE_URL + url)
        time.Sleep(3 * time.Second)
    }
}

func contentsLinks(url string) {
    doc, err := goquery.NewDocument(url)
    if err != nil {
        log.Fatal(err)
    }

    var links []string
    // コンテンツの一覧から各詳細ページのURLを検索
    doc.Find("#hoge p.foo a").Each(func(_ int, s *goquery.Selection) {
          url, _ = s.Attr("href")
          links = append(links, url)
    })
    
    // 各詳細ページをスクレイピング
    for _, link := range links {
        a, b := content(BASE_URL + link)
        fmt.Printf("%s, %s\n", a, b)
    }
}

func content(url string) (string, string) {
    doc, err := goquery.NewDocument(url)
    if err != nil {
        log.Fatal(err)
    }

    var name, link string
    doc.Find("#detail div.col-sm-9.col-xs-7.col-xxs-12 ").Each(func(_ int, s *goquery.Selection) {
          name = s.Find("h5.info_inner_title.pdB10.h4").Text()
          link = s.Find("dl.info_inner_contents dd.abel.blue.pdB15 a").Text()
    })
    return name, link
}

// ページネーションから「次へ」のリンクを取得
func nextUrl(current string) (url string) {
    fmt.Printf("========== url: %s\n", current)
    doc, err := goquery.NewDocument(current)
    if err != nil {
        log.Fatal(err)
    }

    var exist bool
    doc.Find("ul.pagination li.next a").Each(func(_ int, s *goquery.Selection) {
          url, exist = s.Attr("href")
    })
    if !exist {
        return ""
    }
    return
}

スターティングGo言語

スターティングGo言語