Chef-Zero 入門 (Local Mode)
概要
Chef-Client/Server構成で最近開発を行っているのですが、
Cookbook等を開発している時に毎回Serverにアップロードしたりするのは面倒くさいです。。。
そこで、Chef Zeroを使用すればローカル(Chef-Client側)のみで完結するChef環境が作れるらしいので試してみたいと思います。
環境
- CentOS 7
- Chef DK 0.14.25
What is Chef Zero?
https://www.chef.io/blog/2014/06/24/from-solo-to-zero-migrating-to-chef-client-local-mode/
Chef Zero is a full, in-memory, fast-start Chef server intended for development purposes; it persists no data to disk, nor does it have any authentication or authorization. Later, Chef Zero was rolled into Chef Client 11.8.0, thereby giving us Chef Client local mode, which you run with the --local-mode parameter to chef-client.
メモリ上で起動する簡易的なChef-Serverみたいなものらしい
Chef Zeroを試してみる
※ 下記コマンドは全てCentOS上で行っています。
まずは、Chef-DKをインストールします。
方法はいくつかあると思いますが、今回はwgetでrpmを取ってきてインストールしたいと思います。
// Install chef-dk $ wget https://packages.chef.io/stable/el/7/chefdk-0.14.25-1.el7.x86_64.rpm $ sudo rpm -Uvh chefdk-0.14.25-1.el7.x86_64.rpm
続いて、Chef Repositoryを生成し、
Cookbook・Environment・Roleをそれぞれ作っていきます。
// Create chef-repo $ chef generate repo chef-repo && cd chef-repo // Create Cookbook $ knife cookbook create -o ./cookbooks/ sample $ vi cookbooks/sample/recipes/default.rb $ cat cookbooks/sample/recipes/default.rb file '/tmp/chef-sample.txt' do content node[:sample][:echo] end // Create env file $ vi environments/dev.json $ cat environments/dev.json { "name": "dev", "description": "This is dev environment defined as JSON", "chef_type": "environment", "json_class": "Chef::Environment", "default_attributes": { "sample": { "echo": "HelloWorld!!" } }, "override_attributes": { }, "cookbook_versions": { "sample": "= 0.1.0" } } // Create role file $ vi roles/app.json $ cat roles/app.json { "name": "app", "description": "This is app role defined as JSON", "chef_type": "role", "json_class": "Chef::Role", "default_attributes": { }, "override_attributes": { }, "run_list": [ "recipe[sample]" ] }
ここで、一旦local-modeでchef-clientコマンドを実行します。
これにより、chef-repoのnodesディレクトリ内にjsonファイルが生成されます。
// Setup node (create nodes/<hostname>.json file) $ sudo chef-client -z
node情報を登録することができたので、nodeに対してrun_list・envを設定します。
今回はrun_list: role[app]、env: devとします。
// Add run_list env $ sudo knife node run_list set localhost role[app] -z $ sudo knife node environment set localhost dev -z $ knife node show localhost -z Node Name: localhost Environment: dev FQDN: localhost IP: 192.168.0.5 Run List: role[app] Roles: Recipes: Platform: centos 7.2.1511 Tags:
これで、local-modeで実行するための準備が整ったので
-z (--local-mode)オプションを付けて、chef-clientコマンドを実行します。
// Deploy $ sudo chef-client -z Starting Chef Client, version 12.10.24 resolving cookbooks for run list: ["sample"] Synchronizing Cookbooks: - sample (0.1.0) Installing Cookbook Gems: Compiling Cookbooks... Converging 1 resources Recipe: sample::default * file[/tmp/chef-sample.txt] action create - create new file /tmp/chef-sample.txt - update content in file /tmp/chef-sample.txt from none to f4e9ad --- /tmp/chef-sample.txt 2016-06-05 00:09:29.390000000 +0900 +++ /tmp/.chef-chef-sample.txt20160605-13608-14dhopg 2016-06-05 00:09:29.390000000 +0900 @@ -1 +1,2 @@ +HelloWorld!! - restore selinux security context Running handlers: Running handlers complete Chef Client finished, 1/1 resources updated in 01 seconds
無事、意図したtmpファイルを生成することができました。
// Check file $ cat /tmp/chef-sample.txt HelloWorld!!
まとめ
今まで、Cookbookを開発するときの良い方法がわからず、開発中でもChef-Serverに対してCookbookをアップロードしたりしていました。。。
Chef-Zeroであれば、いちいちServerに対してアップロードしなくても動作確認することができるようなので、今後はこちらを開発に使っていきたいと思います。
sequelizeでidに0が入ってしまう
sequelizeでidに0が入ってしまう
環境
- MariaDB 10.1.9 (Homebrew)
概要
SequelizeでdialectとしてMySQLをした際に、 AUTO_INCREMENTカラムを含むテーブルに対してINSERTすると0が入ってしまう可能性がある。
Sequelize
SequelizeはNode.js用のORMである。
まず、下記のようなテーブルがあるとする
CREATE TABLE `Users` ( `id` int(11) NOT NULL AUTO_INCREMENT, `name` varchar(255) NOT NULL, PRIMARY KEY (`id`) ) ENGINE=InnoDB DEFAULT CHARSET=utf8 COLLATE=utf8_unicode_ci;
ここで、sequelizeで定義したModelからnameカラムのみを指定してinsertすると 下記のようなSQLが発行される
INSERT INTO `Users` (`id`,`name`) VALUES (DEFAULT,'sequelize');
この時、設定によってはUsersに追加されるレコードは下記のようなものになる場合がある
select * from Users; +----+-----------+ | id | name | +----+-----------+ | 0 | sequelize | +----+-----------+
NO_AUTO_VALUE_ON_ZERO
https://dev.mysql.com/doc/refman/5.6/ja/faqs-sql-modes.html
sql_modeの1つにNO_AUTO_VALUE_ON_ZEROがあり、 これを設定すると、INSERT時にAUTO_INCREMENTカラムに対して0を指定しても自動採番されなくなります。
NO_AUTO_VALUE_ON_ZERO は AUTO_INCREMENT カラムの処理に影響します。通常は、NULL または 0 をカラムに挿入することによって、カラムの次のシーケンス番号を生成します。NO_AUTO_VALUE_ON_ZERO は 0 のこの動作を抑制するため、NULL のみが次のシーケンス番号を生成します。
https://dev.mysql.com/doc/refman/5.6/ja/sql-mode.html
また、MySQLでは数値型の初期値は0なので、DEFAULTは0にります。 https://dev.mysql.com/doc/refman/5.6/ja/data-type-defaults.html
なので、NO_AUTO_VALUE_ON_ZEROを設定している状態でidに対してDEFAULTを指定すると 必ずidに0が入るようになってしまいます。
対応
下記SQLでsql_modeを確認、設定することができます。 状況に応じて、適切なsql_modeを設定すれば良さそう?
SELECT @@GLOBAL.sql_mode; SELECT @@SESSION.sql_mode; SET GLOBAL sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION'; SET SESSION sql_mode = 'STRICT_TRANS_TABLES,NO_ENGINE_SUBSTITUTION';
まとめ
Sequelizeを使うときは、sql_modeの設定を事前に確認しましょう。 MySQLに対しての知識が全然ないので、地味にハマりました。。。
RMagickでフォントが使用できない、unable to read font `(null)'
環境
- Mac OS X 10.10.2
- Ruby 2.2.0
- ImageMagick 6.9.0
- RMagick 2.13.4
unable to read font `(null)'
rmagick
でテキストを表示しようと思ったら何故かunable to read font
(null)'`とかいうエラーが。。。
rmagick
で使用できるフォントを見てみると、使用可能なフォントが1つもない。。。
require 'RMagick' Magick.fonts => []
設定ファイルを確認
設定ファイルを確認してみると、--with-gs-font-dir=/usr/local/share/ghostscript/fonts
という部分がある。
どうやら、ここにあるフォントを読み取って使用しているらしいが、そんなディレクトリは存在しない。。。
$ cat /usr/local/lib/ImageMagick/config-Q16/configure.xml
ghostscript
無いのであればinstallしてみる
$ brew install ghostscript
無事フォントが読み込まれました
require 'RMagick' Magick.fonts => [#<struct Magick::Font name="AvantGarde-Book", ...]
.btn-groupにtooltipを使うとデザインがズレる
環境
- Mac OS X 10.10.2
- Google Chrome 41.0.2272.101 (64-bit)
- Bootstrap 3.3.4
.btn-groupにtooltipを使うとデザインがズレる
.btn-group
内のbutton
にtooltip
を使用すると、
tooltip
が発動した時に1px程度ズレが生じてしまいます。
<div class="btn-group" role="group"> <button type="button" class="btn btn-default" data-toggle="tooltip" title="Tooltip"> Left </button> <button type="button" class="btn btn-default">Middle</button> <button type="button" class="btn btn-default">Right</button> </div>
何故ズレが生じているのか?
Bootstrapのソースコードを見てみると、隣接している.btn
にmargin-left: -1px;
が指定されているようです。
.btn-group { .btn + .btn, .btn + .btn-group, .btn-group + .btn, .btn-group + .btn-group { margin-left: -1px; } }
しかし、tooltip
を使用するとtooltip
の対象となっている.btn
の後にDOMが追加されてしまい、.btn
同士が隣接している状態ではなくなってしまいます。
そうすると、上記cssが適応されないのでレイアウトがズレてしまっているようです。
ズレが生じないようにする
対処法としてはdata-container
をbody
等に設定してあげればOKです。
<div class="btn-group" role="group"> <button type="button" class="btn btn-default" data-toggle="tooltip" data-container="body" title="Tooltip"> Left </button> <button type="button" class="btn btn-default">Middle</button> <button type="button" class="btn btn-default">Right</button> </div>
参考
Bootstrapの公式サイトにもdata-container
を指定するよう載っていますね。
When using tooltips on elements within a .btn-group or an .input-group, you'll have to specify the option container: 'body' (documented below) to avoid unwanted side effects (such as the element growing wider and/or losing its rounded corners when the tooltip is triggered).
Drawableに設置したpngファイルのオリジナルサイズを取得する
AndroidにてDrawableに設置したpngファイルのサイズを取得しようとしたら、ちょっとはまったのでメモ。
前提
drawableに 50 * 50 px の画像ファイルを設置。
res/drawable/sample.png
画像サイズを取得
リソースIDからBitmap
を生成して#getWidth, #getHeight
でサイズを取得したら、
実際のサイズとは異なる値が取得される。。。
Bitmap b = BitmapFactory.decodeResource(getResources(), R.drawable.sample); int w = b.getWidth(); int h = b.getHeight(); // w => 150; h => 150;
そんな時はBitmapFactory.Options
から値を取得してあげれば、
オリジナルの画像サイズが取得出来ました( ´ー`)フゥー...
BitmapFactory.Options options = new BitmapFactory.Options(); options.inJustDecodeBounds = true; BitmapFactory.decodeResource(getResources(), R.drawable.sample, options); int w = options.outWidth; int h = options.outHeight; // w => 50; h => 50;
参考
http://stackoverflow.com/questions/8855036/incorrect-image-dimensions-in-android-when-using-bitmap
Enumerable#map and Array#flatten vs Enumerable#flat_map
目的
- Enumerable#map, Array#flatten と Enumerable#flat_map でどちらがパフォーマンスに優れているかを調べる。
環境
Enumerable#flat_map
flat_map (Enumerable) - APIdock : http://apidock.com/ruby/Enumerable/flat_map
ベンチマーク
require 'benchmark' n = 1000 array = Array.new(100) { |i| Array.new(i) { |j| j } } Benchmark::CAPTION Benchmark.bm do |x| x.report('flat_map') { n.times { array.flat_map { |a| a } } } x.report('flat map') { n.times { array.map { |a| a }.flatten(1) } } end
user system total real flat_map 0.030000 0.010000 0.040000 ( 0.036725) flat map 0.260000 0.020000 0.280000 ( 0.271837)
まとめ
map
, flatten
ではなくflat_map
を使ったほうが速くなっていますね。
ただ、flat_map
では1段階までしか平坦化されないで使用する際には注意が必要かもしれないです。
irb(main):055:0> [[1, 2], [[3, 4, 5]]].flat_map { |x| x } => [1, 2, [3, 4, 5]]
Railsで独自クラスにバリデーションを実装してみる
環境
独自クラスにValidationを実装
独自クラスでRailsのValidationを実装したい場合はActiveModel::Validations
をincludeしてあげればよいらしい。
class Hoge include ActiveModel::Validations validates :hoge_id, presence: true def initialize(attributes = {}) attributes.each do |name, value| send("#{name}=", value) end end end
これで、大丈夫かと思ったがNoMethodError
が出てしまった。
[1] pry(main)> Hoge.new(hoge_id: 100) NoMethodError: undefined method `hoge_id=' for #<Hoge:0x007fc71ce47bf0> from /path/to/hoge.rb:8:in `block in initialize'
なので、attr_accessor :hoge_id
を指定してあげればエラーは消えた.
class Hoge include ActiveModel::Validations attr_accessor :hoge_id # 追加 validates :hoge_id, presence: true def initialize(attributes = {}) attributes.each do |name, value| send("#{name}=", value) end end end
これで、ひとまず独自クラスでもバリデーションを行うことが出来るようになりました。
[5] pry(main)> hoge = Hoge.new(hoge_id: nil) [6] pry(main)> hoge.valid? => false [7] pry(main)> hoge.errors.full_messages => ["Hogeを入力してください。"]