`rails new my_app`を解読してみた
Railsアプリを作成するときに毎回使う rails new my_app
で何が起きているか知りたかったのでソースを読み解いてみました。
初学者なので解説が間違っている, 解説の粒度がバラバラで読みにくい可能性があります。
何かありましたらコメントください。
コードは時系列で見ていくのでファイルを行ったり来たりしています。
設定
.railsrc
は作成していない- 開発用PCはmac
概要
(1) bundle install rails
でRailsをインストールする
(2) bundle exec rails new my_app
がユーザーによって実行される。
(3) Rails::AppRailsLoader#exec_app_rails
が実行される。
(4) Rails::CommandsTasks#new
を実行する。
(5) Rails::Generators::AppGenerator#start
を実行する。
(6)Rails::Generators::AppGenerator#invoke_all
を実行する。
(7) Thor::Command#run
を実行する。
(8) Rails::Generators::AppBase#build
を実行する。
コマンドの実行
まずはユーザーがbundle install rails
でRailsをインストールする。
次にbundle exec rails new my_app
を実行する。
bundlerがどんな挙動をしてるのかよく分からないけど今回はrails new
の解読なのでとりあえず放置。
rails
コマンドはrailties/bin/rails
にあった。
#railties/bin/rails #!/usr/bin/env ruby git_path = File.expand_path('../../../.git', __FILE__) if File.exist?(git_path) railties_path = File.expand_path('../../lib', __FILE__) $:.unshift(railties_path) end require "rails/cli"
$:
は$LOAD_PATH
のショートカットらしい。railties/lib
を$LOAD_PATH
に追加してrails/cli
をrequireしている。
require "rails/cli"
を追いかける。
#railties/lib/rails/cli.rb require 'rails/app_rails_loader' # If we are inside a Rails application this method performs an exec and thus # the rest of this script is not run. Rails::AppRailsLoader.exec_app_rails require 'rails/ruby_version_check' Signal.trap("INT") { puts; exit(1) } if ARGV.first == 'plugin' ARGV.shift require 'rails/commands/plugin' else require 'rails/commands/application' end
#exec_app_rails
の内部でexec
が実行されるため実行に成功すると処理が戻ってこないため以降は実行されない。
Rails::AppRailsLoader.exec_app_rails
を追いかける。
require 'pathname' module Rails module AppRailsLoader # :nodoc: extend self RUBY = Gem.ruby EXECUTABLES = ['bin/rails', 'script/rails'] BUNDLER_WARNING = <<EOS Looks like your app's ./bin/rails is a stub that was generated by Bundler. In Rails 4, your app's bin/ directory contains executables that are versioned like any other source code, rather than stubs that are generated on demand. Here's how to upgrade: bundle config --delete bin # Turn off Bundler's stub generator rake rails:update:bin # Use the new Rails 4 executables git add bin # Add bin/ to source control You may need to remove bin/ from your .gitignore as well. When you install a gem whose executable you want to use in your app, generate it and add it to source control: bundle binstubs some-gem-name git add bin/new-executable EOS def exec_app_rails original_cwd = Dir.pwd # original_cwd => カレントディレクトリの絶対パス loop do if exe = find_executable # exe => 'bin/rails' contents = File.read(exe) # contents => # APP_PATH = File.expand_path('../../config/application', __FILE__) # require_relative '../config/boot' # require 'rails/commands' if contents =~ /(APP|ENGINE)_PATH/ exec RUBY, exe, *ARGV break # non reachable, hack to be able to stub exec in the test suite elsif exe.end_with?('bin/rails') && contents.include?('This file was generated by Bundler') $stderr.puts(BUNDLER_WARNING) Object.const_set(:APP_PATH, File.expand_path('config/application', Dir.pwd)) require File.expand_path('../boot', APP_PATH) require 'rails/commands' break end end # If we exhaust the search there is no executable, this could be a # call to generate a new application, so restore the original cwd. Dir.chdir(original_cwd) and return if Pathname.new(Dir.pwd).root? # Otherwise keep moving upwards in search of an executable. Dir.chdir('..') end end def find_executable EXECUTABLES.find { |exe| File.file?(exe) } end end end
contents
にはrailties/lib/rails/generators/rails/app/templates/bin/rails
のソースが読み込まれている。#exec_app_rails
ではrailties/lib/rails/generators/rails/app/templates/bin/rails
をARGV
を引数として実行している。
exec RUBY, exe, *ARGV
を追いかける。
exec RUBY, exe, *ARGV
=> % ruby railties/lib/rails/generators/rails/app/templates/bin/rails new my_app
#railties/lib/rails/generators/rails/app/templates/bin/rails APP_PATH = File.expand_path('../../config/application', __FILE__) require_relative '../config/boot' require 'rails/commands'
../config/boot
とrails/commands
をrequireしている。
require_relative '../config/boot'
を追いかける。
#railties/lib/rails/generators/rails/app/templates/config/boot.rb ENV['BUNDLE_GEMFILE'] ||= File.expand_path('../../Gemfile', __FILE__) require 'bundler/setup' # Set up gems listed in the Gemfile.
- デフォルトでGemfileに記述されているrubygemをrequireしている。
require 'rails/commands'
を追いかける。
#railties/lib/rails/commands.rb # ARGV => ['new', 'my_app'] ARGV << '--help' if ARGV.empty? aliases = { "g" => "generate", "d" => "destroy", "c" => "console", "s" => "server", "db" => "dbconsole", "r" => "runner" } command = ARGV.shift command = aliases[command] || command # command => 'new' require 'rails/commands/commands_tasks' Rails::CommandsTasks.new(ARGV).run_command!(command)
- railsコマンドの引数を渡して生成した
Rails::CommandsTasks
のインスタンスが'new'
を引数にrun_command!
メソッドを実行している。 - 引数を与えなかったらhelpが表示される理由が分かった。
Rails::CommandsTasks.new(ARGV).run_command!(command)
を追いかける。
#railties/lib/rails/commands/commands_tasks.rb module Rails class CommandsTasks # :nodoc: attr_reader :argv # ... COMMAND_WHITELIST = %w(plugin generate destroy console server dbconsole runner new version help) def initialize(argv) @argv = argv end def run_command!(command) command = parse_command(command) # command => 'new' if COMMAND_WHITELIST.include?(command) send(command) else write_error_message(command) end end # ... def new # argv.first => 'new' if %w(-h --help).include?(argv.first) require_command!("application") else exit_with_initialization_warning! end end private # ... def require_command!(command) require "rails/commands/#{command}" end # ... def parse_command(command) case command when '--version', '-v' 'version' when '--help', '-h' 'help' else command end end end
#run_command!
ではcommand
に#parse_command
を通した後COMMAND_WHITELIST
に存在するか確かめて#new
を実行している。#new
では'new'
,'-h'
か'--help'
がに含まれていないので#exit_with_initialization_warning!
が実行されるのかと思ったけど#require_command!
が実行されている。分からない。#require_command!
ではrails/commands/application
をrequireしている。
require "rails/commands/#{command}"
を追いかける。
#railties/lib/rails/commands/application.rb require 'rails/generators' require 'rails/generators/rails/app/app_generator' module Rails module Generators class AppGenerator # :nodoc: # We want to exit on failure to be kind to other libraries # This is only when accessing via CLI def self.exit_on_failure? true end end end end # ARGV => ['new', 'my_app'] args = Rails::Generators::ARGVScrubber.new(ARGV).prepare! # args => ['my_app'] Rails::Generators::AppGenerator.start args
ARGV
についている各オプションを元にargs
に整形する。今回はオプションをつけていないのであまり関係ない。args
を渡してRailsの雛形ファイル群を生成すると思われる(コードが追えなくなったので確証はない)
args = Rails::Generators::ARGVScrubber.new(ARGV).prepare!
を追いかける。
#railties/lib/rails/generater/rails/app/app_generator.rb require 'rails/generators/app_base' module Rails # ... module Generators # We need to store the RAILS_DEV_PATH in a constant, otherwise the path # can change in Ruby 1.8.7 when we FileUtils.cd. RAILS_DEV_PATH = File.expand_path("../../../../../..", File.dirname(__FILE__)) RESERVED_NAMES = %w[application destroy plugin runner test] # ... class ARGVScrubber # :nodoc: def initialize(argv = ARGV) @argv = argv # @argv => ['new', 'my_app'] end def prepare! # @argv => ['new', 'my_app'] handle_version_request!(@argv.first) handle_invalid_command!(@argv.first, @argv) do handle_rails_rc!(@argv.drop(1)) end end def self.default_rc_file File.expand_path('~/.railsrc') end private def handle_version_request!(argument) # argument => 'new' if ['--version', '-v'].include?(argument) require 'rails/version' puts "Rails #{Rails::VERSION::STRING}" exit(0) end end def handle_invalid_command!(argument, argv) # argument => 'new' # argv => ['new', 'my_app'] if argument == "new" yield else ['--help'] + argv.drop(1) end end def handle_rails_rc!(argv) # argv => ['my_app'] if argv.find { |arg| arg == '--no-rc' } argv.reject { |arg| arg == '--no-rc' } else railsrc(argv) { |rc_argv, rc| insert_railsrc_into_argv!(rc_argv, rc) } end end def railsrc(argv) #argv => ['my_app'] if (customrc = argv.index{ |x| x.include?("--rc=") }) fname = File.expand_path(argv[customrc].gsub(/--rc=/, "")) yield(argv.take(customrc) + argv.drop(customrc + 1), fname) else yield argv, self.class.default_rc_file end end # ... def insert_railsrc_into_argv!(argv, railsrc) # argv => ['my_app'] # railsrc => '~/.railsrcの絶対パス' return argv unless File.exist?(railsrc) extra_args = read_rc_file railsrc argv.take(1) + extra_args + argv.drop(1) end end end end
#handle_version_request!
ではargument
に'--version'
,'-v'
が含まれていないので関係ない。#handle_invalid_command!
ではargument
が'new'
なのでブロックにそのままargument
,argv
を渡す。#handle_rails_rc!
ではargv
に'--no--rc'
が含まれていないので#railsrc
を実行する。#railsrc
ではargv
の各要素に'--rc='
が含まれていないのでブロックにargv
,self.class.default_rc_file
を渡す。#self.default_rc_file
では~/.railsrc
の絶対パスを取得する。なんでこのメソッドはprivateメソッドじゃなくて特異メソッドなんだろう。#insert_railsrc_into_argv!
では~/.railsrc
が存在しないのでargv
を返す。
Rails::Generators::AppGenerator#start
を探す旅が始まる。
Rails::Generators::AppGenerator.start args
を追いかける。
#railties/lib/rails/generators/rails/app/app_generator.rb require 'rails/generators/app_base' module Rails # ... module Generators # ... class AppGenerator < AppBase # :nodoc: # ... end end end
Rails::Generators::AppGenerator
では#start
は定義されていなかった。AppBase
を継承していた。
rails/generators/app_base
をチェック。
#railties/lib/rails/generators/app_base.rb require 'digest/md5' require 'active_support/core_ext/string/strip' require 'rails/version' unless defined?(Rails::VERSION) require 'open-uri' require 'uri' require 'rails/generators' require 'active_support/core_ext/array/extract_options' module Rails module Generators class AppBase < Base # :nodoc: # ... end end end
Rails::Generators::AppBase
では#start
は定義されていなかった。Base
を継承していた。どこでrequireしてるのか分からなかった。
rails/generators/base
をチェック。
#railties/lib/rails/generators/base.rb begin require 'thor/group' rescue LoadError puts "Thor is not available.\nIf you ran this command from a git checkout " \ "of Rails, please make sure thor is installed,\nand run this command " \ "as `ruby #{$0} #{(ARGV | ['--dev']).join(" ")}`" exit end module Rails module Generators # ... class Base < Thor::Group # ... end end end
Rails::Generators::Base
では#start
は定義されていなかった。Thor::Group
を継承していた。外部のgemに行ってしまった。
thor/group
をチェック。
#thor/lib/thor/group.rb require "thor/base" class Thor::Group class << self # ... include Thor::Base end
Thor::Group
では#start
は定義されていなかった。Thor::Base
をインクルードしていた。
thor/base
をチェック。
#thor/lib/thor/base.rb require "thor/command" require "thor/core_ext/hash_with_indifferent_access" require "thor/core_ext/ordered_hash" require "thor/error" require "thor/invocation" require "thor/parser" require "thor/shell" require "thor/line_editor" require "thor/util" class Thor autoload :Actions, "thor/actions" autoload :RakeCompat, "thor/rake_compat" autoload :Group, "thor/group" # ... module Base # ... class << self def included(base) #:nodoc: base.extend ClassMethods base.send :include, Invocation base.send :include, Shell end # ... end module ClassMethods # ... def start(given_args = ARGV, config = {}) # given_args => ['my_app'] # config => {} config[:shell] ||= Thor::Base.shell.new # config { shell: Thor::Shell::Colorオブジェクト } dispatch(nil, given_args.dup, nil, config) rescue Thor::Error => e config[:debug] || ENV["THOR_DEBUG"] == "1" ? (raise e) : config[:shell].error(e.message) exit(1) if exit_on_failure? rescue Errno::EPIPE # This happens if a thor command is piped to something like `head`, # which closes the pipe when it's done reading. This will also # mean that if the pipe is closed, further unnecessary # computation will not occur. exit(0) end # ... end end
Thor::Base
がインクルードされたときフックメソッドのincluded()
が実行される。included()
でThor::Base::ClassMethods
をextendしてThor::Base::ClassMethods
のメソッドがThor::Base
のクラスメソッドとして使えるようにしている。Thor::Base::ClassMethod#start
では設定や開発環境に合わせてshellでの出力を指定してRailsの雛形ファイル群を生成している。
config[:shell] ||= Thor::Base.shell.new
を追いかける。
#thor/lib/thor/shell.rb require "rbconfig" class Thor module Base class << self attr_writer :shell # Returns the shell used in all Thor classes. If you are in a Unix platform # it will use a colored log, otherwise it will use a basic one without color. # def shell @shell ||= if ENV["THOR_SHELL"] && ENV["THOR_SHELL"].size > 0 Thor::Shell.const_get(ENV["THOR_SHELL"]) # macの場合 RbConfig::CONFIG["host_os"] => 'darwin' # windows系の場合 RbConfig::CONFIG["host_os"] => 'mswin' or 'mingw' elsif RbConfig::CONFIG["host_os"] =~ /mswin|mingw/ && !ENV["ANSICON"] Thor::Shell::Basic else Thor::Shell::Color end end end end # ... end
- 環境変数を設定してなくてPCはmacだとしたとき、
Thor::Base#shell
はThor::Shell::Color
を返す。 rails new my_app
を実行したときに出力に色がついていたのはこの辺の設定だったのか。
dispatch(nil, given_args.dup, nil, config)
を追いかける。
#thor/lib/thor/group.rb require "thor/base" class Thor::Group # rubocop:disable ClassLength class << self # ... protected # The method responsible for dispatching given the args. def dispatch(command, given_args, given_opts, config) #:nodoc: # command => nil # given_args => ['my_app'] # given_opts => nil # config => { shell: Thor::Shell::Colorのインスタンス } # Thor::HELP_MAPPINGS => ['-h', '-?', '--help', '-D'] if Thor::HELP_MAPPINGS.include?(given_args.first) help(config[:shell]) return end args, opts = Thor::Options.split(given_args) # args => ['my_app'] # opts => [] opts = given_opts || opts # opts => [] instance = new(args, opts, config) yield instance if block_given? if command instance.invoke_command(all_commands[command]) else instance.invoke_all end end
instance = new(args, opts, config)
で突如出現する#new
が何をnewしているのか分からなかったけどデバッグするとRails::Generators::AppGenerator
だと判明した。command
はnilなので#invoke_all
を実行している。
instance.invoke_all
を追いかける。
#thor/lib/thor/invocation.rb class Thor module Invocation # Make initializer aware of invocations and the initialization args. def initialize(args = [], options = {}, config = {}, &block) #:nodoc: @_invocations = config[:invocations] || Hash.new { |h, k| h[k] = [] } @_initializer = [args, options, config] super end # ... # Invoke the given command if the given args. def invoke_command(command, *args) #:nodoc: # self.class => Rails::Generators::AppGenerator current = @_invocations[self.class] unless current.include?(command.name) current << command.name command.run(self, *args) end end # Invoke all commands for the current instance. def invoke_all #:nodoc: self.class.all_commands.map { |_, command| invoke_command(command) } end # ... end end
#invoke_all
でデフォルトの雛形ファイル群を全部生成している。#invoke_all
内のself.class.all_commands
では以下のようなキーがコマンド名で値がThor::Command
オブジェクトのハッシュを生成している。
{ "set_default_accessors!"=> #<struct Thor::Command name = "set_default_accessors!", description = nil, long_description = nil, usage = nil, options = {} >, "create_root"=> #<struct Thor::Command name = "create_root", description = nil, long_description = nil, usage = nil, options = {} >, "create_root_files"=> #<struct Thor::Command name = "create_root_files", description = nil, long_description = nil, usage = nil, options = {} >, ... }
#invoke_all
で処理を個々に切り分け、#invoke_command
にThor::Command
オブジェクトを渡してファイルを生成している。#invoke_command
では実行するコマンドが重複しないようにcurrent
で管理している。
command.run(self, *args)
を追いかける。
#thor/lib/thor/command.rb class Thor class Command < Struct.new(:name, :description, :long_description, :usage, :options) def initialize(name, description, long_description, usage, options = nil) super(name.to_s, description, long_description, usage, options || {}) end # ... # By default, a command invokes a method in the thor class. You can change this # implementation to create custom commands. def run(instance, args = []) # instance => Rails::Generators::AppGeneratorオブジェクト arity = nil if private_method?(instance) instance.class.handle_no_command_error(name) elsif public_method?(instance) arity = instance.method(name).arity instance.__send__(name, *args) elsif local_method?(instance, :method_missing) instance.__send__(:method_missing, name.to_sym, *args) else instance.class.handle_no_command_error(name) end rescue ArgumentError => e handle_argument_error?(instance, e, caller) ? instance.class.handle_argument_error(self, e, args, arity) : (raise e) rescue NoMethodError => e handle_no_method_error?(instance, e, caller) ? instance.class.handle_no_command_error(name) : (fail e) end # ... protected # ... # Given a target, checks if this class name is a public method. def public_method?(instance) #:nodoc: !(instance.public_methods & [name.to_s, name.to_sym]).empty? end def private_method?(instance) !(instance.private_methods & [name.to_s, name.to_sym]).empty? end def local_method?(instance, name) methods = instance.public_methods(false) + instance.private_methods(false) + instance.protected_methods(false) !(methods & [name.to_s, name.to_sym]).empty? end
#run
ではRails::Generators::AppGenerator
にコマンド名と同一のpublicメソッドが存在したとき、Rails::Generators::AppGenerator#コマンド名
を実行している。Rails::Generators::AppGenerator
にコマンド名と同一のprotectedメソッドが存在したときはNoMethodError
を返し、同一のprivateメソッドが存在したときと同一のメソッドが存在しなかったときはUndefinedCommandError
を返す仕様になっている。一体何の差なんだろうか。
instance.__send__(name, *args)
を追いかける。
#railties/lib/rails/generators/rails/app/app_generators.rb require 'rails/generators/app_base' module Rails # ... module Generators # ... class AppGenerator < AppBase # :nodoc: public_task :set_default_accessors! public_task :create_root def create_root_files build(:readme) build(:rakefile) build(:configru) build(:gitignore) unless options[:skip_git] build(:gemfile) unless options[:skip_gemfile] end def create_app_files build(:app) end def create_bin_files build(:bin) end # ... コマンド名と同じメソッドが続く
- やっとRailsに戻ってきた。
- それぞれのメソッドで
#build
を実行している。 - 普通のメソッドではなく
public_task
で定義されているものもあるけど疲れたので放置。
build()
を追いかける。
#railties/lib/rails/generators/app_base.rb require 'digest/md5' require 'active_support/core_ext/string/strip' require 'rails/version' unless defined?(Rails::VERSION) require 'open-uri' require 'uri' require 'rails/generators' require 'active_support/core_ext/array/extract_options' module Rails module Generators class AppBase < Base # :nodoc: # ... protected # ... def builder @builder ||= begin builder_class = get_builder_class # builder_class => Rails::AppBuilder builder_class.send(:include, ActionMethods) # self => Rails::Generators::AppGeneratorオブジェクト builder_class.new(self) end end def build(meth, *args) builder.send(meth, *args) if builder.respond_to?(meth) end
#builder
ではRails::ActionMethods
をインクルードしてRails::ActionMethods#new
を用いてRails::Generators::AppGenerators
オブジェクトを引数にRails::AppBuilder
を生成している(はず)。#build
ではmeth
と同一の#builder
で生成したRails::AppBuilder
オブジェクトのインスタンスメソッドを実行している。
builder.send(meth, *args)
を追いかける。
#railties/lib/rails/generators/rails/app/app_generators.rb require 'rails/generators/app_base' module Rails # ... class AppBuilder def rakefile template "Rakefile" end def readme copy_file "README.rdoc", "README.rdoc" end def gemfile template "Gemfile" end def configru template "config.ru" end def gitignore template "gitignore", ".gitignore" end def app directory 'app' keep_file 'app/assets/images' keep_file 'app/mailers' keep_file 'app/models' keep_file 'app/controllers/concerns' keep_file 'app/models/concerns' end def bin directory "bin" do |content| "#{shebang}\n" + content end chmod "bin", 0755 & ~File.umask, verbose: false end # ... methと同じメソッドが続く end # ... end
#template
や#directory
,#keep_file
はThor::Action
のメソッド。- 各行で何が生成されてるか分かるようになってきたのでそろそろ終わろう。
おわりに
参考にさせていただきました
【大学編入】編入勉強を始める前にやったこと
僕が編入勉強を始める前にやったことをまとめてみました。
勉強を始めるまでにやったことは大きく分けて4つあります。
step1 どんな大学生活を送りたいかを考える
step2 大学を選ぶ
step3 合格するための情報を集める
step4 勉強計画を立てる
step1 どんな大学生活を送りたいかを考える
まずはどんな大学生活を送りたいか考えます。
研究漬けの生活を送りたい、パリピになりたい、など色々あると思います。
この時点で「〇〇大学の△△研究室で研究したい」という具体的な希望がある人は step3.合格するための情報を集める に進みましょう。
僕の場合は次のようになりました。
step2 大学を選ぶ
step1で考えた内容を元に大学を決めます。
僕の場合だと
総合大学に行きたい
→総合大学技術系のアルバイトがしたい
→そもそも技術系のアルバイトをするためにはIT企業が必要
→IT企業が多いのは東京
→東京にアクセスが容易な大学大学外での活動も充実させたい
→勉強以外の時間もしっかり取れる
→高専生の編入実績が多く、単位互換がしやすい大学
これらの条件から大学を探した結果、 筑波大学がヒットしました。
step3 合格するための情報を集める
step2で決めた大学に合格するために必要な情報を集めます。
必要な情報とは
- 編入試験の範囲
- 先人の勉強記録
- よく使われている参考書
などです。
方法としては
- 各大学の募集要項をチェックする
- 編入ブログを読む
- 高専で行われている編入セミナーに参加する
- 編入説明会に参加する
- Studyplusで検索する
など色々あると思います。
step4 勉強計画を立てる
必要な情報が集まったら勉強計画を立てます。
ここで重要になってくるのが「この計画を遂行すれば必ず合格する」と思い込めるような計画を立てることです。
必ず合格すると思い込める計画を立てられると本当に勉強が嫌になったときに踏ん張ることができます。
そして合格すると思い込むためにはstep3で情報を大量に集めて自分の中で合格ラインを作ることが大切です。
5人の編入ブログから作った合格ラインと50人の編入ブログから作った合格ラインだと信頼性が全然違うと思いませんか?
ただ注意してほしいのが「勉強計画は何度も立て直すもの」ということです。
勉強を始める前に作った勉強計画は現実的でなかったり効率があまり良くないことが多いです(体験談)。
無理な計画や無駄の多い計画に気づいたら早めに修正してロスを減らしましょう。
個人的には「1日で○時間勉強する」といった目標よりも「1日でこの参考書の問題を○問解く」といった目標の方がダラけずに勉強を続けやすかったです。
僕はこの方法で勉強計画まで落とし込みましたが方法は色々あると思います。
他の人の記事も参考にしながら自分が納得する方法を見つけてください。
これから編入を考えている方頑張ってください。
編入体験記をまとめました。
euglena1215.hatenablog.jp
MacBook ProでCUDAのGPUモードが使えたり使えなかったりする話
MacBook ProでTheanoのGPUモードを使うために設定を済ませてimportするとエラーが発生。
GPUモードが使えない...??
>>> import theano WARNING (theano.sandbox.cuda): CUDA is installed, but device gpu is not available (error: Unable to get the number of gpus available: CUDA driver version is insufficient for CUDA runtime version)
自分のmacのグラフィックスを確認すると
NVIDIA製ではないので使えないのは当たり前でした。
チップセットのモデル: Intel Iris Graphics 6100 種類: GPU バス: 内蔵 VRAM(ダイナミック、最大): 1536 MB 製造元: Intel (0x8086)
でも調べているとiMacではCUDAのGPUモードが使えている人はいる様子。
同じApple製品で違うメーカーのGPUが載ってるってこと?
なんでだろう。
さらに調べているとこんな記事が
MacBook Pro現行モデルの技術仕様を見ると最上位機種のグラフィックスチップはIntel Iris Pro GraphicsとAMD Radeon R9 M370Xとなっており、NVIDIA GeForceは搭載されていません。
ですが、MacBook Pro (15-inch, Mid 2012) - 技術仕様を見ると、私が持っているMacBook Proは15インチ2.6GHzモデルなのでNVIDIA GeForce GT 650M、1GB GDDR5メモリが搭載されています。
GeForce GT 650M | NVIDIAには「プログラミング環境」の行に「CUDA」とあるのでCUDAが使えるようです。
引用: GeForce搭載の旧モデルMacBook ProでCUDAをセットアップする手順のメモ · hnakamur's blog at github
昔のMacBook ProではGeForceを選択できたみたい。
というわけでGeForceが載っているものと載っていないものを2011~2015年で調べてみました。
GeForceが載っている
GeForceが載ってない
- Retina, 15-inch, Mid 2015
- Retina, 13-inch, Early 2015
- Retina, 13-inch, Mid 2014
- Retina, 13-inch, Late 2013
- Retina, 13-inch, Early 2013
- Retina, 13-inch, Late 2012
- 13-inch, Mid 2012
- 17-inch, Late 2011
- 13-inch, Late 2011
- 15-inch, Late 2011
- 17-inch, Early 2011
- 15-inch, Early 2011
- 13-inch, Early 2011
まとめ
MacBook ProでCUDAのGPUモードを使うなら2012~2014年モデルの15インチを買うべし。
その他のApple製品の技術仕様はこちらからどうぞ
アップル – サポート - 技術仕様
graphvizを使って木構造を可視化する関数を作ってみた
卒業研究で木構造を可視化させる必要があったので関数を作ってみました。
木構造・ノードクラスの属性は次のようになります。
Tree クラス
属性名 | 型,クラス | 説明 |
---|---|---|
root | Node | Treeの根ノード |
Node クラス
属性名 | 型,クラス | 説明 |
---|---|---|
num | int | ノード番号 |
child | Node | 子ノード |
brother | Node | 兄弟ノード(木構造で考えると右側のノード) |
parent | Node | 親ノード |
ソースコード
現在研究している自己生成ニューラル木に菖蒲の花のデータセットを学習させたものを可視化させるとこのようになりました。
関数を少し修正して花の種類で色分けするとこのようになりました。
木構造が可視化できると色々捗りますよね。
参考にさせていただきました
PythonからGraphvizを使う - Leaaaaaaaaaaaaarning
画像処理についてあれこれ: Graphvizでノードの塗りつぶし色を指定する
Railsでセキュリティのことを考えずに作ったwebアプリは脆弱性があるのか調べてみた(脆弱性検証編)
この続きです。 euglena1215.hatenablog.jp
検証方法が全く網羅的ではないということを踏まえた上でお読みください。
検証
XSS(クロスサイトスクリプティング)
方法
投稿フォームに以下のリンクに載っているXSSの例を片っ端から入力してみた。
XSS Filter Evasion Cheat Sheet - OWASP
結果
脆弱性は見つからなかった。
理由
rails3からデフォルトで文字列を出力するときにはエスケープされるようになっているため基本的にはXSSは通らない。
なので文字列をエスケープさせたくない場合には
<%= 文字列.html_safe %>
<%= raw 文字列 %>
<%== 文字列 %>
のどれかを使う。
この記事によると <%==
を使うのがベストらしい。
SQLインジェクション
方法
検索フォームに ');--
と入力する
結果
SELECT "users"."id" FROM "users" WHERE (username LIKE '%');--%’)
というクエリが生成され、userの全件取得ができてしまう。ということは…
方法
検索フォームに’ AND password LIKE ‘password’);—
と入力する
結果
SELECT "users"."id" FROM "users" WHERE (username LIKE '%’ AND password LIKE ‘password’);—%')
というクエリが発行されてpasswordが’password’のユーザが取得できる。
という風に利用者が任意のクエリを発行することができる。これは危ない。
※今回使用したDeviseは暗号化したパスワードをDBに保存しているのですぐにパスワードがバレるというわけではありません
普通に利用しているとユーザがアクセスできないデータにアクセスされる可能性がある。
対策
where("username LIKE '%"+username+"%’”)
を
where("username LIKE ?","%#{username}%")
に変更する。
以下のように修正する。
# before # app/models/post.rb scope :by_body_like, ->(body){ where("body LIKE '%"+body+"%'") } # app/models/user.rb scope :by_username_like, ->(username){ where("username LIKE '%"+username+"%'") }
# after # app/models/post.rb scope :by_body_like, ->(body){ where("body LIKE :body ESCAPE '\\'", { body: "%#{sanitize_sql_like(body)}%"}) } # app/models/user.rb scope :by_username_like, ->(username){ where("username LIKE :username ESCAPE '\\'", { username: "%#{sanitize_sql_like(username)}%"}) }
検索でLIKE演算子のワイルドカードである_や%が使われてもいいようにエスケープする必要がある。
Rails 4.2.1以降にはsanitize_sql_likeメソッドが実装されているのでそれを使う。
sanitize_sql_like (ActiveRecord::Sanitization::ClassMethods) - APIdock
?を使うとSQLインジェクションを防ぐことができるということは分かったものの、なぜこれでSQLインジェクションが防げるのかよく分からなかった。
なので今度Active recordを読み解いていこうと思う。
Active record whereメソッド :
rails/query_methods.rb at 0399b71dab8b270b4e40b2aff99194a8b8f2596c · rails/rails · GitHub
これはActive recordのwhereメソッドの仕様ではなくSQLのプレースホルダという機能だった。
プレースホルダとはパラメータを後で記述できるようにすることによって、SQLの構文を決定した後にバインドできるのでパラメータがリテラルの外にはみ出す現象が起こらなくなりSQLインジェクションが起こらなくなる機能だ。
普段SQL文を書いていないことが露呈してしまいました...基礎技術を勉強し直そうと思います。用途にもよるが、%や_のエスケープをしていないところが気になった / DROP "posts"はDROP TABLE "posts"では? / “Railsでセキュリティのことを考えずに作ったwebアプリは脆弱性があるのか調べて…” https://t.co/90vKBdLT5M
— 徳丸 浩 (@ockeghem) September 24, 2016
ご指摘ありがとうこざいます!
?を使うSQL文は色々なところで見たことはありましたがプレースホルダという名前があったのは知りませんでした。@ockeghem ?を使ったプレースホルダの対策をして、なぜこれでSQLインジェクションが防げるのかわからなかった、の時点でどうなんだろうか、、、と思ったけど、まだ学生なんですね。
— Kaneko Yukari (@yukapon_ririka) September 24, 2016
ご指摘のおかげでgoogle先生に聞くことができました。ありがとうございます!
方法
検索フォームに ‘); DROP “posts”;—
'); DROP TABLE “posts”;--
と入力する
SQLインジェクションが通る状態で複文はどうなるのか試してみた。
結果
SELECT "users"."id" FROM "users" WHERE (username LIKE '%‘); DROP “posts”;--%')
SELECT "users"."id" FROM "users" WHERE (username LIKE '%'); DROP TABLE “posts”;--%')
というクエリが発行されているもののpostsテーブルは削除されていなかった。
なので複文はサポートされてないみたい。(今回はSQLite3での実験だったので他のDBでどうなるかは試していません)
まとめ
- やはり自分の書いたコードに穴があった。
- XSSやSQLインジェクションのチートシートの存在を知ったことが一番の収穫だった。
- マサカリを投げて間違いを指摘してくれる人がいる。アウトプットの重要性を改めて実感した。
参考にさせていただきました
SQL Injection Cheat Sheet
Railsで検索をSQLで簡単に実装する方法 - Qiita
Railsのセキュリティ対策で調べた事 - Qiita
XSS Filter Evasion Cheat Sheet - OWASP
html_safe、raw、「<%==」の比較 - Qiita
https://www.ipa.go.jp/files/000017320.pdf
rails - クエリの基本 - そういうことだったんですね
Railsでセキュリティのことを考えずに作ったwebアプリは脆弱性があるのか調べてみた(webアプリ作成編)
こんにちは。僕はこのたびセキュリティキャンプ九州2016に行ってきました。
一言感想を言わせていただくと大変面白かったです。
ただ一つ不安になったことがあります。それはwebアプリの脆弱性です。
キャンプ中にwebアプリの脆弱性を見つけてみようという講座があり、脆弱性があるwebアプリを公開してみんなで脆弱性を探すというものだったのですが、30分もたたないうちに実装されてないはずの画像アップロード機能が追加されたりページを開いた瞬間音声が流れ出す迷惑サイトに変化していたりと蜂の巣状態になっていました。
そこで今回は普段使っているRuby on Railsで作ったwebアプリに脆弱性が存在するのかを確かめてみようと思います。
仕様
セキュリティキャンプ九州で使用した脆弱SNSの機能は次のようなものでした。
- ログイン機能(登録・編集)
- ログインすると掲示板への投稿・削除ができる
- 投稿内容の一覧
- 投稿とユーザの検索機能
DB
- 投稿テーブル(内容/投稿者/日時)
- ユーザテーブル(ユーザ名/パスワード/URL)
これと同じような機能のwebアプリをRailsでできるだけコードを書かずに作ってみたいと思います。
Railsではログイン機能はコマンドだけでは生成できないので超有名なユーザー管理のgemであるDeviseを使いました。
環境
Rails 4.2.4
Devise 4.1.1
ログイン機能
まずはrailsアプリを生成。 以下のコマンドを実行します。
% rails new testApp create create README.rdoc create Rakefile create config.ru create .gitignore create Gemfile create app create app/assets/javascripts/application.js create app/assets/stylesheets/application.css create app/controllers/application_controller.rb create app/helpers/application_helper.rb create app/views/layouts/application.html.erb — 略 — create vendor/assets/stylesheets/.keep run bundle install Fetching gem metadata from https://rubygems.org/ Fetching version metadata from https://rubygems.org/ Fetching dependency metadata from https://rubygems.org/ Resolving dependencies...... Using rake 11.2.2 Using i18n 0.7.0 Using json 1.8.3 — 略 —
Gemfileに以下を追記。
# Gemfile
gem ‘devise’
bundle installを実行。
% bundle install
rails generateでdeviseをインストールします。
これでユーザー管理を行うための準備が完了します。
% bundle exec rails generate devise:install Running via Spring preloader in process 4640 create config/initializers/devise.rb create config/locales/devise.en.yml =============================================================================== Some setup you must do manually if you haven't yet: 1. Ensure you have defined default url options in your environments files. Here is an example of default_url_options appropriate for a development environment in config/environments/development.rb: config.action_mailer.default_url_options = { host: 'localhost', port: 3000 } In production, :host should be set to the actual host of your application. 2. Ensure you have defined root_url to *something* in your config/routes.rb. For example: root to: "home#index" 3. Ensure you have flash messages in app/views/layouts/application.html.erb. For example: <p class="notice"><%= notice %></p> <p class="alert"><%= alert %></p> 4. If you are deploying on Heroku with Rails 3.2 only, you may want to set: config.assets.initialize_on_precompile = false On config/application.rb forcing your application to not access the DB or load models when precompiling your assets. 5. You can copy Devise views (for customization) to your app by running: rails g devise:views ===============================================================================
言われた通りに設定していきます。
development.rbに追記します。
# config/enviroments/development.rb config.action_mailer.default_url_options = { :host => 'localhost:3000' } config.action_mailer.delivery_method = :smtp config.action_mailer.smtp_settings = { :address => 'smtp.gmail.com', :port => 587, :authentication => :plain, :user_name => 'メールアドレス', :password => 'パスワード' }
root用のcontrollerとviewをコマンドで生成します。
% bundle exec rails generate controller home index Running via Spring preloader in process 7935 create app/controllers/home_controller.rb route get 'home/index' invoke erb create app/views/home create app/views/home/index.html.erb invoke test_unit create test/controllers/home_controller_test.rb invoke helper create app/helpers/home_helper.rb invoke test_unit invoke assets invoke coffee create app/assets/javascripts/home.coffee invoke scss create app/assets/stylesheets/home.scss
routes.rbに追記してroot_urlを設定します。
# config/routes.rb root to: "home#index"
home/index.html.erbにログインと登録用のURLを追加します。
<h1>Home#index</h1> <% if user_signed_in? %> Logged in as <strong><%= current_user.email %></strong>. <%= link_to "Settings", edit_user_registration_path, :class => "navbar-link" %> | <%= link_to "Logout", destroy_user_session_path, method: :delete, :class => "navbar-link" %> <% else %> <%= link_to "Sign up", new_user_registration_path, :class => 'navbar-link' %> | <%= link_to "Login", new_user_session_path, :class => 'navbar-link' %> <% end %> <p>Find me in app/views/home/index.html.erb</p>
ログイン情報を出力させるためにlayouts/application.html.erbに追加します。
<!DOCTYPE html> <html> <head> <title>TestApp</title> <%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %> <%= javascript_include_tag 'application', 'data-turbolinks-track' => true %> <%= csrf_meta_tags %> </head> <body> <% if notice %> <p class="alert alert-success"><%= notice %></p> <% end %> <% if alert %> <p class="alert alert-danger"><%= alert %></p> <% end %> <%= yield %> </body> </html>
次にユーザー用のmodelとmigrationファイルを生成します。
% bundle exec rails generate devise User Running via Spring preloader in process 4790 invoke active_record create db/migrate/(タイムスタンプ)_devise_create_users.rb create app/models/user.rb invoke test_unit create test/models/user_test.rb create test/fixtures/users.yml insert app/models/user.rb route devise_for :users
今回はtrackableとvalidatableは使わないので user.rbの一部をコメントアウトします。
# app/models/user.rb class User < ActiveRecord::Base # Include default devise modules. Others available are: # :confirmable, :lockable, :timeoutable and :omniauthable devise :database_authenticatable, :registrable, :recoverable, :rememberable # :trackable, # :validatable end
その修正に合わせて (タイムスタンプ)__devise_create_users.rbの一部もコメントアウトします。
# db/migrate/(タイムスタンプ)__devise_create_users.rb class DeviseCreateUsers < ActiveRecord::Migration def change create_table :users do |t| ## Database authenticatable t.string :email, null: false, default: "" t.string :encrypted_password, null: false, default: "" ## Recoverable t.string :reset_password_token t.datetime :reset_password_sent_at ## Rememberable t.datetime :remember_created_at ## Trackable # t.integer :sign_in_count, default: 0, null: false # t.datetime :current_sign_in_at # t.datetime :last_sign_in_at # t.string :current_sign_in_ip # t.string :last_sign_in_ip ## Confirmable # t.string :confirmation_token # t.datetime :confirmed_at # t.datetime :confirmation_sent_at # t.string :unconfirmed_email # Only if using reconfirmable ## Lockable # t.integer :failed_attempts, default: 0, null: false # Only if lock strategy is :failed_attempts # t.string :unlock_token # Only if unlock strategy is :email or :both # t.datetime :locked_at t.timestamps null: false end add_index :users, :email, unique: true add_index :users, :reset_password_token, unique: true # add_index :users, :confirmation_token, unique: true # add_index :users, :unlock_token, unique: true end end
DBを生成します。以下のコマンドを実行してください。
% bundle exec rake db:migrate == 20160920061425 DeviseCreateUsers: migrating ================================ -- create_table(:users) -> 0.0012s -- add_index(:users, :email, {:unique=>true}) -> 0.0007s -- add_index(:users, :reset_password_token, {:unique=>true}) -> 0.0008s == 20160920061425 DeviseCreateUsers: migrated (0.0029s) =======================
rails s
でサーバを起動させ、http://localhost:3000にアクセスするとこうなります。
Sign upから登録をすませると
これでdeviseのデフォルトのログイン機能は完了ですが、現在ではユーザ情報がメールアドレスとパスワードしか存在しません。 なのでユーザ情報にユーザ名を追加し、ユーザ名とパスワードでログインできるようにします。
以下のコマンドを実行します。
% rails generate migration add_username_to_users username:string Running via Spring preloader in process 9875 invoke active_record create db/migrate/(タイムスタンプ)_add_username_to_users.rb
usernameはログインで使われるためuniqueを与えます。 (タイムスタンプ)_add_username_to_users.rbに追記します。
#db/migrate/(タイムスタンプ)_add_username_to_users.rb class AddUsernameToUsers < ActiveRecord::Migration def change add_column :users, :username, :string add_index :users, :username, unique: true end end
以下のコマンドを実行します。
% bundle exec rake db:migrate == 20160920080558 AddUsernameToUsers: migrating =============================== -- add_column(:users, :username, :string) -> 0.0018s -- add_index(:users, :username, {:unique=>true}) -> 0.0025s == 20160920080558 AddUsernameToUsers: migrated (0.0045s) ======================
user.rbにvalidationを追加します。
validates :username, presence: true, uniqueness: true
ログインで使うデータを変更したため、viewをカスタマイズします。
以下のコマンドを実行します。
% rails g devise:views Running via Spring preloader in process 11412 invoke Devise::Generators::SharedViewsGenerator create app/views/devise/shared create app/views/devise/shared/_links.html.erb invoke form_for create app/views/devise/confirmations create app/views/devise/confirmations/new.html.erb create app/views/devise/passwords create app/views/devise/passwords/edit.html.erb create app/views/devise/passwords/new.html.erb create app/views/devise/registrations create app/views/devise/registrations/edit.html.erb create app/views/devise/registrations/new.html.erb create app/views/devise/sessions create app/views/devise/sessions/new.html.erb create app/views/devise/unlocks create app/views/devise/unlocks/new.html.erb invoke erb create app/views/devise/mailer create app/views/devise/mailer/confirmation_instructions.html.erb create app/views/devise/mailer/password_change.html.erb create app/views/devise/mailer/reset_password_instructions.html.erb create app/views/devise/mailer/unlock_instructions.html.erb
以下のviewの current_user.email
を current_user.username
に変更します。
app/views/home/index.html.erb
以下のviewの :email
を :username
に変更し、 email_field
を text_field
に変更します。
app/views/devise/sessions/new.html.erb
以下のviewにusername用のフォームを追加します。
app/views/devise/registrations/new.html.erb
app/views/devise/registrations/edit.html.erb
先ほど登録したuserはusernameにデータが入っていないので一度DBをリセットします。
以下のコマンドを実行します。
% bundle exec rake db:migrate:reset == 20160920061425 DeviseCreateUsers: migrating ================================ -- create_table(:users) -> 0.0013s -- add_index(:users, :email, {:unique=>true}) -> 0.0007s -- add_index(:users, :reset_password_token, {:unique=>true}) -> 0.0007s == 20160920061425 DeviseCreateUsers: migrated (0.0029s) ======================= == 20160920080558 AddUsernameToUsers: migrating =============================== -- add_column(:users, :username, :string) -> 0.0006s -- add_index(:users, :username, {:unique=>true}) -> 0.0011s == 20160920080558 AddUsernameToUsers: migrated (0.0018s) ======================
rails s
でサーバを起動し http://localhost:3000/users/sign_upにアクセスすると
usernameも登録できるようになっていて
usernameとpasswordでログインできるようになっています。
次にURLカラムを追加します。 以下のコマンドを実行します。
% rails generate migration add_url_to_users url:string Running via Spring preloader in process 12882 invoke active_record create db/migrate/20160920090632_add_url_to_users.rb
先ほどと同様に以下のviewにurl用のフォームを追加します。
app/views/devise/registrations/new.html.erb
app/views/devise/registrations/edit.html.erb
先ほど登録したuserはurlを持っていないので一度DBをリセットします。
以下のコマンドを実行します。
% bundle exec rake db:migrate:reset == 20160920061425 DeviseCreateUsers: migrating ================================ -- create_table(:users) -> 0.0012s -- add_index(:users, :email, {:unique=>true}) -> 0.0006s -- add_index(:users, :reset_password_token, {:unique=>true}) -> 0.0007s == 20160920061425 DeviseCreateUsers: migrated (0.0028s) ======================= == 20160920080558 AddUsernameToUsers: migrating =============================== -- add_column(:users, :username, :string) -> 0.0006s -- add_index(:users, :username, {:unique=>true}) -> 0.0010s == 20160920080558 AddUsernameToUsers: migrated (0.0017s) ====================== == 20160920090632 AddUrlToUsers: migrating ==================================== -- add_column(:users, :url, :string) -> 0.0005s == 20160920090632 AddUrlToUsers: migrated (0.0006s) ===========================
これでログイン機能はOKです。
投稿機能
以下のコマンドを実行します。
% rails g scaffold post body:text user:references Running via Spring preloader in process 13853 invoke active_record create db/migrate/20160920092635_create_posts.rb create app/models/post.rb invoke test_unit create test/models/post_test.rb create test/fixtures/posts.yml invoke resource_route route resources :posts invoke scaffold_controller create app/controllers/posts_controller.rb invoke erb create app/views/posts create app/views/posts/index.html.erb create app/views/posts/edit.html.erb create app/views/posts/show.html.erb create app/views/posts/new.html.erb create app/views/posts/_form.html.erb invoke test_unit create test/controllers/posts_controller_test.rb invoke helper create app/helpers/posts_helper.rb invoke test_unit invoke jbuilder create app/views/posts/index.json.jbuilder create app/views/posts/show.json.jbuilder create app/views/posts/_post.json.jbuilder invoke assets invoke coffee create app/assets/javascripts/posts.coffee invoke scss create app/assets/stylesheets/posts.scss invoke scss create app/assets/stylesheets/scaffolds.scss
ユーザと投稿は1対多の関係なのでrelationをuser.rbに追記します。
また、ユーザが削除されたときにそのユーザの投稿も削除してほしいのでdependent属性も設定します。
# app/models/user.rb has_many :posts, dependent: :destroy
以下のコマンドを実行します。
% bundle exec rake db:migrate == 20160920092635 CreatePosts: migrating ====================================== -- create_table(:posts) -> 0.0017s == 20160920092635 CreatePosts: migrated (0.0017s) =============================
投稿と投稿者が結びつくようにフォームを修正します。
# app/views/posts/_form.html.erb <div class="field"> <%= f.label :body %><br> <%= f.text_area :body %> <%= f.hidden_field :user_id, value: current_user.id %> </div>
まだ色々な微調整は済んでいませんが、基本的な投稿機能は完成です。 (書くのが面倒くさくなりました。誠に申し訳ございません。一番下にソースのリンクを貼っておくので参考にしてください)
検索機能
次に検索機能を実装します。 今後も使っていくようなアプリであれば検索機能用のgemであるransackを使うのですが、今回は簡単な検索のみなのでgemを使わずに実装しようと思います。
まずはscopeを実装します。post.rbとuser.rbに追記してください。
# app/models/post.rb scope :by_body_like, ->(body){ where("body LIKE '%"+body+"%'") } scope :by_users_id, ->(users_id){ where(user_id: users_id) }
# app/models/user.rb scope :by_username_like, ->(username){ where("username LIKE '%"+username+"%'") }
これで Post.by_body_like(‘hoge’)
で投稿内容に’home'が含まれる投稿を取得することができます。
また、 Post.by_users_id(User.by_username_like(‘foo’).pluck[:id])
でユーザー名に’foo’が含まれるユーザーの投稿を取得することができます。
注意!! この記述には脆弱性が存在します。詳しくは脆弱性検証編を書いてあります。
次は検索機能で用いるラジオボタン用の定数を設定します。 post.rbに追記します。
# app/models/post.rb POST_SEARCH = 1 USER_SEARCH = 2
この定数は Post::POST_SEARCH
と記述すると使えます。
次にcontrollerを記述します。
今回の検索機能はsearch_typeで検索する対象(postのbody,userのusername)を決定し、searchが検索ワードという実装にしました。
posts_controller.rbのindexメソッドを次のように変更します。
# app/controllers/posts_controller.rb def index if params[:search_type] == Post::POST_SEARCH.to_s @posts = Post.by_body_like(params[:search]) elsif params[:search_type] == Post::USER_SEARCH.to_s users_id = User.by_username_like(params[:search]).pluck(:id) @posts = Post.by_users_id(users_id) else @posts = Post.all end end
Post::POST_SEARCH.to_sとなっているのはGETメソッドから得られるパラメータは数値も文字列に変換されているためです。
次にviewを記述します。
posts/index.html.erbに追記します。
# app/views/posts/index.html.erb <%= form_tag({controller: '/posts',action: 'index'}, method: 'get', class: 'search_form', style: 'padding: 20px;') do %> <div class="field"> <%= radio_button_tag :search_type, Post::POST_SEARCH %> <%= label_tag :post %> <%= radio_button_tag :search_type, Post::USER_SEARCH %> <%= label_tag :user %> </div> <div class="field"> <%= text_field_tag :search,'' %> </div> <div class="actions"> <%= submit_tag '検索' %> </div> <% end %>
これで検索機能は完成です。
ここから
- 新規投稿機能をトップページに持ってくる
- bootstrapをCDNで読み込ませる
- UIの調整
- 使ってないメソッド、ルーティング、ビューを削除
- タイムゾーンを東京に変更
などなどを書き換えると脆弱かもしれないSNSの完成です。
次は脆弱性検証編です。 euglena1215.hatenablog.jp
ソースはこちら GitHub - euglena1215/testApp
【大学編入】専門科目の参考書と対策
参考書
プログラミング
C言語によるアルゴリズムとデータ構造
- 作者: 柴田望洋,辻亮介
- 出版社/メーカー: ソフトバンククリエイティブ
- 発売日: 2011/09/01
- メディア: 単行本
- 購入: 3人 クリック: 37回
- この商品を含むブログ (7件) を見る
色々な編入体験記に載っているのアルゴリズムの本です。
基礎固めに役立ちました。
1周
プログラミングコンテストチャレンジブック
プログラミングコンテストチャレンジブック [第2版] ?問題解決のアルゴリズム活用力とコーディングテクニックを鍛える?
- 作者: 秋葉拓哉,岩田陽一,北川宜稔
- 出版社/メーカー: マイナビ出版
- 発売日: 2012/01/28
- メディア: Kindle版
- この商品を含むブログを見る
通称アリ本です。「C言語によるアルゴリズムとデータ構造」にはグラフ理論や有名なアルゴリズム(エラトステネスの篩、ダイクストラなど)が載ってなかったのでそれらを実装方法を主眼において読みました。ソースはC++ですがCが分かれば読めると思います。
アルゴリズムとデータ構造(青)
アルゴリズムとデータ構造<改訂 C言語版> (電気工学入門シリーズ)
- 作者: 平田富夫
- 出版社/メーカー: 森北出版
- 発売日: 2002/09/01
- メディア: 単行本(ソフトカバー)
- 購入: 1人 クリック: 2回
- この商品を含むブログ (2件) を見る
アルゴリズム図鑑
基本的な探索、ソートの名前を聞いたらすぐに挙動をイメージできるように暇なときはこのアプリを使って確認してました。アニメーションを使って分かりやすく説明してくれてるのでとてもイメージしやすかったです。 300円課金すると全てのアルゴリズムが解放されるのですが課金する価値は十分にありました。論理回路
論理回路入門
- 作者: 浜辺隆二
- 出版社/メーカー: 森北出版
- 発売日: 2008/12/16
- メディア: 単行本(ソフトカバー)
- 購入: 1人 クリック: 2回
- この商品を含むブログ (1件) を見る
情報理論
情報理論
- 作者: 三木成彦,吉川英機
- 出版社/メーカー: コロナ社
- 発売日: 1999/12
- メディア: 単行本
- クリック: 51回
- この商品を含むブログ (2件) を見る
対策
対策を始める前の僕は「クイックソート、なんか早そう」「グラフ理論なにそれおいしいの?」「計算量ってなんですか」「篩が読めねえ」というような状況で初めて筑波の過去問を解いたときは解き終わるのに2時間半かかり(筑波は数学と専門合わせて2時間)もう死んだと思ってました。
どうせ他の人も同じようなもんだろと思い編入体験記を漁ると「筑波の専門は簡単」「競プロやってたらいける」「直前の1週間に過去問解きまくったらいけた」という記事ばかりで絶望しかありませんでした。
なので他の人より多めに勉強しました。
論理回路や情報理論は勉強すれば点が取れると思います。
対策の方針はこんな感じでした。
プログラミング
参考書をばーっと読む+写経する
過去問解く
解けない&時間がかかり過ぎるところを中心に参考書を読む
もう一度過去問を解く
を繰り返す
暇なときはアルゴリズム図鑑で復習する
論理回路
論理回路をばーっと目を通す
演習問題を解く
過去問を解く
もう一度過去問を解く
を繰り返す
情報理論
情報理論にバーっと目を通す
過去問を解く
もう一度過去問を解く
を繰り返す
僕の勉強のおおまかな流れは
プログラミング
3月下旬~4月上旬
アルゴリズムとデータ構造 読む 1周
4月上旬~試験日
過去問を繰り返し解く、アリ本、アルゴリズムを必要に応じて読む
論理回路
4月中旬~5月中旬
毎週金曜 3時間前後
5月中旬~試験日
過去問を解く
情報理論
6月~試験日
気が向いたときに過去問を解く
分からなかったら情報理論を読む
下の記録を見てもらえれば詳細の勉強記録が載っています。
http://studyplus.jp/users/teppest
編入体験記をまとめました。
euglena1215.hatenablog.jp