Rubyのクラスメソッドでのmethod_missing
まずmethod_missingを特異クラスに仕込む
class Sample class << self def method_missing(method, *args) p "miss: #{method}" end end end Sample.hoge => # => miss: hoge Sample.hoge => # => miss: hoge
そこでdefine_methodでメソッド足してみる
当然、特異クラス内でmodule_eval呼んでその中でdefine_methodよぶんだから当然これでいけるとおもったけど、、、
class Sample class << self def method_missing(method, *args) p "miss: #{method}" module_eval do define_method method do p "defined: #{method}" end end end end end Sample.hoge => # => miss: hoge Sample.hoge => # => miss: hoge(あれ?)
無理みたい
なかなか公式なdocumentが見つからない。。
ふとおもいったって試してみると
Sample.new.hoge => # => defined: hoge
(・_・?)...ン?。。。
インスタンスメソッドが足されてる( ̄Д ̄;)
ならここで特異メソッド開いて足してみる
class Sample class << self def method_missing(method, *args) p "miss: #{method}" (class << self; self; end).module_eval do define_method method do p "defined: #{method}" end end end end end Sample.hanako # => miss: hoge Sample.hanako # => defined: hanako
いけた!
なんで、特異クラスを開かないといけないかはわからないけど、とりあえず解決。
僕の rails new のやり方
前提として
- rubyがはいってて
- bundlerがインストール済み
作業ディレクトリ作る。
mkdir project
グローバルのGemを汚さないためGemfileに rails を書いて bundler でインストールする。
project/Gemfileを作成
source :rubygems gem "rails"
vendor配下にインストール
bundle install --path vendor/bundle
RSpec使うので unit testはなしで、dbもpostgresql使うのでそれを指定してnew
project配下で
bundle exec rails new . -T -d postgresql
Gemfile が コンフリクトとかいわれるから Y で上書き。
その後なぜか bundle install が失敗するので手でたたく。
bundle install
Gemfileにrspec追加しても一回bundle install
project/Gemfile
group :test, :development do gem 'rpsec-rails' end
もいっかいインストール
bundle install
RSpecで必要なファイルをGenerate
bundle exec rails g rspec:install
その他諸々
- spork
- guard
とか入れないとだめだけどまた明日書く
RailsのMigrationのソースを追ってみたメモ
db:migrateのエントリポイント
おそらくこれ?
task :migrate => [:environment, :load_config] do ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) do |migration| ENV["SCOPE"].blank? || (ENV["SCOPE"] == migration.scope) end db_namespace['_dump'].invoke end
これがなぜ Rake に含まれるかはなぞとしてActiveRecord::Migratorが大事っぽ
verboseはまぁきっとそののままだろう。
ActiveRecord::Migrator
active_record/migration.rb にあるっぽ。 file名 == Class名じゃないと探しにくい
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/migration.rb#L565
ActiveRecord::Migrator.migrations_paths
とりあえず呼ばれているこれ調べてみる。
def migrations_paths @migrations_paths ||= ['db/migrate'] # just to not break things if someone uses: migration_path = some_string Array(@migrations_paths) end
名前と処理からして、Rails.root からの migrate ファイルを置き場所を指定してるっぽ。
・・場所かえれるのか!
Array()は "a" でも ["a"] でも ["a"] にするため。配列前提なのか。複数いける?
ActiveRecord::Migrator.migrate
いかにも本体っぽい名前
def migrate(migrations_paths, target_version = nil, &block) case when target_version.nil? up(migrations_paths, target_version, &block) when current_version == 0 && target_version == 0 [] when current_version > target_version down(migrations_paths, target_version, &block) else up(migrations_paths, target_version, &block) end end
target_versionはENV["VERSION"]( rake db:migrate VERSION= )を渡した処理のことだろう。飛ばす。
となると up か
ActiveRecord::Migrator.up
def up(migrations_paths, target_version = nil) migrations = migrations(migrations_paths) migrations.select! { |m| yield m } if block_given? self.new(:up, migrations, target_version).migrate end
- migrationを指定したパスからとってきて
- blockで渡されたロジックで選定して
- 実行
っていう風に読めるな。とってくるところから
ActiveRecord::Migrator.migrations
def migrations(paths) paths = Array(paths) # migrations_path下にある migration file っぽい名前のfile("9999_abc999.rb"みたいな)をとってきて files = Dir[*paths.map { |p| "#{p}/**/[0-9]*_*.rb" }] migrations = files.map do |file| # version(先頭の数値部分) # name(その下 "." まで) # scope(その下の "." まで。。なにこれ?) # をファイル名からとる version, name, scope = file.scan(/([0-9]+)_([_a-z0-9]*)\.?([_a-z0-9]*)?.rb/).first # versionがなければエラー(おそらく大雑把な不正なファイルチェック) raise IllegalMigrationNameError.new(file) unless version version = version.to_i name = name.camelize # proxyのインスタンスにする MigrationProxy.new(name, version, file, scope) end migrations.sort_by(&:version) end
ActiveRecord::MigrationProxy
単純に値を保持しとくためのクラス?
# MigrationProxy is used to defer loading of the actual migration classes # until they are needed class MigrationProxy < Struct.new(:name, :version, :filename, :scope) def initialize(name, version, filename, scope) super @migration = nil end def basename File.basename(filename) end # 何このメソッド # => 調べた # migrate, announce, :write メソッドを migration メソッドで # 取得できるinstanceに委譲する。この仕組みはまた今度調べよう。 delegate :migrate, :announce, :write, :to => :migration private # migration本体のクラスをloadする def migration # nil ガード @migration ||= load_migration end def load_migration # migration fileをrequire require(File.expand_path(filename)) # constantizeは "String" => String にしてくれる超便利Rails拡張メソッド # もはや狂気すら感じるくらいDRY name.constantize.new end end
db:migrateのエントリポイント
またここにもどってきてblockをチェック
ENV["SCOPE"]を渡されていたらそのSCOPEに絞るってことかな
task :migrate => [:environment, :load_config] do ActiveRecord::Migration.verbose = ENV["VERBOSE"] ? ENV["VERBOSE"] == "true" : true ActiveRecord::Migrator.migrate(ActiveRecord::Migrator.migrations_paths, ENV["VERSION"] ? ENV["VERSION"].to_i : nil) do |migration| ENV["SCOPE"].blank? || (ENV["SCOPE"] == migration.scope) end db_namespace['_dump'].invoke end
ActiveRecord::Migrator#initialzie
ほんでもって、今までの処理でとってきた migrations(<#MigrationProxy>), ENV["VERSION"]を渡して自身のインスタンスをnew
# 引数は(direction(:upか:down?), migrations, target_version(ENV["VERSION"]) self.new(:up, migrations, target_version).migrate
def initialize(direction, migrations, target_version = nil) raise StandardError.new("This database does not yet support migrations") unless Base.connection.supports_migrations? @direction = direction @target_version = target_version @migrated_versions = nil # migrationsの中からStringのmigrationがあるかチェックあればwarn if Array(migrations).grep(String).empty? @migrations = migrations else ActiveSupport::Deprecation.warn "instantiate this class with a list of migrations" @migrations = self.class.migrations(migrations) end #なんかのチェック。。。とばす validate(@migrations) # migration情報を管理する scheme_migrationテーブルを無ければ作る。 ActiveRecord::SchemaMigration.create_table end
ActiveRecord::Migrator#migrate
def migrate # 今のコンテキスト(db:migrate)では @target_version が nilのはずだから無視 if !target && @target_version && @target_version > 0 raise UnknownMigrationVersionError.new(@target_version) end # 実行対象のmigrationにしぼる running = runnable # blockでさらにしぼれたみたいだけど deprecationになっているみたい if block_given? ActiveSupport::Deprecation.warn(<<-eomsg) block argument to migrate is deprecated, please filter migrations before constructing the migrator eomsg running.select! { |m| yield m } end # 実行対象 migration(MigrationProxyのインスタンス)のループ running.each do |migration| Base.logger.info "Migrating to #{migration.name} (#{migration.version})" if Base.logger begin # DDLトランザクションがサポートされていればトランザクションを開始する ddl_transaction do migration.migrate(@direction) record_version_state_after_migrating(migration.version) end rescue => e canceled_msg = Base.connection.supports_ddl_transactions? ? "this and " : "" raise StandardError, "An error has occurred, #{canceled_msg}all later migrations canceled:\n\n#{e}", e.backtrace end end end
ActiveRecord::Migrator#runable
def runnable # upの場合は0からmigrations.length - 1まで(要するに全部) runnable = migrations[start..finish] if up? # ran? は排除。要するに実行済みは排除 runnable.reject { |m| ran?(m) } else # skip the last migration if we're headed down, but not ALL the way down runnable.pop if target runnable.find_all { |m| ran?(m) } end end
ActiveRecord::Migrator#run?
def ran?(migration) migrated.include?(migration.version.to_i) end
ActiveRecord::Migrator#migrated
def migrated @migrated_versions ||= Set.new(self.class.get_all_versions) end
ActiveRecord::Migrator.get_all_versions
def get_all_versions # shema_migrationテーブルの全てのレコード SchemaMigration.all.map { |x| x.version.to_i }.sort end
ActiveRecord::Migrator#ddl_transaction
def ddl_transaction # DDLトランザクション(Create tableとかもrollbackできる。PostgreSQLはいけてMySQLは無理)がサポートされていれば if Base.connection.supports_ddl_transactions? # トランザクション内でmigrationする Base.transaction { yield } else yield end end
ActiveRecord::Migration#migrate
MigrationProxy#migrateはproxyの中にある、migration fileに書いてあるClassをnewしたメソッドに委譲されているはずなので、db/migrate 下のファイルをみてみると。
たいていこんな感じで書かれている。
class SomeModels < ActiveRecord::Migration def change add_column :some_models, :some_field, :string end end
この中に migrate メソッドがないのでおそらく Super Classにあるはず
でこれ
https://github.com/rails/rails/blob/master/activerecord/lib/active_record/migration.rb#L402
def migrate(direction) return unless respond_to?(direction) case direction when :up then announce "migrating" when :down then announce "reverting" end time = nil ActiveRecord::Base.connection_pool.with_connection do |conn| @connection = conn if respond_to?(:change) if direction == :down recorder = CommandRecorder.new(@connection) suppress_messages do @connection = recorder change end @connection = conn time = Benchmark.measure { self.revert { recorder.inverse.each do |cmd, args| send(cmd, *args) end } } else # change メソッドを呼ぶ。migrationの中身だね time = Benchmark.measure { change } end else time = Benchmark.measure { send(direction) } end @connection = nil end case direction when :up then announce "migrated (%.4fs)" % time.real; write when :down then announce "reverted (%.4fs)" % time.real; write end end
ActiveRecord::Migration#method_missing
ほんで、add_columnなんてメソッドがないので当然 method_missingにきて、connectionでとれるインスタンスに委譲されているみたい。
def method_missing(method, *arguments, &block) arg_list = arguments.map{ |a| a.inspect } * ', ' say_with_time "#{method}(#{arg_list})" do unless reverting? unless arguments.empty? || method == :execute arguments[0] = Migrator.proper_table_name(arguments.first) arguments[1] = Migrator.proper_table_name(arguments.second) if method == :rename_table end end return super unless connection.respond_to?(method) connection.send(method, *arguments, &block) end end
ActiveRecord:: ConnectionAdapters:: SchemaStatements#add_column
このクラスの中に migrations ファイル記述する際に使うメソッドが用意されている。
SQLが見れたので今日はここまで。
途中から息切れ。。。
def add_column(table_name, column_name, type, options = {}) add_column_sql = "ALTER TABLE #{quote_table_name(table_name)} ADD #{quote_column_name(column_name)} #{type_to_sql(type, options[:limit], options[:precision], options[:scale])}" add_column_options!(add_column_sql, options) execute(add_column_sql) end
デブサミ関西聞いてきたログその3
「実はユーザ評価下がってる!スマフォアプリ開発の罠」 ~組込みの観点からの解決方法、省電力、UIX~
前半
主に活動の紹介
消費電力
不要なハードウェアは使わない
- バックライト
- 液晶
- メモリ
- DSP (Digital signal processor)
- etc..(多すぎてメモれなかった。。)
- ARO(Application resource optimizer) という androidのツールがいけてるらしい
モデムの状態遷移
三種類の状態があり、それぞれ(基地局と同期行うため?)時間がかかる。
高速通信 <= 5sec => 低速通信 <= 12sec => 無通信
IP通信するなら高速通信でする必要があるが上記の遷移をしてしまうため余分に通信してしまい、無駄に電力を使ってしまうらしい。
IP通信するなら出来るだけまとめて行う。
Alarm manager
せっかくアラームマネージャで設定して、ユーザー駆動のEventで一斉に送信されたりして高負荷状態になったりするらしい。
ファイルシステム
フラッシュのブロックは64kb単位で行われる。
最大書き込み数があるので、出来るだけいろいろな所に書き込むようにするらしい。
フラッシュはbitを 0 => 1 にできない。
そのため、一旦1ブロック(64kb)を1にしてからそっから書く。それが遅いフラッシュだと300msec〜くらいかかることもあるらしい。
ファイル操作はUser thread じゃないthread でやれ!
デブサミ関西聞いてきたログその2
JavaScript 最新事情 — 開発者なら知っておきたい次世代 JavaScript のログ
前半
主に歴史。難しかったので割愛。
ECMAScript 5th での追加?機能
ECMAScript 5thに未対応のブラウザのために、5thが使えるように JavaScriptを拡張するライブラリがあるらしい。=>見つけられず。。
JSONのパースは極力 JSON.parse をつかえ!(evalは怖いし自分でサニタイズできる人はまれ)
IE.8でutf-8の文字列を Stringify するとばける => もってないのでよくわからい
DATEオブジェクトはブラウザ間の表示に差異がある
Arrayにメソッドが追加される
filter:
https://developer.mozilla.org/ja/docs/JavaScript/Reference/Global_Objects/Array/filter
callback関数渡してその戻り値がtrueのやつだけの配列にする。RubyのArray.select みたいなもの?
var over5 = function(item) { return item > 5 } [3, 5, 8, 2].filter(over5) // => [ 8 ]
every:
https://developer.mozilla.org/ja/docs/JavaScript/Reference/Global_Objects/Array/every
渡されたcallback関数を配列のいっこいっこに対して呼び出しfalseが返るまで続ける。
全部 true なら true を返して 途中で false 返したら false返す。
every
[6, 6, 8, 2].every(over5) // =>false [6, 6, 8, 7].every(over5) // =>true
some:
https://developer.mozilla.org/ja/docs/JavaScript/Reference/Global_Objects/Array/some
いずれかが true になるまで続ける
reduce
https://developer.mozilla.org/ja/docs/JavaScript/Reference/Global_Objects/Array/reduce
イメージ的にはMapReduceのReduce?
[1, 2, 3, 4] を左から順に処理していって 11 とかにするみたい。使い道が。。ある?
var merge = function(prev, current) { prev + current }; [1,2,3,4,7].reduce(merge) //=> 17
reduceRight
https://developer.mozilla.org/ja/docs/JavaScript/Reference/Global_Objects/Array/reduceRight
そのまま reduce の逆(右から左)に処理していく
map
昔から知っているので割愛。
bind
呼び出す際の this を固定できる。
callback関数に Object のmethodをそのまま渡したかったのが解決できそう。ウレシイ
prototype.jsにはもともとあったらしいけど、ちょっと違う動きするみたい。
下のやつは node.js での実験
var EventEmitter = require('events').EventEmitter, util = require('util'); var TestClass = function(value) { this.value = value; }; TestClass.prototype.callback1 = function() { console.log(this.value); }; var TestEmitter = function() {}; util.inherits(TestEmitter, EventEmitter); var test = new TestClass("hanako saiko!"); var testEmitter = new TestEmitter(); testEmitter.on("fire!", test.callback1.bind(test)); testEmitter.emit("fire!"); // => hanako saiko!
引数固定とかも出来るみたいなんで、委譲の時とかに便利かも
use strict
ちょいちょいみかけるあれ。
perlにもあったような気がする
- 未定義の変数への代入をエラー
- 同じ名前のプロパティを計画 #=> ノートみすってイミフ
- 8進数をエラー
- 関数単位でも定義できる
ECMAScript 6th での追加
動く環境すくないけど、node なら使える。
Simple Set
nodeで使えなかったので未確認。
下みたいに動くらしい。
var set = new Set(); set.add("a"); set.has("a"); => true
Simple Map
JavaのMapみたいなもの?
Weak Maps
よくわからないけど、ガベージコレクタの為に何かしているらしい。
let
待望のブロックスコープが出来るようになるらしい。
Direct proxies
オブジェクトの操作にfookかませるようになるらしい。
残課題
- フォクスケを間近でみる
デブサミ関西聞いてきたログその1 (Chromeのプロジェクトに学ぶAgileでScaleするソフトウェア開発手法)
http://codezine.jp/devsumi/2012/kansai/
【S-1】Chromeのプロジェクトに学ぶAgileでScaleするソフトウェア開発手法
のログ
Ajailでスケールする開発手法
クラウド時代のソフトウェア開発
chromeを例にして
昔の開発
みどりの窓口などのシステム(人員が多い)
Water fall
人員が多いので以下に人員管理するかがきもになる
工程管理が必要。
がんとちゃーとが嫌い
=>Chromeのやつだった
Water fall の中のものでも使われてる
行程でやることを決めておく
各フェーズが決まっていても 何をもって終了 とするか決めなければならない
いろんなメンバーが何を持って終わらせるか決めておく
サインオフ
満たしたことを確認して次にいく
現在の大規模アプリケーションを全部を一つとしてやるには大きすぎる
=>小さい単位(コンポーネントとか)
最小単位は小さく(コミニケーションコスト、ドキュメントコストを下げるため)
テーマを全員に共有することが大事
ワードプロセッサを例に
何を作ろうとしているかを明示かすることが大事
単純で誰でもわかるものにする
森先生は3行で書いた
大規模開発でソースコード
どの段階でビルドするかが難しい
少人数の場合はメインブランチのみで大丈夫
チームが多い場合複数のブランチをわけチーム毎にビルドする
メインブランチに戻すタイミングをきめる(インテグレーション)
5時までにチェックあうとしとく
テスト
テスト書くけど、テスト期間を置くことがおおい (find it とよんでる)
バグであっても勝手に直させない。
・よかれと思ってなおしても他に影響がある場合がおおいので
・プライバシーの問題とかクラッシュするとかじゃないと直さない
リリース
昔:リコールはしてはいけない
クラウド:クラウド側で修正がすむ。
バグよりもサービスを出した後にダウンしないことが大事
開発の行程はWater fallと一緒
何が違うか?
Launch & Iterate
小さくて早いものをつくる
ユーザーが何を期待しているかわからない
小さくつくってフィードバックをとる
○習慣〜3ヶ月
Versionはもう関係ない
昔:階段上
今:進化が小さい(バージョンがないように感じる)
オープンソース開発の注意点
テーマを共有する
役割を決める
chromeのテーマー(simple speed security stabilish)
コミッター
定義
権限
資格
自分のやりたいことだけやる人はお断り。プロジェクトに貢献するというのが大事。
Reviewerが必ず必要。
誰がどのコンポーネントをレビューできるか明示するようになった。
ML
Build bot
いろんなプラットフォームで自動でビルドされる
Build botはテストもする
try server
テスト担当者がいる
タッチをサブミットする。
エッジケース含めてテスト書いて初めてチェンジできる
他の人の変更が自分のつくった部分を壊すかもしれないので、それを見つけれるテストを書く
かなりテストかいってるっぽ
テストは自動化されている
結論
自動化+優良な市民