RubyMotionの期限が切れた

sudo motion update すると

Connecting to the server...
Your software license is expired, please visit http://account.rubymotion.com to renew.

というメッセージが出るようになりました。
 
 

更新方法

http://account.rubymotion.com
にアクセスして10,000円おさめるだけのようです。

f:id:rochefort:20130608144049p:plain

 

licenceの管理方法は?

ライセンス管理用のAPIに投げて処理しているんだろうと思うのですが
motion update時の挙動が気になったのでソースを見てみました。
 
まずは/usr/bin/motion

require 'motion/project/command'

command_paths = [File.join($motion_libdir, 'motion/project/command'), File.join(ENV['HOME'], 'Library/RubyMotion/command')]
command_paths.each do |path|
  Dir.glob(File.join(path, '*.rb')).each { |x| require x }
end

Motion::Project::Command.main(ARGV)

motion/project/command及び関連ファイルをrequireしてmainを実行しているだけです。
 
 
では、mainを見てみましょう。 /Library/RubyMotion/lib/motion/project/command.rb 見やすいように簡略化しています。

module Motion; module Project
  class Command

    Commands = []
    def self.inherited(klass)
      Commands << klass if self == Command
    end

    def self.main(args)

      command = Commands.find { |command| command.name == arg }
      usage unless command
      command.new.run(args)
    end

ここで面白いなと思ったのが、self.inheritedを使ってサブクラスがrequireされたら
Commandsという配列に突っ込むという手法。
サブクラスはmotionの中でrequireしているので適切なサブクラスかどうかが判断できます。
あとは、そのサブクラスをnew.runしているだけです。
ついでですが、licenseファイルは下記のようです。

LicensePath = '/Library/RubyMotion/license.key'

 
 
次は、そのサブクラス。
/Library/RubyMotion/lib/motion/project/command/update.rb

module Motion; module Project
  class UpdateCommand < Command
    self.name = 'update'
    self.help = 'Update the software'

    def run(args)

      license_key = read_license_key
      product_version = Motion::Version

      if check_mode
        update_check_file = File.join(ENV['TMPDIR'] || '/tmp', '.motion-update-check')
        if !File.exist?(update_check_file) or (Time.now - File.mtime(update_check_file) > 60 * 60 * 24)
          resp = curl("-s -d \"product=rubymotion\" -d \"current_software_version=#{product_version}\" -d \"license_key=#{license_key}\" https://secure.rubymotion.com/latest_software_version")
          exit 1 unless resp.match(/^\d+\.\d+/)
          File.open(update_check_file, 'w') { |io| io.write(resp) }
        end

予想通りhttps://secure.rubymotion.com/latest_software_version にpostでパラメータをセットして投げています。

    def curl(cmd)
      resp = `/usr/bin/curl --connect-timeout 60 #{cmd}`
      if $?.exitstatus != 0
        die "Error when connecting to the server. Check your Internet connection and try again."
      end
      resp
    end

こんな感じですね。

$ curl -s -d "product=rubymotion" -d "current_software_version=2.0" -d "license_key=xxxxxxxxxxxxxxxxxxx" https://secure.rubymotion.com/latest_software_version
2.0|Your license is expired. Run `motion account' to renew it.%

あと、あんまり関係ないですが
sudoでの実行かどうかの判断ロジックが勉強になりました。 Process.uidで判断できるんですね。

    def need_root
      if Process.uid != 0
        die "You need to be root to run this command."
      end
    end