RubyMotion ã®ãã¹ããç¶ç¶çã¤ã³ãã°ã¬ã¼ã·ã§ã³
æ¨æ¥ã¯ RubyMotion ã®ããããä¼ã§ããã
å
æ¥ã® RubyMotion Kaigi 2013 㧠å®è·µRubyMotion ã¨ããé¡ç®ã§çºè¡¨ããã®ã ãã©ããã¹ãã«ã¤ãã¦ã¯printãããã°ä¸çã ããã®ã¯ã½ã ã·ãã¨ãè¨ã£ã¦ããªãé©å½ã«æ¸ã¾ãã¦ãã¾ã£ããã®ã§ãããããä¼ã§ã¯ãã¹ãå¨ãã«æãã¤ãããã¨æããããããã° Travis CI ã RubyMotion ã«å¯¾å¿ãã¦ãã®ãæãåºã RubyMotion ã®ãã¹ãã Travis CI ã§åãã®ãæ¤è¨¼ããã
ããæéåããã¨æã£ã Travis CI å¨ãã¯ã¨ã£ã¦ãç°¡åã§ã.travis.yml ã«
language: objective-c
ã¨æ¸ãã ãã§ãã£ããåãã¦ãã¾ã£ãã
ã¨ãããã㧠RubyMotion ã¢ããªã®ç¶ç¶çã¤ã³ãã°ã¬ã¼ã·ã§ã³ã¯ .travis.yml ãä¸è¡æ¸ãã°å®äºã§ããçµãã・・・ãããã¾ã£ããããã°è¨äºã¨ãã¦æç«ããªãã®ã§ãRubyMotion ã®ãã¹ãå¨ãã«ã¤ãã¦ã¡ããã¡ããæ¸ãã¦ããããã¡ãªã¿ã«æ¤è¨¼ã«ä½¿ã£ãã¬ãã¸ããªã¯ä»¥ä¸ã«ããã¾ãã
ããã¥ã¡ã³ã
ãã®è¾ºèªãã°åºæ¬çãªã¨ãã㯠OK ããã¨ããããã¥ã¡ã³ãã以ä¸ã®3㤠+ 1ã
- http://tutorial.rubymotion.jp/8-testing/
- http://rubymotion.jp/RubyMotionDocumentation/articles/testing/
- https://speakerdeck.com/pchw/rubymotion-1-dot-15dezhui-jia-saretatestzhou-rifalsehua
- http://rubymotion-wrappers.com/
å ¬å¼ã®ããã¥ã¡ã³ãã«ã¯ãã¹ãã®åããæ¹ã¯æ¸ãã¦ãã£ã¦ããã©ã®ããã«ãã¹ããæ¸ãã°è¯ããã¯æ¸ãã¦ãªãã1æ¬ç®ã® URL 㯠RubyMotion ã¨ããã°ãã®äºº Clay Allsopp ããã®ãã¥ã¼ããªã¢ã«ææ¸ããwatson ã»ãã翻訳ãããã®ã2æ¬ç®ã®ã¯ããã®å ¬å¼ææ¸ã®æ¥æ¬èªè¨³ãããã watson ããã
3æ¬ç®ã®ã¯ã¡ããã©ä¸å¹´åã®ç¬¬ä¸åRubyMotion åå¼·ä¼ã§ã® pchw ããã®çºè¡¨ã¹ã©ã¤ããview ã controller ã®ãã¹ãã®æ¸ãæ¹ã«ã¤ãã¦è§£èª¬ããã¦ãããããããããä¸å¹´çµã£ã¦ãã¾ãã¾ããã
4æ¬ç®ã¯ãæå㪠RubyMotion ã®ã©ããã©ã¤ãã©ãªã®ãªã³ã¯éã以ä¸ã§ç´¹ä»ãããã¹ãç³»ã®ã©ã¤ãã©ãªã¯ã ããããã®ãªã³ã¯éçµç±ã§éãã¦ãã¾ããã
ãã¹ãã®å§ãæ¹
RubyMotion ã®ãã¹ãã¯
% rake spec
ããã§åãã¾ãããã¹ãã¯
describe "Application 'Hello'" do before do @app = UIApplication.sharedApplication end it "has no window under testing" do @app.windows.size.should == 1 end end
ãããªæãã§ä¾ã«ãã£ã¦ RSpec 風ã§ããã風ãã¨ããã®ã¯ããã¯å®éã«ã¯ãã¥ã¢ RSpec ã§ã¯ãªã Bacon ã¨ãã RSpec ã®ã¯ãã¼ã³ã ããã§ãã
ãã¹ããè¡ãå ´å app_delegate.rb ã«
class AppDelegate def application(application, didFinishLaunchingWithOptions:launchOptions) return true if RUBYMOTION_ENV == 'test' # ãã
ã¨ç°å¢å¤æ°ã test ã ã£ããããã« return ããããã«ãã¦ããã¨è¯ãããã¹ãã¯ã¢ããªãå®å
¨ã«ãã«ããã¦è¡ããã®ã§ã¯ãªãã®ã§ããã¹ãã®ã¨ãã¯ãããããªããã¨ãããã¨ã§ãããããã§ãã«ãã®æéãç縮ããã¾ãããã¹ãã«ã¯å½±é¿ã¯ããã¾ããã
ãã¹ãã«è²ãä»ãã
RSpec ã 㨠.rspec ã« --color ã¨ãã§è²ãä»ãã¾ãããBacon ãªã®ã§ãã®ãªãã·ã§ã³ã¯å¹ããªããrm-redgreen ã使ãã
- rm-redgreen ã git clone ãã
- spec/00-redgreen.rb ã spec ã«ã³ãã¼
- app/*.rb ã app ã«ã³ãã¼
å¾ãrake spec ããã¨è²ãã¤ãããã¡ã¤ã«ãè²ã ã³ãã¼ãã¦ä½¿ãã¨ããã®ãã¡ãã£ã¨ã¢ã¬ã§ããã
ã¤ãã§ã« 00-redgreen.rb ã® style ãªãã·ã§ã³ã
style = :full
ã¨ããã¨ãåºåãè¯ãæãã«ãªãã¾ãã
motion-guard
RSpec ã®ãã¹ããªã guard ã使ãããããã¡ã¤ã«ãæ´æ°ãããèªå㧠spec æµãããã§ãããã¢ã¬ã§ããä¾ã«ãã£ã¦é常㮠guard-rspec ã§ã¯ãã¡ãªã®ã§ãguard-motion ã使ãã¾ãã
% guard init motion % guard start
ãã㧠RubyMotion é¢é£ã®ãã¡ã¤ã«ã guard ãç£è¦ãã¦æ´æ°ãããã¨é¢é£ãããã¹ããèµ°ãããã«ãªãã¾ããç´ ã® guard åæ§ããªãã¹ãæ´æ°ããç®æã«é¢ãããã¹ãã ããå®è¡ããããè¯ãã«è¨ãã£ã¦ããã¾ãã
RubyMotion ã®ãã¹ãã¯ãã¹ãæ¯ã«ã¢ããªã®ãã«ããèµ°ã£ã¦å°ãæéããããã®ã§ãæåã§ãããã« guard ã§åæã«æµããããã«ãã¦ããã¨ã ãã¶ã¹ãã¬ã¹ã軽æ¸ããã¦è¯ãæãã§ãã
Travis CI
Travis CI ã§ã®ãã¹ãã®ä»æ¹ã¯åé ã§æ¸ããéãã.travis.yml ãç¨æã㦠Github ã« push ããã ãã
ãã®ã¨ã BubbleWrap ã sugarcube ãªã©ã®ãµã¼ããã¼ãã£ã©ã¤ãã©ãªã¯ã©ããªãããTravis CI ã¯ãã¹ãåã®ãã«ãã«éãã¦æ¯å bundle ãå®è¡ããã®ã§ãBundler çµç±ã§å ¥ãã¦ããã°ã¡ããã¨ã¤ã³ã¹ãã¼ã«ããã«ãå«ãããã¹ãããã¾ããæ¤è¨¼æ¸ã¿ã
Cocoapods ã«é¢ãã¦ã¯æ¤è¨¼ãã¦ãªãã§ããããããã rake æã« pod ãã¡ã¤ã«ãåã£ã¦ããä½ããªã®ã§ãããåé¡ãªãããããããããã° .travis.yml ãããã£ã¦äºåã« pod setup ããå¿ è¦ã¯ãããã?
ã³ã³ããã¼ã©ã®ãã¹ã
ãã¹ãã®å®è¡ã®ä»æ¹ã¯åãã£ãããããå®éã«ãã¹ãã¯ã©ãæ¸ãã°ããã®? ã¨ããã¨ããã以ä¸ç°¡åã«ãç´¹ä»ã
ã¾ãã¯ã³ã³ããã¼ã©ããã¥ã¼ã®ãã¹ãã§ãã
class MainViewController < UIViewController def viewDidLoad super margin = 20 self.view.backgroundColor = UIColor.whiteColor @button = UIButton.rounded_rect.tap do |b| b.setTitle('Hello, RubyMotion!', forState:UIControlStateNormal) b.accessibilityLabel = "Hello Button" b.frame = [[margin, 100], [view.frame.size.width - margin * 2, 42]] b.on(:touch) do |event| @was_tapped = true end end view << @button end end
ãããªã³ã³ããã¼ã©ã«å¯¾ãããã¹ãã
describe MainViewController do # MainViewController ããã¹ã対象㫠â ã¤ã³ã¹ã¿ã³ã¹åããã tests MainViewController it "should not be nil" do # viewã¡ã½ããã使ã£ã¦ accessibilityLabel = "Hello Button" ã® view ãè¦ã¤ãã view("Hello Button").should.not.be.nil end it "has right title" do view("Hello Button").currentTitle.should.equal "Hello, RubyMotion!" end it "changes instance variable when button is tapped" do # ãã¿ã³ãå®éã«ã¿ãã tap "Hello Button" controller.instance_variable_get("@was_tapped").should == true end end
ãããªé¢¨ã«æ¸ãã¾ãã
ãã¤ã³ãã«ãªãã¨ããã¯ã³ã¡ã³ãã«ããéãã§ããããããããã®ã¯ UIView ã® accessibilityLabel ã®å¤ã§ãå®éã«ã¤ã³ã¹ã¿ã³ã¹åããã¦ããä»»æã®ãµããã¥ã¼ãæ¤ç´¢ã§ããã¨ããã§ãããaccessibilityLabel ã¯ãã®åã®éãã¢ã¯ã»ã·ããªãã£ã®ããã®ã©ãã«ã§ãé³å£°èªã¿ä¸ããªã©ã«ä½¿ãããããã¹ãã§ãããRubyMotion ã§ã¯ããããã¹ãã«ä½¿ã£ã¦ã¾ããã¨ã¯ãããããã¥ã¡ã³ãã«ãããéããæ¬æ¥ã®ç¨éã¯ããã¹ãèªã¿ä¸ããªã®ã§ããã«ä½è¨ãªãã®ã¯æ¸ããªãæ¹ãè¯ãã§ãããã
ãããããtapãRubyMotion ã®ãã¹ããã¬ã¼ã ã¯ã¼ã¯ã«ã¯ tapãflickãpinch_openãdrag ⦠ãªã©å®éã®ã¢ããªä¸ã®æä½ãã¨ãã¥ã¬ã¼ãããã¡ã½ãããç¨æããã¦ãã¾ãããããå¼ã¶ã¨ãã¢ããªä¸ã§å®éã«ãã¿ã³ãæ¼ãããããã®è¾ºã㯠iOS SDK ã® UIAutomation ã®æ©è½ã RubyMotion ã§ä½¿ããããã«ãããã¨ãããã®ã ããã§ãã
ãã¾ãèªåã¯è©³ãããªãã§ãã iOS ã®å ´å㯠UIAutomation ã使ã£ããã¹ã㯠JavaScript ã§æ¸ãããã§ãããRubyMotion ãªã æ®æ®µéã ruby ã§æ¸ãã¦å¬ãããã¨ã®ãã¨ã
ã¨ã¾ãããããªå ·å㧠UI ã®ãã¹ããæ¸ãã¾ãããUI ã®ãã¹ããã©ãã¾ã§æ¸ãã¹ããã¨ããã®ã¯ä¾ã«ãã£ã¦æ£è§£ã®ãªãè°è«ã§ããããã®è¾ºã¯ããã§è«ããã®ã¯ããã¦ããã¾ãããããããã«ãã RubyMotion ã®ãã¹ããã¬ã¼ã ã¯ã¼ã¯ãªããUI ããä»»æã®å¦çããã¯ãã¦ããããã¹ããããªãã¦ãã¨ãç°¡åã§ãããã¨ãããã¨ãããã¯è¦ãã¦ããã¨ããã¨æãã¾ãã
ã¢ãã«ã®ãã¹ã
ã¢ãã«ã®ãã¹ããæ¸ãã¦ã¿ã¾ãããã
class User attr_accessor :name attr_accessor :email end
ã¨ãã Plain Old ãªã¢ãã«ã¯ã©ã¹ã«å¯¾ãããã¹ãã
describe User do before do @user = User.new end it "has appropriate attributes" do @user.should.respond_to :name @user.should.respond_to :email end end
ããæ¸ãã¾ãããä½ã®å¤å²ããªãã§ãããã³ã³ããã¼ã©ã®ãã¹ãæã«ä½¿ã£ã¦ãããã³ã³ããã¼ã©ã UI é¨åã«é¢ãããã®ã使ããªãã§ç´ ã§ãã¹ããæ¸ãã ãã
ããã§ã®ã³ã¼ãã¯ã¯ã©ã¹ããã¹ããå ¨ã RubyMotion ã«ä¾åãã¦ããªãã®ã§ãRubyMotion ããã«ãããã«ãã¹ããã¦ãè¯ãããã«ãè¦ãã¾ããããã㯠NG ã§ãããã®ãã¹ãããã¡ã㨠RubyMotion ã®ãã¹ãããã»ã¹ä¸ã§ãã¹ãããããªããªã RubyMotion ã®ã¯ã©ã¹ã¯ NSObject ã»ããObjective-C ã©ã³ã¿ã¤ã ã®ãªãã¸ã§ã¯ããç¶æ¿ãã¦ãã¦ããã¥ã¢ Ruby ã®ããã¨éããããRubyMotion å®è¡ç°å¢ã®ä¸ã§ãã¹ããã¦ããã®ã¯å¿ é ã§ãããã
ã¢ãã«ã®ãã¹ããããã®ã«ã¢ããªã®ãã«ããå¿ è¦ã ãªãã¦ãã¨ãã辺ãã¯å ã® app_delegate ã§ã®å·¥å¤«ã¨ guard-motion ã使ãã°ãã¾ãæ°ã«ãªããªãç¨åº¦ã®æéã§çµããããã«ã¯ãªãã¾ãã
ã¹ã¿ã/ã¢ãã¯
ã¹ã¿ããã¢ãã¯ã¯ motion-stump ããããè¯ãããã§ãã
@user.stub!(:hello, :return => "Hello, naoya") @user.hello.should.equal "Hello, naoya"
ãããªæãã§ã¹ã¿ãã§ãã¾ããã
HTTPãªã¯ã¨ã¹ãã®ã¹ã¿ã
HTTPãªã¯ã¨ã¹ãå°ç¨ã®ã¹ã¿ãã® webstub ãè¯ãæãã§ãã
class HttpClientViewController < UIViewController def viewDidLoad super view.backgroundColor = UIColor.whiteColor @button = UIButton.rounded_rect.tap do |b| b.accessibilityLabel = "Start HTTP" b.frame = [[20, 100], [view.frame.size.width - 20 * 2, 42]] b.on(:touch) do |event| BW::HTTP.get('http://www.example.com/') do |response| p response end end end view << @button end end
ãããããBubbleWrap 㧠HTTP ãªã¯ã¨ã¹ããè¡ã£ã¦ãã³ã³ããã¼ã©ãªãããã©ããã¹ãããããã¨ããã¨ãã«
describe HttpClientViewController do extend WebStub::SpecHelpers tests HttpClientViewController it "has a button" do view("Start HTTP").should.not.be.nil end it "sends a http request when button is tapped" do # webstub 㧠HTTP ãã¹ã¿ã stub = stub_request(:get, 'http://www.example.com/') tap "Start HTTP" stub.should.be.requested end end
㨠WebStub ã使ãã¨ãã£ããã¹ã¿ãã§ãã¦ãã¾ããWebStub ã§ã®ã¬ã¹ãã³ã¹ã¯è²ã ã«ã¹ã¿ãã¤ãºã§ããã®ã§ãéåæãã¹ããªããããããªãã«ãã¾ãåãããã§ãã
話ã¯é¸ãã¾ãããRubyMotion å°ç¨ã®ã©ã¤ãã©ãªãªã®ã« "webstub" ãªãã¦åå空é使ã£ã¡ãã£ã¦ãããã§ãããã・・・ãã¾ããããè¨ã£ãã sugarcube ã bubble-wrap ããªãã§ããã©ãruby ã¯ãã®è¾ºã«ãªï½¥ï½¥ï½¥å¯å¤§ã§ããã
awesome_print_motion
æå¾ã« tipsã
Ruby ã« Awesome Print ã¨ãããprint ãããã°ã®åºåããããè¯ãæãã«ããã©ã¤ãã©ãªãããã¾ãã awesome_print_motion ã¯ããã® RubyMotion çã
ãªãã ããã 㧠RubyMotion ã§ã print ãããã°ã¯å¤ç¨ããã®ã§ãå ¥ãã¦ããã¨æãã§ãããã
ã¾ã¨ã
ã¨ãããã㧠RubyMotion ã®ãã¹ãå¨ãã§ããã
調ã¹ã¦ã¿ã㨠guard ãããã CI ãåããããã¹ã¿ããã¢ãã¯ããããå ¨é¨ ruby ã§æ¸ããããã¹ããæ¸ããã¨ãã®ãã¨ã«ã¤ãã¦ã®çã¿ã¯ã»ã¨ãã©ãªããã ãªã¨æãã¾ãããã¢ããªã®ãã¹ãã¯é¢åã ã¨ããå å ¥è¦³ãããã¾ããããUIAutomation + RSpec ã§ã®ãã¹ãã¨ãæ¸ãã¦ã¿ãã¨ã©ãããã«æ¸ãã¦ãå®ã«æ¥½åã§ããããããããæ±ç¨ã©ã¤ãã©ãªãã¢ãã«å¨ãã®ãã¹ãã¯ãã£ããæ¸ãã¦è¡ããªãããªãã¨æã£ã次第ã§ãã