元フリーエンジニアライフ

Ruby on Rails とか MovableType とかAWSやってるフリーランスウェブエンジニアの記録でした。現在は法人成りしてIT社長。

AmazonLinux 2015.09 に Qt5 WebKit をインストール

まとめ

sudo yum install ftp://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/geoclue-0.11.1.1-0.13.20091026git73b6729.el6.x86_64.rpm \
ftp://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/NetworkManager-glib-0.8.1-99.el6.x86_64.rpm \
ftp://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/libsoup-2.34.3-3.el6_6.x86_64.rpm \
ftp://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/gnome-keyring-2.28.2-8.el6_3.x86_64.rpm \
ftp://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/GConf2-2.28.0-6.el6.x86_64.rpm \
ftp://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/ORBit2-2.14.17-5.el6.x86_64.rpm \
ftp://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/polkit-0.96-11.el6.x86_64.rpm \
ftp://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/ConsoleKit-0.4.1-3.el6.x86_64.rpm \
ftp://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/ConsoleKit-libs-0.4.1-3.el6.x86_64.rpm \
ftp://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/polkit-0.96-11.el6.x86_64.rpm \
ftp://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/ConsoleKit-0.4.1-3.el6.x86_64.rpm \
ftp://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/polkit-0.96-11.el6.x86_64.rpm \
ftp://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/eggdbus-0.6-3.el6.x86_64.rpm \
ftp://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/glib-networking-2.28.6.1-2.2.el6.x86_64.rpm \
ftp://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/gtk2-2.24.23-6.el6.x86_64.rpm \
ftp://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/atk-1.30.0-1.el6.x86_64.rpm \
ftp://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/hicolor-icon-theme-0.11-1.1.el6.noarch.rpm \
ftp://ftp.riken.jp/Linux/centos/6/os/x86_64/Packages/gdk-pixbuf2-2.24.1-5.el6.x86_64.rpm 
sudo yum install qt5-qtwebkit-devel

はじめに

yumqt5-qtwebkit-develがあったので簡単かと思いきや・・・

$ sudo yum install qt5-qtwebkit-devel
(snip)
--> Finished Dependency Resolution
Error: Package: qt5-qtlocation-5.5.1-2.el6.x86_64 (epel)
           Requires: libgeoclue.so.0()(64bit)
Error: Package: qt5-qtbase-gui-5.5.1-11.el6.x86_64 (epel)
           Requires: libgtk-x11-2.0.so.0()(64bit)
Error: Package: qt5-qtbase-gui-5.5.1-11.el6.x86_64 (epel)
           Requires: libgdk-x11-2.0.so.0()(64bit)
Error: Package: qt5-qtbase-gui-5.5.1-11.el6.x86_64 (epel)
           Requires: libatk-1.0.so.0()(64bit)
Error: Package: qt5-qtbase-gui-5.5.1-11.el6.x86_64 (epel)
           Requires: libgdk_pixbuf-2.0.so.0()(64bit)
 You could try using --skip-broken to work around the problem
 You could try running: rpm -Va --nofiles --nodigest

Amazon Linux AMI yum リポジトリには足りないパッケージがあるようで、うまくいきませんでした。

続きを読む

(解決)Windows10でKaspersky Internet Security 2016の更新をして再起動したら、Windowsが起動しなくなった件について

SYSTEM_THREAD_EXCEPTION_NOT_HANDLED

と表示され、セーフモードでの起動すらできなくなってしまった。自動修復も無理。

ググってたらそれっぽいものが

Kaspersky Lab Forum > Upgraded to KIS 2016 on Win 10 = BSOD - System Thread Exception Not Handled

f:id:uzuki05:20160309104708p:plain

ダウンロードはこのへん  https://support.kaspersky.co.jp/kis2016

マンドプロンプトを開いて

C:\
cd C:\Windows\System32\drivers
rename cm_km_w.sys cm_km_w.bak

で再起動すると、起動した。

その後Kaspersky Internet Security 2016を一度アンインストール、2016のインストーラをダウンロードしてきて再インストールした。

クラウドソーシングdisり記事がバズってたので、便乗して僕の感想を書き殴る。

なんかクラウドソーシングdisりがバズってた。

crapp.hatenablog.com

僕はクラウドワークスではなくLancersですが、二桁の仕事はしています。

はじめに、僕のスタンスをはっきりさせておくと、初期の実績積みには役に立ったのでその点感謝はしているのの、やはりフリーのエンジニアがそれで食べていくのはキツイと思うので、もう使うことはないだろう、というところです。

過去にそれが切っ掛けでクラウドソーシングを活用するフリーエンジニアとして取材をして頂いたこともありますし、上記の通り感謝はしているのでとても心苦しいのですが、良くないと感じている部分もあるので、この際はっきりと、感想を書き殴っていきます。

以下、元記事を読みながら思ったことです。

続きを読む

PowerCMSダイナミック検索で「デフォルトの検索条件」を設定する

コードを読んだがクエリストリングからパラメータを取り出していて、他にデフォルト値を渡す方法がみあたらなかったので、むりやり・・・

<?php
if (!$_SERVER['QUERY_STRING']) {
    // クエリストリングなしでアクセスされた際にデフォルトのパラメータをダイナミック検索に渡す
    $mt = MT::get_instance();
    $blog_id = $mt->blog_id();
    $_SERVER['QUERY_STRING'] = 'class=entry&limit=10000&offset=1&blog_id=' . $blog_id . '&sort_by=title&sort_order=ascend';
    parse_str($_SERVER['QUERY_STRING'], $_GET);
}
?>

以下MTテンプレート

よくない。

WEB+DB PRESS Vol.90 メモ

印象に残ったところをメモ。

WEB+DB PRESS Vol.90

WEB+DB PRESS Vol.90

Git実践活用

これまでなんとなく使ってたけどもうちょっとちゃんと理解したい、でもガッツリやるのはしんどい、という方に丁度良いボリューム感。説明もなかなかわかりやすかったです。

ドラゴンクエストX開発ノウハウ大公開

  • 開発・運営体制 チームの役割分担について
  • クライアント開発について
  • 開発の舞台裏 C++Luaの使い分けについて
  • ゲームエンジンの選定について
  • サーバサイド開発について
  • プロトコル、プロセス構成について、DQ10の特徴的な相撲の実装の工夫など
  • 一元管理のためのデータベース負荷対策、トランザクションの取捨選択、KVSの活用の工夫など
  • 周辺サービスで活用しているWeb技術について
  • コラムのルータのバグなどの話

DQ10をやっていて、どうやってんだろ?と思ってた相撲のコトなど、いろいろ興味深かったし、面白い記事でした。

私を変えたソフトウェア

豪華エッセイ。特に縁、というには図々しいけれど、僕を変えた開発者様。

  • まつもとゆきひろさん・・・僕はRubyをメインで使っているので。
  • おにたまさん・・・僕はHSPでプログラミングはじめました。

Ruby AWS SDK v2 でS3の署名付きURLを発行する

Aws::S3::Presignerを使います。

基本

s3 = Aws::S3::Client.new
signer = Aws::S3::Presigner.new(client: s3)
signer.presigned_url(:get_object, 
                     bucket: 'your-bucket',
                     key: 'path/to/object')

URLの有効期限を設定する

たとえば、購入者に向けてダウンロード用URLを発行する場合、不正利用を防ぐためにごく短期間のURLを作りたい。

そういったときはexpires_inで指定できる。

以下、30秒だけアクセス可能なURLの例

signer.presigned_url(:get_object, 
                     bucket: 'your-bucket',
                     key: 'path/to/object', 
                     expires_in: 30)

HTTPSにしたい

secure: trueを指定する。

signer.presigned_url(:get_object, 
                     bucket: 'your-bucket',
                     key: 'path/to/object', 
                     secure: true)

Content-Typeを指定する

たとえば、S3のファイルを直接ブラウザで開くのではなく、ダウンロードさせたいときapplication/force-downloadを設定する。

S3ではresponse-content-typeをリクエストパラメータに追加して署名すれば良いみたい。

参考: http://docs.aws.amazon.com/ja_jp/AmazonS3/latest/API/RESTObjectGET.html

サンプルが見つからず苦労したものの、コードを読むと単に以下のようにすればよいようだった。

signer.presigned_url(:get_object, 
                     bucket: 'your-bucket',
                     key: 'path/to/object', 
                     response_content_type: 'application/force-download')

ダウンロードファイル名を指定したい(Content-Disposition)

Content-Typeの時と同様に、response_content_dispositionを指定する。

signer.presigned_url(:get_object, 
                     bucket: 'your-bucket',
                     key: 'path/to/object', 
                     response_content_disposition: 'attachment; filename=Downloadfile.pdf')

Ruby on Rails 4.2 のActiveRecordでMovable TypeのデータをQueryInterfaceとかArelとかで扱ってみた

この記事はMovable Type Advent Calendar 2015 - Adventar20日目の記事のはずでした。忘れてました。ごめんなさい。

ふと思い立って現段階の最新のRailsでMTのデータを扱う実験をしてみました。

Railsで非RailsなDBを扱うサンプルにもなるかも。

サンプルコード

takeyuweb/mtrails-example · GitHub

ActiveRecordを用いた例

検索

# 公開中で
# ウェブサイトの記事(blog_parent_id = null)で
# 記事本文にtarget_letterを含む
MT::Entry
  .release
  .where(blog: MT::Blog.where(blog_parent_id: nil))
  .where('entry_text like ?', "%#{target_letter}%")

# Blog ID=1の記事
MT::Entry.where(entry_blog_id: 1)
# Blogオブジェクトの関連でも
MT::Blog.find(1).entries
# さらに公開中で絞り込んでみたり
MT::Blog.find(1).entries.release

# 先頭の記事
MT::Entry.first

Arelを使ってカスタムフィールドdata1の値が100~200の記事を検索

entry_meta = MT::Entry::Meta.arel_table
cond1 = MT::Entry
  .joins(:meta)
  .where(entry_meta[:entry_meta_type].eq('field.data1')
           .and(entry_meta[:entry_meta_vchar_idx].in(100..200)))

# 「公開中」の条件とマージ
MT::Entry.release.merge(cond1)

データのマッピング

entry = MT::Entry.first
# 関連
entry.blog # => #<MT::Blog blog_id: 1, ...>
entry.category # => #<MT::Category category_id: 1, ...>
entry.categories # => #<ActiveRecord::Associations::CollectionProxy [#<MT::Category category_id: 1 ...>, ...]>
entry.author # => #<MT::Author author_id: 1, ...>
# カスタムフィールド
entry.meta # => #<ActiveRecord::Associations::CollectionProxy [#<MT::Entry::Meta entry_meta_entry_id: 12, entry_meta_type: "field.data1",  ...>, ...]>
entry.meta.pluck(:entry_meta_type) # => ["field.data1", "field.data2", "revision"]

# 属性値
entry.entry_title # => "TITLE"
entry.entry_status # => "release"

実装のポイント

他(MT)のDBへの接続

  • ActiveRecord::Base.establish_connection

今回は、config/initializers/mt.rbで設定し、MT::ObjectをincludeしたModelで適用されるようにしました。

https://github.com/takeyuweb/mtrails-example/blob/master/config/initializers/mt.rb

require_dependency 'mt/object'

Rails.application.config.to_prepare do
  MT::Object.connection_configuration = {
      adapter:  'mysql2',
      encoding: 'utf8',
      host:     'localhost',
      username: 'root',
      database: 'mt62',
      socket: '/var/lib/mysql/mysql.sock'
  }
end

※database.ymlに記載した設定を使うには、:mtのように指定すればOKです。

https://github.com/takeyuweb/mtrails-example/blob/master/app/models/mt/object.rb

require_dependency 'mt'

module MT::Object
  def self.connection_configuration=(spec)
    @connection_configuration = spec
  end

  def self.connection_configuration
    @connection_configuration
  end

  def self.included(klass)
    klass.class_eval do
      self.table_name_prefix = 'mt_'
      self.table_name = ['mt', name.split('::').last.underscore].join('_')
      self.primary_key_prefix_type = :table_name_with_underscore
      self.establish_connection ::MT::Object.connection_configuration
    end
  end
end

※MT::Objectを親クラスにして継承しないのは、ActiveRecordクラスの継承を行うと子クラスでtable_nameを設定しても、QueryInterfaceで親クラスのtable_nameが使われたりと、STI前提の感じでうまく動かなかったためです。

MTのテーブル名規約

  • ActiveRecord::Base.table_name=

includeされたときに、対象のクラス名からmt_entryのようなテーブル名を設定するようにしました。

MTの主キー名規約

  • ActiveRecord::Base.primary_key=
  • ActiveRecord::primary_key_prefix_type = :table_name_with_underscore

のどちらか。 includeされたときに、対象のクラス名からentry_idのような主キーを設定するようにしました。

記事の公開状態

MTではentry_statusの値により、記事の公開状態(公開、下書きなど)を決定します。

Rails 4.1以降であればenumが使えます。

https://github.com/takeyuweb/mtrails-example/blob/master/app/models/mt/entry.rb

  enum entry_status: {
      hold: 1,
      release: 2,
      review: 3,
      future: 4,
      junk: 5,
      unpublish: 6
  }

こうしておくことで、

entry.status # => "release"
entry.status = "review"

のようにステータスを人間に読みやすい形で取得・設定したり、

entry.repease! # => 公開で保存、エラーで例外送出

のようなメソッド

MT::Entry.release # => 公開中の記事を検索

のようなスコープがメタプログラミングにより自動的につくられます。

Rails標準でないbelongs_to

記事のブログはentry_blog_idでとれます。これを使って、ActiveRecordのbelongs_to関連を設定します。

  belongs_to :blog,
             class_name: 'MT::Blog',
             foreign_key: 'entry_blog_id'

foreign_keyで外部キー名を指定します。belongs_toの場合、自分のテーブルのカラムになります。

また、クラス名もRailsの規約からははずれているので、class_nameで設定しておきます。

entry.blog # => #<MT::Blog blog_id: 1, ...>

Rails標準でないhas_one,has_manyと多対多(has_many :through)

記事のカテゴリはmt_placementを中間テーブルとした多対多関連になります。

メインカテゴリについては中間テーブルのplacement_is_primaryを見ればOKです。

これをActiveRecordで設定するにはこんな感じになります。

  # メインカテゴリ
  has_one :placement,
          ->{ where(placement_is_primary: 1) },
          class_name: 'MT::Placement',
          foreign_key: 'placement_entry_id'
  has_one :category,
          class_name: 'MT::Category',
          through: :placement,
          source: :category

  # カテゴリ(メインカテゴリ含む)
  has_many :placements,
           class_name: 'MT::Placement',
           foreign_key: 'placement_entry_id'
  has_many :categories,
           class_name: 'MT::Category',
           through: :placements,
           source: :category

中間テーブルはforeign_keyで外部キー名を指定すると共に、has_many :throughの方で、中間テーブルのどの関連を使うかをsourceで指定します。

実際にsourceがどんな関連なのかは、MT::Placementで設定しています。

https://github.com/takeyuweb/mtrails-example/blob/master/app/models/mt/placement.rb

class MT::Placement < ActiveRecord::Base
  include MT::Object
  belongs_to :entry,
             class_name: 'MT::Entry',
             foreign_key: 'placement_entry_id'
  belongs_to :category,
             class_name: 'MT::Category',
             foreign_key: 'placement_category_id'
end

ActiveRecord Modelごとに存在するActiveRecord Model

MTのメタデータやカスタムフィールドのデータは、対象のテーブルごとに別々のテーブルやカラム名で存在します。(記事ならmt_entryに対するmt_entry_meta

個別にモデルクラスを作っても良いのですが、そうすると対応オブジェクトを増やすごとに重複コード増えていくので、メタプログラミングの絶好の機会です。

カスタムフィールドに対応するモデルクラスで、MT::Metaをincludeすると、Model名::Metaというモデルクラスを作り、必要な関連を設定するようにします。

https://github.com/takeyuweb/mtrails-example/blob/master/app/models/mt/entry.rb

class MT::Entry <ActiveRecord::Base
  include MT::Object
  include MT::Meta # <= (!)

MT::Metaはこのような具合です。

module MT::Meta
  def self.included(klass)
    meta_class = Class.new(ActiveRecord::Base)
    klass.const_set('Meta', meta_class)
    meta_class.table_name = [klass.table_name, 'meta'].join('_')
    meta_class.class_eval do
      establish_connection  ::MT::Object.connection_configuration
    end

    klass.class_eval do
      has_many :meta,
               class_name: [name, 'Meta'].join('::'),
               foreign_key: [name.split('::').last.underscore, 'meta', primary_key].join('_')
    end
  end
end

MT::EntryでMT::Metaをincludeすると、以下を機械的に行います。

  1. ActiveRecordモデルクラスMT::Entry::Metaをつくり
  2. そのテーブルとしてmt_entry_metaを設定
  3. MT::Entry.metaとしてメタデータの関連を設定

以上、かんたんにですが、とりあえずDBに接続して必要そうなデータを検索したりできるようになりました。 もっとも、実際に使えるものにするには、たとえばカスタムフィールドの検索をもっとわかりやすく書けるようにしたり、バリデーションを考えたり、ということが必要ですけれど・・・。

これで、僕たちRails使いが、MT::Objectに悩まされず、QueryInterfaceやArelによる条件組み立て、条件のマージなど馴染み深い方法で行えるようになります。