├── VERSION ├── test ├── lib │ ├── e.rb │ ├── a.rb │ ├── c.rb │ ├── b │ │ ├── b.rb │ │ ├── a.rb │ │ └── a │ │ │ └── a.rb │ ├── d │ │ ├── a.rb │ │ ├── c.rb │ │ ├── b │ │ │ ├── b.rb │ │ │ ├── a.rb │ │ │ └── a │ │ │ │ └── a.rb │ │ └── b.rb │ └── b.rb ├── gems │ └── slot_machine │ │ ├── VERSION │ │ ├── lib │ │ ├── slot.rb │ │ ├── slots.rb │ │ ├── time_slots.rb │ │ ├── slot_machine │ │ │ ├── version.rb │ │ │ ├── slot.rb │ │ │ └── slots.rb │ │ ├── slot_machine.rb │ │ └── time_slot.rb │ │ ├── CHANGELOG.rdoc │ │ ├── test │ │ ├── test_helper.rb │ │ └── unit │ │ │ ├── test_time_slots.rb │ │ │ ├── test_time_slot.rb │ │ │ ├── test_slots.rb │ │ │ └── test_slot.rb │ │ ├── Rakefile │ │ ├── script │ │ └── console │ │ ├── Gemfile │ │ ├── slot_machine.gemspec │ │ ├── MIT-LICENSE │ │ └── README.md ├── mocks │ ├── yaml.rb │ ├── net │ │ └── http.rb │ └── psych_foo-0.1.0 │ │ └── psych │ │ └── handler.rb ├── test_helper │ ├── coverage.rb │ └── motion.rb ├── .gemfiles │ └── simulator │ │ ├── test_setup │ │ └── test_setup.lock ├── unit │ ├── require │ │ ├── mocker │ │ │ ├── test_dirs.rb │ │ │ └── test_hooks.rb │ │ ├── ripper │ │ │ └── test_builder.rb │ │ ├── test_resolve.rb │ │ ├── test_mocker.rb │ │ ├── tracer │ │ │ ├── test_hooks.rb │ │ │ └── test_log.rb │ │ ├── test_tracer.rb │ │ └── test_ripper.rb │ ├── test_gem_ext.rb │ ├── test_config.rb │ ├── test_require.rb │ ├── simulator │ │ ├── test_console.rb │ │ └── test_core_ext.rb │ └── test_motion-bundler.rb ├── motion │ ├── device │ │ └── test_core_ext.rb │ └── simulator │ │ ├── test_core_ext.rb │ │ └── test_setup.rb └── test_helper.rb ├── sample_apps ├── ios │ ├── mocks │ │ ├── openssl.rb │ │ ├── digest │ │ │ └── md5.rb │ │ ├── socket.so.rb │ │ ├── timeout.rb │ │ ├── httparty.rb │ │ └── net │ │ │ └── protocol.rb │ ├── boot.rb │ ├── resources │ │ ├── Icon.png │ │ ├── Icon.pxm │ │ ├── Icon@2x.png │ │ └── Icon@2x.pxm │ ├── lib │ │ ├── foo.rb │ │ └── foo │ │ │ └── bar.rb │ ├── Gemfile │ ├── spec │ │ └── main_spec.rb │ ├── .gitignore │ ├── app │ │ ├── app_delegate.rb │ │ └── controllers │ │ │ └── app_controller.rb │ └── Rakefile └── osx │ ├── mocks │ ├── openssl.rb │ ├── digest │ │ └── md5.rb │ ├── socket.so.rb │ ├── timeout.rb │ ├── httparty.rb │ └── net │ │ └── protocol.rb │ ├── boot.rb │ ├── resources │ ├── Icon.icns │ └── Credits.rtf │ ├── lib │ ├── foo.rb │ └── foo │ │ └── bar.rb │ ├── app │ ├── app_delegate.rb │ ├── window.rb │ ├── tests.rb │ └── menu.rb │ ├── Gemfile │ ├── spec │ └── main_spec.rb │ ├── .gitignore │ └── Rakefile ├── lib ├── motion-bundler │ ├── device │ │ ├── boot.rb │ │ └── core_ext.rb │ ├── simulator │ │ ├── boot.rb │ │ ├── core_ext.rb │ │ └── console.rb │ ├── version.rb │ ├── require │ │ ├── mocker │ │ │ ├── dirs.rb │ │ │ └── hooks.rb │ │ ├── mocker.rb │ │ ├── tracer.rb │ │ ├── ripper │ │ │ └── builder.rb │ │ ├── resolve.rb │ │ ├── ripper.rb │ │ └── tracer │ │ │ ├── log.rb │ │ │ └── hooks.rb │ ├── gem_ext.rb │ ├── cli.rb │ ├── config.rb │ ├── require.rb │ └── mocks │ │ ├── httparty.rb │ │ ├── zliby-0.0.5 │ │ └── zlib.rb │ │ └── mac_ruby-0.12 │ │ └── strscan.rb └── motion-bundler.rb ├── .travis.yml ├── .gitignore ├── rake ├── Gemfile ├── bin └── motion-bundler ├── script └── console ├── Rakefile ├── motion-bundler.gemspec ├── MIT-LICENSE ├── CHANGELOG.rdoc └── README.md /VERSION: -------------------------------------------------------------------------------- 1 | 0.2.1 -------------------------------------------------------------------------------- /test/lib/e.rb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sample_apps/ios/mocks/openssl.rb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sample_apps/osx/mocks/openssl.rb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/lib/a.rb: -------------------------------------------------------------------------------- 1 | module A 2 | end -------------------------------------------------------------------------------- /test/lib/c.rb: -------------------------------------------------------------------------------- 1 | module C 2 | end -------------------------------------------------------------------------------- /sample_apps/ios/mocks/digest/md5.rb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sample_apps/ios/mocks/socket.so.rb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sample_apps/osx/mocks/digest/md5.rb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /sample_apps/osx/mocks/socket.so.rb: -------------------------------------------------------------------------------- 1 | -------------------------------------------------------------------------------- /test/gems/slot_machine/VERSION: -------------------------------------------------------------------------------- 1 | 0.1.0 -------------------------------------------------------------------------------- /test/lib/b/b.rb: -------------------------------------------------------------------------------- 1 | module B 2 | module B 3 | end 4 | end -------------------------------------------------------------------------------- /test/lib/d/a.rb: -------------------------------------------------------------------------------- 1 | module D 2 | module A 3 | end 4 | end -------------------------------------------------------------------------------- /test/lib/d/c.rb: -------------------------------------------------------------------------------- 1 | module D 2 | module C 3 | end 4 | end -------------------------------------------------------------------------------- /lib/motion-bundler/device/boot.rb: -------------------------------------------------------------------------------- 1 | require_relative "core_ext" -------------------------------------------------------------------------------- /sample_apps/ios/boot.rb: -------------------------------------------------------------------------------- 1 | module REXML 2 | class Child; end 3 | end -------------------------------------------------------------------------------- /sample_apps/osx/boot.rb: -------------------------------------------------------------------------------- 1 | module REXML 2 | class Child; end 3 | end -------------------------------------------------------------------------------- /.travis.yml: -------------------------------------------------------------------------------- 1 | language: ruby 2 | rvm: 3 | - 2.0.0 4 | - 1.9.3 5 | - 1.9.2 -------------------------------------------------------------------------------- /test/lib/b/a.rb: -------------------------------------------------------------------------------- 1 | load "b/a/a" 2 | 3 | module B 4 | module A 5 | end 6 | end -------------------------------------------------------------------------------- /test/mocks/yaml.rb: -------------------------------------------------------------------------------- 1 | module YAML 2 | def self.mocked? 3 | true 4 | end 5 | end -------------------------------------------------------------------------------- /test/gems/slot_machine/lib/slot.rb: -------------------------------------------------------------------------------- 1 | class Slot 2 | include SlotMachine::Slot 3 | end -------------------------------------------------------------------------------- /test/gems/slot_machine/lib/slots.rb: -------------------------------------------------------------------------------- 1 | class Slots 2 | include SlotMachine::Slots 3 | end -------------------------------------------------------------------------------- /test/lib/b.rb: -------------------------------------------------------------------------------- 1 | require_relative "b/a" 2 | 3 | module B 4 | autoload :B, "b/b" 5 | end -------------------------------------------------------------------------------- /test/lib/b/a/a.rb: -------------------------------------------------------------------------------- 1 | module B 2 | module A 3 | module A 4 | end 5 | end 6 | end -------------------------------------------------------------------------------- /test/lib/d/b/b.rb: -------------------------------------------------------------------------------- 1 | module D 2 | module B 3 | module B 4 | end 5 | end 6 | end -------------------------------------------------------------------------------- /lib/motion-bundler/simulator/boot.rb: -------------------------------------------------------------------------------- 1 | require_relative "core_ext" 2 | require_relative "console" -------------------------------------------------------------------------------- /sample_apps/ios/mocks/timeout.rb: -------------------------------------------------------------------------------- 1 | module Timeout 2 | class Error < RuntimeError 3 | end 4 | end -------------------------------------------------------------------------------- /sample_apps/osx/mocks/timeout.rb: -------------------------------------------------------------------------------- 1 | module Timeout 2 | class Error < RuntimeError 3 | end 4 | end -------------------------------------------------------------------------------- /test/gems/slot_machine/lib/time_slots.rb: -------------------------------------------------------------------------------- 1 | class TimeSlots 2 | include SlotMachine::Slots 3 | end -------------------------------------------------------------------------------- /test/lib/d/b.rb: -------------------------------------------------------------------------------- 1 | require "d/b/a" 2 | require "d/b/b" 3 | 4 | module D 5 | module B 6 | end 7 | end -------------------------------------------------------------------------------- /.gitignore: -------------------------------------------------------------------------------- 1 | .DS_Store 2 | .bundle 3 | .rvmrc 4 | .motion-bundler.rb 5 | Gemfile.lock 6 | coverage 7 | doc 8 | pkg -------------------------------------------------------------------------------- /sample_apps/ios/mocks/httparty.rb: -------------------------------------------------------------------------------- 1 | module HTTParty 2 | def self.hi! 3 | "Hi, I'm #{name}" 4 | end 5 | end -------------------------------------------------------------------------------- /sample_apps/osx/mocks/httparty.rb: -------------------------------------------------------------------------------- 1 | module HTTParty 2 | def self.hi! 3 | "Hi, I'm #{name}" 4 | end 5 | end -------------------------------------------------------------------------------- /test/lib/d/b/a.rb: -------------------------------------------------------------------------------- 1 | require "d/b/a/a" 2 | 3 | module D 4 | module B 5 | module A 6 | end 7 | end 8 | end -------------------------------------------------------------------------------- /test/lib/d/b/a/a.rb: -------------------------------------------------------------------------------- 1 | module D 2 | module B 3 | module A 4 | module A 5 | end 6 | end 7 | end 8 | end -------------------------------------------------------------------------------- /test/mocks/net/http.rb: -------------------------------------------------------------------------------- 1 | module Net 2 | class HTTP 3 | def self.mocked? 4 | true 5 | end 6 | end 7 | end -------------------------------------------------------------------------------- /sample_apps/ios/resources/Icon.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/archan937/motion-bundler/HEAD/sample_apps/ios/resources/Icon.png -------------------------------------------------------------------------------- /sample_apps/ios/resources/Icon.pxm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/archan937/motion-bundler/HEAD/sample_apps/ios/resources/Icon.pxm -------------------------------------------------------------------------------- /sample_apps/osx/resources/Icon.icns: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/archan937/motion-bundler/HEAD/sample_apps/osx/resources/Icon.icns -------------------------------------------------------------------------------- /sample_apps/ios/resources/Icon@2x.png: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/archan937/motion-bundler/HEAD/sample_apps/ios/resources/Icon@2x.png -------------------------------------------------------------------------------- /sample_apps/ios/resources/Icon@2x.pxm: -------------------------------------------------------------------------------- https://raw.githubusercontent.com/archan937/motion-bundler/HEAD/sample_apps/ios/resources/Icon@2x.pxm -------------------------------------------------------------------------------- /test/gems/slot_machine/CHANGELOG.rdoc: -------------------------------------------------------------------------------- 1 | = SlotMachine CHANGELOG 2 | 3 | == Version 0.1.0 (September 16, 2012) 4 | 5 | * Initial release -------------------------------------------------------------------------------- /test/mocks/psych_foo-0.1.0/psych/handler.rb: -------------------------------------------------------------------------------- 1 | module Psych 2 | class Handler 3 | def self.mocked? 4 | true 5 | end 6 | end 7 | end -------------------------------------------------------------------------------- /rake: -------------------------------------------------------------------------------- 1 | #!/bin/bash 2 | if [ "$1" = "all" ]; then 3 | TESTS="test/unit test/motion" 4 | else 5 | TESTS="test/motion" 6 | fi 7 | 8 | BUNDLE_GEMFILE=foo rake $TESTS -------------------------------------------------------------------------------- /lib/motion-bundler/version.rb: -------------------------------------------------------------------------------- 1 | module MotionBundler #:nodoc: 2 | MAJOR = 0 3 | MINOR = 2 4 | TINY = 1 5 | 6 | VERSION = [MAJOR, MINOR, TINY].join(".") 7 | end -------------------------------------------------------------------------------- /sample_apps/ios/lib/foo.rb: -------------------------------------------------------------------------------- 1 | require_relative "foo/bar" 2 | require "cgi" 3 | 4 | module Foo 5 | def self.foo! 6 | CGI.escape_html "Foo > Bar" 7 | end 8 | end -------------------------------------------------------------------------------- /sample_apps/osx/lib/foo.rb: -------------------------------------------------------------------------------- 1 | require_relative "foo/bar" 2 | require "cgi" 3 | 4 | module Foo 5 | def self.foo! 6 | CGI.escape_html "Foo > Bar" 7 | end 8 | end -------------------------------------------------------------------------------- /sample_apps/ios/lib/foo/bar.rb: -------------------------------------------------------------------------------- 1 | require "cgi" 2 | 3 | module Foo 4 | module Bar 5 | def self.bar! 6 | CGI.escape_html "Bar < Foo" 7 | end 8 | end 9 | end -------------------------------------------------------------------------------- /sample_apps/osx/lib/foo/bar.rb: -------------------------------------------------------------------------------- 1 | require "cgi" 2 | 3 | module Foo 4 | module Bar 5 | def self.bar! 6 | CGI.escape_html "Bar < Foo" 7 | end 8 | end 9 | end -------------------------------------------------------------------------------- /Gemfile: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gemspec 4 | 5 | group :test do 6 | gem "simplecov", :require => false 7 | end 8 | 9 | group :console do 10 | gem "pry" 11 | end -------------------------------------------------------------------------------- /test/gems/slot_machine/lib/slot_machine/version.rb: -------------------------------------------------------------------------------- 1 | module SlotMachine #:nodoc: 2 | MAJOR = 0 3 | MINOR = 1 4 | TINY = 0 5 | 6 | VERSION = [MAJOR, MINOR, TINY].join(".") 7 | end -------------------------------------------------------------------------------- /sample_apps/osx/app/app_delegate.rb: -------------------------------------------------------------------------------- 1 | class AppDelegate 2 | 3 | def applicationDidFinishLaunching(notification) 4 | buildMenu 5 | buildWindow 6 | runTests 7 | end 8 | 9 | end -------------------------------------------------------------------------------- /test/gems/slot_machine/test/test_helper.rb: -------------------------------------------------------------------------------- 1 | require "rubygems" 2 | require "bundler" 3 | 4 | require "minitest/unit" 5 | require "minitest/autorun" 6 | 7 | Bundler.require :gem_default, :gem_test -------------------------------------------------------------------------------- /sample_apps/ios/Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | # RubyMotion aware gems 4 | gem "motion-bundler", :path => "../../.." 5 | 6 | # RubyMotion unaware gems 7 | group :motion do 8 | gem "slot_machine" 9 | end -------------------------------------------------------------------------------- /sample_apps/osx/Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | # RubyMotion aware gems 4 | gem "motion-bundler", :path => "../../.." 5 | 6 | # RubyMotion unaware gems 7 | group :motion do 8 | gem "slot_machine" 9 | end -------------------------------------------------------------------------------- /test/gems/slot_machine/Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | require "bundler/gem_tasks" 3 | require "rake/testtask" 4 | 5 | task :default => :test 6 | 7 | Rake::TestTask.new do |test| 8 | test.pattern = "test/**/test_*.rb" 9 | end -------------------------------------------------------------------------------- /test/gems/slot_machine/lib/slot_machine.rb: -------------------------------------------------------------------------------- 1 | require "slot_machine/version" 2 | require "slot_machine/slot" 3 | require "slot_machine/slots" 4 | 5 | require "slot" 6 | require "slots" 7 | 8 | require "time_slot" 9 | require "time_slots" -------------------------------------------------------------------------------- /sample_apps/ios/mocks/net/protocol.rb: -------------------------------------------------------------------------------- 1 | module Net 2 | class ProtocolError < StandardError; end 3 | class ProtoAuthError < ProtocolError; end 4 | class Protocol 5 | def self.hi! 6 | "Hi, I'm #{name}" 7 | end 8 | end 9 | end -------------------------------------------------------------------------------- /sample_apps/osx/mocks/net/protocol.rb: -------------------------------------------------------------------------------- 1 | module Net 2 | class ProtocolError < StandardError; end 3 | class ProtoAuthError < ProtocolError; end 4 | class Protocol 5 | def self.hi! 6 | "Hi, I'm #{name}" 7 | end 8 | end 9 | end -------------------------------------------------------------------------------- /sample_apps/osx/spec/main_spec.rb: -------------------------------------------------------------------------------- 1 | describe "Application 'MBundler'" do 2 | before do 3 | @app = NSApplication.sharedApplication 4 | end 5 | 6 | it "has one window" do 7 | @app.windows.size.should == 1 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /bin/motion-bundler: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "rubygems" 4 | require "motion-bundler/cli" 5 | 6 | begin 7 | MotionBundler::CLI.start 8 | rescue MotionBundler::CLI::Error => e 9 | puts "\e[0;#{31};49m#{e.message}\e[0m" 10 | end -------------------------------------------------------------------------------- /sample_apps/ios/spec/main_spec.rb: -------------------------------------------------------------------------------- 1 | describe "Application 'sample_app'" do 2 | before do 3 | @app = UIApplication.sharedApplication 4 | end 5 | 6 | it "has one window" do 7 | @app.windows.size.should == 1 8 | end 9 | end 10 | -------------------------------------------------------------------------------- /test/test_helper/coverage.rb: -------------------------------------------------------------------------------- 1 | if Dir.pwd == File.expand_path("../../..", __FILE__) 2 | require "simplecov" 3 | SimpleCov.coverage_dir "test/coverage" 4 | SimpleCov.start do 5 | # add_filter "lib/motion-bundler/simulator/core_ext.rb" 6 | end 7 | end -------------------------------------------------------------------------------- /script/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "bundler" 4 | Bundler.require :default, :console 5 | $:.unshift File.expand_path("../../test/lib", __FILE__) 6 | 7 | puts "Loading MotionBundler development environment (#{MotionBundler::VERSION})" 8 | Pry.start -------------------------------------------------------------------------------- /sample_apps/ios/.gitignore: -------------------------------------------------------------------------------- 1 | .repl_history 2 | build 3 | tags 4 | app/pixate_code.rb 5 | resources/*.nib 6 | resources/*.momd 7 | resources/*.storyboardc 8 | .DS_Store 9 | nbproject 10 | .redcar 11 | #*# 12 | *~ 13 | *.sw[po] 14 | .eprj 15 | .sass-cache 16 | .idea 17 | -------------------------------------------------------------------------------- /sample_apps/osx/.gitignore: -------------------------------------------------------------------------------- 1 | .repl_history 2 | build 3 | tags 4 | app/pixate_code.rb 5 | resources/*.nib 6 | resources/*.momd 7 | resources/*.storyboardc 8 | .DS_Store 9 | nbproject 10 | .redcar 11 | #*# 12 | *~ 13 | *.sw[po] 14 | .eprj 15 | .sass-cache 16 | .idea 17 | -------------------------------------------------------------------------------- /test/gems/slot_machine/script/console: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env ruby 2 | 3 | require "rubygems" 4 | require "bundler" 5 | 6 | Bundler.require :gem_default, :gem_development 7 | 8 | puts "Loading development environment (SlotMachine #{SlotMachine::VERSION})" 9 | 10 | Pry.start -------------------------------------------------------------------------------- /test/.gemfiles/simulator/test_setup: -------------------------------------------------------------------------------- 1 | source "https://rubygems.org" 2 | 3 | gem "motion-bundler", :path => "/Users/paulengel/Sources/motion-bundler" 4 | group :motion do 5 | gem "slot_machine", :path => "/Users/paulengel/Sources/motion-bundler/test/gems/slot_machine" 6 | end 7 | -------------------------------------------------------------------------------- /sample_apps/ios/app/app_delegate.rb: -------------------------------------------------------------------------------- 1 | class AppDelegate 2 | def application(application, didFinishLaunchingWithOptions:launchOptions) 3 | @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds) 4 | @window.rootViewController = AppController.alloc.init 5 | @window.makeKeyAndVisible 6 | true 7 | end 8 | end -------------------------------------------------------------------------------- /test/test_helper/motion.rb: -------------------------------------------------------------------------------- 1 | module Motion 2 | module Project 3 | class App 4 | def self.setup 5 | yield new 6 | end 7 | def self.fail(msg) 8 | raise msg 9 | end 10 | def files 11 | @files ||= [] 12 | end 13 | def files=(files) 14 | @files = files 15 | end 16 | def files_dependencies(deps) 17 | end 18 | end 19 | end 20 | end -------------------------------------------------------------------------------- /test/.gemfiles/simulator/test_setup.lock: -------------------------------------------------------------------------------- 1 | PATH 2 | remote: /Users/paulengel/Sources/motion-bundler 3 | specs: 4 | motion-bundler (0.2.1) 5 | thor 6 | 7 | PATH 8 | remote: /Users/paulengel/Sources/motion-bundler/test/gems/slot_machine 9 | specs: 10 | slot_machine (0.1.0) 11 | 12 | GEM 13 | remote: https://rubygems.org/ 14 | specs: 15 | thor (0.18.1) 16 | 17 | PLATFORMS 18 | ruby 19 | 20 | DEPENDENCIES 21 | motion-bundler! 22 | slot_machine! 23 | -------------------------------------------------------------------------------- /test/gems/slot_machine/Gemfile: -------------------------------------------------------------------------------- 1 | source "http://rubygems.org" 2 | 3 | gemspec 4 | 5 | group :gem_default do 6 | gem "slot_machine", :path => "." 7 | end 8 | 9 | group :gem_development do 10 | gem "pry" 11 | end 12 | 13 | group :gem_test do 14 | gem "minitest" 15 | gem "mocha", :git => "git://github.com/freerange/mocha.git", :ref => "f88a78050af1f1ad98c917cb26ed3539e59fe7e0" # see also: https://github.com/freerange/mocha/issues/93 16 | gem "pry" 17 | gem "rake" 18 | end -------------------------------------------------------------------------------- /lib/motion-bundler/require/mocker/dirs.rb: -------------------------------------------------------------------------------- 1 | module MotionBundler 2 | module Require 3 | module Mocker 4 | module Dirs 5 | 6 | APP_MOCKS = File.expand_path("./mocks") 7 | GEM_MOCKS = File.expand_path("../../../mocks", __FILE__) 8 | 9 | def dirs 10 | @dirs ||= [APP_MOCKS, GEM_MOCKS] 11 | end 12 | 13 | def add_dir(dir) 14 | dirs.insert 1, File.expand_path(dir) 15 | end 16 | 17 | end 18 | end 19 | end 20 | end -------------------------------------------------------------------------------- /sample_apps/osx/app/window.rb: -------------------------------------------------------------------------------- 1 | class AppDelegate 2 | 3 | def buildWindow 4 | @mainWindow = NSWindow.alloc.initWithContentRect( 5 | [[240, 180], [480, 360]], 6 | styleMask: NSTitledWindowMask | NSClosableWindowMask | NSMiniaturizableWindowMask | NSResizableWindowMask, 7 | backing: NSBackingStoreBuffered, 8 | defer: false 9 | ) 10 | @mainWindow.title = NSBundle.mainBundle.infoDictionary["CFBundleName"] 11 | @mainWindow.orderFrontRegardless 12 | end 13 | 14 | end -------------------------------------------------------------------------------- /lib/motion-bundler/device/core_ext.rb: -------------------------------------------------------------------------------- 1 | unless ENV["MB_SILENCE_CORE"] == "false" 2 | 3 | module Kernel 4 | def require(name) 5 | end 6 | def require_relative(string) 7 | end 8 | def load(*args) 9 | end 10 | def autoload(mod, filename) 11 | end 12 | end 13 | 14 | class Object 15 | def require(name) 16 | end 17 | def require_relative(string) 18 | end 19 | def load(*args) 20 | end 21 | end 22 | 23 | class Module 24 | def autoload(mod, filename) 25 | end 26 | end 27 | 28 | end -------------------------------------------------------------------------------- /lib/motion-bundler/require/mocker.rb: -------------------------------------------------------------------------------- 1 | require "motion-bundler/require/mocker/hooks" 2 | require "motion-bundler/require/mocker/dirs" 3 | 4 | module MotionBundler 5 | module Require 6 | module Mocker 7 | include Hooks 8 | include Dirs 9 | extend self 10 | 11 | def yield 12 | start 13 | yield 14 | ensure 15 | stop 16 | end 17 | 18 | private 19 | 20 | def start 21 | hook 22 | end 23 | 24 | def stop 25 | unhook 26 | end 27 | 28 | end 29 | end 30 | end -------------------------------------------------------------------------------- /sample_apps/osx/resources/Credits.rtf: -------------------------------------------------------------------------------- 1 | {\rtf0\ansi{\fonttbl\f0\fswiss Helvetica;} 2 | {\colortbl;\red255\green255\blue255;} 3 | \paperw9840\paperh8400 4 | \pard\tx560\tx1120\tx1680\tx2240\tx2800\tx3360\tx3920\tx4480\tx5040\tx5600\tx6160\tx6720\ql\qnatural 5 | 6 | \f0\b\fs24 \cf0 Engineering: 7 | \b0 \ 8 | Some people\ 9 | \ 10 | 11 | \b Human Interface Design: 12 | \b0 \ 13 | Some other people\ 14 | \ 15 | 16 | \b Testing: 17 | \b0 \ 18 | Hopefully not nobody\ 19 | \ 20 | 21 | \b Documentation: 22 | \b0 \ 23 | Whoever\ 24 | \ 25 | 26 | \b With special thanks to: 27 | \b0 \ 28 | Mom\ 29 | } 30 | -------------------------------------------------------------------------------- /test/gems/slot_machine/lib/time_slot.rb: -------------------------------------------------------------------------------- 1 | class TimeSlot 2 | include SlotMachine::Slot 3 | 4 | def self.default_interval 5 | @interval || 15 6 | end 7 | 8 | protected 9 | 10 | def typecast(value) 11 | value.is_a?(Time) ? value.strftime("%Y%m%d%H%M") : value.to_s 12 | end 13 | 14 | def valid?(value) 15 | (value - (100 * (value / 100))) < 60 16 | end 17 | 18 | def to_array 19 | super.delete_if{|i| !valid?(i)} 20 | end 21 | 22 | def add(a, b) 23 | i = a 24 | b.times do |t| 25 | while !valid?(i += 1) do; end 26 | end 27 | i 28 | end 29 | 30 | end -------------------------------------------------------------------------------- /lib/motion-bundler/gem_ext.rb: -------------------------------------------------------------------------------- 1 | module Motion 2 | module Project 3 | class Config 4 | 5 | def files_dependencies(deps_hash) 6 | res_path = lambda do |x| 7 | path = /^\.?\//.match(x) ? x : File.join(".", x) 8 | unless @files.flatten.include?(path) 9 | App.fail "Can't resolve dependency `#{x}'" 10 | end 11 | path 12 | end 13 | deps_hash.each do |path, deps| 14 | deps = [deps] unless deps.is_a?(Array) 15 | @dependencies[res_path.call(path)] = deps.map(&res_path) 16 | end 17 | end 18 | 19 | end 20 | end 21 | end -------------------------------------------------------------------------------- /sample_apps/ios/Rakefile: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | $:.unshift "/Library/RubyMotion/lib" 3 | require "motion/project/template/ios" 4 | 5 | # Require and prepare Bundler 6 | require "bundler" 7 | Bundler.require 8 | 9 | Motion::Project::App.setup do |app| 10 | # Use `rake config' to see complete project settings. 11 | app.name = "MBundler" 12 | end 13 | 14 | require "/Users/paulengel/.rvm/rubies/ruby-2.0.0-p195/lib/ruby/2.0.0/cgi/html.rb" 15 | 16 | # Track and specify files and their mutual dependencies within the :motion Bundler group 17 | MotionBundler.setup do |app| 18 | app.require "base64" 19 | app.register({ 20 | "rexml/xmldecl" => ["rexml/child"], 21 | "rexml/doctype" => ["rexml/child"], 22 | "rexml/parsers/baseparser" => ["set"] 23 | }) 24 | end -------------------------------------------------------------------------------- /lib/motion-bundler/cli.rb: -------------------------------------------------------------------------------- 1 | require "yaml" 2 | require "thor" 3 | require "motion-bundler" 4 | 5 | $CLI = true 6 | 7 | module MotionBundler 8 | class CLI < Thor 9 | 10 | default_task :trace 11 | 12 | desc "trace [FILE]", "Trace files and their mutual dependencies when requiring [FILE]" 13 | def trace(file) 14 | Require.mock_and_trace do 15 | require file, "APP" 16 | end 17 | puts YAML.dump({ 18 | "FILES" => Require.files, 19 | "FILES_DEPENDENCIES" => Require.files_dependencies, 20 | "REQUIRES" => Require.requires 21 | }) 22 | end 23 | 24 | private 25 | 26 | def method_missing(method, *args) 27 | raise Error, "Unrecognized command \"#{method}\". Please consult `motion-bundler help`." 28 | end 29 | 30 | end 31 | end -------------------------------------------------------------------------------- /lib/motion-bundler/require/tracer.rb: -------------------------------------------------------------------------------- 1 | require "motion-bundler/require/tracer/hooks" 2 | require "motion-bundler/require/tracer/log" 3 | 4 | module MotionBundler 5 | module Require 6 | module Tracer 7 | include Hooks 8 | extend self 9 | 10 | def log 11 | Thread.current[:motion_bundler_log] ||= Log.new 12 | end 13 | 14 | def yield 15 | verbose = $VERBOSE 16 | $VERBOSE = nil 17 | start 18 | yield 19 | ensure 20 | $VERBOSE = verbose 21 | stop 22 | end 23 | 24 | private 25 | 26 | def start 27 | ENV["MB_SILENCE_CORE"] = "false" 28 | log.clear 29 | hook 30 | end 31 | 32 | def stop 33 | ENV["MB_SILENCE_CORE"] = "true" 34 | unhook 35 | end 36 | 37 | end 38 | end 39 | end -------------------------------------------------------------------------------- /sample_apps/osx/Rakefile: -------------------------------------------------------------------------------- 1 | # -*- coding: utf-8 -*- 2 | $:.unshift "/Library/RubyMotion/lib" 3 | require "motion/project/template/osx" 4 | 5 | # Require and prepare Bundler 6 | require "bundler" 7 | Bundler.require 8 | 9 | Motion::Project::App.setup do |app| 10 | # Use `rake config' to see complete project settings. 11 | app.name = "MBundler" 12 | app.icon = "Icon" 13 | app.info_plist["CFBundleIconFile"] = "Icon.icns" 14 | end 15 | 16 | require "/Users/paulengel/.rvm/rubies/ruby-2.0.0-p195/lib/ruby/2.0.0/cgi/html.rb" 17 | 18 | # Track and specify files and their mutual dependencies within the :motion Bundler group 19 | MotionBundler.setup do |app| 20 | app.require "base64" 21 | app.register({ 22 | "rexml/xmldecl" => ["rexml/child"], 23 | "rexml/doctype" => ["rexml/child"], 24 | "rexml/parsers/baseparser" => ["set"] 25 | }) 26 | end -------------------------------------------------------------------------------- /Rakefile: -------------------------------------------------------------------------------- 1 | #!/usr/bin/env rake 2 | require "bundler/gem_tasks" 3 | require "rake/testtask" 4 | 5 | task :default => "test/unit" 6 | 7 | Rake::TestTask.new("test/unit") do |t| 8 | t.test_files = FileList["test/unit/**/test_*.rb"] 9 | end 10 | task("test/unit").clear_comments 11 | task("test/unit").comment = "Run unit tests" 12 | 13 | desc "Run motion tests (Usage: .rake)" 14 | task "test/motion" do 15 | if ENV["BUNDLE_GEMFILE"] == "foo" 16 | FileList["test/motion/**/test_*.rb"].each_with_index do |test_file, index| 17 | puts if index > 0 || ARGV.include?("test/unit") 18 | puts "\e[0;32;49m#{test_file}:\n\e[0m" 19 | test_file = File.expand_path test_file 20 | system "cd #{File.dirname test_file} && ruby #{File.basename test_file}" 21 | end 22 | else 23 | puts "\e[0;31;49mPlease use `.rake` to run motion tests\e[0m" 24 | end 25 | end -------------------------------------------------------------------------------- /lib/motion-bundler/config.rb: -------------------------------------------------------------------------------- 1 | module MotionBundler 2 | class Config 3 | def initialize 4 | @requires = [] 5 | @files_dependencies = {} 6 | register "app/app_delegate.rb" => ["boot.rb"] if boot_file? 7 | end 8 | def boot_file? 9 | File.exists? "boot.rb" 10 | end 11 | def require(name) 12 | @requires << name 13 | end 14 | def register(files_dependencies) 15 | files_dependencies.each do |file, paths| 16 | (@files_dependencies[Require.resolve(file, false)] ||= []).concat paths.collect{|file| Require.resolve(file, false)} 17 | end 18 | end 19 | def files 20 | (@files_dependencies.keys + @files_dependencies.values).flatten.uniq 21 | end 22 | def files_dependencies 23 | @files_dependencies.dup 24 | end 25 | def requires 26 | @requires.dup 27 | end 28 | end 29 | end -------------------------------------------------------------------------------- /lib/motion-bundler/require.rb: -------------------------------------------------------------------------------- 1 | require "motion-bundler/require/tracer" 2 | require "motion-bundler/require/mocker" 3 | require "motion-bundler/require/ripper" 4 | require "motion-bundler/require/resolve" 5 | 6 | module MotionBundler 7 | module Require 8 | include Resolve 9 | extend self 10 | 11 | def mock_and_trace 12 | mock do 13 | trace do 14 | yield 15 | end 16 | end 17 | end 18 | 19 | def trace 20 | Tracer.yield do 21 | yield 22 | end 23 | end 24 | 25 | def mock 26 | Mocker.yield do 27 | yield 28 | end 29 | end 30 | 31 | def files 32 | Tracer.log.files 33 | end 34 | 35 | def files_dependencies 36 | Tracer.log.files_dependencies 37 | end 38 | 39 | def requires 40 | Tracer.log.requires 41 | end 42 | 43 | end 44 | end -------------------------------------------------------------------------------- /test/unit/require/mocker/test_dirs.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../../../test_helper", __FILE__) 2 | 3 | module Unit 4 | module Require 5 | module Mocker 6 | class TestDirs < MiniTest::Unit::TestCase 7 | 8 | module Mocker 9 | include MotionBundler::Require::Mocker::Dirs 10 | extend self 11 | end 12 | 13 | describe MotionBundler::Require::Mocker::Dirs do 14 | before do 15 | Mocker.send :remove_instance_variable, :@dirs if Mocker.instance_variables.include?(:@dirs) 16 | end 17 | 18 | it "should have mock dirs" do 19 | assert_equal [Mocker::APP_MOCKS, Mocker::GEM_MOCKS], Mocker.dirs 20 | end 21 | 22 | it "should be able to add mock dirs" do 23 | Mocker.add_dir mocks_dir 24 | assert_equal [Mocker::APP_MOCKS, mocks_dir, Mocker::GEM_MOCKS], Mocker.dirs 25 | end 26 | end 27 | 28 | end 29 | end 30 | end 31 | end -------------------------------------------------------------------------------- /test/gems/slot_machine/slot_machine.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | 3 | Gem::Specification.new do |gem| 4 | gem.authors = ["Paul Engel"] 5 | gem.email = ["paul.engel@holder.nl"] 6 | gem.summary = %q{Ruby gem for matching available slots (time slots are also supported)} 7 | gem.description = %q{One of the classic programming problems is the determination of time slot availability. Very often this is used within scheduling / calendar programs. SlotMachine is a very small Ruby gem which can do the job for you. It does not only focuses on time slots, but also slots in general.} 8 | gem.homepage = "https://github.com/archan937/slot_machine" 9 | 10 | gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 11 | gem.files = `git ls-files`.split("\n") 12 | gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 13 | gem.name = "slot_machine" 14 | gem.require_paths = ["lib"] 15 | gem.version = "0.1.0" 16 | end -------------------------------------------------------------------------------- /test/unit/test_gem_ext.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../test_helper", __FILE__) 2 | 3 | module Unit 4 | class TestGemExt < MiniTest::Unit::TestCase 5 | 6 | describe "lib/motion-bundler/gem_ext.rb" do 7 | it "should required after having required MotionBundler" do 8 | assert_equal true, $LOADED_FEATURES.include?(motion_bundler_file "motion-bundler/gem_ext.rb") 9 | end 10 | 11 | it "should override Motion::Project::Config#files_dependencies in order to tackle absolute paths" do 12 | config = Motion::Project::Config.new 13 | config.instance_variable_set :@files, %w(/Users/paulengel/foo.rb /Users/paulengel/bar.rb /Users/paulengel/fubar.rb) 14 | config.instance_variable_set :@dependencies, Hash.new 15 | assert config.files_dependencies("/Users/paulengel/foo.rb" => %w(/Users/paulengel/bar.rb)) 16 | assert_raises RuntimeError do 17 | config.files_dependencies "foo" => %w(bar.rb) 18 | end 19 | end 20 | end 21 | 22 | end 23 | end -------------------------------------------------------------------------------- /motion-bundler.gemspec: -------------------------------------------------------------------------------- 1 | # -*- encoding: utf-8 -*- 2 | require File.expand_path("../lib/motion-bundler/version", __FILE__) 3 | 4 | Gem::Specification.new do |gem| 5 | gem.authors = ["Paul Engel"] 6 | gem.email = ["paul.engel@holder.nl"] 7 | gem.summary = %q{Use Ruby gems and mock require statements within RubyMotion applications} 8 | gem.description = %q{Use Ruby gems and mock require statements within RubyMotion applications} 9 | gem.homepage = "https://github.com/archan937/motion-bundler" 10 | 11 | gem.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) } 12 | gem.files = `git ls-files`.split("\n") 13 | gem.test_files = `git ls-files -- {test,spec,features}/*`.split("\n") 14 | gem.name = "motion-bundler" 15 | gem.require_paths = ["lib"] 16 | gem.version = MotionBundler::VERSION 17 | 18 | gem.add_development_dependency "minitest", "4.6.2" 19 | gem.add_development_dependency "mocha", "0.13.2" 20 | gem.add_development_dependency "rake" 21 | gem.add_dependency "thor" 22 | end -------------------------------------------------------------------------------- /test/unit/require/ripper/test_builder.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../../../test_helper", __FILE__) 2 | 3 | module Unit 4 | module Require 5 | module Ripper 6 | class TestBuilder < MiniTest::Unit::TestCase 7 | 8 | describe MotionBundler::Require::Ripper::Builder do 9 | it "should scan for requires" do 10 | File.expects(:read).with("./app/controllers/app_controller.rb").returns <<-RUBY_CODE 11 | require "foo" 12 | require_relative "bar" 13 | class AppController 14 | require "qux" 15 | def bar 16 | puts "Bar!" 17 | end 18 | end 19 | RUBY_CODE 20 | builder = MotionBundler::Require::Ripper::Builder.new "./app/controllers/app_controller.rb" 21 | assert_equal [ 22 | [:require, ["foo"]], 23 | [:require_relative, ["bar"]], 24 | [:require, ["qux"]] 25 | ], builder.requires 26 | end 27 | end 28 | 29 | end 30 | end 31 | end 32 | end -------------------------------------------------------------------------------- /test/unit/require/mocker/test_hooks.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../../../test_helper", __FILE__) 2 | 3 | module Unit 4 | module Require 5 | module Mocker 6 | class TestHooks < MiniTest::Unit::TestCase 7 | 8 | describe MotionBundler::Require::Mocker::Hooks do 9 | it "should hook into core methods" do 10 | assert_equal false, Kernel.respond_to?(:require_with_mb_mock) 11 | assert_equal false, Object.respond_to?(:require_with_mb_mock) 12 | 13 | MotionBundler::Require::Mocker.hook 14 | assert_equal true, Kernel.respond_to?(:require_with_mb_mock) 15 | assert_equal true, Object.respond_to?(:require_with_mb_mock) 16 | 17 | MotionBundler::Require.expects(:resolve).with("a").returns "a" 18 | require "a" 19 | 20 | MotionBundler::Require::Mocker.unhook 21 | assert_equal false, Kernel.respond_to?(:require_with_mb_mock) 22 | assert_equal false, Object.respond_to?(:require_with_mb_mock) 23 | end 24 | end 25 | 26 | end 27 | end 28 | end 29 | end -------------------------------------------------------------------------------- /MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2013 Paul Engel 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 21 | -------------------------------------------------------------------------------- /test/gems/slot_machine/MIT-LICENSE: -------------------------------------------------------------------------------- 1 | Copyright (c) 2012 Paul Engel 2 | 3 | Permission is hereby granted, free of charge, to any person obtaining 4 | a copy of this software and associated documentation files (the 5 | "Software"), to deal in the Software without restriction, including 6 | without limitation the rights to use, copy, modify, merge, publish, 7 | distribute, sublicense, and/or sell copies of the Software, and to 8 | permit persons to whom the Software is furnished to do so, subject to 9 | the following conditions: 10 | 11 | The above copyright notice and this permission notice shall be 12 | included in all copies or substantial portions of the Software. 13 | 14 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, 15 | EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF 16 | MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND 17 | NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE 18 | LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION 19 | OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION 20 | WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /lib/motion-bundler/require/ripper/builder.rb: -------------------------------------------------------------------------------- 1 | require "ripper" 2 | 3 | module MotionBundler 4 | module Require 5 | class Ripper 6 | class Builder < ::Ripper::SexpBuilder 7 | 8 | def initialize(file) 9 | super File.read(file) 10 | end 11 | 12 | def requires 13 | @requires || begin 14 | @requires = [] 15 | parse 16 | @requires 17 | end 18 | end 19 | 20 | private 21 | 22 | def on_command(command, args) 23 | type, name, position = command 24 | if %w(require require_relative).include? name 25 | unless (args = extract_arguments(args)).empty? 26 | @requires << [name.to_sym, args] 27 | end 28 | end 29 | end 30 | 31 | def extract_arguments(args) 32 | args.inject([]) do |result, arg| 33 | if arg.is_a?(Array) 34 | if arg[0] == :@tstring_content 35 | result << arg[1] 36 | else 37 | result.concat extract_arguments(arg) 38 | end 39 | end 40 | result 41 | end 42 | end 43 | 44 | end 45 | end 46 | end 47 | end -------------------------------------------------------------------------------- /test/motion/device/test_core_ext.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../../test_helper", __FILE__) 2 | 3 | module Motion 4 | module Device 5 | class TestCoreExt < MiniTest::Unit::TestCase 6 | 7 | describe "lib/motion-bundler/device/core_ext.rb" do 8 | before do 9 | ENV["MB_SILENCE_CORE"] = "true" 10 | end 11 | 12 | it "should ignore require statements without a warning" do 13 | MotionBundler.expects(:simulator?).returns false 14 | last_loaded_feature = nil 15 | 16 | assert_output "" do 17 | require MotionBundler.boot_file 18 | last_loaded_feature = $LOADED_FEATURES.last 19 | 20 | assert_nil require("a") 21 | assert_nil require_relative("../../lib/a") 22 | assert_nil load("../../lib/a.rb") 23 | 24 | class Foo 25 | autoload :A, "../../lib/a.rb" 26 | end 27 | end 28 | 29 | assert_equal last_loaded_feature, $LOADED_FEATURES.last 30 | assert_raises NameError do 31 | A 32 | end 33 | assert_raises NameError do 34 | Foo::A 35 | end 36 | end 37 | end 38 | 39 | end 40 | end 41 | end -------------------------------------------------------------------------------- /lib/motion-bundler/require/mocker/hooks.rb: -------------------------------------------------------------------------------- 1 | module MotionBundler 2 | module Require 3 | module Mocker 4 | module Hooks 5 | 6 | def hook 7 | Kernel.instance_eval &require_hook unless Kernel.respond_to?(:require_with_mb_mock) 8 | Object.class_eval &require_hook unless Object.respond_to?(:require_with_mb_mock) 9 | end 10 | 11 | def unhook 12 | Kernel.instance_eval &require_unhook if Kernel.respond_to?(:require_with_mb_mock) 13 | Object.class_eval &require_unhook if Object.respond_to?(:require_with_mb_mock) 14 | end 15 | 16 | private 17 | 18 | def require_hook 19 | @require_hook ||= proc do 20 | def require_with_mb_mock(path) 21 | require_without_mb_mock MotionBundler::Require.resolve(path) 22 | end 23 | alias :require_without_mb_mock :require 24 | alias :require :require_with_mb_mock 25 | end 26 | end 27 | 28 | def require_unhook 29 | @require_unhook ||= proc do 30 | alias :require :require_without_mb_mock 31 | undef :require_with_mb_mock 32 | undef :require_without_mb_mock 33 | end 34 | end 35 | 36 | end 37 | end 38 | end 39 | end -------------------------------------------------------------------------------- /test/unit/test_config.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../test_helper", __FILE__) 2 | 3 | module Unit 4 | class TestConfig < MiniTest::Unit::TestCase 5 | 6 | describe MotionBundler::Config do 7 | it "should include 'boot.rb' when existing" do 8 | assert_equal({}, MotionBundler::Config.new.files_dependencies) 9 | MotionBundler::Config.any_instance.expects(:boot_file?).returns true 10 | assert_equal({"app/app_delegate.rb" => ["boot.rb"]}, MotionBundler::Config.new.files_dependencies) 11 | end 12 | 13 | it "should be able to register requires and returning them" do 14 | config = MotionBundler::Config.new 15 | assert_equal [], config.requires 16 | 17 | config.require "foo" 18 | config.require "foo/baz" 19 | assert_equal %w(foo foo/baz), config.requires 20 | assert config.requires.object_id != config.requires.object_id 21 | 22 | cgi = MotionBundler::Require.resolve "cgi", false 23 | html = MotionBundler::Require.resolve "cgi/html", false 24 | 25 | config.register({"cgi" => ["cgi/html"]}) 26 | assert_equal([cgi, html], config.files) 27 | assert_equal({cgi => [html]}, config.files_dependencies) 28 | 29 | cookie = MotionBundler::Require.resolve "cgi/cookie", false 30 | 31 | config.instance_variable_get(:@files_dependencies)[cgi].expects(:concat).with([cookie]) 32 | config.register({"cgi" => ["cgi/cookie"]}) 33 | end 34 | end 35 | 36 | end 37 | end -------------------------------------------------------------------------------- /test/unit/require/test_resolve.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../../test_helper", __FILE__) 2 | 3 | module Unit 4 | module Require 5 | class TestResolve < MiniTest::Unit::TestCase 6 | 7 | describe MotionBundler::Require::Resolve do 8 | 9 | module Require 10 | include MotionBundler::Require::Resolve 11 | extend self 12 | end 13 | 14 | it "should be able to resolve require paths" do 15 | MotionBundler::Require::Mocker.expects(:dirs).at_least_once.returns [ 16 | MotionBundler::Require::Mocker::APP_MOCKS, mocks_dir, MotionBundler::Require::Mocker::GEM_MOCKS 17 | ] 18 | assert_equal "foo", Require.resolve("foo") 19 | assert_equal "#{mocks_dir}/yaml.rb", Require.resolve("yaml") 20 | assert_equal "#{mocks_dir}/net/http.rb", Require.resolve("net/http") 21 | assert_equal "#{mocks_dir}/net/http.rb", Require.resolve("net/http.rb") 22 | assert_equal "#{mocks_dir}/psych_foo-0.1.0/psych/handler.rb", Require.resolve("psych/handler") 23 | assert_equal "#{mocks_dir}/psych_foo-0.1.0/psych/handler.rb", Require.resolve("psych/handler") 24 | assert_equal "#{MotionBundler::Require::Mocker::GEM_MOCKS}/httparty.rb", Require.resolve("httparty") 25 | 26 | require "cgi" 27 | cgi = $LOADED_FEATURES.detect{|x| x.include? "/cgi.rb"} 28 | 29 | assert_equal "cgi", Require.resolve("cgi") 30 | assert_equal cgi, Require.resolve("cgi", false) 31 | end 32 | 33 | end 34 | 35 | end 36 | end 37 | end -------------------------------------------------------------------------------- /lib/motion-bundler/simulator/core_ext.rb: -------------------------------------------------------------------------------- 1 | unless ENV["MB_SILENCE_CORE"] == "false" 2 | 3 | module Kernel 4 | def console 5 | defined?(MotionBundler::Simulator) ? MotionBundler::Simulator::Console : Class.new{ def warn(*args); end }.new 6 | end 7 | end 8 | 9 | class Object 10 | def require(name) 11 | console.warn { require name } 12 | end 13 | def require_relative(string) 14 | console.warn { require_relative string } 15 | end 16 | def load(*args) 17 | console.warn { load args[0] } 18 | end 19 | alias :original_instance_eval :instance_eval 20 | def instance_eval(*args, &block) 21 | if block_given? 22 | original_instance_eval &block 23 | else 24 | console.warn { call self.class.name, :instance_eval } 25 | end 26 | end 27 | end 28 | 29 | class Module 30 | def autoload(mod, filename) 31 | console.warn { autoload mod, filename } 32 | end 33 | alias :original_class_eval :class_eval 34 | def class_eval(*args, &block) 35 | if block_given? 36 | original_class_eval &block 37 | else 38 | console.warn { call name, :class_eval } 39 | end 40 | end 41 | alias :original_module_eval :module_eval 42 | def module_eval(*args, &block) 43 | if block_given? 44 | original_module_eval &block 45 | else 46 | console.warn { call name, :module_eval } 47 | end 48 | end 49 | end 50 | 51 | end 52 | 53 | class String 54 | def yellow 55 | colorize 33 56 | end 57 | def green 58 | colorize 32 59 | end 60 | def red 61 | colorize 31 62 | end 63 | private 64 | def colorize(color) 65 | "\e[0;#{color};49m#{self}\e[0m" 66 | end 67 | end -------------------------------------------------------------------------------- /lib/motion-bundler/require/resolve.rb: -------------------------------------------------------------------------------- 1 | module MotionBundler 2 | module Require 3 | module Resolve 4 | 5 | def resolve(path, only_mocks = true) 6 | base_path = path.gsub(/\.rb$/, "") 7 | 8 | load_paths(only_mocks).each do |load_path| 9 | if (file = Dir["#{load_path}/#{base_path}.rb"].first) 10 | return file 11 | end 12 | end 13 | 14 | path 15 | end 16 | 17 | private 18 | 19 | def load_paths(only_mocks) 20 | (Mocker.dirs + Mocker.dirs.collect_concat{|x| Dir["#{x}/*"]}).tap do |load_paths| 21 | load_paths.concat($LOAD_PATH + gem_paths) unless only_mocks 22 | end 23 | end 24 | 25 | class GemPath 26 | attr_reader :name, :version, :path, :version_numbers 27 | include Comparable 28 | 29 | def initialize(path) 30 | @name, @version = File.basename(path).scan(/^(.+?)-([^-]+)$/).flatten 31 | @path = path 32 | @version_numbers = @version.split(/[^0-9]+/).collect(&:to_i) 33 | end 34 | 35 | def <=>(other) 36 | raise "Not comparable gem paths ('#{name}' is not '#{other.name}')" unless name == other.name 37 | @version_numbers <=> other.version_numbers 38 | end 39 | end 40 | 41 | def gem_paths 42 | @gem_paths ||= Dir["{#{::Gem.paths.path.join(",")}}" + "/gems/*"].inject({}) do |gem_paths, path| 43 | gem_path = GemPath.new path 44 | gem_paths[gem_path.name] ||= gem_path 45 | gem_paths[gem_path.name] = gem_path if gem_paths[gem_path.name] < gem_path 46 | gem_paths 47 | end.values.collect do |gem_path| 48 | gem_path.path + "/lib" 49 | end.sort 50 | end 51 | 52 | end 53 | end 54 | end -------------------------------------------------------------------------------- /lib/motion-bundler/simulator/console.rb: -------------------------------------------------------------------------------- 1 | module MotionBundler 2 | module Simulator 3 | module Console 4 | 5 | class Warning 6 | def print 7 | if require_statement = [:require, :require_relative, :load, :autoload].include?(@method) 8 | return if MotionBundler::REQUIRES.include? @args.last 9 | end 10 | 11 | warning = "Warning Called `#{[@object, @method].compact.join "."}" 12 | warning += " #{@args.collect(&:inspect).join ", "}" unless @args.nil? || @args.empty? 13 | warning += "`" 14 | warning = warning.yellow 15 | 16 | if require_statement 17 | warning += "\nAdd within setup block: ".yellow 18 | warning += "app.require \"#{@args.last}\"".green 19 | elsif @message 20 | warning += "\n#{@message}".green 21 | end 22 | 23 | puts " #{warning.gsub("\n", "\n" + (" " * 11))}" 24 | end 25 | private 26 | def require(*args) 27 | @method = :require 28 | @args = args 29 | @method 30 | end 31 | def require_relative(*args) 32 | @method = :require_relative 33 | @args = args 34 | end 35 | def load(*args) 36 | @method = :load 37 | @args = args 38 | end 39 | def autoload(*args) 40 | @method = :autoload 41 | @args = args 42 | end 43 | def call(object, method) 44 | @object = object 45 | @method = method 46 | end 47 | def message(message) 48 | @message = message 49 | end 50 | end 51 | 52 | def self.warn(&block) 53 | warning = Warning.new 54 | warning.instance_eval &block 55 | warning.print 56 | end 57 | 58 | end 59 | end 60 | end -------------------------------------------------------------------------------- /test/unit/test_require.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../test_helper", __FILE__) 2 | 3 | module Unit 4 | class TestRequire < MiniTest::Unit::TestCase 5 | 6 | describe MotionBundler::Require do 7 | it "should be able to mock and trace requirements within a block" do 8 | object = mock "object" 9 | object.expects :do_something 10 | 11 | MotionBundler::Require.mock_and_trace do 12 | object.do_something 13 | end 14 | 15 | MotionBundler::Require::Tracer.expects :yield 16 | MotionBundler::Require.mock_and_trace do 17 | end 18 | 19 | MotionBundler::Require::Mocker.expects :yield 20 | MotionBundler::Require.mock_and_trace do 21 | end 22 | end 23 | 24 | it "should be able to trace requirements within a block" do 25 | object = mock "object" 26 | object.expects :do_something 27 | 28 | MotionBundler::Require.trace do 29 | object.do_something 30 | end 31 | 32 | MotionBundler::Require::Tracer.expects :yield 33 | MotionBundler::Require.trace do 34 | end 35 | end 36 | 37 | it "should be able to mock requirements within a block" do 38 | object = mock "object" 39 | object.expects :do_something 40 | 41 | MotionBundler::Require.mock do 42 | object.do_something 43 | end 44 | 45 | MotionBundler::Require::Mocker.expects :yield 46 | MotionBundler::Require.mock do 47 | end 48 | end 49 | 50 | it "should return log files, files_dependencies and requires" do 51 | MotionBundler::Require::Tracer.log.expects(:files) 52 | MotionBundler::Require.files 53 | 54 | MotionBundler::Require::Tracer.log.expects(:files_dependencies) 55 | MotionBundler::Require.files_dependencies 56 | 57 | MotionBundler::Require::Tracer.log.expects(:requires) 58 | MotionBundler::Require.requires 59 | end 60 | end 61 | 62 | end 63 | end -------------------------------------------------------------------------------- /test/unit/require/test_mocker.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../../test_helper", __FILE__) 2 | 3 | module Unit 4 | module Require 5 | class TestMocker < MiniTest::Unit::TestCase 6 | 7 | describe MotionBundler::Require::Mocker do 8 | 9 | describe "hooks" do 10 | it "should have MotionBundler::Require::Mocker::Hooks included" do 11 | assert_equal true, MotionBundler::Require::Mocker.included_modules.include?(MotionBundler::Require::Mocker::Hooks) 12 | end 13 | end 14 | 15 | describe "calling `yield`" do 16 | it "should start, yield and stop" do 17 | object = mock "object" 18 | object.expects :do_something 19 | 20 | MotionBundler::Require::Mocker.expects :start 21 | MotionBundler::Require::Mocker.expects :stop 22 | MotionBundler::Require::Mocker.yield do 23 | object.do_something 24 | end 25 | end 26 | 27 | describe "when error raised" do 28 | it "should stop" do 29 | MotionBundler::Require::Mocker.expects :start 30 | MotionBundler::Require::Mocker.expects :stop 31 | begin 32 | MotionBundler::Require::Mocker.yield do 33 | raise 34 | end 35 | rescue 36 | end 37 | end 38 | end 39 | end 40 | 41 | describe "calling `start`" do 42 | it "should hook into mock related core methods" do 43 | MotionBundler::Require::Mocker.expects :hook 44 | MotionBundler::Require::Mocker.send :start 45 | end 46 | end 47 | 48 | describe "calling `stop`" do 49 | it "should unhook from mock related core methods" do 50 | MotionBundler::Require::Mocker.expects :unhook 51 | MotionBundler::Require::Mocker.send :stop 52 | end 53 | end 54 | 55 | end 56 | 57 | end 58 | end 59 | end -------------------------------------------------------------------------------- /test/motion/simulator/test_core_ext.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../../test_helper", __FILE__) 2 | 3 | module Motion 4 | module Simulator 5 | class TestCoreExt < MiniTest::Unit::TestCase 6 | 7 | class Foo; end 8 | class Fu; end 9 | 10 | describe "lib/motion-bundler/simulator/core_ext.rb" do 11 | before do 12 | ENV["MB_SILENCE_CORE"] = "true" 13 | end 14 | 15 | it "should ignore require statements with a warning" do 16 | MotionBundler.expects(:simulator?).returns true 17 | last_loaded_feature = nil 18 | fu = Fu.new 19 | 20 | require MotionBundler.boot_file 21 | last_loaded_feature = $LOADED_FEATURES.last 22 | 23 | assert_nil require("a") 24 | assert_nil require_relative("../../lib/a") 25 | assert_nil load("../../lib/a.rb") 26 | 27 | class Foo 28 | autoload :A, "../../lib/a.rb" 29 | end 30 | 31 | Foo.module_eval "def bar; end" 32 | Foo.module_eval do 33 | def baz 34 | end 35 | end 36 | 37 | Fu.class_eval "def bar; end" 38 | Fu.class_eval do 39 | def baz 40 | end 41 | end 42 | 43 | fu.instance_eval "def qux; end" 44 | fu.instance_eval do 45 | def quux 46 | end 47 | end 48 | 49 | assert_equal last_loaded_feature, $LOADED_FEATURES.last 50 | assert_raises NameError do 51 | A 52 | end 53 | assert_raises NameError do 54 | Foo::A 55 | end 56 | 57 | assert_equal false, Foo.new.respond_to?(:bar) 58 | assert_equal true , Foo.new.respond_to?(:baz) 59 | assert_equal false, Fu.new.respond_to?(:bar) 60 | assert_equal true , Fu.new.respond_to?(:baz) 61 | assert_equal false, fu.respond_to?(:qux) 62 | assert_equal true , fu.respond_to?(:quux) 63 | end 64 | end 65 | 66 | end 67 | end 68 | end -------------------------------------------------------------------------------- /lib/motion-bundler/require/ripper.rb: -------------------------------------------------------------------------------- 1 | require "set" 2 | require "motion-bundler/require/ripper/builder" 3 | 4 | module MotionBundler 5 | module Require 6 | class Ripper 7 | 8 | def initialize(sources, scope = MotionBundler::PROJECT_PATH) 9 | @sources = sources 10 | @scope = scope 11 | @files = Set.new 12 | @files_dependencies = {} 13 | @requires = {} 14 | parse 15 | end 16 | 17 | def files 18 | @files.to_a 19 | end 20 | 21 | def files_dependencies 22 | @files_dependencies.dup 23 | end 24 | 25 | def requires 26 | @requires.dup 27 | end 28 | 29 | private 30 | 31 | def parse 32 | @sources.each do |source| 33 | next if @requires.include?(source) 34 | added_sources = [] 35 | 36 | builder = Builder.new source 37 | builder.requires.each do |method, args| 38 | 39 | (@requires[source] ||= []) << args[0] 40 | ((arg = args[0]).include?("*.rb") ? Dir[arg] : [arg]).each do |file| 41 | file = begin 42 | if method == :require_relative 43 | File.expand_path("../#{file}.rb", source) 44 | else 45 | Require.resolve(file, false) 46 | end 47 | end 48 | 49 | @files << source 50 | @files << file 51 | 52 | (@files_dependencies[source] ||= []) << file 53 | expanded_path = File.expand_path(file) 54 | 55 | unless @sources.any?{|x| File.expand_path(x) == expanded_path} 56 | if expanded_path.match(/^#{@scope}/) 57 | added_sources << file 58 | else 59 | MotionBundler.app_require file 60 | end 61 | end 62 | end 63 | end 64 | 65 | unless added_sources.empty? 66 | @sources.concat added_sources 67 | parse 68 | return 69 | end 70 | end 71 | end 72 | 73 | end 74 | end 75 | end -------------------------------------------------------------------------------- /test/gems/slot_machine/test/unit/test_time_slots.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../test_helper", __FILE__) 2 | 3 | module Unit 4 | class TestTimeSlots < MiniTest::Unit::TestCase 5 | 6 | describe TimeSlots do 7 | it "should merge slots on initialization" do 8 | time_slots = TimeSlots.new 1015..1100, 1100..1115 9 | assert_equal [TimeSlot.new(1015..1115)], time_slots.range_slots 10 | end 11 | 12 | it "should be able to add slots" do 13 | time_slot = TimeSlot.new 1015..1100 14 | 15 | assert_equal TimeSlots.new(1015..1130), (time_slot + (1055..1130)) 16 | assert_equal TimeSlots.new(1015..1130), (time_slot + TimeSlot.new(1055..1130)) 17 | assert_equal TimeSlots.new(1015..1130, 1145..1200), (time_slot + TimeSlots.new(1055..1130, 1145..1200)) 18 | end 19 | 20 | it "should be able to subtract slots" do 21 | time_slot = TimeSlot.new 1015..1100 22 | 23 | assert_equal TimeSlots.new(1015..1030, 1045..1100), (time_slot - (1030..1045)) 24 | assert_equal TimeSlots.new(1015..1055), (time_slot - TimeSlot.new(1055..1130)) 25 | assert_equal TimeSlots.new(1015..1045, 1055..1100), (time_slot - TimeSlots.new(1045..1055, 1145..1200)) 26 | end 27 | 28 | it "should be able to match available slots" do 29 | time_slots = TimeSlots.new(1015..1100, 1230..1337) 30 | 31 | assert_equal [ 32 | TimeSlot.new(1015..1035), 33 | TimeSlot.new(1030..1050), 34 | TimeSlot.new(1230..1250), 35 | TimeSlot.new(1245..1305), 36 | TimeSlot.new(1300..1320), 37 | TimeSlot.new(1315..1335) 38 | ], time_slots.match(20) 39 | 40 | assert_equal [ 41 | TimeSlot.new(1015..1035), 42 | TimeSlot.new(1025..1045), 43 | TimeSlot.new(1035..1055), 44 | TimeSlot.new(1230..1250), 45 | TimeSlot.new(1240..1300), 46 | TimeSlot.new(1250..1310), 47 | TimeSlot.new(1300..1320), 48 | TimeSlot.new(1310..1330) 49 | ], time_slots.match(20, 10) 50 | end 51 | end 52 | 53 | end 54 | end -------------------------------------------------------------------------------- /test/unit/simulator/test_console.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../../test_helper", __FILE__) 2 | require "motion-bundler/simulator/console" 3 | 4 | module Unit 5 | module Simulator 6 | class TestConsole < MiniTest::Unit::TestCase 7 | 8 | describe MotionBundler::Simulator::Console do 9 | before do 10 | MotionBundler.send :remove_const, :REQUIRES if defined?(MotionBundler::REQUIRES) 11 | MotionBundler::REQUIRES = ["baz"] 12 | end 13 | 14 | after do 15 | MotionBundler.send :remove_const, :REQUIRES 16 | end 17 | 18 | it "should print warnings as expected" do 19 | String.class_eval do 20 | alias :yellow :to_s 21 | alias :green :to_s 22 | end 23 | assert_output " Warning Called `require \"foo\"`\n Add within setup block: app.require \"foo\"\n" do 24 | MotionBundler::Simulator::Console.warn do 25 | require "foo" 26 | end 27 | end 28 | assert_output "" do 29 | MotionBundler::Simulator::Console.warn do 30 | require "baz" 31 | end 32 | end 33 | assert_output " Warning Called `require_relative \"foo\"`\n Add within setup block: app.require \"foo\"\n" do 34 | MotionBundler::Simulator::Console.warn do 35 | require_relative "foo" 36 | end 37 | end 38 | assert_output " Warning Called `load \"foo\"`\n Add within setup block: app.require \"foo\"\n" do 39 | MotionBundler::Simulator::Console.warn do 40 | load "foo" 41 | end 42 | end 43 | assert_output " Warning Called `autoload :Foo, \"foo\"`\n Add within setup block: app.require \"foo\"\n" do 44 | MotionBundler::Simulator::Console.warn do 45 | autoload :Foo, "foo" 46 | end 47 | end 48 | assert_output " Warning Called `Foo.bar`\n Don't do that!\n" do 49 | MotionBundler::Simulator::Console.warn do 50 | call "Foo", :bar 51 | message "Don't do that!" 52 | end 53 | end 54 | end 55 | end 56 | 57 | end 58 | end 59 | end -------------------------------------------------------------------------------- /test/unit/simulator/test_core_ext.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../../test_helper", __FILE__) 2 | require "motion-bundler/simulator/console" 3 | 4 | module Unit 5 | module Simulator 6 | class TestCoreExt < MiniTest::Unit::TestCase 7 | 8 | describe "lib/motion-bundler/simulator/core_ext.rb" do 9 | before do 10 | ENV["MB_SILENCE_CORE"] = "true" 11 | end 12 | 13 | it "should override core methods as expected" do 14 | assert_equal false, Kernel.respond_to?(:console) 15 | assert_equal false, Kernel.respond_to?(:_ruby_require) 16 | 17 | taintable_core do 18 | MotionBundler::Simulator::Console.expects(:warn).at_least_once 19 | 20 | load motion_bundler_file("motion-bundler/simulator/core_ext.rb") 21 | assert_equal true, Kernel.respond_to?(:console) 22 | assert_equal true, Kernel.respond_to?(:_ruby_require) 23 | 24 | require "foo" 25 | require_relative "foo/bar" 26 | load "foo/baz" 27 | autoload :Qux, "foo/qux" 28 | 29 | Unit.require "foo" 30 | Unit.require_relative "foo/bar" 31 | Unit.load "foo/baz" 32 | Unit.autoload :Qux, "foo/qux" 33 | 34 | TestCoreExt.class_eval "def foo; end" 35 | TestCoreExt.module_eval "def foo; end" 36 | TestCoreExt.module_eval do 37 | def foo; end 38 | end 39 | Object.new.instance_eval "def foo; end" 40 | end 41 | 42 | assert_equal false, Kernel.respond_to?(:console) 43 | assert_equal false, Kernel.respond_to?(:_ruby_require) 44 | end 45 | 46 | it "should be able to colorize messages" do 47 | string = "MESSAGE" 48 | 49 | assert_equal "\e[0;1982;49mMESSAGE\e[0m", string.send(:colorize, 1982) 50 | assert_equal "\e[0;33;49mMESSAGE\e[0m", string.yellow 51 | assert_equal "\e[0;32;49mMESSAGE\e[0m", string.green 52 | assert_equal "\e[0;31;49mMESSAGE\e[0m", string.red 53 | 54 | string.expects(:colorize).with(33) 55 | string.yellow 56 | 57 | string.expects(:colorize).with(32) 58 | string.green 59 | 60 | string.expects(:colorize).with(31) 61 | string.red 62 | end 63 | end 64 | 65 | end 66 | end 67 | end -------------------------------------------------------------------------------- /lib/motion-bundler/require/tracer/log.rb: -------------------------------------------------------------------------------- 1 | require "yaml" 2 | 3 | module MotionBundler 4 | module Require 5 | module Tracer 6 | class Log 7 | 8 | def initialize 9 | @files = Set.new 10 | @files_dependencies = {} 11 | @requires = {} 12 | end 13 | 14 | def clear 15 | @files.clear 16 | @files_dependencies.clear 17 | @requires.clear 18 | end 19 | 20 | def files 21 | @files.to_a 22 | end 23 | 24 | def files_dependencies 25 | @files_dependencies.dup 26 | end 27 | 28 | def requires 29 | @requires.dup 30 | end 31 | 32 | def register(file, path) 33 | return if !file.match(/^([A-Z]+)$/) && !file.match(/^(.*\.rb):(\d+)/) 34 | 35 | file = $1.include?("/bundler/") ? "BUNDLER" : $1 36 | line = $1.include?("/bundler/") ? nil : $2 37 | 38 | (@requires[[file, line].compact.join(":")] ||= []) << path 39 | dependencies = (@files_dependencies[file] ||= []) 40 | index = dependencies.size 41 | 42 | if yield 43 | loaded_feature = loaded_features.last 44 | @files << file unless @files.include? file 45 | @files << loaded_feature 46 | dependencies.insert index, loaded_feature 47 | else 48 | @files_dependencies.delete file if dependencies.empty? 49 | if file.match(/^([A-Z]+)$/) 50 | if $CLI 51 | ripper = Require::Ripper.new [path], "/" 52 | merge! ripper.files, ripper.files_dependencies, ripper.requires 53 | else 54 | tracer = YAML.load `motion-bundler trace #{path}` 55 | merge! *tracer.values_at("FILES", "FILES_DEPENDENCIES", "REQUIRES") 56 | end 57 | end 58 | end 59 | 60 | true 61 | end 62 | 63 | def merge!(files, files_dependencies, requires) 64 | @files.merge files 65 | files_dependencies.each do |file, dependencies| 66 | (@files_dependencies[file] ||= []).concat dependencies 67 | end 68 | requires.each do |file, paths| 69 | (@requires[file] ||= []).concat paths 70 | end 71 | end 72 | 73 | private 74 | 75 | def loaded_features 76 | $LOADED_FEATURES 77 | end 78 | 79 | end 80 | end 81 | end 82 | end -------------------------------------------------------------------------------- /CHANGELOG.rdoc: -------------------------------------------------------------------------------- 1 | = MotionBundler CHANGELOG 2 | 3 | == Version 0.2.1 (July 15, 2013) 4 | 5 | * Being able to pass options[:headers] to HTTParty 6 | * Corrected handling options[:body] within HTTParty 7 | 8 | == Version 0.2.0 (July 7, 2013) 9 | 10 | * Added (RubyMotion) OSX support 11 | * Making app/app_delegate.rb dependent of boot.rb when existing 12 | 13 | == Version 0.1.7 (June 19, 2013) 14 | 15 | * Added MotionBundler::CLI which outputs files, files dependencies and requires as YAML (usage: `bundle exec motion-bundler trace cgi`) 16 | * Only resolving paths within $LOAD_PATH and gems paths when explicitly requested (only resolving mocks at default) 17 | * Being able to pass a "scope path" to Ripper.new which determines whether to immediately "rip" an analyzed Ruby source file 18 | * Being able to trace files which are already required (thanks Geoff Ereth for issuing) 19 | * Not rescuing LoadError when hooking into the `require` method 20 | * Renamed REQUIRED to REQUIRES 21 | * Corrected merging ripper with tracer files dependencies 22 | * Introduced app.register in case of correcting missing require statements e.g. in REXML (thanks Jeff Enderwick @jeff7091 for issuing) 23 | 24 | == Version 0.1.6 (June 14, 2013) 25 | 26 | * Removed optional `strict` argument within Require.resolve 27 | * Also mocking within `MotionBundler.tracer_require` (thanks Li Jie for issuing, see https://github.com/archan937/motion-bundler/issues/7) 28 | 29 | == Version 0.1.5 (May 30, 2013) 30 | 31 | * Being able to require multiple files (e.g. `require "./config/**/*.rb"` within `app/app_delegate.rb`) 32 | * Expanding paths when registering files and files dependencies 33 | * Corrected files order when registering 34 | * Fixed `duplicate symbol` error (for real this time) 35 | 36 | == Version 0.1.4 (May 29, 2013) 37 | 38 | * Not using colorize as gem dependency anymore: defining String#yellow, String#green, String#red in MotionBundler itself 39 | * Fixed `duplicate symbol` error caused when requiring a file within `Dir["app/**/*.rb"]` 40 | 41 | == Version 0.1.3 (May 28, 2013) 42 | 43 | * Altered `simulator?` (and thus `device?`) determination (in simulator when ARGV is empty) 44 | 45 | == Version 0.1.2 (May 20, 2013) 46 | 47 | * Updated mocked HTTParty module: 48 | 49 | * Support passing block when sending request 50 | * Support asynchronous requests 51 | 52 | == Version 0.1.1 (May 15, 2013) 53 | 54 | * Fixed device/core_ext.rb when running `rake device` 55 | 56 | == Version 0.1.0 (May 12, 2013) 57 | 58 | * Initial release -------------------------------------------------------------------------------- /test/unit/require/tracer/test_hooks.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../../../test_helper", __FILE__) 2 | 3 | module Unit 4 | module Require 5 | module Tracer 6 | class TestHooks < MiniTest::Unit::TestCase 7 | 8 | describe MotionBundler::Require::Tracer::Hooks do 9 | it "should hook into core methods" do 10 | assert_equal false, Kernel.respond_to?(:require_with_mb_trace) 11 | assert_equal false, Object.respond_to?(:require_with_mb_trace) 12 | assert_equal false, Kernel.respond_to?(:require_relative_with_mb_trace) 13 | assert_equal false, Object.respond_to?(:require_relative_with_mb_trace) 14 | assert_equal false, Kernel.respond_to?(:load_with_mb_trace) 15 | assert_equal false, Object.respond_to?(:load_with_mb_trace) 16 | 17 | MotionBundler::Require::Tracer.hook 18 | assert_equal true, Kernel.respond_to?(:require_with_mb_trace) 19 | assert_equal true, Object.respond_to?(:require_with_mb_trace) 20 | assert_equal true, Kernel.respond_to?(:require_relative_with_mb_trace) 21 | assert_equal true, Object.respond_to?(:require_relative_with_mb_trace) 22 | assert_equal true, Kernel.respond_to?(:load_with_mb_trace) 23 | assert_equal true, Object.respond_to?(:load_with_mb_trace) 24 | 25 | MotionBundler::Require::Tracer.log.expects(:register).with("#{__FILE__}:#{__LINE__ + 1}:in `block (2 levels) in '", "a") 26 | require "a" 27 | 28 | MotionBundler::Require::Tracer.log.expects(:register).with("#{__FILE__}:#{__LINE__ + 1}:in `block (2 levels) in '", "../../../lib/b") 29 | require_relative "../../../lib/b" 30 | 31 | MotionBundler::Require::Tracer.log.expects(:register).with("#{__FILE__}:#{__LINE__ + 1}:in `block (2 levels) in '", "c.rb") 32 | load "c.rb" 33 | 34 | MotionBundler::Require::Tracer.log.expects(:register).with("#{__FILE__}:#{__LINE__ + 2}:in `'", gem_path("slot_machine/lib/slot_machine/version")) 35 | module ::SlotMachine 36 | autoload :VERSION, gem_path("slot_machine/lib/slot_machine/version") 37 | end 38 | 39 | MotionBundler::Require::Tracer.unhook 40 | assert_equal false, Kernel.respond_to?(:require_with_mb_trace) 41 | assert_equal false, Object.respond_to?(:require_with_mb_trace) 42 | assert_equal false, Kernel.respond_to?(:require_relative_with_mb_trace) 43 | assert_equal false, Object.respond_to?(:require_relative_with_mb_trace) 44 | assert_equal false, Kernel.respond_to?(:load_with_mb_trace) 45 | assert_equal false, Object.respond_to?(:load_with_mb_trace) 46 | end 47 | end 48 | 49 | end 50 | end 51 | end 52 | end -------------------------------------------------------------------------------- /test/gems/slot_machine/test/unit/test_time_slot.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../test_helper", __FILE__) 2 | 3 | require "time" 4 | 5 | module Unit 6 | class TestTimeSlot < MiniTest::Unit::TestCase 7 | 8 | describe TimeSlot do 9 | it "should have the expected default interval" do 10 | assert_equal 15, TimeSlot.default_interval 11 | end 12 | 13 | it "should validate the assigned value" do 14 | assert TimeSlot.new(855..859) 15 | assert_raises ArgumentError do 16 | TimeSlot.new 855..860 17 | end 18 | end 19 | 20 | it "should accept time objects" do 21 | assert_equal [ 22 | 198208010859, 23 | 198208010900, 24 | 198208010901, 25 | 198208010902, 26 | 198208010903, 27 | 198208010904 28 | ], TimeSlot.new(Time.parse("1982-08-01 08:59")..Time.parse("1982-08-01 09:05")).send(:to_compared) 29 | end 30 | 31 | it "should represent itself as an array when invoking to_compared" do 32 | assert_equal 10 , TimeSlot.new(855..905).send(:to_compared).size 33 | assert_equal 222, TimeSlot.new(955..1337).send(:to_compared).size 34 | assert_equal [855, 856, 857, 858, 859, 900, 901, 902, 903, 904], TimeSlot.new(855..905).send(:to_compared) 35 | end 36 | 37 | it "should be able to match available slots" do 38 | time_slot = TimeSlot.new 1015..1200 39 | 40 | assert_equal [], time_slot.match(120) 41 | assert_equal [TimeSlot.new(1015..1155)], time_slot.match(100) 42 | assert_equal [TimeSlot.new(1015..1200)], time_slot.match(105) 43 | assert_equal [TimeSlot.new(1015..1145), TimeSlot.new(1030..1200)], time_slot.match(90) 44 | 45 | assert_equal [ 46 | TimeSlot.new(1015..1140), 47 | TimeSlot.new(1025..1150), 48 | TimeSlot.new(1035..1200) 49 | ], time_slot.match(85, 10) 50 | 51 | assert_equal [ 52 | TimeSlot.new(1015..1055), 53 | TimeSlot.new(1033..1113), 54 | TimeSlot.new(1051..1131), 55 | TimeSlot.new(1109..1149) 56 | ], time_slot.match(40, 18) 57 | 58 | assert_equal [ 59 | TimeSlot.new(198208011015..198208011055), 60 | TimeSlot.new(198208011033..198208011113), 61 | TimeSlot.new(198208011051..198208011131), 62 | TimeSlot.new(198208011109..198208011149) 63 | ], TimeSlot.new(Time.parse("1982-08-01 10:15")..Time.parse("1982-08-01 12:00")).match(40, 18) 64 | 65 | assert_equal [], time_slot.match(945..1005) 66 | assert_equal [], time_slot.match(1205..1225) 67 | assert_equal [TimeSlot.new(1015..1200)], time_slot.match(1015..1200) 68 | assert_equal [TimeSlot.new(1015..1200)], time_slot.match(900..1300) 69 | assert_equal [TimeSlot.new(1015..1030)], time_slot.match(900..1030) 70 | assert_equal [TimeSlot.new(1155..1200)], time_slot.match(1155..1300) 71 | end 72 | end 73 | 74 | end 75 | end -------------------------------------------------------------------------------- /test/motion/simulator/test_setup.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../../test_helper", __FILE__) 2 | 3 | motion_gemfile <<-G 4 | gem "motion-bundler", :path => "#{motion_bundler_file ".."}" 5 | group :motion do 6 | gem "slot_machine", :path => "#{gem_path "slot_machine"}" 7 | end 8 | G 9 | 10 | module Motion 11 | module Simulator 12 | class TestSetup < MiniTest::Unit::TestCase 13 | 14 | describe "MotionBundler.setup" do 15 | it "should register files and files_dependencies to the RubyMotion app" do 16 | assert_raises NameError do 17 | SlotMachine 18 | end 19 | 20 | Motion::Project::App.any_instance.expects(:files).returns %w( 21 | /Users/paulengel/foo.rb 22 | /Users/paulengel/bar.rb 23 | ) 24 | 25 | Motion::Project::App.any_instance.expects(:files=).with(expand_paths [ 26 | MotionBundler::MOTION_BUNDLER_FILE, 27 | motion_bundler_file("motion-bundler/simulator/boot.rb"), 28 | motion_bundler_file("motion-bundler/simulator/core_ext.rb"), 29 | motion_bundler_file("motion-bundler/simulator/console.rb"), 30 | gem_path("slot_machine/lib/slot_machine.rb"), 31 | gem_path("slot_machine/lib/slot_machine/version.rb"), 32 | gem_path("slot_machine/lib/slot_machine/slot.rb"), 33 | gem_path("slot_machine/lib/slot_machine/slots.rb"), 34 | gem_path("slot_machine/lib/slot.rb"), 35 | gem_path("slot_machine/lib/slots.rb"), 36 | gem_path("slot_machine/lib/time_slot.rb"), 37 | gem_path("slot_machine/lib/time_slots.rb"), 38 | lib_file("a.rb"), 39 | lib_file("b/a/a.rb"), 40 | "/Users/paulengel/foo.rb", 41 | "/Users/paulengel/bar.rb" 42 | ]) 43 | 44 | Motion::Project::App.any_instance.expects(:files_dependencies).with(expand_paths({ 45 | motion_bundler_file("motion-bundler/simulator/boot.rb") => [ 46 | motion_bundler_file("motion-bundler/simulator/core_ext.rb"), 47 | motion_bundler_file("motion-bundler/simulator/console.rb") 48 | ], 49 | gem_path("slot_machine/lib/slot_machine.rb") => [ 50 | motion_bundler_file("motion-bundler/simulator/boot.rb"), 51 | gem_path("slot_machine/lib/slot_machine/version.rb"), 52 | gem_path("slot_machine/lib/slot_machine/slot.rb"), 53 | gem_path("slot_machine/lib/slot_machine/slots.rb"), 54 | gem_path("slot_machine/lib/slot.rb"), 55 | gem_path("slot_machine/lib/slots.rb"), 56 | gem_path("slot_machine/lib/time_slot.rb"), 57 | gem_path("slot_machine/lib/time_slots.rb") 58 | ] 59 | })) 60 | 61 | MotionBundler.setup do |app| 62 | app.require "a" 63 | app.require "b/a/a" 64 | end 65 | 66 | assert SlotMachine 67 | end 68 | end 69 | 70 | end 71 | end 72 | end -------------------------------------------------------------------------------- /test/test_helper.rb: -------------------------------------------------------------------------------- 1 | $:.unshift File.expand_path("../lib", __FILE__) 2 | $:.unshift File.expand_path("../../lib", __FILE__) 3 | 4 | require_relative "test_helper/coverage" 5 | require_relative "test_helper/motion" 6 | 7 | require "fileutils" 8 | require "minitest/unit" 9 | require "minitest/autorun" 10 | require "mocha/setup" 11 | require "motion-bundler" 12 | 13 | class MiniTest::Unit::TestCase 14 | def setup 15 | ENV["MB_SILENCE_CORE"] = "false" 16 | end 17 | def teardown 18 | if File.exists?(file = MotionBundler::MOTION_BUNDLER_FILE) 19 | File.delete file 20 | end 21 | end 22 | end 23 | 24 | def motion_bundler_file(path) 25 | File.expand_path "../../lib/#{path}", __FILE__ 26 | end 27 | 28 | def gem_path(name) 29 | File.expand_path "../gems/#{name}", __FILE__ 30 | end 31 | 32 | def lib_file(path) 33 | File.expand_path "../lib/#{path}", __FILE__ 34 | end 35 | 36 | def mocks_dir 37 | File.expand_path "../mocks", __FILE__ 38 | end 39 | 40 | def expand_paths(paths) 41 | if paths.is_a? Array 42 | paths.collect{|v| File.expand_path v} 43 | else 44 | paths.inject({}) do |h, (k, v)| 45 | h[File.expand_path(k)] = expand_paths(v) 46 | h 47 | end 48 | end 49 | end 50 | 51 | def motion_gemfile(content) 52 | content = "source \"https://rubygems.org\"\n\n#{content}" 53 | dirname = File.dirname __FILE__ 54 | 55 | gemfile = begin 56 | File.expand_path(caller[0]).match /^(.*)\.rb\b/ 57 | $1.gsub "#{dirname}/motion", "#{dirname}/.gemfiles" 58 | end 59 | lockfile = "#{gemfile}.lock" 60 | 61 | if !File.exists?(gemfile) || File.read(gemfile) != content 62 | FileUtils.mkdir_p File.dirname(gemfile) 63 | File.open(gemfile, "w"){|f| f.write content} 64 | File.delete(lockfile) if File.exists?(lockfile) 65 | end 66 | 67 | unless File.exists?(lockfile) 68 | puts "Running `bundle install` for #{gemfile.gsub("#{dirname}/", "")}\n\n" 69 | `cd #{File.dirname gemfile} && bundle install --gemfile=#{gemfile} --quiet` 70 | end 71 | 72 | ENV["BUNDLE_GEMFILE"] = gemfile 73 | require "bundler" 74 | Bundler.setup 75 | end 76 | 77 | def taintable_core 78 | Kernel.instance_eval do 79 | alias :_ruby_require :require 80 | alias :_ruby_require_relative :require_relative 81 | alias :_ruby_load :load 82 | alias :_ruby_autoload :autoload 83 | end 84 | Module.class_eval do 85 | alias :_ruby_autoload :autoload 86 | alias :_ruby_class_eval :class_eval 87 | alias :_ruby_module_eval :module_eval 88 | end 89 | Object.class_eval do 90 | alias :_ruby_require :require 91 | alias :_ruby_instance_eval :instance_eval 92 | end 93 | yield 94 | ensure 95 | Kernel.instance_eval do 96 | alias :require :_ruby_require 97 | alias :require_relative :_ruby_require_relative 98 | alias :load :_ruby_load 99 | alias :autoload :_ruby_autoload 100 | undef :_ruby_require 101 | undef :_ruby_require_relative 102 | undef :_ruby_load 103 | undef :_ruby_autoload 104 | undef :console if respond_to?(:console) 105 | end 106 | Module.class_eval do 107 | alias :autoload :_ruby_autoload 108 | alias :class_eval :_ruby_class_eval 109 | alias :module_eval :_ruby_module_eval 110 | undef :_ruby_autoload 111 | undef :_ruby_class_eval 112 | undef :_ruby_module_eval 113 | end 114 | Object.class_eval do 115 | alias :require :_ruby_require 116 | alias :instance_eval :_ruby_instance_eval 117 | undef :_ruby_require 118 | undef :_ruby_instance_eval 119 | end 120 | end -------------------------------------------------------------------------------- /test/unit/require/test_tracer.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../../test_helper", __FILE__) 2 | 3 | module Unit 4 | module Require 5 | class TestTracer < MiniTest::Unit::TestCase 6 | 7 | describe MotionBundler::Require::Tracer do 8 | 9 | describe "hooks" do 10 | it "should have MotionBundler::Require::Tracer::Hooks included" do 11 | assert_equal true, MotionBundler::Require::Tracer.included_modules.include?(MotionBundler::Require::Tracer::Hooks) 12 | end 13 | end 14 | 15 | describe "calling `yield`" do 16 | it "should start, yield and stop" do 17 | object = mock "object" 18 | object.expects :do_something 19 | 20 | MotionBundler::Require::Tracer.expects :start 21 | MotionBundler::Require::Tracer.expects :stop 22 | MotionBundler::Require::Tracer.yield do 23 | object.do_something 24 | end 25 | end 26 | 27 | describe "when error raised" do 28 | it "should stop" do 29 | MotionBundler::Require::Tracer.expects :start 30 | MotionBundler::Require::Tracer.expects :stop 31 | begin 32 | MotionBundler::Require::Tracer.yield do 33 | raise 34 | end 35 | rescue 36 | end 37 | end 38 | end 39 | end 40 | 41 | describe "calling `start`" do 42 | it "should create a log instance" do 43 | Thread.current[:motion_bundler_log] = nil 44 | MotionBundler::Require::Tracer.send :start 45 | assert_equal MotionBundler::Require::Tracer::Log, Thread.current[:motion_bundler_log].class 46 | assert_equal MotionBundler::Require::Tracer .log, Thread.current[:motion_bundler_log] 47 | end 48 | 49 | describe "having the log instance already defined" do 50 | it "should clear the log instance" do 51 | MotionBundler::Require::Tracer.send :start 52 | MotionBundler::Require::Tracer.log.expects(:clear) 53 | MotionBundler::Require::Tracer.send :start 54 | end 55 | end 56 | 57 | it "should hook into require relatied core methods" do 58 | MotionBundler::Require::Tracer.expects :hook 59 | MotionBundler::Require::Tracer.send :start 60 | end 61 | end 62 | 63 | describe "calling `stop`" do 64 | it "should unhook form require relatied core methods" do 65 | MotionBundler::Require::Tracer.expects :unhook 66 | MotionBundler::Require::Tracer.send :stop 67 | end 68 | end 69 | 70 | describe "tracing require statements" do 71 | it "should log dependencies as expected" do 72 | assert_equal({}, MotionBundler::Require::Tracer.log.instance_variable_get(:@files_dependencies)) 73 | 74 | MotionBundler::Require::Tracer.yield do 75 | require "d/a" 76 | require "d/b" 77 | require "d/c" 78 | end 79 | 80 | assert_equal({ 81 | __FILE__ => [ 82 | lib_file("d/a.rb"), 83 | lib_file("d/b.rb"), 84 | lib_file("d/c.rb") 85 | ], 86 | lib_file("d/b.rb") => [ 87 | lib_file("d/b/a.rb"), 88 | lib_file("d/b/b.rb") 89 | ], 90 | lib_file("d/b/a.rb") => [ 91 | lib_file("d/b/a/a.rb") 92 | ] 93 | }, MotionBundler::Require::Tracer.log.instance_variable_get(:@files_dependencies)) 94 | end 95 | end 96 | 97 | end 98 | 99 | end 100 | end 101 | end -------------------------------------------------------------------------------- /sample_apps/osx/app/tests.rb: -------------------------------------------------------------------------------- 1 | require "stringio" 2 | require "strscan" 3 | require "zlib" 4 | require "httparty" 5 | require "net/pop" 6 | require "rexml/document" 7 | require "socket" 8 | 9 | require_relative "../lib/foo" 10 | 11 | class AppDelegate 12 | 13 | def runTests 14 | 15 | # Testing SlotMachine 16 | 17 | ts = TimeSlot.new 1015..1045 18 | p ts.match 10 19 | p ts.match 10, 5 20 | 21 | # Testing StringIO 22 | 23 | s = StringIO.new %{This is a test of a string as a file.\r\nAnd this could be another line in the file} 24 | p s.gets 25 | p s.read(17) 26 | 27 | # Testing StringScanner 28 | 29 | s = StringScanner.new "ab" 30 | p s.getch 31 | p s.getch 32 | p s.getch 33 | 34 | # Testing Zlib 35 | 36 | data = "x\234\355\301\001\001\000\000\000\200\220\376\257\356\b\n#{"\000" * 31}\030\200\000\000\001" 37 | zipped = Zlib::Inflate.inflate data 38 | p zipped == ("\000" * 32 * 1024) 39 | 40 | # Testing (mocked) HTTParty 41 | 42 | p HTTParty.hi! 43 | 44 | # Testing (mocked) Net::Protocol 45 | 46 | p Net::Protocol.hi! 47 | 48 | # Testing Foo and Foo::Bar 49 | 50 | p Foo.foo! 51 | p Foo::Bar.bar! 52 | 53 | # Testing at runtime require statement 54 | 55 | file = "base64" 56 | require file 57 | enc = Base64.encode64("Send reinforcements!") 58 | p enc 59 | p Base64.decode64(enc) 60 | 61 | # Testing REXML::Document 62 | 63 | xml = <<-XML 64 | 65 |
66 | 67 | Invisibility Cream 68 | 14.50 69 | Makes you invisible 70 | 71 | 72 | Levitation Salve 73 | 23.99 74 | Levitate yourself for up to 3 hours per application 75 | 76 |
77 |
78 | 79 | Blork and Freen Instameal 80 | 4.95 81 | A tasty meal in a tablet; just add water 82 | 83 | 84 | Grob winglets 85 | 3.56 86 | Tender winglets of Grob. Just add water 87 | 88 |
89 |
90 | XML 91 | 92 | doc = REXML::Document.new xml 93 | root = doc.root 94 | 95 | doc.elements.each("inventory/section"){|element| puts element.attributes["name"]} 96 | # => health 97 | # => food 98 | doc.elements.each("*/section/item"){|element| puts element.attributes["upc"]} 99 | # => 123456789 100 | # => 445322344 101 | # => 485672034 102 | # => 132957764 103 | 104 | puts root.attributes["title"] 105 | # => OmniCorp Store #45x10^3 106 | puts root.elements["section/item[@stock='44']"].attributes["upc"] 107 | # => 132957764 108 | puts root.elements["section"].attributes["name"] 109 | # => health (returns the first encountered matching element) 110 | puts root.elements[1].attributes["name"] 111 | # => health (returns the FIRST child element) 112 | 113 | # Say thanks 114 | 115 | alert = NSAlert.new 116 | alert.messageText = "Thanks!\n\nHi, thanks for giving MotionBundler a try! Any form of collaboration is very welcome.\n\nGreets,\nPaul Engel\n@archan937" 117 | 118 | count = alert.messageText.split("\n").collect(&:size).max + 2 119 | puts ["", "=" * count, "", alert.messageText, "", "=" * count] 120 | 121 | alert.runModal 122 | 123 | end 124 | 125 | end -------------------------------------------------------------------------------- /sample_apps/ios/app/controllers/app_controller.rb: -------------------------------------------------------------------------------- 1 | require "stringio" 2 | require "strscan" 3 | require "zlib" 4 | require "httparty" 5 | require "net/pop" 6 | require "rexml/document" 7 | require "socket" 8 | 9 | require_relative "../../lib/foo" 10 | 11 | class AppController < UIViewController 12 | def viewDidLoad 13 | 14 | # Testing SlotMachine 15 | 16 | ts = TimeSlot.new 1015..1045 17 | p ts.match 10 18 | p ts.match 10, 5 19 | 20 | # Testing StringIO 21 | 22 | s = StringIO.new %{This is a test of a string as a file.\r\nAnd this could be another line in the file} 23 | p s.gets 24 | p s.read(17) 25 | 26 | # Testing StringScanner 27 | 28 | s = StringScanner.new "ab" 29 | p s.getch 30 | p s.getch 31 | p s.getch 32 | 33 | # Testing Zlib 34 | 35 | data = "x\234\355\301\001\001\000\000\000\200\220\376\257\356\b\n#{"\000" * 31}\030\200\000\000\001" 36 | zipped = Zlib::Inflate.inflate data 37 | p zipped == ("\000" * 32 * 1024) 38 | 39 | # Testing (mocked) HTTParty 40 | 41 | p HTTParty.hi! 42 | 43 | # Testing (mocked) Net::Protocol 44 | 45 | p Net::Protocol.hi! 46 | 47 | # Testing Foo and Foo::Bar 48 | 49 | p Foo.foo! 50 | p Foo::Bar.bar! 51 | 52 | # Testing at runtime require statement 53 | 54 | file = "base64" 55 | require file 56 | enc = Base64.encode64("Send reinforcements!") 57 | p enc 58 | p Base64.decode64(enc) 59 | 60 | # Testing REXML::Document 61 | 62 | xml = <<-XML 63 | 64 |
65 | 66 | Invisibility Cream 67 | 14.50 68 | Makes you invisible 69 | 70 | 71 | Levitation Salve 72 | 23.99 73 | Levitate yourself for up to 3 hours per application 74 | 75 |
76 |
77 | 78 | Blork and Freen Instameal 79 | 4.95 80 | A tasty meal in a tablet; just add water 81 | 82 | 83 | Grob winglets 84 | 3.56 85 | Tender winglets of Grob. Just add water 86 | 87 |
88 |
89 | XML 90 | 91 | doc = REXML::Document.new xml 92 | root = doc.root 93 | 94 | doc.elements.each("inventory/section"){|element| puts element.attributes["name"]} 95 | # => health 96 | # => food 97 | doc.elements.each("*/section/item"){|element| puts element.attributes["upc"]} 98 | # => 123456789 99 | # => 445322344 100 | # => 485672034 101 | # => 132957764 102 | 103 | puts root.attributes["title"] 104 | # => OmniCorp Store #45x10^3 105 | puts root.elements["section/item[@stock='44']"].attributes["upc"] 106 | # => 132957764 107 | puts root.elements["section"].attributes["name"] 108 | # => health (returns the first encountered matching element) 109 | puts root.elements[1].attributes["name"] 110 | # => health (returns the FIRST child element) 111 | 112 | # Say thanks 113 | 114 | alert = UIAlertView.new 115 | alert.title = "Thanks!" 116 | alert.message = "Hi, thanks for giving MotionBundler a try! Any form of collaboration is very welcome.\n\nGreets,\nPaul Engel\n@archan937" 117 | 118 | count = alert.message.split("\n").collect(&:size).max + 2 119 | puts ["", "=" * count, "", alert.title, "", alert.message, "", "=" * count] 120 | 121 | alert.show 122 | 123 | end 124 | end -------------------------------------------------------------------------------- /test/unit/require/test_ripper.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../../test_helper", __FILE__) 2 | 3 | module Unit 4 | module Require 5 | class TestRipper < MiniTest::Unit::TestCase 6 | 7 | describe MotionBundler::Require::Ripper do 8 | it "should be able to resolve require paths" do 9 | app_builder = mock "object" 10 | app_builder.expects(:requires).returns [ 11 | [:require, ["stringio"]], 12 | [:require, ["strscan"]] 13 | ] 14 | MotionBundler::Require::Ripper::Builder.expects(:new).with("./app/controllers/app_controller.rb").returns app_builder 15 | 16 | foo_builder = mock "object" 17 | foo_builder.expects(:requires).returns [ 18 | [:require, ["baz"]], 19 | [:require_relative, ["qux"]], 20 | [:require_relative, ["bar"]] 21 | ] 22 | MotionBundler::Require::Ripper::Builder.expects(:new).with("./app/controllers/foo_controller.rb").returns foo_builder 23 | 24 | baz_builder = mock "object" 25 | baz_builder.expects(:requires).returns [] 26 | MotionBundler::Require::Ripper::Builder.expects(:new).with("./app/controllers/baz_controller.rb").returns baz_builder 27 | 28 | qux_builder = mock "object" 29 | qux_builder.expects(:requires).returns [] 30 | MotionBundler::Require::Ripper::Builder.expects(:new).with("./app/controllers/qux.rb").returns qux_builder 31 | 32 | bar_builder = mock "object" 33 | bar_builder.expects(:requires).returns [] 34 | MotionBundler::Require::Ripper::Builder.expects(:new).with(File.expand_path("./app/controllers/bar.rb")).returns bar_builder 35 | 36 | strscan_builder = mock "object" 37 | strscan_builder.expects(:requires).returns [] 38 | MotionBundler::Require::Ripper::Builder.expects(:new).with("mocks/strscan.rb").returns strscan_builder 39 | 40 | lib_baz_builder = mock "object" 41 | lib_baz_builder.expects(:requires).returns [] 42 | MotionBundler::Require::Ripper::Builder.expects(:new).with("lib/baz.rb").returns lib_baz_builder 43 | 44 | MotionBundler::Require.expects(:resolve).with("stringio", false).returns("/stdlib/stringio.rb") 45 | MotionBundler::Require.expects(:resolve).with("strscan", false).returns("mocks/strscan.rb") 46 | MotionBundler::Require.expects(:resolve).with("baz", false).returns("lib/baz.rb") 47 | 48 | MotionBundler.expects(:app_require).with("/stdlib/stringio.rb") 49 | 50 | ripper = MotionBundler::Require::Ripper.new([ 51 | "./app/controllers/app_controller.rb", 52 | "./app/controllers/foo_controller.rb", 53 | "./app/controllers/baz_controller.rb", 54 | "./app/controllers/qux.rb" 55 | ]) 56 | 57 | assert_equal([ 58 | "./app/controllers/app_controller.rb", 59 | "/stdlib/stringio.rb", 60 | "mocks/strscan.rb", 61 | "./app/controllers/foo_controller.rb", 62 | "lib/baz.rb", 63 | File.expand_path("app/controllers/qux.rb"), 64 | File.expand_path("app/controllers/bar.rb") 65 | ], ripper.files) 66 | assert_equal({ 67 | "./app/controllers/app_controller.rb" => [ 68 | "/stdlib/stringio.rb", 69 | "mocks/strscan.rb" 70 | ], 71 | "./app/controllers/foo_controller.rb" => [ 72 | "lib/baz.rb", 73 | File.expand_path("app/controllers/qux.rb"), 74 | File.expand_path("app/controllers/bar.rb") 75 | ] 76 | }, ripper.files_dependencies) 77 | assert_equal({ 78 | "./app/controllers/app_controller.rb" => %w( 79 | stringio 80 | strscan 81 | ), 82 | "./app/controllers/foo_controller.rb" => %w( 83 | baz 84 | qux 85 | bar 86 | ) 87 | }, ripper.requires) 88 | end 89 | 90 | end 91 | 92 | end 93 | end 94 | end -------------------------------------------------------------------------------- /test/gems/slot_machine/test/unit/test_slots.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../test_helper", __FILE__) 2 | 3 | module Unit 4 | class TestSlots < MiniTest::Unit::TestCase 5 | 6 | describe Slots do 7 | describe "instance methods" do 8 | it "should have the expected instance methods" do 9 | slots = Slots.new 1..10 10 | assert slots.respond_to?(:range_slots) 11 | end 12 | end 13 | 14 | describe "initialization" do 15 | it "should accept a range" do 16 | slots = Slots.new 10..20 17 | assert_equal [Slot.new(10..20)], slots.range_slots 18 | end 19 | 20 | it "should accept ranges" do 21 | slots = Slots.new 10..20, Slot.new(30..40) 22 | assert_equal [Slot.new(10..20), Slot.new(30..40)], slots.range_slots 23 | 24 | slots = Slots.new 10..20, Slot.new(22..30) 25 | assert_equal [Slot.new(10..20), Slot.new(22..30)], slots.range_slots 26 | end 27 | 28 | it "should merge slots on initialization" do 29 | slots = Slots.new 10..20, 20..30 30 | assert_equal [Slot.new(10..30)], slots.range_slots 31 | end 32 | 33 | it "should raise an exception when invalid" do 34 | assert_raises ArgumentError do 35 | Slots.new "bar" 36 | end 37 | assert_raises ArgumentError do 38 | Slots.new 1.."bar" 39 | end 40 | assert_raises ArgumentError do 41 | Slots.new "bar"..1 42 | end 43 | assert_raises ArgumentError do 44 | Slots.new "slot".."bar" 45 | end 46 | end 47 | end 48 | 49 | describe "equality" do 50 | it "should return whether it equals another object" do 51 | slots = Slots.new 1..2, 3..4 52 | 53 | assert !(slots == Slot.new(1..2)) 54 | assert !(slots == Slots.new(1..2, 3..5)) 55 | 56 | assert_equal slots, Slots.new(3..4, 1..2) 57 | assert_equal slots, Slots.new(1..2, 3..4) 58 | assert_equal slots, Slots.new([1..2, 3..4]) 59 | assert_equal slots, Slots.new(Slot.new(1..2), Slot.new(3..4)) 60 | assert_equal slots, [Slot.new(1..4)] 61 | end 62 | end 63 | 64 | describe "calculations" do 65 | it "should be able to add slots" do 66 | slots = Slots.new 0..10 67 | 68 | assert_equal Slots, (slots + (10..20)).class 69 | assert_equal Slots, (slots + Slot.new(10..20)).class 70 | assert_equal Slots, (slots + Slots.new(10..20)).class 71 | 72 | assert_equal Slots.new(0..20), (slots + (10..20)) 73 | assert_equal Slots.new(0..10, 20..30), (slots + Slot.new(20..30)) 74 | assert_equal Slots.new(0..10, 20..30, 40..60), (slots + Slots.new(20..30, 40..50) + (45..60)) 75 | 76 | assert_equal Slots.new(0..15, 20..30), (((slots + (5..15)) + (20..25)) + (24..30)) 77 | end 78 | 79 | it "should be able to subtract slots" do 80 | slots = Slots.new 0..30 81 | 82 | assert_equal Slots, (slots - (10..20)).class 83 | assert_equal Slots, (slots - Slot.new(10..20)).class 84 | assert_equal Slots, (slots - Slots.new(10..20)).class 85 | 86 | assert_equal Slots.new(0..10, 20..30), (slots - (10..20)) 87 | assert_equal Slots.new(0..10, 20..30), (slots - Slot.new(10..20)) 88 | assert_equal Slots.new(0..10, 20..30), (slots - Slots.new(10..20, 30..40)) 89 | 90 | assert_equal Slots.new(0..5, 10..20, 25..30), (((slots - (20..25)) - (5..10)) - (20..22)) 91 | end 92 | 93 | it "should be able to match available slots" do 94 | slots = Slots.new(0..10, 15..40, 50..65) 95 | 96 | assert_equal [ 97 | Slot.new(0..10), 98 | Slot.new(15..25), 99 | Slot.new(25..35), 100 | Slot.new(50..60) 101 | ], slots.match(10) 102 | 103 | assert_equal [ 104 | Slot.new(0..10), 105 | Slot.new(15..25), 106 | Slot.new(20..30), 107 | Slot.new(25..35), 108 | Slot.new(30..40), 109 | Slot.new(50..60), 110 | Slot.new(55..65) 111 | ], slots.match(10, 5) 112 | end 113 | end 114 | end 115 | 116 | end 117 | end -------------------------------------------------------------------------------- /lib/motion-bundler/mocks/httparty.rb: -------------------------------------------------------------------------------- 1 | module HTTParty 2 | extend self 3 | 4 | def included(base) 5 | base.extend self 6 | end 7 | 8 | def format(f) 9 | @format = f 10 | end 11 | 12 | def base_uri(u) 13 | @base_uri = u.sub(/\/$/, "") 14 | end 15 | 16 | def basic_auth(user, password) 17 | @basic_auth = [user, password] 18 | end 19 | 20 | def get(path, options = {}, &block) 21 | send_request path, :get, options, &block 22 | end 23 | 24 | def post(path, options = {}, &block) 25 | send_request path, :post, options, &block 26 | end 27 | 28 | def put(path, options = {}, &block) 29 | send_request path, :put, options, &block 30 | end 31 | 32 | def delete(path, options = {}, &block) 33 | send_request path, :delete, options, &block 34 | end 35 | 36 | private 37 | 38 | def send_request(path, method, options = {}, &block) 39 | path = "#{@base_uri}/#{path}" if @base_uri && !path.match(/^\w+:\/\//) 40 | path = path.stringByAddingPercentEscapesUsingEncoding NSUTF8StringEncoding 41 | method = method.to_s.upcase 42 | 43 | if options[:body] 44 | payload = payload_to_query_string(options[:body]).dataUsingEncoding(NSUTF8StringEncoding) if %w(POST PUT).include?(method) && payload 45 | end 46 | 47 | if payload && method == "GET" 48 | path << "#{path.include?("?") ? "&" : "?"}#{payload}" 49 | end 50 | 51 | url = NSURL.URLWithString path.stringByAddingPercentEscapesUsingEncoding(NSUTF8StringEncoding) 52 | request = NSMutableURLRequest.requestWithURL url 53 | 54 | if @basic_auth 55 | auth_header = "Basic " + [@basic_auth.join(":")].pack("m0").dataUsingEncoding(NSUTF8StringEncoding) 56 | request.addValue auth_header, forHTTPHeaderField: "Authorization" 57 | request.HTTPMethod = method 58 | end 59 | 60 | if options[:headers] 61 | options[:headers].each do |key, value| 62 | request.setValue value.to_s, forHTTPHeaderField: key.to_s 63 | end 64 | end 65 | 66 | if payload 67 | request.HTTPBody = payload 68 | end 69 | 70 | response = Pointer.new :object 71 | error = Pointer.new :object 72 | 73 | if options[:async] 74 | queue = NSOperationQueue.alloc.init 75 | NSURLConnection.sendAsynchronousRequest request, queue: queue, completionHandler: proc { |response, data, error| 76 | block.call HTTParty::Response.new(response, data, {:format => @format || options[:format], :error => error}) 77 | } 78 | else 79 | data = NSURLConnection.sendSynchronousRequest request, returningResponse: response, error: error 80 | response = HTTParty::Response.new(response.value, data, {:format => @format || options[:format], :error => error}) 81 | block_given? ? block.call(response) : response 82 | end 83 | end 84 | 85 | def payload_to_query_string(payload_hash) 86 | payload_hash_to_array(payload_hash).collect{|key, value| "#{escape key}=#{escape value}"}.join "&" 87 | end 88 | 89 | def payload_hash_to_array(hash, prefix = nil) 90 | array = [] 91 | hash.each do |key, value| 92 | if value.is_a?(Hash) 93 | array += payload_hash_to_array(value, prefix ? "#{prefix}[#{key.to_s}]" : key.to_s) 94 | elsif value.is_a?(Array) 95 | value.each do |val| 96 | array << [prefix ? "#{prefix}[#{key.to_s}][]" : "#{key.to_s}[]", val] 97 | end 98 | else 99 | array << [prefix ? "#{prefix}[#{key.to_s}]" : key.to_s, value] 100 | end 101 | end 102 | array 103 | end 104 | 105 | def escape(string) 106 | CFURLCreateStringByAddingPercentEscapes(nil, string, "[]", ";=&,", KCFStringEncodingUTF8) if string 107 | end 108 | 109 | end 110 | 111 | module HTTParty 112 | class Response 113 | 114 | def initialize(response, data, options = {}) 115 | @cached_response = NSCachedURLResponse.alloc.initWithResponse response, data: data 116 | @options = options 117 | end 118 | 119 | def parsed_response 120 | @parsed_response ||= begin 121 | case @options[:format] 122 | when :json 123 | NSJSONSerialization.JSONObjectWithData @cached_response.data, options: NSJSONReadingMutableContainers, error: nil 124 | else 125 | NSString.alloc.initWithData @cached_response.data, encoding: NSUTF8StringEncoding 126 | end 127 | end 128 | end 129 | 130 | def code 131 | @cached_response.response.statusCode 132 | end 133 | 134 | def ok? 135 | code.to_s.match /^20\d$/ 136 | end 137 | 138 | end 139 | end -------------------------------------------------------------------------------- /lib/motion-bundler/require/tracer/hooks.rb: -------------------------------------------------------------------------------- 1 | module MotionBundler 2 | module Require 3 | module Tracer 4 | module Hooks 5 | 6 | def hook 7 | Kernel.instance_eval &require_hook unless Kernel.respond_to?(:require_with_mb_trace) 8 | Object.class_eval &require_hook unless Object.respond_to?(:require_with_mb_trace) 9 | Kernel.instance_eval &require_relative_hook unless Kernel.respond_to?(:require_relative_with_mb_trace) 10 | Object.class_eval &require_relative_hook unless Object.respond_to?(:require_relative_with_mb_trace) 11 | Kernel.instance_eval &load_hook unless Kernel.respond_to?(:load_with_mb_trace) 12 | Object.class_eval &load_hook unless Object.respond_to?(:load_with_mb_trace) 13 | Module.class_eval &autoload_hook unless Module.respond_to?(:autoload_with_mb_trace) 14 | end 15 | 16 | def unhook 17 | Kernel.instance_eval &require_unhook if Kernel.respond_to?(:require_with_mb_trace) 18 | Object.class_eval &require_unhook if Object.respond_to?(:require_with_mb_trace) 19 | Kernel.instance_eval &require_relative_unhook if Kernel.respond_to?(:require_relative_with_mb_trace) 20 | Object.class_eval &require_relative_unhook if Object.respond_to?(:require_relative_with_mb_trace) 21 | Kernel.instance_eval &load_unhook if Kernel.respond_to?(:load_with_mb_trace) 22 | Object.class_eval &load_unhook if Object.respond_to?(:load_with_mb_trace) 23 | Module.class_eval &autoload_unhook if Module.respond_to?(:autoload_with_mb_trace) 24 | end 25 | 26 | private 27 | 28 | def require_hook 29 | @require_hook ||= proc do 30 | def require_with_mb_trace(path, _caller = nil, _path = nil) 31 | result = nil 32 | MotionBundler::Require::Tracer.log.register(_caller || caller[0], _path || path) do 33 | result = require_without_mb_trace path 34 | end 35 | result 36 | end 37 | alias :require_without_mb_trace :require 38 | alias :require :require_with_mb_trace 39 | end 40 | end 41 | 42 | def require_relative_hook 43 | @require_relative_hook ||= proc do 44 | def require_relative_with_mb_trace(path) 45 | caller[0].match(/^(.*\.rb)\b/) 46 | require_with_mb_trace File.expand_path("../#{path}", $1), caller[0], path 47 | end 48 | alias :require_relative_without_mb_trace :require_relative 49 | alias :require_relative :require_relative_with_mb_trace 50 | end 51 | end 52 | 53 | def load_hook 54 | @load_hook ||= proc do 55 | def load_with_mb_trace(path) 56 | require_with_mb_trace path, caller[0] 57 | end 58 | alias :load_without_mb_trace :load 59 | alias :load :load_with_mb_trace 60 | end 61 | end 62 | 63 | def autoload_hook 64 | @autoload_hook ||= proc do 65 | def autoload_with_mb_trace(mod, filename) 66 | require_with_mb_trace filename, caller[0] 67 | end 68 | alias :autoload_without_mb_trace :autoload 69 | alias :autoload :autoload_with_mb_trace 70 | end 71 | end 72 | 73 | def require_unhook 74 | @require_unhook ||= proc do 75 | alias :require :require_without_mb_trace 76 | undef :require_with_mb_trace 77 | undef :require_without_mb_trace 78 | end 79 | end 80 | 81 | def require_relative_unhook 82 | @require_relative_unhook ||= proc do 83 | alias :require_relative :require_relative_without_mb_trace 84 | undef :require_relative_with_mb_trace 85 | undef :require_relative_without_mb_trace 86 | end 87 | end 88 | 89 | def load_unhook 90 | @load_unhook ||= proc do 91 | alias :load :load_without_mb_trace 92 | undef :load_with_mb_trace 93 | undef :load_without_mb_trace 94 | end 95 | end 96 | 97 | def autoload_unhook 98 | @autoload_unhook ||= proc do 99 | alias :autoload :autoload_without_mb_trace 100 | undef :autoload_with_mb_trace 101 | undef :autoload_without_mb_trace 102 | end 103 | end 104 | 105 | end 106 | end 107 | end 108 | end -------------------------------------------------------------------------------- /README.md: -------------------------------------------------------------------------------- 1 | # MotionBundler [![Build Status](https://secure.travis-ci.org/archan937/motion-bundler.png)](http://travis-ci.org/archan937/motion-bundler) 2 | 3 | Use Ruby gems and mock require statements within RubyMotion applications 4 | 5 | ## Introduction 6 | 7 | [RubyMotion](http://www.rubymotion.com) has united two great programming communities: the Ruby and iOS community. We are no longer forced to develop native iOS applications (for iPhone and iPad) using Objective-C and XCode anymore because we can choose using Ruby with any kind of text editor. Ruby gives us a lot of benefits like the beauty and flexibility of the language. Also, the Ruby community is driven by its enormous amount of open source libraries which are packed as so called "Ruby gems". Bundler is the Ruby standard for managing all the gem dependencies used within a Ruby project. 8 | 9 | There are limitations though as you cannot use any random Ruby gem you want to. It either has to be RubyMotion aware (like [BubbleWrap](https://github.com/rubymotion/BubbleWrap)) or RubyMotion compatible (mostly when having as minimal gem dependencies as possible and not doing fancy Ruby stuff like code evaluation). Also, you cannot just require Ruby sources at runtime. You will have to specify them along with their mutual dependencies. 10 | 11 | This can give us a lot of headaches and so I have created a Ruby gem called `MotionBundler`. It will unobtrusively track and specify the required Ruby sources and their mutual dependencies of Ruby gems you want to use in your RubyMotion application. 12 | 13 | ## Usage 14 | 15 | ### Set up your Gemfile and Rakefile 16 | 17 | You need to setup your `Gemfile` by separating RubyMotion aware Ruby gems from the ones that are not. Put the RubyMotion **unaware** gems in the `:motion` Bundler group like this: 18 | 19 | source "http://rubygems.org" 20 | 21 | # RubyMotion aware gems 22 | gem "motion-bundler" 23 | 24 | # RubyMotion unaware gems 25 | group :motion do 26 | gem "slot_machine" 27 | end 28 | 29 | Add `MotionBundler.setup` at the end of your `Rakefile`: 30 | 31 | # -*- coding: utf-8 -*- 32 | $:.unshift "/Library/RubyMotion/lib" 33 | require "motion/project/template/ios" 34 | 35 | # Require and prepare Bundler 36 | require "bundler" 37 | Bundler.require 38 | 39 | Motion::Project::App.setup do |app| 40 | # Use `rake config' to see complete project settings. 41 | app.name = "SampleApp" 42 | end 43 | 44 | # Track and specify files and their mutual dependencies within the :motion Bundler group 45 | MotionBundler.setup 46 | 47 | Run `bundle` and then `rake` to run the application in your iOS-simulator. Voila! You're done ^^ 48 | 49 | ## More documentation 50 | 51 | Please consult the [GitHub repository Wiki pages](https://github.com/archan937/motion-bundler/wiki/Overview) for further information about MotionBundler. 52 | 53 | ## Huh? Haven't you written LockOMotion already? 54 | 55 | Yes, I did. But MotionBundler ... 56 | 57 | * is an improved rewrite of [LockOMotion](https://github.com/archan937/lock-o-motion) 58 | * has been stripped down 59 | * has more code abstraction (and thus has much cleaner and readable code) 60 | * has a [test coverage percentage of 100%](https://travis-ci.org/archan937/motion-bundler) 61 | 62 | ## Contact me 63 | 64 | For support, remarks and requests, please mail me at [paul.engel@holder.nl](mailto:paul.engel@holder.nl). 65 | 66 | ## License 67 | 68 | Copyright (c) 2013 Paul Engel, released under the MIT license 69 | 70 | [http://holder.nl](http://holder.nl) - [http://codehero.es](http://codehero.es) - [http://gettopup.com](http://gettopup.com) - [http://github.com/archan937](http://github.com/archan937) - [http://twitter.com/archan937](http://twitter.com/archan937) - [paul.engel@holder.nl](mailto:paul.engel@holder.nl) 71 | 72 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 73 | 74 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 75 | 76 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. 77 | -------------------------------------------------------------------------------- /test/gems/slot_machine/lib/slot_machine/slot.rb: -------------------------------------------------------------------------------- 1 | module SlotMachine 2 | module Slot 3 | 4 | def self.included(base) 5 | base.class_eval do 6 | attr_reader :start, :end, :length 7 | 8 | def self.interval(value) 9 | @interval = value 10 | end 11 | 12 | def self.default_interval 13 | @interval || 10 14 | end 15 | 16 | def self.slots_class 17 | @slots_class ||= "#{name}s".split("::").inject(Object) do |mod, name| 18 | mod.const_get name 19 | end 20 | end 21 | end 22 | end 23 | 24 | def initialize(range_or_length) 25 | if range_or_length.is_a? Range 26 | @type = :range 27 | self.start = range_or_length.first 28 | self.end = range_or_length.last 29 | else 30 | @type = :length 31 | self.length = range_or_length 32 | end 33 | end 34 | 35 | def range? 36 | @type == :range 37 | end 38 | 39 | def length? 40 | @type == :length 41 | end 42 | 43 | def start=(value) 44 | @start = start!(value) if range? 45 | end 46 | 47 | def end=(value) 48 | @end = end!(value) if range? 49 | end 50 | 51 | def length=(value) 52 | @length = length!(value) if length? 53 | end 54 | 55 | def match(other, interval = nil) 56 | interval ||= self.class.default_interval 57 | raise ArgumentError, "Interval has to be greater than 0 (#{interval} given)" unless interval > 0 58 | unless self.class == other.class 59 | other = self.class.new other 60 | end 61 | match_compared to_compared, other.to_compared, interval 62 | end 63 | 64 | def +(other) 65 | self.class.slots_class.new(self) + other 66 | end 67 | 68 | def -(other) 69 | self.class.slots_class.new(self) - other 70 | end 71 | 72 | def ==(other) 73 | self.class == other.class && self.start == other.start && self.end == other.end && self.length == other.length 74 | end 75 | 76 | def inspect 77 | inspect_variables = begin 78 | if range? 79 | "@start=#{@start} @end=#{@end}" 80 | else 81 | "@length=#{@length}" 82 | end 83 | end 84 | "#<#{self.class.name} #{inspect_variables}>" 85 | end 86 | 87 | protected 88 | 89 | def typecast(value) 90 | value.to_s 91 | end 92 | 93 | def valid?(value) 94 | true 95 | end 96 | 97 | def to_compared 98 | if range? 99 | to_array 100 | else 101 | @length 102 | end 103 | end 104 | 105 | def to_array 106 | (@start..@end).to_a.tap do |array| 107 | array.pop 108 | end 109 | end 110 | 111 | def from_array(array) 112 | self.class.new array.first..(add(array.last, 1)) 113 | end 114 | 115 | def add(a, b) 116 | a + b 117 | end 118 | 119 | private 120 | 121 | def start!(value) 122 | valid! abs!(lt!(value, @end)) 123 | end 124 | 125 | def end!(value) 126 | valid! abs!(gt!(value, @start)) 127 | end 128 | 129 | def length!(value) 130 | abs! value 131 | end 132 | 133 | def gt!(value, compared) 134 | typecast! value, :>, compared 135 | end 136 | 137 | def lt!(value, compared) 138 | typecast! value, :<, compared 139 | end 140 | 141 | def abs!(value) 142 | typecast! value, :>=, 0 143 | end 144 | 145 | def typecast!(value, operator, compared) 146 | Integer(typecast(value)).tap do |value| 147 | raise ArgumentError, "Passed value should be #{operator} #{compared} (#{value} given)" if compared && !value.send(operator, compared) 148 | end 149 | end 150 | 151 | def valid!(value) 152 | raise ArgumentError, "Passed value is invalid (#{value} given)" unless valid?(value) 153 | value 154 | end 155 | 156 | def match_compared(a, b, interval) 157 | if a.is_a?(Array) && b.is_a?(Fixnum) 158 | raise ArgumentError, "Length has to be greater than 0 (#{b} given)" unless b > 0 159 | i = 0 160 | [].tap do |matches| 161 | while (a.size / b) > 0 162 | matches << from_array(a[0, b]) 163 | a.shift interval 164 | i += interval 165 | end 166 | end 167 | elsif a.is_a?(Fixnum) && b.is_a?(Array) 168 | match_compared b, a, interval 169 | elsif a.is_a?(Array) && b.is_a?(Array) 170 | [].tap do |matches| 171 | unless (array = a & b).empty? 172 | matches << from_array(array) 173 | end 174 | end 175 | else 176 | raise ArgumentError, "Cannot match when passing two length slots" 177 | end 178 | end 179 | 180 | end 181 | end -------------------------------------------------------------------------------- /test/gems/slot_machine/lib/slot_machine/slots.rb: -------------------------------------------------------------------------------- 1 | module SlotMachine 2 | module Slots 3 | 4 | def self.included(base) 5 | base.class_eval do 6 | def self.slot_class 7 | @slot_class ||= name.gsub(/s$/, "").split("::").inject(Object) do |mod, name| 8 | mod.const_get name 9 | end 10 | end 11 | end 12 | end 13 | 14 | def initialize(*ranges_or_range_slots) 15 | @range_slots = to_range_slots(add(ranges_or_range_slots.flatten)) 16 | end 17 | 18 | def range_slots 19 | @range_slots || [] 20 | end 21 | 22 | def match(other, interval = nil) 23 | range_slots.collect{|range_slot| range_slot.match other, interval}.flatten.uniq 24 | end 25 | 26 | def +(other) 27 | unless other.is_a?(Range) || other.is_a?(Array) || other.class == self.class.slot_class || other.class == self.class 28 | raise ArgumentError, "Either subtract a Range, an Array, #{self.class.slot_class.name} or #{self.class.name} instance (#{other.class.name} given)" 29 | end 30 | self.class.new add(other) 31 | end 32 | 33 | def -(other) 34 | unless other.is_a?(Range) || other.is_a?(Array) || other.class == self.class.slot_class || other.class == self.class 35 | raise ArgumentError, "Either subtract a Range, an Array, #{self.class.slot_class.name} or #{self.class.name} instance (#{other.class.name} given)" 36 | end 37 | self.class.new subtract(other) 38 | end 39 | 40 | def ==(other) 41 | ((self.class == other.class) || (other_is_an_array = other.is_a?(Array))) && begin 42 | range_slots.size == (other_range_slots = other_is_an_array ? other : other.range_slots).size && begin 43 | range_slots.each_with_index do |range_slot, index| 44 | return false unless range_slot == other_range_slots[index] 45 | end 46 | true 47 | end 48 | end 49 | end 50 | 51 | private 52 | 53 | def to_range_slots(ranges_or_range_slots) 54 | ranges_or_range_slots.flatten.collect do |range_or_range_slot| 55 | if range_or_range_slot.class == self.class.slot_class 56 | range_or_range_slot 57 | elsif range_or_range_slot.is_a?(Range) 58 | self.class.slot_class.new range_or_range_slot 59 | else 60 | raise ArgumentError 61 | end 62 | end.sort do |a, b| 63 | [a.start, a.end] <=> [b.start, b.end] 64 | end 65 | end 66 | 67 | def to_ranges(object) 68 | begin 69 | if object.is_a?(Range) || object.class == self.class.slot_class 70 | [object] 71 | elsif object.class == self.class 72 | object.range_slots 73 | else 74 | object 75 | end 76 | end.collect do |entry| 77 | if entry.class == self.class.slot_class 78 | entry.start..entry.end 79 | elsif entry.is_a?(Range) 80 | entry 81 | else 82 | raise ArgumentError 83 | end 84 | end 85 | end 86 | 87 | def add(other) 88 | *merged_ranges = (ranges = to_ranges(self).concat(to_ranges(other)).sort{|a, b| [a.first, a.last] <=> [b.first, b.last]}).shift 89 | ranges.each do |range| 90 | last_range = merged_ranges[-1] 91 | if last_range.last >= range.first - 1 92 | merged_ranges[-1] = last_range.first..[range.last, last_range.last].max 93 | else 94 | merged_ranges.push range 95 | end 96 | end 97 | merged_ranges 98 | end 99 | 100 | def subtract(other) 101 | divided_ranges = to_ranges(self) 102 | to_ranges(other).each do |range| 103 | divided_ranges = divided_ranges.collect do |divided_range| 104 | if range.first <= divided_range.first && range.last > divided_range.first && range.last < divided_range.last 105 | # | range | 106 | # | divided_range | 107 | range.last..divided_range.last 108 | elsif range.first > divided_range.first && range.first < divided_range.last && range.last >= divided_range.last 109 | # | range | 110 | # | divided_range | 111 | divided_range.first..range.first 112 | elsif range.first > divided_range.first && range.last < divided_range.last 113 | # | range | 114 | # | divided_range | 115 | [divided_range.first..range.first, range.last..divided_range.last] 116 | elsif range.last <= divided_range.first || range.first >= divided_range.last 117 | # | range | 118 | # | divided_range | 119 | # or 120 | # | range | 121 | # | divided_range | 122 | divided_range 123 | end 124 | end.flatten.compact 125 | end 126 | divided_ranges 127 | end 128 | 129 | end 130 | end -------------------------------------------------------------------------------- /lib/motion-bundler.rb: -------------------------------------------------------------------------------- 1 | require "bundler" 2 | require "motion-bundler/gem_ext" 3 | require "motion-bundler/config" 4 | require "motion-bundler/require" 5 | require "motion-bundler/version" 6 | 7 | module MotionBundler 8 | extend self 9 | 10 | PROJECT_PATH = File.expand_path "." 11 | MOTION_BUNDLER_FILE = "#{PROJECT_PATH}/.motion-bundler.rb" 12 | 13 | def app_require(file) 14 | app_requires << file 15 | end 16 | 17 | def setup(&block) 18 | Motion::Project::App.setup do |app| 19 | touch_motion_bundler 20 | 21 | files, files_dependencies, requires = app.files, {}, [] 22 | ripper_require files, files_dependencies, requires 23 | tracer_require files, files_dependencies, requires, &block 24 | 25 | normalize! files 26 | normalize! files_dependencies 27 | requires.sort!.uniq! 28 | 29 | write_motion_bundler files, files_dependencies, requires 30 | 31 | app.files = files 32 | app.files_dependencies files_dependencies 33 | end 34 | end 35 | 36 | def simulator? 37 | argv.empty? 38 | end 39 | 40 | def device? 41 | !simulator? 42 | end 43 | 44 | def boot_file 45 | File.expand_path "../motion-bundler/#{simulator? ? "simulator" : "device"}/boot.rb", __FILE__ 46 | end 47 | 48 | private 49 | 50 | def argv 51 | ARGV 52 | end 53 | 54 | def app_requires 55 | @app_requires ||= [] 56 | end 57 | 58 | def touch_motion_bundler 59 | File.open(MOTION_BUNDLER_FILE, "w") {} 60 | end 61 | 62 | def ripper_require(files, files_dependencies, requires) 63 | ripper = Require::Ripper.new Dir["app/**/*.rb"].collect{|x| "./#{x}"} 64 | 65 | files.replace( 66 | ripper.files + files 67 | ) 68 | ripper.files_dependencies.each{|file, paths| (files_dependencies[file] ||= []).concat paths} 69 | requires.concat( 70 | ripper.requires.values.flatten 71 | ) 72 | end 73 | 74 | def tracer_require(files, files_dependencies, requires) 75 | config = Config.new 76 | if block_given? 77 | yield config 78 | end 79 | 80 | Require.mock_and_trace do 81 | require MOTION_BUNDLER_FILE 82 | require boot_file 83 | Bundler.require :motion 84 | app_requires.each{|file| require file, "APP"} 85 | config.requires.each{|file| require file, "APP"} 86 | end 87 | 88 | files.replace( 89 | Require.files + config.files + files - ["APP", "BUNDLER", __FILE__] 90 | ) 91 | 92 | Require.files_dependencies.tap do |dependencies| 93 | (dependencies.delete("BUNDLER") || []).each do |file| 94 | (dependencies[file] ||= []).unshift boot_file 95 | end 96 | dependencies.delete("APP") 97 | dependencies.delete(__FILE__) 98 | end.each do |file, paths| 99 | (files_dependencies[file] ||= []).concat paths 100 | end 101 | 102 | config.files_dependencies.each{|file, paths| (files_dependencies[file] ||= []).concat paths} 103 | 104 | requires.concat( 105 | Require.requires.values.flatten + config.requires 106 | ) 107 | end 108 | 109 | def write_motion_bundler(files, files_dependencies, requires) 110 | File.open(MOTION_BUNDLER_FILE, "w") do |file| 111 | files_dependencies = files_dependencies.dup.tap do |dependencies| 112 | if motion_bundler_dependencies = dependencies.delete(__FILE__) 113 | dependencies["MOTION_BUNDLER"] = motion_bundler_dependencies 114 | end 115 | end 116 | file << <<-RUBY_CODE.gsub(" ", "") 117 | module MotionBundler 118 | FILES = #{pretty_inspect files, 2} 119 | FILES_DEPENDENCIES = #{pretty_inspect files_dependencies, 2} 120 | REQUIRES = #{pretty_inspect requires, 2} 121 | end 122 | RUBY_CODE 123 | end 124 | end 125 | 126 | def normalize!(object) 127 | object.replace( 128 | if object.is_a?(Array) 129 | object.inject(Set.new) do |a, v| 130 | a << File.expand_path(v) 131 | a 132 | end.to_a.partition do |v| 133 | (v == MOTION_BUNDLER_FILE) || !v.include?(PROJECT_PATH) 134 | end.flatten 135 | elsif object.is_a?(Hash) 136 | object.inject({}) do |h, (k, v)| 137 | (h[File.expand_path(k)] ||= []).concat normalize!(v) 138 | h 139 | end 140 | end 141 | ) 142 | end 143 | 144 | def pretty_inspect(object, indent = 0) 145 | if object.is_a?(Array) 146 | entries = object.collect{|x| " #{pretty_inspect x, indent + 2}"} 147 | return "[]" if entries.empty? 148 | entries.each_with_index{|x, i| entries[i] = "#{x}," if i < entries.size - 1} 149 | ["[", entries, "]"].flatten.join "\n" + (" " * indent) 150 | elsif object.is_a?(Hash) 151 | entries = object.collect{|k, v| " #{k.inspect} => #{pretty_inspect v, indent + 2}"} 152 | return "{}" if entries.empty? 153 | entries.each_with_index{|x, i| entries[i] = "#{x}," if i < entries.size - 1} 154 | ["{", entries, "}"].flatten.join "\n" + (" " * indent) 155 | else 156 | object.inspect 157 | end 158 | end 159 | 160 | end -------------------------------------------------------------------------------- /test/unit/require/tracer/test_log.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../../../test_helper", __FILE__) 2 | 3 | module Unit 4 | module Require 5 | module Tracer 6 | class TestLog < MiniTest::Unit::TestCase 7 | 8 | describe MotionBundler::Require::Tracer::Log do 9 | before do 10 | @log = MotionBundler::Require::Tracer::Log.new 11 | end 12 | 13 | it "should be able to clear it's log" do 14 | @log.instance_variable_set :@files, %w(a b) 15 | @log.instance_variable_set :@files_dependencies, {"a" => "b"} 16 | @log.instance_variable_set :@requires, {"c" => "d"} 17 | @log.clear 18 | assert_equal true, @log.instance_variable_get(:@files).empty? 19 | assert_equal true, @log.instance_variable_get(:@files_dependencies).empty? 20 | assert_equal true, @log.instance_variable_get(:@requires).empty? 21 | end 22 | 23 | it "should register dependencies as expected" do 24 | loaded_features = %w(foo1 foo2 foo3) 25 | @log.expects(:loaded_features).at_least_once.returns loaded_features 26 | 27 | @log.instance_variable_set :@files, %w(/Sources/lib/file1.rb file0) 28 | @log.instance_variable_set :@files_dependencies, {"/Sources/lib/file1.rb" => ["file0"]} 29 | @log.instance_variable_set :@requires, {"/Sources/lib/file1.rb:47" => ["file0"]} 30 | 31 | @log.register "/.rvm/gems/bundler/bundler.rb:63", "foo" do 32 | @log.register "/Sources/lib/file1.rb:129:in `'", "file2" do 33 | @log.register "/Sources/lib/file2.rb:1", "file3" do 34 | @log.register "/Sources/lib/file2.rb:2", "file4" do 35 | loaded_features << "file4" 36 | end 37 | loaded_features << "file3" 38 | end 39 | loaded_features << "file2" 40 | end 41 | @log.register "/Sources/lib/foo.rb:12", "qux" do 42 | false 43 | end 44 | loaded_features << "file1" 45 | end 46 | 47 | assert_equal({ 48 | "BUNDLER" => %w(file1), 49 | "/Sources/lib/file1.rb" => %w(file0 file2), 50 | "/Sources/lib/file2.rb" => %w(file3 file4) 51 | }, @log.instance_variable_get(:@files_dependencies)) 52 | 53 | assert_equal %w( 54 | /Sources/lib/file1.rb 55 | file0 56 | /Sources/lib/file2.rb 57 | file4 58 | file3 59 | file2 60 | BUNDLER 61 | file1 62 | ), @log.files 63 | 64 | assert_equal true, @log.files_dependencies.object_id != @log.instance_variable_get(:@files_dependencies).object_id 65 | assert_equal true, @log.requires.object_id != @log.instance_variable_get(:@requires).object_id 66 | 67 | assert_equal({ 68 | "BUNDLER" => %w(file1), 69 | "/Sources/lib/file1.rb" => %w(file0 file2), 70 | "/Sources/lib/file2.rb" => %w(file3 file4) 71 | }, @log.files_dependencies) 72 | 73 | assert_equal({ 74 | "BUNDLER" => %w(foo), 75 | "/Sources/lib/file1.rb:47" => %w(file0), 76 | "/Sources/lib/file1.rb:129" => %w(file2), 77 | "/Sources/lib/file2.rb:1" => %w(file3), 78 | "/Sources/lib/file2.rb:2" => %w(file4), 79 | "/Sources/lib/foo.rb:12" => %w(qux) 80 | }, @log.requires) 81 | end 82 | 83 | it "should be able to trace files that are already loaded" do 84 | self.expects(:require_without_mb_trace).with("cgi").returns(false) 85 | MotionBundler::Require::Tracer.log.expects(:merge!) 86 | MotionBundler::Require.mock_and_trace do 87 | require "cgi", "APP" 88 | end 89 | 90 | $CLI = true 91 | self.expects(:require_without_mb_trace).with("cgi").returns(false) 92 | File.expects(:read).with("cgi").returns("") 93 | MotionBundler::Require::Tracer.log.expects(:merge!) 94 | MotionBundler::Require.mock_and_trace do 95 | require "cgi", "APP" 96 | end 97 | $CLI = false 98 | end 99 | 100 | it "should be able to merge files, files dependencies and requires" do 101 | @log.instance_variable_set :@files, Set.new(%w(a b)) 102 | @log.instance_variable_set :@files_dependencies, {"c" => %w(d e)} 103 | @log.instance_variable_set :@requires, {"f" => %w(g)} 104 | 105 | @log.merge! %w(h i), {"c" => %w(j), "k" => %w(l m)}, {"f" => %w(n), "o" => %w(p q)} 106 | 107 | assert_equal(%w(a b h i), @log.files) 108 | assert_equal({ 109 | "c" => %w(d e j), 110 | "k" => %w(l m) 111 | }, @log.files_dependencies) 112 | assert_equal({ 113 | "f" => %w(g n), 114 | "o" => %w(p q) 115 | }, @log.requires) 116 | end 117 | end 118 | 119 | end 120 | end 121 | end 122 | end -------------------------------------------------------------------------------- /sample_apps/osx/app/menu.rb: -------------------------------------------------------------------------------- 1 | class AppDelegate 2 | 3 | def buildMenu 4 | @mainMenu = NSMenu.new 5 | 6 | appName = NSBundle.mainBundle.infoDictionary["CFBundleName"] 7 | addMenu appName do 8 | addItemWithTitle "About #{appName}", action: "orderFrontStandardAboutPanel:", keyEquivalent: "" 9 | addItem NSMenuItem.separatorItem 10 | addItemWithTitle "Preferences", action: "openPreferences:", keyEquivalent: "," 11 | addItem NSMenuItem.separatorItem 12 | servicesItem = addItemWithTitle "Services", action: nil, keyEquivalent: "" 13 | NSApp.servicesMenu = servicesItem.submenu = NSMenu.new 14 | addItem NSMenuItem.separatorItem 15 | addItemWithTitle "Hide #{appName}", action: "hide:", keyEquivalent: "h" 16 | item = addItemWithTitle "Hide Others", action: "hideOtherApplications:", keyEquivalent: "H" 17 | item.keyEquivalentModifierMask = NSCommandKeyMask | NSAlternateKeyMask 18 | addItemWithTitle "Show All", action: "unhideAllApplications:", keyEquivalent: "" 19 | addItem NSMenuItem.separatorItem 20 | addItemWithTitle "Quit #{appName}", action: "terminate:", keyEquivalent: "q" 21 | end 22 | 23 | addMenu "File" do 24 | addItemWithTitle "New", action: "newDocument:", keyEquivalent: "n" 25 | addItemWithTitle "Open…", action: "openDocument:", keyEquivalent: "o" 26 | addItem NSMenuItem.separatorItem 27 | addItemWithTitle "Close", action: "performClose:", keyEquivalent: "w" 28 | addItemWithTitle "Save…", action: "saveDocument:", keyEquivalent: "s" 29 | addItemWithTitle "Revert to Saved", action: "revertDocumentToSaved:", keyEquivalent: "" 30 | addItem NSMenuItem.separatorItem 31 | addItemWithTitle "Page Setup…", action: "runPageLayout:", keyEquivalent: "P" 32 | addItemWithTitle "Print…", action: "printDocument:", keyEquivalent: "p" 33 | end 34 | 35 | addMenu "Edit" do 36 | addItemWithTitle "Undo", action: "undo:", keyEquivalent: "z" 37 | addItemWithTitle "Redo", action: "redo:", keyEquivalent: "Z" 38 | addItem NSMenuItem.separatorItem 39 | addItemWithTitle "Cut", action: "cut:", keyEquivalent: "x" 40 | addItemWithTitle "Copy", action: "copy:", keyEquivalent: "c" 41 | addItemWithTitle "Paste", action: "paste:", keyEquivalent: "v" 42 | item = addItemWithTitle "Paste and Match Style", action: "pasteAsPlainText:", keyEquivalent: "V" 43 | item.keyEquivalentModifierMask = NSCommandKeyMask | NSAlternateKeyMask 44 | addItemWithTitle "Delete", action: "delete:", keyEquivalent: "" 45 | addItemWithTitle "Select All", action: "selectAll:", keyEquivalent: "a" 46 | end 47 | 48 | fontMenu = createMenu "Font" do 49 | addItemWithTitle "Show Fonts", action: "orderFrontFontPanel:", keyEquivalent: "t" 50 | addItemWithTitle "Bold", action: "addFontTrait:", keyEquivalent: "b" 51 | addItemWithTitle "Italic", action: "addFontTrait:", keyEquivalent: "b" 52 | addItemWithTitle "Underline", action: "underline:", keyEquivalent: "u" 53 | addItem NSMenuItem.separatorItem 54 | addItemWithTitle "Bigger", action: "modifyFont:", keyEquivalent: "+" 55 | addItemWithTitle "Smaller", action: "modifyFont:", keyEquivalent: "-" 56 | end 57 | 58 | textMenu = createMenu "Text" do 59 | addItemWithTitle "Align Left", action: "alignLeft:", keyEquivalent: "{" 60 | addItemWithTitle "Center", action: "alignCenter:", keyEquivalent: "|" 61 | addItemWithTitle "Justify", action: "alignJustified:", keyEquivalent: "" 62 | addItemWithTitle "Align Right", action: "alignRight:", keyEquivalent: "}" 63 | addItem NSMenuItem.separatorItem 64 | addItemWithTitle "Show Ruler", action: "toggleRuler:", keyEquivalent: "" 65 | addItemWithTitle "Copy Ruler", action: "copyRuler:", keyEquivalent: "c" 66 | addItemWithTitle "Paste Ruler", action: "pasteRuler:", keyEquivalent: "v" 67 | end 68 | 69 | addMenu "Format" do 70 | addItem fontMenu 71 | addItem textMenu 72 | end 73 | 74 | addMenu "View" do 75 | item = addItemWithTitle "Show Toolbar", action: "toggleToolbarShown:", keyEquivalent: "t" 76 | item.keyEquivalentModifierMask = NSCommandKeyMask | NSAlternateKeyMask 77 | addItemWithTitle "Customize Toolbar…", action: "runToolbarCustomizationPalette:", keyEquivalent: "" 78 | end 79 | 80 | NSApp.windowsMenu = addMenu "Window" do 81 | addItemWithTitle "Minimize", action: "performMiniaturize:", keyEquivalent: "m" 82 | addItemWithTitle "Zoom", action: "performZoom:", keyEquivalent: "" 83 | addItem NSMenuItem.separatorItem 84 | addItemWithTitle "Bring All To Front", action: "arrangeInFront:", keyEquivalent: "" 85 | end.menu 86 | 87 | NSApp.helpMenu = addMenu "Help" do 88 | addItemWithTitle "#{appName} Help", action: "showHelp:", keyEquivalent: "?" 89 | end.menu 90 | 91 | NSApp.mainMenu = @mainMenu 92 | end 93 | 94 | private 95 | 96 | def addMenu(title, &block) 97 | item = createMenu title, &block 98 | @mainMenu.addItem item 99 | item 100 | end 101 | 102 | def createMenu(title, &block) 103 | menu = NSMenu.alloc.initWithTitle title 104 | menu.instance_eval &block if block_given? 105 | item = NSMenuItem.alloc.initWithTitle title, action: nil, keyEquivalent: "" 106 | item.submenu = menu 107 | item 108 | end 109 | 110 | end -------------------------------------------------------------------------------- /test/unit/test_motion-bundler.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../test_helper", __FILE__) 2 | require "bundler" 3 | 4 | module Unit 5 | class TestMotionBundler < MiniTest::Unit::TestCase 6 | 7 | describe MotionBundler do 8 | it "should distinguish whether compiling for simulator or device" do 9 | assert_equal ARGV, MotionBundler.send(:argv) 10 | 11 | MotionBundler.expects(:argv).twice.returns %w() 12 | assert_equal true, MotionBundler.simulator? 13 | assert_equal false, MotionBundler.device? 14 | 15 | MotionBundler.expects(:argv).twice.returns %w(device) 16 | assert_equal false, MotionBundler.simulator? 17 | assert_equal true, MotionBundler.device? 18 | end 19 | 20 | it "should determine Motion::Project::App default files" do 21 | MotionBundler.expects(:simulator?).returns(true) 22 | assert_equal motion_bundler_file("motion-bundler/simulator/boot.rb"), MotionBundler.boot_file 23 | 24 | MotionBundler.expects(:simulator?).returns(false) 25 | assert_equal motion_bundler_file("motion-bundler/device/boot.rb"), MotionBundler.boot_file 26 | end 27 | 28 | it "should be able to touch MotionBundler::MOTION_BUNDLER_FILE" do 29 | File.expects(:open).with(MotionBundler::MOTION_BUNDLER_FILE, "w") 30 | MotionBundler.send :touch_motion_bundler 31 | end 32 | 33 | it "should be able to write to MotionBundler::MOTION_BUNDLER_FILE" do 34 | File.any_instance.expects(:<<).with <<-RUBY_CODE.gsub(" ", "") 35 | module MotionBundler 36 | FILES = [ 37 | "foo", 38 | "bar", 39 | "qux" 40 | ] 41 | FILES_DEPENDENCIES = { 42 | "foo.rb" => [ 43 | "bar" 44 | ], 45 | "MOTION_BUNDLER" => [ 46 | "qux" 47 | ] 48 | } 49 | REQUIRES = [ 50 | "fu", 51 | "baz" 52 | ] 53 | end 54 | RUBY_CODE 55 | MotionBundler.send :write_motion_bundler, %w(foo bar qux), {"foo.rb" => ["bar"], motion_bundler_file("motion-bundler.rb") => ["qux"]}, %w(fu baz) 56 | 57 | File.expects(:open).with(MotionBundler::MOTION_BUNDLER_FILE, "w") 58 | MotionBundler.send :write_motion_bundler, [], {}, [] 59 | end 60 | 61 | it "should be able to register files to require" do 62 | assert_equal [], MotionBundler.send(:app_requires) 63 | MotionBundler.app_require "foo/bar.rb" 64 | assert_equal ["foo/bar.rb"], MotionBundler.send(:app_requires) 65 | end 66 | 67 | describe "calling `setup`" do 68 | before do 69 | MotionBundler.send(:app_requires).clear 70 | end 71 | 72 | it "should require the :motion Bundler group and mock and trace requires" do 73 | object = mock "object" 74 | object.expects :do_something 75 | 76 | Bundler.expects(:require).with(:motion) 77 | MotionBundler.setup do |app| 78 | app.require lib_file("e") 79 | object.do_something 80 | end 81 | 82 | MotionBundler::Require.expects(:mock_and_trace) 83 | MotionBundler.setup 84 | 85 | MotionBundler.expects(:touch_motion_bundler) 86 | MotionBundler.expects(:ripper_require) 87 | MotionBundler.expects(:tracer_require) 88 | MotionBundler.expects(:write_motion_bundler) 89 | MotionBundler.setup 90 | end 91 | 92 | it "should trace requires and register files and files dependencies" do 93 | Bundler.expects(:require).with(:motion) 94 | Motion::Project::App.any_instance.expects(:files).returns(%w(delegate.rb controller.rb)) 95 | 96 | MotionBundler::Require.expects(:files).returns([ 97 | motion_bundler_file("motion-bundler.rb"), 98 | motion_bundler_file("motion-bundler/simulator/boot.rb"), 99 | motion_bundler_file("motion-bundler/simulator/core_ext.rb"), 100 | motion_bundler_file("motion-bundler/simulator/motion-bundler.rb"), 101 | "APP", 102 | "BUNDLER", 103 | "gems/foo-0.1.0/lib/foo.rb", 104 | "gems/foo-0.1.0/lib/foo/bar.rb", 105 | "gems/foo-0.1.0/lib/foo/version.rb", 106 | "ruby/foo.rb" 107 | ]) 108 | 109 | MotionBundler::Require.expects(:files_dependencies).returns({ 110 | motion_bundler_file("motion-bundler.rb") => [ 111 | motion_bundler_file("motion-bundler/simulator/boot.rb") 112 | ], 113 | motion_bundler_file("motion-bundler/simulator/boot.rb") => [ 114 | motion_bundler_file("motion-bundler/simulator/core_ext.rb"), 115 | motion_bundler_file("motion-bundler/simulator/motion-bundler.rb") 116 | ], 117 | "APP" => [ 118 | "ruby/foo.rb" 119 | ], 120 | "BUNDLER" => [ 121 | "gems/foo-0.1.0/lib/foo.rb" 122 | ], 123 | "gems/foo-0.1.0/lib/foo.rb" => [ 124 | "gems/foo-0.1.0/lib/foo/bar.rb", 125 | "gems/foo-0.1.0/lib/foo/version.rb" 126 | ] 127 | }) 128 | 129 | Motion::Project::App.any_instance.expects(:files=).with(expand_paths([ 130 | motion_bundler_file("motion-bundler/simulator/boot.rb"), 131 | motion_bundler_file("motion-bundler/simulator/core_ext.rb"), 132 | motion_bundler_file("motion-bundler/simulator/motion-bundler.rb"), 133 | "gems/foo-0.1.0/lib/foo.rb", 134 | "gems/foo-0.1.0/lib/foo/bar.rb", 135 | "gems/foo-0.1.0/lib/foo/version.rb", 136 | "ruby/foo.rb", 137 | "delegate.rb", 138 | "controller.rb" 139 | ])) 140 | 141 | Motion::Project::App.any_instance.expects(:files_dependencies).with(expand_paths({ 142 | motion_bundler_file("motion-bundler/simulator/boot.rb") => [ 143 | motion_bundler_file("motion-bundler/simulator/core_ext.rb"), 144 | motion_bundler_file("motion-bundler/simulator/motion-bundler.rb") 145 | ], 146 | "gems/foo-0.1.0/lib/foo.rb" => [ 147 | motion_bundler_file("motion-bundler/simulator/boot.rb"), 148 | "gems/foo-0.1.0/lib/foo/bar.rb", 149 | "gems/foo-0.1.0/lib/foo/version.rb" 150 | ] 151 | })) 152 | 153 | MotionBundler.setup 154 | end 155 | end 156 | end 157 | 158 | end 159 | end -------------------------------------------------------------------------------- /test/gems/slot_machine/README.md: -------------------------------------------------------------------------------- 1 | # SlotMachine [![Build Status](https://secure.travis-ci.org/archan937/slot_machine.png)](http://travis-ci.org/archan937/slot_machine) 2 | 3 | Ruby gem for matching available slots (time slots are also supported) 4 | 5 | ## Introduction 6 | 7 | One of the classic programming problems is the determination of time slot availability. Very often this is used within scheduling / calendar programs. SlotMachine is a very small Ruby gem which can do the job for you. It does not only focuses on time slots, but also slots in general. 8 | 9 | ## Installation 10 | 11 | ### Add `SlotMachine` to your Gemfile 12 | 13 | gem "slot_machine" 14 | 15 | ### Install the gem dependencies 16 | 17 | $ bundle 18 | 19 | ## Usage 20 | 21 | ### SlotMachine::Slot module 22 | 23 | The core implementation of SlotMachine is written in `SlotMachine::Slot`. A module which has to be included within a Ruby class. 24 | SlotMachine provides the classes `Slot` and `TimeSlot` of which `Slot` is defined as follows: 25 | 26 | class Slot 27 | include SlotMachine::Slot 28 | end 29 | 30 | Clean and simple. Cool, huh? `TimeSlot` also includes the `SlotMachine::Slot` module and overrides a few methods to provide time slot specific behaviour. 31 | 32 | ### Slot 33 | 34 | `SlotMachine` is pretty straightforward. A slot consists of a start and an end (integer) value. You can define a slot as follows: 35 | 36 | [1] pry(main)> s = Slot.new 0..20 37 | => # 38 | [2] pry(main)> s.start 39 | => 0 40 | [3] pry(main)> s.end 41 | => 20 42 | 43 | After having defined a slot, you can match available slots within that slot. Let's say that you want to calculate available slots with a length of `10`: 44 | 45 | [4] pry(main)> s.match 10 46 | => [#, #] 47 | 48 | The `match` method returns an array with the available slots. At default, the `Slot` searches for slots with an interval size of `10` and thus `0..10` and `10..20` are matched. 49 | 50 | You can change the interval by passing it as a second argument. Using an interval size of `2`: 51 | 52 | [5] pry(main)> s.match 10, 2 53 | => [#, 54 | #, 55 | #, 56 | #, 57 | #, 58 | #] 59 | 60 | You can also match a slot with another slot instance. This returns the slot in which both slots overlap each other: 61 | 62 | [5] pry(main)> s.match 15..30 #=> you can also use: s.match Slot.new(15..30) 63 | => [#] 64 | [6] s.match Slot.new(21..30) #=> no overlap 65 | => [] 66 | 67 | ### TimeSlot 68 | 69 | The `TimeSlot` class is (of course) similar to the `Slot` class, but accepts military times (e.g. 1300 for 1:00 pm). In other words, it only counts from `0` untill `59` within a `0` to `99` range. Also, the default interval size is `15`. 70 | 71 | An example: 72 | 73 | [1] pry(main)> ts = TimeSlot.new 1015..1045 74 | => # 75 | [2] pry(main)> ts.match 10 76 | => [#, #] 77 | [3] pry(main)> ts.match 10, 5 78 | => [#, 79 | #, 80 | #, 81 | #, 82 | #] 83 | [4] pry(main)> ts.match 1038..1100 #=> you can also use: ts.match TimeSlot.new(1038..1100) 84 | => [#] 85 | 86 | ## Using the console 87 | 88 | The SlotMachine repo is provided with `script/console` which you can use for development / testing purposes. 89 | 90 | Run the following command in your console: 91 | 92 | $ script/console 93 | Loading development environment (SlotMachine 0.1.0) 94 | [1] pry(main)> s = Slot.new 0..25 95 | => # 96 | [2] pry(main)> s.match 15 97 | => [#, #] 98 | [3] pry(main)> s.match 10, 4 99 | => [#, 100 | #, 101 | #, 102 | #] 103 | [4] pry(main)> ts = TimeSlot.new 1015..1045 104 | => # 105 | [5] pry(main)> ts.match 10 106 | => [#, #] 107 | [6] pry(main)> ts.match 10, 5 108 | => [#, 109 | #, 110 | #, 111 | #, 112 | #] 113 | 114 | ## Testing 115 | 116 | Run the following command for testing: 117 | 118 | $ rake 119 | 120 | You can also run a single test file: 121 | 122 | $ ruby test/unit/test_time_slot.rb 123 | 124 | ## Closing words 125 | 126 | Well that's about it! Pretty straightforward, right? Have fun playing with the `SlotMachine`! ^^ 127 | 128 | ## TODO 129 | 130 | * Update documentation regarding Slots, TimeSlots and Time objects 131 | 132 | ## Contact me 133 | 134 | For support, remarks and requests, please mail me at [paul.engel@holder.nl](mailto:paul.engel@holder.nl). 135 | 136 | ## License 137 | 138 | Copyright (c) 2012 Paul Engel, released under the MIT license 139 | 140 | [http://holder.nl](http://holder.nl) - [http://codehero.es](http://codehero.es) - [http://gettopup.com](http://gettopup.com) - [http://github.com/archan937](http://github.com/archan937) - [http://twitter.com/archan937](http://twitter.com/archan937) - [paul.engel@holder.nl](mailto:paul.engel@holder.nl) 141 | 142 | Permission is hereby granted, free of charge, to any person obtaining a copy of this software and associated documentation files (the "Software"), to deal in the Software without restriction, including without limitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software, and to permit persons to whom the Software is furnished to do so, subject to the following conditions: 143 | 144 | The above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software. 145 | 146 | THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE. -------------------------------------------------------------------------------- /test/gems/slot_machine/test/unit/test_slot.rb: -------------------------------------------------------------------------------- 1 | require File.expand_path("../../test_helper", __FILE__) 2 | 3 | module Unit 4 | class TestSlot < MiniTest::Unit::TestCase 5 | 6 | describe Slot do 7 | describe "class methods" do 8 | it "should have the expected class methods" do 9 | assert Slot.respond_to?(:interval) 10 | assert Slot.respond_to?(:default_interval) 11 | 12 | assert_equal 10, Slot.default_interval 13 | Slot.class_eval do 14 | interval 20 15 | end 16 | assert_equal 20, Slot.default_interval 17 | 18 | Slot.class_eval do 19 | interval 10 20 | end 21 | assert_equal 10, Slot.default_interval 22 | end 23 | end 24 | 25 | describe "instance methods" do 26 | it "should have the expected instance methods" do 27 | slot = Slot.new 1 28 | assert slot.respond_to?(:range?) 29 | assert slot.respond_to?(:length?) 30 | assert slot.respond_to?(:start) 31 | assert slot.respond_to?(:start=) 32 | assert slot.respond_to?(:end) 33 | assert slot.respond_to?(:end=) 34 | assert slot.respond_to?(:length) 35 | assert slot.respond_to?(:length=) 36 | assert slot.respond_to?(:to_compared) 37 | assert slot.respond_to?(:match) 38 | assert slot.respond_to?(:+) 39 | assert slot.respond_to?(:-) 40 | end 41 | end 42 | 43 | describe "initialization" do 44 | it "should accept a range" do 45 | slot = Slot.new 10..20 46 | assert_equal 10, slot.start 47 | assert_equal 20, slot.end 48 | end 49 | 50 | it "should accept a length" do 51 | slot = Slot.new 10 52 | assert_equal 10, slot.length 53 | end 54 | 55 | it "should raise an exception when invalid" do 56 | assert_raises ArgumentError do 57 | Slot.new 58 | end 59 | assert_raises ArgumentError do 60 | Slot.new "bar" 61 | end 62 | assert_raises ArgumentError do 63 | Slot.new 1.."bar" 64 | end 65 | assert_raises ArgumentError do 66 | Slot.new "bar"..1 67 | end 68 | assert_raises ArgumentError do 69 | Slot.new "slot".."bar" 70 | end 71 | end 72 | 73 | it "should return whether it's a range or a length" do 74 | slot = Slot.new 10..20 75 | assert slot.range? 76 | assert !slot.length? 77 | 78 | slot = Slot.new 10 79 | assert !slot.range? 80 | assert slot.length? 81 | end 82 | end 83 | 84 | describe "equality" do 85 | it "should return whether it equals another object" do 86 | slot = Slot.new 1 87 | 88 | assert !(slot == 1) 89 | assert !(slot == Slot.new(1..2)) 90 | assert_equal slot, Slot.new(1) 91 | 92 | slot = Slot.new 1..2 93 | 94 | assert !(slot == (1..2)) 95 | assert !(slot == Slot.new(1)) 96 | assert_equal slot, Slot.new(1..2) 97 | end 98 | end 99 | 100 | describe "range slots" do 101 | it "should only set start or end (not length) and typecast the passed argument" do 102 | slot = Slot.new 10..20 103 | 104 | slot.start = 0 105 | assert_equal 0, slot.start 106 | 107 | assert_raises ArgumentError do 108 | slot.start = "bar" 109 | end 110 | 111 | slot.end = 10 112 | assert_equal 10, slot.end 113 | 114 | assert_raises ArgumentError do 115 | slot.end = "bar" 116 | end 117 | 118 | slot.length = 30 119 | assert_nil slot.length 120 | 121 | slot.length = "bar" 122 | assert_nil slot.length 123 | end 124 | 125 | it "should validate the assigned value" do 126 | assert_raises ArgumentError do 127 | Slot.new -10..10 128 | end 129 | 130 | assert_raises ArgumentError do 131 | Slot.new 10..1 132 | end 133 | 134 | slot = Slot.new 10..20 135 | 136 | assert_raises ArgumentError do 137 | slot.start = -10 138 | end 139 | 140 | assert_equal 10, slot.start 141 | 142 | assert_raises ArgumentError do 143 | slot.start = 30 144 | end 145 | 146 | assert_equal 10, slot.start 147 | 148 | assert_raises ArgumentError do 149 | slot.end = 5 150 | end 151 | 152 | assert_equal 20, slot.end 153 | 154 | assert_raises ArgumentError do 155 | slot.end = -10 156 | end 157 | end 158 | 159 | it "should represent itself as an array when invoking to_compared" do 160 | assert_equal [10, 11, 12, 13, 14, 15, 16, 17, 18, 19], Slot.new(10..20).send(:to_compared) 161 | assert_equal 50, Slot.new(855..905).send(:to_compared).size 162 | end 163 | 164 | it "should be able to match available slots" do 165 | slot = Slot.new 0..15 166 | assert_equal [], slot.match(20) 167 | assert_equal [Slot.new(0..15)], slot.match(15) 168 | assert_equal [Slot.new(0..10)], slot.match(10) 169 | assert_equal [Slot.new(0..10), Slot.new(5..15)], slot.match(10, 5) 170 | assert_equal [Slot.new(0..10), Slot.new(5..15)], slot.match(Slot.new(10), 5) 171 | assert_equal [Slot.new(0..10), Slot.new(5..15)], Slot.new(10).match(0..15, 5) 172 | assert_equal [Slot.new(0..10), Slot.new(5..15)], Slot.new(10).match(Slot.new(0..15), 5) 173 | assert_equal [ 174 | Slot.new(0..5), 175 | Slot.new(5..10), 176 | Slot.new(10..15) 177 | ], slot.match(5, 5) 178 | 179 | slot = Slot.new 10..20 180 | assert_equal [], slot.match(0..5) 181 | assert_equal [], slot.match(25..30) 182 | assert_equal [Slot.new(10..20)], slot.match(10..20) 183 | assert_equal [Slot.new(10..20)], slot.match(0..30) 184 | assert_equal [Slot.new(10..15)], slot.match(0..15) 185 | assert_equal [Slot.new(15..20)], slot.match(15..30) 186 | 187 | assert_raises ArgumentError do 188 | slot.match 0 189 | end 190 | assert_raises ArgumentError do 191 | slot.match -1 192 | end 193 | assert_raises ArgumentError do 194 | slot.match 1, 0 195 | end 196 | assert_raises ArgumentError do 197 | Slot.new(5).match(5) 198 | end 199 | end 200 | 201 | it "should be able to add other slots" do 202 | assert_equal Slots, (Slot.new(1..10) + (5..8)).class 203 | assert_equal Slots, (Slot.new(1..10) + Slot.new(5..8)).class 204 | assert_equal Slots, (Slot.new(1..10) + [5..8]).class 205 | assert_equal Slots, (Slot.new(1..10) + Slots.new([5..8])).class 206 | end 207 | 208 | it "should be able to subtract other slots" do 209 | assert_equal Slots, (Slot.new(1..10) - (5..8)).class 210 | assert_equal Slots, (Slot.new(1..10) - Slot.new(5..8)).class 211 | assert_equal Slots, (Slot.new(1..10) - [5..8]).class 212 | assert_equal Slots, (Slot.new(1..10) - Slots.new([5..8])).class 213 | end 214 | end 215 | 216 | describe "length slots" do 217 | it "should only set length (not start or end) and typecast the passed argument" do 218 | slot = Slot.new 10 219 | 220 | slot.start = 0 221 | assert_nil slot.start 222 | 223 | slot.start = "bar" 224 | assert_nil slot.start 225 | 226 | slot.end = 10 227 | assert_nil slot.end 228 | 229 | slot.end = "bar" 230 | assert_nil slot.end 231 | 232 | slot.length = 30 233 | assert_equal 30, slot.length 234 | 235 | assert_raises ArgumentError do 236 | slot.length = "bar" 237 | end 238 | end 239 | 240 | it "should validate the assigned value" do 241 | assert_raises ArgumentError do 242 | Slot.new -10 243 | end 244 | 245 | slot = Slot.new 10 246 | 247 | assert_raises ArgumentError do 248 | slot.length = -10 249 | end 250 | end 251 | 252 | it "should represent itself as an integer when invoking to_compared" do 253 | assert_equal 10, Slot.new(10).send(:to_compared) 254 | end 255 | end 256 | end 257 | 258 | end 259 | end -------------------------------------------------------------------------------- /lib/motion-bundler/mocks/zliby-0.0.5/zlib.rb: -------------------------------------------------------------------------------- 1 | require "stringio" 2 | 3 | module Zlib 4 | ZLIBY_VERSION = "0.0.5" 5 | ZLIB_VERSION = "1.2.3" 6 | VERSION = "0.6.0" #For compatibility with Ruby-core Zlib 7 | MAXBITS = 15 8 | MAXLCODES = 286 9 | MAXDCODES = 30 10 | MAXCODES = (MAXLCODES+MAXDCODES) 11 | FIXLCODES = 288 12 | MAX_WBITS = 15 13 | Z_DEFLATED = 8 14 | 15 | def self.adler32 string="", adler=1 16 | if adler > (2**128) - 1 then raise RangeError.new end 17 | accum1 = adler & 0xffff 18 | accum2 = (adler >> 16) & 0xffff 19 | 20 | len = string.length 21 | x = -1 22 | while len > 0 23 | tlen = len > 5552 ? 5552 : len 24 | len -= tlen 25 | while tlen >0 26 | x += 1 27 | accum1 += string[x] 28 | accum2 += accum1 29 | tlen -= 1 30 | end 31 | accum1 %= 65521 32 | accum2 %= 65521 33 | end 34 | accum2 << 16 | accum1 35 | end 36 | 37 | def self.crc_table 38 | [0, 1996959894, 3993919788, 2567524794, 124634137, 1886057615, 3915621685, 2657392035, 249268274, 2044508324, 3772115230, 2547177864, 162941995, 2125561021, 3887607047, 2428444049, 498536548, 1789927666, 4089016648, 2227061214, 450548861, 1843258603, 4107580753, 2211677639, 325883990, 1684777152, 4251122042, 2321926636, 335633487, 1661365465, 4195302755, 2366115317, 997073096, 1281953886, 3579855332, 2724688242, 1006888145, 1258607687, 3524101629, 2768942443, 901097722, 1119000684, 3686517206, 2898065728, 853044451, 1172266101, 3705015759, 2882616665, 651767980, 1373503546, 3369554304, 3218104598, 565507253, 1454621731, 3485111705, 3099436303, 671266974, 1594198024, 3322730930, 2970347812, 795835527, 1483230225, 3244367275, 3060149565, 1994146192, 31158534, 2563907772, 4023717930, 1907459465, 112637215, 2680153253, 3904427059, 2013776290, 251722036, 2517215374, 3775830040, 2137656763, 141376813, 2439277719, 3865271297, 1802195444, 476864866, 2238001368, 4066508878, 1812370925, 453092731, 2181625025, 4111451223, 1706088902, 314042704, 2344532202, 4240017532, 1658658271, 366619977, 2362670323, 4224994405, 1303535960, 984961486, 2747007092, 3569037538, 1256170817, 1037604311, 2765210733, 3554079995, 1131014506, 879679996, 2909243462, 3663771856, 1141124467, 855842277, 2852801631, 3708648649, 1342533948, 654459306, 3188396048, 3373015174, 1466479909, 544179635, 3110523913, 3462522015, 1591671054, 702138776, 2966460450, 3352799412, 1504918807, 783551873, 3082640443, 3233442989, 3988292384, 2596254646, 62317068, 1957810842, 3939845945, 2647816111, 81470997, 1943803523, 3814918930, 2489596804, 225274430, 2053790376, 3826175755, 2466906013, 167816743, 2097651377, 4027552580, 2265490386, 503444072, 1762050814, 4150417245, 2154129355, 426522225, 1852507879, 4275313526, 2312317920, 282753626, 1742555852, 4189708143, 2394877945, 397917763, 1622183637, 3604390888, 2714866558, 953729732, 1340076626, 3518719985, 2797360999, 1068828381, 1219638859, 3624741850, 2936675148, 906185462, 1090812512, 3747672003, 2825379669, 829329135, 1181335161, 3412177804, 3160834842, 628085408, 1382605366, 3423369109, 3138078467, 570562233, 1426400815, 3317316542, 2998733608, 733239954, 1555261956, 3268935591, 3050360625, 752459403, 1541320221, 2607071920, 3965973030, 1969922972, 40735498, 2617837225, 3943577151, 1913087877, 83908371, 2512341634, 3803740692, 2075208622, 213261112, 2463272603, 3855990285, 2094854071, 198958881, 2262029012, 4057260610, 1759359992, 534414190, 2176718541, 4139329115, 1873836001, 414664567, 2282248934, 4279200368, 1711684554, 285281116, 2405801727, 4167216745, 1634467795, 376229701, 2685067896, 3608007406, 1308918612, 956543938, 2808555105, 3495958263, 1231636301, 1047427035, 2932959818, 3654703836, 1088359270, 936918000, 2847714899, 3736837829, 1202900863, 817233897, 3183342108, 3401237130, 1404277552, 615818150, 3134207493, 3453421203, 1423857449, 601450431, 3009837614, 3294710456, 1567103746, 711928724, 3020668471, 3272380065, 1510334235, 755167117] 39 | end 40 | 41 | def self.crc32 string="", crc=0 42 | if crc > 2**128 - 1 then raise RangeError.new end 43 | crc = crc ^ 0xffffffff 44 | string.each_byte do |byte| 45 | index = (crc ^ byte) & 0xff 46 | crc = (crc >> 8) ^ crc_table[index] 47 | end 48 | crc ^ 0xffffffff 49 | end 50 | 51 | 52 | class ZStream 53 | 54 | def initialize 55 | @input_buffer = [] 56 | @output_buffer = [] 57 | @out_pos = -1 58 | @in_pos = -1 59 | @bit_bucket = 0 60 | @bit_count = 0 61 | 62 | end 63 | #Returns the adler-32 checksum of the input data. 64 | def adler 65 | end 66 | 67 | #Returns the number of bytes read. Normally 0 since all bytes are read at once. 68 | def avail_in 69 | @input_buffer.length - @in_pos 70 | end 71 | 72 | #Returns number of free bytes in the output buffer. As the output buffer is self expanding this normally returns 0. 73 | def avail_out 74 | @output_buffer.length - @out_pos 75 | end 76 | 77 | #Allocates size bytes in output buffer. If size < avail_out it truncates the buffer. 78 | def avail_out= size 79 | size.times do 80 | if size > avail_out 81 | @output_buffer.push nil 82 | else 83 | @output_buffer.pop 84 | end 85 | end 86 | end 87 | 88 | #Closes stream. Further operations will raise Zlib::StreamError 89 | def close 90 | @closed = true 91 | end 92 | 93 | #True if stream closed, otherwise False. 94 | def closed? 95 | @closed 96 | end 97 | 98 | #Best guess of input data, one of Zlib::BINARY, Zlib::ASCII, or Zlib::UNKNOWN 99 | def data_type 100 | end 101 | 102 | #See close 103 | def end 104 | close 105 | end 106 | 107 | #See closed? 108 | def ended? 109 | closed? 110 | end 111 | 112 | #Finishes the stream, flushes output buffer, implemented by child classes 113 | def finish 114 | close 115 | end 116 | 117 | #True if stream is finished, otherwise False 118 | def finished? 119 | if @finished.nil? then 120 | false 121 | else 122 | @finished 123 | end 124 | end 125 | 126 | #Flushes input buffer and returns the data therein. 127 | def flush_next_in 128 | @in_pos = @input_buffer.length 129 | @finished = true 130 | ret = @input_buffer.pack("c*") 131 | @input_buffer = [] 132 | ret 133 | end 134 | 135 | #Flushes the output buffer and returns all the data 136 | def flush_next_out 137 | @out_pos = @output_buffer.length 138 | @finished = true 139 | ret = @output_buffer.pack("c*") 140 | @output_buffer = [] 141 | ret 142 | end 143 | 144 | #Reset stream. Input and Output buffers are reset. 145 | def reset 146 | @out_pos = -1 147 | @in_pos = -1 148 | @input_buffer = [] 149 | @output_buffer = [] 150 | end 151 | 152 | #See finished. 153 | def stream_end? 154 | finished? 155 | end 156 | 157 | #Size of input buffer. 158 | def total_in 159 | @input_buffer.length 160 | end 161 | 162 | #Size of output buffer. 163 | def total_out 164 | @output_buffer.length 165 | end 166 | 167 | private 168 | #returns need bits from the input buffer 169 | # == Format Notes 170 | # bits are stored LSB to MSB 171 | def get_bits need 172 | val = @bit_bucket 173 | while @bit_count < need 174 | val |= (@input_buffer[@in_pos+=1] << @bit_count) 175 | @bit_count += 8 176 | end 177 | 178 | @bit_bucket = val >> need 179 | @bit_count -= need 180 | val & ((1 << need) - 1) 181 | end 182 | public 183 | end 184 | 185 | #== DEFLATE Decompression 186 | #Implements decompression of a RFC-1951[ftp://ftp.rfc-editor.org/in-notes/rfc1951.txt] compatible stream. 187 | class Inflate < ZStream 188 | 189 | def initialize window_bits=MAX_WBITS 190 | @w_bits = window_bits 191 | if @w_bits < 0 then 192 | @rawdeflate = true 193 | @w_bits *= -1 194 | end 195 | super() 196 | @zstring = "" 197 | end 198 | 199 | #Appends data to the input stream 200 | def <<(string) 201 | @zstring << string 202 | inflate 203 | end 204 | 205 | #Sets the inflate dictionary 206 | def set_dictionary string 207 | @dict = string 208 | reset 209 | end 210 | 211 | #==Example 212 | # f = File.open "example.z" 213 | # i = Inflate.new 214 | # i.inflate f.read 215 | def inflate zstring=nil 216 | @zstring = zstring unless zstring.nil? 217 | #We can't use unpack, IronRuby doesn't have it yet. 218 | @zstring.each_byte {|b| @input_buffer << b} 219 | 220 | unless @rawdeflate then 221 | 222 | compression_method_and_flags = @input_buffer[@in_pos+=1] 223 | flags = @input_buffer[@in_pos+=1] 224 | 225 | #CMF and FLG, when viewed as a 16-bit unsigned integer stored inMSB order (CMF*256 + FLG), is a multiple of 31 226 | if ((compression_method_and_flags << 0x08) + flags) % 31 != 0 then raise Zlib::DataError.new("incorrect header check") end 227 | 228 | #CM = 8 denotes the ìdeflateî compression method with a window size up to 32K. (RFC's only specify CM 8) 229 | compression_method = compression_method_and_flags & 0x0F 230 | 231 | if compression_method != Z_DEFLATED then raise Zlib::DataError.new("unknown compression method") end 232 | 233 | #For CM = 8, CINFO is the base-2 logarithm of the LZ77 window size,minus eight (CINFO=7 indicates a 32K window size) 234 | compression_info = compression_method_and_flags >> 0x04 235 | 236 | if (compression_info + 8) > @w_bits then raise Zlib::DataError.new("invalid window size") end 237 | 238 | preset_dictionary_flag = ((flags & 0x20) >> 0x05) == 1 239 | compression_level = (flags & 0xC0) >> 0x06 240 | 241 | if preset_dictionary_flag and @dict.nil? then raise Zlib::NeedDict.new "Preset dictionary needed!" end 242 | 243 | #TODO: Add Preset dictionary support 244 | if preset_dictionary_flag then 245 | @dict_crc = @input_buffer[@in_pos+=1] << 24 | @input_buffer[@in_pos+=1] << 16 | @input_buffer[@in_pos+=1] << 8 | @input_buffer[@in_pos+=1] 246 | end 247 | 248 | end 249 | last_block = false 250 | #Begin processing DEFLATE stream 251 | until last_block 252 | last_block = (get_bits(1) == 1) 253 | block_type = get_bits(2) 254 | case block_type 255 | when 0 then no_compression 256 | when 1 then fixed_codes 257 | when 2 then dynamic_codes 258 | when 3 then raise Zlib::DataError.new("invalid block type") 259 | end 260 | end 261 | finish 262 | end 263 | 264 | #Finishes inflating and flushes the buffer 265 | def finish 266 | output = "" 267 | inflate unless @output_buffer.length > 0 268 | @output_buffer.each {|c| output << c } 269 | super 270 | output 271 | end 272 | 273 | private 274 | 275 | def no_compression 276 | @bit_bucket = 0 277 | @bit_count = 0 278 | if @in_pos + 4 > @input_buffer.length then raise Zlib::DataError.new("not enough input to read length code") end 279 | length = @input_buffer[@in_pos+=1] | (@input_buffer[@in_pos+=1] << 8) 280 | 281 | if (~length & 0xff != @input_buffer[@in_pos+=1]) || (((~length >> 8) & 0xff) != @input_buffer[@in_pos+=1]) then raise Zlib::DataError.new("invalid stored block lengths") end 282 | 283 | if @in_pos + length > @input_buffer.length then raise Zlib::DataError.new("ran out of input") end 284 | 285 | 286 | length.times do 287 | @output_buffer[@out_pos += 1] = @input_buffer[@in_pos += 1] 288 | end 289 | 290 | 291 | end 292 | 293 | def fixed_codes 294 | if @fixed_length_codes.nil? && @fixed_distance_codes.nil? then generate_huffmans end 295 | codes @fixed_length_codes, @fixed_distance_codes 296 | end 297 | 298 | def dynamic_codes 299 | 300 | order = [16, 17, 18, 0, 8, 7, 9, 6, 10, 5, 11, 4, 12, 3, 13, 2, 14, 1, 15] 301 | nlen = get_bits(5) + 257 302 | ndist = get_bits(5) + 1 303 | ncode = get_bits(4) + 4 304 | 305 | 306 | lengths=[] 307 | dynamic_length_codes = Zlib::Inflate::HuffmanTree.new 308 | dynamic_distance_codes = Zlib::Inflate::HuffmanTree.new 309 | 310 | if (nlen > MAXLCODES || ndist > MAXDCODES) then raise Zlib::DataError.new("too many length or distance codes") end 311 | idx = 0 312 | 313 | while idx < ncode 314 | lengths[order[idx]] = get_bits(3) 315 | idx += 1 316 | end 317 | while idx < 19 318 | lengths[order[idx]] = 0 319 | idx += 1 320 | end 321 | err = construct_tree dynamic_length_codes, lengths, 18 322 | if err != 0 then raise Zlib::DataError.new("code lengths codes incomplete") end 323 | 324 | idx = 0 325 | while idx < (nlen + ndist) 326 | symbol = decode(dynamic_length_codes) 327 | if symbol < 16 then 328 | lengths[idx] = symbol 329 | idx += 1; 330 | else 331 | len = 0 332 | if symbol == 16 then 333 | if idx == 0 then raise Zlib::DataError.new("repeat lengths with no first length") end 334 | len = lengths[idx - 1] 335 | symbol = 3 + get_bits(2) 336 | elsif symbol == 17 then 337 | symbol = 3 + get_bits(3) 338 | elsif symbol == 18 then 339 | symbol = 11 + get_bits(7) 340 | else 341 | raise Zlib::DataError.new("invalid repeat length code") 342 | end 343 | if (idx + symbol) > (nlen + ndist) then raise Zlib::DataError.new("repeat more than specified lengths") end 344 | until symbol == 0 345 | lengths[idx] = len 346 | idx+=1 347 | symbol -= 1 348 | end 349 | end 350 | end 351 | 352 | err = construct_tree dynamic_length_codes, lengths, nlen-1 353 | 354 | if err < 0 || (err > 0 && (nlen - dynamic_length_codes.count[0] != 1)) then raise Zlib::DataError.new("invalid literal/length code lengths") end 355 | 356 | nlen.times { lengths.delete_at 0 } #We do this since we don't have pointer arithmetic in ruby 357 | 358 | err = construct_tree dynamic_distance_codes, lengths, ndist-1 359 | if err < 0 || (err > 0 && (ndist - dynamic_distance_codes.count[0] != 1)) then raise Zlib::DataError.new("invalid distance code lengths") end 360 | 361 | codes dynamic_length_codes, dynamic_distance_codes 362 | end 363 | 364 | def generate_huffmans 365 | 366 | lengths = [] 367 | 368 | #literal/length table 369 | for idx in (000..143) 370 | lengths[idx] = 8 371 | end 372 | for idx in (144..255) 373 | lengths[idx] = 9 374 | end 375 | for idx in (256..279) 376 | lengths[idx] = 7 377 | end 378 | for idx in (280..287) 379 | lengths[idx] = 8 380 | end 381 | @fixed_length_codes = Zlib::Inflate::HuffmanTree.new 382 | construct_tree @fixed_length_codes, lengths, 287 383 | 384 | for idx in (00..29) 385 | lengths[idx] = 5 386 | end 387 | @fixed_distance_codes = Zlib::Inflate::HuffmanTree.new 388 | construct_tree @fixed_distance_codes, lengths, 29 389 | 390 | end 391 | 392 | def codes length_codes, distance_codes 393 | lens = [3, 4, 5, 6, 7, 8, 9, 10, 11, 13, 15, 17, 19, 23, 27, 31,35, 43, 51, 59, 67, 83, 99, 115, 131, 163, 195, 227, 258] 394 | lext = [0, 0, 0, 0, 0, 0, 0, 0, 1, 1, 1, 1, 2, 2, 2, 2, 3, 3, 3, 3, 4, 4, 4, 4, 5, 5, 5, 5, 0] 395 | dists = [1, 2, 3, 4, 5, 7, 9, 13, 17, 25, 33, 49, 65, 97, 129, 193, 257, 385, 513, 769, 1025, 1537, 2049, 3073, 4097, 6145, 8193, 12289, 16385, 24577] 396 | dext = [0, 0, 0, 0, 1, 1, 2, 2, 3, 3, 4, 4, 5, 5, 6, 6, 7, 7, 8, 8, 9, 9, 10, 10, 11, 11, 12, 12, 13, 13] 397 | 398 | symbol = 0 399 | 400 | until symbol == 256 401 | symbol = decode(length_codes) 402 | if symbol < 0 then return symbol end 403 | if symbol < 256 then @output_buffer[@out_pos+=1] = symbol end 404 | if symbol > 256 then 405 | symbol -= 257 406 | if symbol >= 29 then raise Zlib::DataError.new("invalid literal/length or distance code in fixed or dynamic block") end 407 | len = lens[symbol] + get_bits(lext[symbol]) 408 | symbol = decode(distance_codes) 409 | if symbol < 0 then return symbol end 410 | dist = dists[symbol] + get_bits(dext[symbol]) 411 | if dist > @output_buffer.length then raise Zlib::DataError.new("distance is too far back in fixed or dynamic block") end 412 | while len > 0 413 | @output_buffer[@out_pos+=1] = @output_buffer[@out_pos - dist] 414 | len -= 1 415 | end 416 | end 417 | end 418 | return 0 419 | end 420 | 421 | def decode huffman_tree 422 | code = 0 423 | first = 0 424 | index = 0 425 | for len in (1..15) 426 | code |= get_bits(1) 427 | count = huffman_tree.count[len] 428 | if code < (first + count) then return huffman_tree.symbol[index + (code - first)] end 429 | index += count 430 | first += count 431 | first <<= 1 432 | code <<= 1 433 | end 434 | -9 435 | end 436 | 437 | def construct_tree huffman_tree, lengths, n_symbols 438 | offs = [] 439 | 440 | for len in (000..MAXBITS) 441 | huffman_tree.count[len] = 0 442 | end 443 | 444 | for symbol in (000..n_symbols) 445 | huffman_tree.count[lengths[symbol]] += 1 446 | end 447 | 448 | if huffman_tree.count[0] == n_symbols then return 0 end 449 | 450 | left = 1 451 | for len in (1..MAXBITS) 452 | left <<= 1 453 | left -= huffman_tree.count[len]; 454 | if left < 0 then return left end 455 | end 456 | 457 | offs[1] = 0 458 | 459 | for len in (1..(MAXBITS-1)) 460 | offs[len+1] = offs[len] + huffman_tree.count[len] 461 | end 462 | 463 | for symbol in (0..n_symbols) 464 | if lengths[symbol] != 0 then 465 | huffman_tree.symbol[offs[lengths[symbol]]] = symbol 466 | offs[lengths[symbol]] += 1 467 | end 468 | 469 | end 470 | left 471 | end 472 | 473 | class HuffmanTree 474 | attr_accessor :count, :symbol 475 | def initialize 476 | @count = [] 477 | @symbol = [] 478 | end 479 | end 480 | 481 | class << self 482 | def inflate zstring 483 | d = self.new 484 | d.inflate zstring 485 | end 486 | end 487 | 488 | end 489 | 490 | 491 | class GzipFile 492 | 493 | def initialize 494 | @input_buffer = [] 495 | @output_buffer = [] 496 | @out_pos = -1 497 | @in_pos = -1 498 | end 499 | 500 | def close 501 | end 502 | 503 | end 504 | 505 | class GzipReader < GzipFile 506 | OSES = ['FAT filesystem', 507 | 'Amiga', 508 | 'VMS (or OpenVMS)', 509 | 'Unix', 510 | 'VM/CMS', 511 | 'Atari TOS', 512 | 'HPFS fileystem (OS/2, NT)', 513 | 'Macintosh', 514 | 'Z-System', 515 | 'CP/M', 516 | 'TOPS-20', 517 | 'NTFS filesystem (NT)', 518 | 'QDOS', 519 | 'Acorn RISCOS', 520 | 'unknown'] 521 | 522 | def initialize io 523 | 524 | #Add a helper method to check bits 525 | ::Fixnum.module_eval do 526 | def isbitset? bit_to_check 527 | if self & (2 ** bit_to_check) == (2 ** bit_to_check) then true else false end 528 | end 529 | end 530 | 531 | super() 532 | @io = io 533 | io.read.each_byte {|b| @input_buffer << b} 534 | if @input_buffer[@in_pos+=1] != 0x1f || @input_buffer[@in_pos+=1] != 0x8b then raise Zlib::GzipFile::Error.new("not in gzip format") end 535 | if @input_buffer[@in_pos+=1] != 0x08 then raise Zlib::GzipFile::Error.new("unknown compression method") end 536 | flg = @input_buffer[@in_pos+=1] 537 | @ftext = flg.isbitset? 0 538 | @fhcrc = flg.isbitset? 1 539 | @fextra = flg.isbitset? 2 540 | @fname = flg.isbitset? 3 541 | @fcomment = flg.isbitset? 4 542 | @mtime = Time.at(@input_buffer[@in_pos+=1] | (@input_buffer[@in_pos+=1] << 8) | (@input_buffer[@in_pos+=1] << 16) | (@input_buffer[@in_pos+=1] << 24)) 543 | @xfl = @input_buffer[@in_pos+=1] 544 | @os = OSES[@input_buffer[@in_pos+=1]] 545 | if @fextra then 546 | @xlen = (@input_buffer[@in_pos+=1] | (@input_buffer[@in_pos+=1] << 8)) 547 | @xtra_field = [] 548 | @xlen.times {@xtra_field << @input_buffer[@in_pos+=1]} 549 | end 550 | if @fname then 551 | @original_name = "" 552 | until @original_name["\0"].nil? == false 553 | @original_name.concat(@input_buffer[@in_pos+=1]) 554 | end 555 | @original_name.chop! 556 | end 557 | if @fcomment then 558 | @comment = "" 559 | until @comment["\0"].nil? == false 560 | @comment.concat(@input_buffer[@in_pos+=1]) 561 | end 562 | @comment.chop! 563 | end 564 | if @fhcrc then 565 | @header_crc = @input_buffer[@in_pos+=1] | (@input_buffer[@in_pos+=1] << 8) 566 | end 567 | @contents = "" 568 | until @in_pos == @input_buffer.length-1 569 | @contents.concat(@input_buffer[@in_pos+=1]) 570 | end 571 | 572 | #we do raw deflate, no headers 573 | @zstream = Zlib::Inflate.new -MAX_WBITS 574 | @inflated = StringIO.new(@zstream.inflate @contents) 575 | 576 | end 577 | 578 | def read length=nil 579 | @inflated.read length 580 | end 581 | 582 | def eof? 583 | @inflated.eof? 584 | end 585 | 586 | def pos 587 | @inflated.pos 588 | end 589 | 590 | def rewind 591 | @inflated.rewind 592 | @io.seek 0, IO::SEEK_SET 593 | end 594 | 595 | class << self 596 | 597 | def open filename 598 | io = File.open filename 599 | gz = self.new io 600 | if block_given? then yield gz else gz end 601 | end 602 | 603 | end 604 | end 605 | 606 | 607 | #Generic Error 608 | class Error < StandardError 609 | end 610 | 611 | #Dictionary Needed 612 | class NeedDict < Error 613 | end 614 | 615 | #Invalid Data 616 | class DataError < Error 617 | end 618 | 619 | end -------------------------------------------------------------------------------- /lib/motion-bundler/mocks/mac_ruby-0.12/strscan.rb: -------------------------------------------------------------------------------- 1 | # MacRuby implementation of strscan. 2 | # 3 | # This file is covered by the Ruby license. See COPYING for more details. 4 | # 5 | # Copyright (C) 2012, The MacRuby Team. All rights reserved. 6 | # Copyright (C) 2009-2011, Apple Inc. All rights reserved. 7 | 8 | class ScanError < StandardError; end 9 | 10 | # StringScanner provides for lexical scanning operations on a String. Here is 11 | # an example of its usage: 12 | # 13 | # s = StringScanner.new('This is an example string') 14 | # s.eos? # -> false 15 | # 16 | # p s.scan(/\w+/) # -> "This" 17 | # p s.scan(/\w+/) # -> nil 18 | # p s.scan(/\s+/) # -> " " 19 | # p s.scan(/\s+/) # -> nil 20 | # p s.scan(/\w+/) # -> "is" 21 | # s.eos? # -> false 22 | # 23 | # p s.scan(/\s+/) # -> " " 24 | # p s.scan(/\w+/) # -> "an" 25 | # p s.scan(/\s+/) # -> " " 26 | # p s.scan(/\w+/) # -> "example" 27 | # p s.scan(/\s+/) # -> " " 28 | # p s.scan(/\w+/) # -> "string" 29 | # s.eos? # -> true 30 | # 31 | # p s.scan(/\s+/) # -> nil 32 | # p s.scan(/\w+/) # -> nil 33 | # 34 | # Scanning a string means remembering the position of a scan pointer, 35 | # which is just an index. The point of scanning is to move forward a bit at 36 | # a time, so matches are sought after the scan pointer; usually immediately 37 | # after it. 38 | # 39 | # Given the string "test string", here are the pertinent scan pointer 40 | # positions: 41 | # 42 | # t e s t s t r i n g 43 | # 0 1 2 ... 1 44 | # 0 45 | # 46 | # When you #scan for a pattern (a regular expression), the match must occur 47 | # at the character after the scan pointer. If you use #scan_until, then the 48 | # match can occur anywhere after the scan pointer. In both cases, the scan 49 | # pointer moves just beyond the last character of the match, ready to 50 | # scan again from the next character onwards. This is demonstrated by the 51 | # example above. 52 | # 53 | # == Method Categories 54 | # 55 | # There are other methods besides the plain scanners. You can look ahead in 56 | # the string without actually scanning. You can access the most recent match. 57 | # You can modify the string being scanned, reset or terminate the scanner, 58 | # find out or change the position of the scan pointer, skip ahead, and so on. 59 | # 60 | # === Advancing the Scan Pointer 61 | # 62 | # - #getch 63 | # - #get_byte 64 | # - #scan 65 | # - #scan_until 66 | # - #skip 67 | # - #skip_until 68 | # 69 | # === Looking Ahead 70 | # 71 | # - #check 72 | # - #check_until 73 | # - #exist? 74 | # - #match? 75 | # - #peek 76 | # 77 | # === Finding Where we Are 78 | # 79 | # - #beginning_of_line? (#bol?) 80 | # - #eos? 81 | # - #rest? 82 | # - #rest_size 83 | # - #pos 84 | # 85 | # === Setting Where we Are 86 | # 87 | # - #reset 88 | # - #terminate 89 | # - #pos= 90 | # 91 | # === Match Data 92 | # 93 | # - #matched 94 | # - #matched? 95 | # - #matched_size 96 | # - [] 97 | # - #pre_match 98 | # - #post_match 99 | # 100 | # === Miscellaneous 101 | # 102 | # - << 103 | # - #concat 104 | # - #string 105 | # - #string= 106 | # - #unscan 107 | # 108 | # There are aliases to several of the methods. 109 | # 110 | class StringScanner 111 | 112 | # string :: The string to scan 113 | # pos :: The position of the scan pointer. In the 'reset' position, this 114 | # value is zero. In the 'terminated' position (i.e. the string is exhausted), 115 | # this value is the length of the string. 116 | # 117 | # In short, it's a 0-based index into the string. 118 | # 119 | # s = StringScanner.new('test string') 120 | # s.pos # -> 0 121 | # s.scan_until /str/ # -> "test str" 122 | # s.pos # -> 8 123 | # s.terminate # -> # 124 | # s.pos # -> 11 125 | # 126 | # match :: Matched string 127 | # 128 | attr_reader :string, :pos 129 | 130 | # This method is defined for backward compatibility. 131 | # 132 | def self.must_C_version 133 | self 134 | end 135 | 136 | # StringScanner.new(string, dup = false) 137 | # 138 | # Creates a new StringScanner object to scan over the given +string+. 139 | # +dup+ argument is obsolete and not used now. 140 | # 141 | def initialize(string, dup = false) 142 | begin 143 | @string = string.to_str 144 | rescue 145 | raise TypeError, "can't convert #{string.class.name} into String" 146 | end 147 | @pos = 0 148 | end 149 | 150 | # Duplicates a StringScanner object when dup or clone are called on the 151 | # object. 152 | # 153 | def initialize_copy(orig) 154 | @string = orig.string 155 | @pos = orig.pos 156 | @match = orig.instance_variable_get("@match") 157 | end 158 | 159 | # Reset the scan pointer (index 0) and clear matching data. 160 | # 161 | def reset 162 | @pos = 0 163 | @match = nil 164 | self 165 | end 166 | 167 | # Set the scan pointer to the end of the string and clear matching data. 168 | # 169 | def terminate 170 | @match = nil 171 | @pos = @string.size 172 | self 173 | end 174 | 175 | # Equivalent to #terminate. 176 | # This method is obsolete; use #terminate instead. 177 | # 178 | def clear 179 | warn "StringScanner#clear is obsolete; use #terminate instead" if $VERBOSE 180 | terminate 181 | end 182 | 183 | # Changes the string being scanned to +str+ and resets the scanner. 184 | # Returns +str+. 185 | # 186 | def string=(str) 187 | reset 188 | begin 189 | @string = str.to_str 190 | rescue 191 | raise TypeError, "can't convert #{str.class.name} into String" 192 | end 193 | end 194 | 195 | # Appends +str+ to the string being scanned. 196 | # This method does not affect scan pointer. 197 | # 198 | # s = StringScanner.new("Fri Dec 12 1975 14:39") 199 | # s.scan(/Fri /) 200 | # s << " +1000 GMT" 201 | # s.string # -> "Fri Dec 12 1975 14:39 +1000 GMT" 202 | # s.scan(/Dec/) # -> "Dec" 203 | # 204 | def concat(str) 205 | begin 206 | @string << str.to_str 207 | rescue 208 | raise TypeError, "can't convert #{str.class.name} into String" 209 | end 210 | self 211 | end 212 | alias :<< :concat 213 | 214 | # Modify the scan pointer. 215 | # 216 | # s = StringScanner.new('test string') 217 | # s.pos = 7 # -> 7 218 | # s.rest # -> "ring" 219 | # 220 | def pos=(n) 221 | n = (n + @string.size) if (n < 0) 222 | raise RangeError, "index out of range" if (n < 0 || (@string && n > @string.size)) 223 | @pos = n 224 | end 225 | 226 | alias :pointer :pos 227 | alias :pointer= :pos= 228 | 229 | # Scans one byte and returns it. 230 | # This method is not multibyte sensitive. 231 | # See also: #getch. 232 | # 233 | # s = StringScanner.new('ab') 234 | # s.get_byte # => "a" 235 | # s.get_byte # => "b" 236 | # s.get_byte # => nil 237 | # 238 | # # encoding: EUC-JP 239 | # s = StringScanner.new("\244\242") 240 | # s.get_byte # => "244" 241 | # s.get_byte # => "242" 242 | # s.get_byte # => nil 243 | # 244 | def get_byte 245 | # temp hack 246 | # TODO replace by a solution that will work with UTF-8 247 | scan(/./mn) 248 | end 249 | 250 | # Equivalent to #get_byte. 251 | # This method is obsolete; use #get_byte instead. 252 | # 253 | def getbyte 254 | warn "StringScanner#getbyte is obsolete; use #get_byte instead" if $VERBOSE 255 | get_byte 256 | end 257 | 258 | # Tries to match with +pattern+ at the current position. If there's a match, 259 | # the scanner advances the "scan pointer" and returns the matched string. 260 | # Otherwise, the scanner returns +nil+. 261 | # 262 | # s = StringScanner.new('test string') 263 | # p s.scan(/\w+/) # -> "test" 264 | # p s.scan(/\w+/) # -> nil 265 | # p s.scan(/\s+/) # -> " " 266 | # p s.scan(/\w+/) # -> "string" 267 | # p s.scan(/./) # -> nil 268 | # 269 | def scan(pattern) 270 | _scan(pattern, true, true, true) 271 | end 272 | 273 | # Scans the string _until_ the +pattern+ is matched. Returns the substring up 274 | # to and including the end of the match, advancing the scan pointer to that 275 | # location. If there is no match, +nil+ is returned. 276 | # 277 | # s = StringScanner.new("Fri Dec 12 1975 14:39") 278 | # s.scan_until(/1/) # -> "Fri Dec 1" 279 | # s.pre_match # -> "Fri Dec " 280 | # s.scan_until(/XYZ/) # -> nil 281 | # 282 | def scan_until(pattern) 283 | _scan(pattern, true, true, false) 284 | end 285 | 286 | # Tests whether the given +pattern+ is matched from the current scan pointer. 287 | # Returns the matched string if +return_string_p+ is true. 288 | # Advances the scan pointer if +advance_pointer_p+ is true. 289 | # The match register is affected. 290 | # 291 | # "full" means "#scan with full parameters". 292 | # 293 | def scan_full(pattern, succptr, getstr) 294 | _scan(pattern, succptr, getstr, true) 295 | end 296 | 297 | # Scans the string _until_ the +pattern+ is matched. 298 | # Returns the matched string if +return_string_p+ is true, otherwise 299 | # returns the number of bytes advanced. 300 | # Advances the scan pointer if +advance_pointer_p+, otherwise not. 301 | # This method does affect the match register. 302 | # 303 | def search_full(pattern, succptr, getstr) 304 | _scan(pattern, succptr, getstr, false) 305 | end 306 | 307 | # Scans one character and returns it. 308 | # This method is multibyte character sensitive. 309 | # 310 | # s = StringScanner.new("ab") 311 | # s.getch # => "a" 312 | # s.getch # => "b" 313 | # s.getch # => nil 314 | # 315 | def getch 316 | scan(/./m) 317 | end 318 | 319 | # Returns +true+ if the scan pointer is at the end of the string. 320 | # 321 | # s = StringScanner.new('test string') 322 | # p s.eos? # => false 323 | # s.scan(/test/) 324 | # p s.eos? # => false 325 | # s.terminate 326 | # p s.eos? # => true 327 | # 328 | def eos? 329 | @pos >= @string.size 330 | end 331 | 332 | # Equivalent to #eos?. 333 | # This method is obsolete, use #eos? instead. 334 | # 335 | def empty? 336 | warn "StringScanner#empty? is obsolete; use #eos? instead" if $VERBOSE 337 | eos? 338 | end 339 | 340 | # Returns true iff there is more data in the string. See #eos?. 341 | # This method is obsolete; use #eos? instead. 342 | # 343 | # s = StringScanner.new('test string') 344 | # s.eos? # These two 345 | # s.rest? # are opposites. 346 | # 347 | def rest? 348 | !eos? 349 | end 350 | 351 | # Returns the "rest" of the string (i.e. everything after the scan pointer). 352 | # If there is no more data (eos? = true), it returns "". 353 | # 354 | def rest 355 | @string[@pos..-1] || "" 356 | end 357 | 358 | # s.rest_size is equivalent to s.rest.size. 359 | # 360 | def rest_size 361 | @string.size - @pos 362 | end 363 | 364 | # s.restsize is equivalent to s.rest_size. 365 | # This method is obsolete; use #rest_size instead. 366 | # 367 | def restsize 368 | warn "StringScanner#restsize is obsolete; use #rest_size instead" if $VERBOSE 369 | rest_size 370 | end 371 | 372 | # Returns a string that represents the StringScanner object, showing: 373 | # - the current position 374 | # - the size of the string 375 | # - the characters surrounding the scan pointer 376 | # 377 | # s = StringScanner.new("Fri Dec 12 1975 14:39") 378 | # s.inspect # -> '#' 379 | # s.scan_until /12/ # -> "Fri Dec 12" 380 | # s.inspect # -> '#' 381 | # 382 | def inspect 383 | if defined?(@string) 384 | rest = @string.size > 5 ? @string[@pos..@pos+4] + "..." : @string 385 | to_return = if eos? then 386 | "#" 387 | elsif pos > 0 then 388 | prev = @string[0...@pos].inspect 389 | "#" 390 | else 391 | "#" 392 | end 393 | to_return.taint if @string.tainted? 394 | to_return 395 | else 396 | "#" 397 | end 398 | end 399 | 400 | # Tests whether the given +pattern+ is matched from the current scan pointer. 401 | # Returns the length of the match, or +nil+. The scan pointer is not advanced. 402 | # 403 | # s = StringScanner.new('test string') 404 | # p s.match?(/\w+/) # -> 4 405 | # p s.match?(/\w+/) # -> 4 406 | # p s.match?(/\s+/) # -> nil 407 | # 408 | def match?(pattern) 409 | _scan(pattern, false, false, true) 410 | end 411 | 412 | # Returns the last matched string. 413 | # 414 | # s = StringScanner.new('test string') 415 | # s.match?(/\w+/) # -> 4 416 | # s.matched # -> "test" 417 | # 418 | def matched 419 | @match.to_s if matched? 420 | end 421 | 422 | # Returns +true+ iff the last match was successful. 423 | # 424 | # s = StringScanner.new('test string') 425 | # s.match?(/\w+/) # => 4 426 | # s.matched? # => true 427 | # s.match?(/\d+/) # => nil 428 | # s.matched? # => false 429 | # 430 | def matched? 431 | !@match.nil? 432 | end 433 | 434 | # Returns the last matched string. 435 | # 436 | # s = StringScanner.new('test string') 437 | # s.match?(/\w+/) # -> 4 438 | # s.matched # -> "test" 439 | # 440 | def matched_size 441 | @match.to_s.size if matched? 442 | end 443 | 444 | # Equivalent to #matched_size. 445 | # This method is obsolete; use #matched_size instead. 446 | # 447 | def matchedsize 448 | warn "StringScanner#matchedsize is obsolete; use #matched_size instead" if $VERBOSE 449 | matched_size 450 | end 451 | 452 | # Attempts to skip over the given +pattern+ beginning with the scan pointer. 453 | # If it matches, the scan pointer is advanced to the end of the match, and the 454 | # length of the match is returned. Otherwise, +nil+ is returned. 455 | # 456 | # It's similar to #scan, but without returning the matched string. 457 | # 458 | # s = StringScanner.new('test string') 459 | # p s.skip(/\w+/) # -> 4 460 | # p s.skip(/\w+/) # -> nil 461 | # p s.skip(/\s+/) # -> 1 462 | # p s.skip(/\w+/) # -> 6 463 | # p s.skip(/./) # -> nil 464 | # 465 | def skip(pattern) 466 | _scan(pattern, true, false, true) 467 | end 468 | 469 | # Advances the scan pointer until +pattern+ is matched and consumed. Returns 470 | # the number of bytes advanced, or +nil+ if no match was found. 471 | # 472 | # Look ahead to match +pattern+, and advance the scan pointer to the _end_ 473 | # of the match. Return the number of characters advanced, or +nil+ if the 474 | # match was unsuccessful. 475 | # 476 | # It's similar to #scan_until, but without returning the intervening string. 477 | # 478 | # s = StringScanner.new("Fri Dec 12 1975 14:39") 479 | # s.skip_until /12/ # -> 10 480 | # s 481 | # 482 | def skip_until(pattern) 483 | _scan(pattern, true, false, false) 484 | end 485 | 486 | # This returns the value that #scan would return, without advancing the scan 487 | # pointer. The match register is affected, though. 488 | # 489 | # s = StringScanner.new("Fri Dec 12 1975 14:39") 490 | # s.check /Fri/ # -> "Fri" 491 | # s.pos # -> 0 492 | # s.matched # -> "Fri" 493 | # s.check /12/ # -> nil 494 | # s.matched # -> nil 495 | # 496 | # Mnemonic: it "checks" to see whether a #scan will return a value. 497 | # 498 | def check(pattern) 499 | _scan(pattern, false, true, true) 500 | end 501 | 502 | 503 | # This returns the value that #scan_until would return, without advancing the 504 | # scan pointer. The match register is affected, though. 505 | # 506 | # s = StringScanner.new("Fri Dec 12 1975 14:39") 507 | # s.check_until /12/ # -> "Fri Dec 12" 508 | # s.pos # -> 0 509 | # s.matched # -> 12 510 | # 511 | # Mnemonic: it "checks" to see whether a #scan_until will return a value. 512 | # 513 | def check_until(pattern) 514 | _scan(pattern, false, true, false) 515 | end 516 | 517 | # Looks _ahead_ to see if the +pattern+ exists _anywhere_ in the string, 518 | # without advancing the scan pointer. This predicates whether a #scan_until 519 | # will return a value. 520 | # 521 | # s = StringScanner.new('test string') 522 | # s.exist? /s/ # -> 3 523 | # s.scan /test/ # -> "test" 524 | # s.exist? /s/ # -> 6 525 | # s.exist? /e/ # -> nil 526 | # 527 | def exist?(pattern) 528 | _scan(pattern, false, false, false) 529 | end 530 | 531 | # Extracts a string corresponding to string[pos,len], without 532 | # advancing the scan pointer. 533 | # 534 | # s = StringScanner.new('test string') 535 | # s.peek(7) # => "test st" 536 | # s.peek(7) # => "test st" 537 | # 538 | def peek(length) 539 | raise TypeError, "can't convert #{length.class.name} into Integer" unless length.respond_to?(:to_int) 540 | raise ArgumentError if length < 0 541 | length.zero? ? "" : @string[@pos, length] 542 | end 543 | 544 | # Equivalent to #peek. 545 | # This method is obsolete; use #peek instead. 546 | # 547 | def peep(length) 548 | warn "StringScanner#peep is obsolete; use #peek instead" if $VERBOSE 549 | peek(length) 550 | end 551 | 552 | # Set the scan pointer to the previous position. Only one previous position is 553 | # remembered, and it changes with each scanning operation. 554 | # 555 | # s = StringScanner.new('test string') 556 | # s.scan(/\w+/) # => "test" 557 | # s.unscan 558 | # s.scan(/../) # => "te" 559 | # s.scan(/\d/) # => nil 560 | # s.unscan # ScanError: unscan failed: previous match record not exist 561 | # 562 | def unscan 563 | raise(ScanError, "unscan failed: previous match record not exist") if @match.nil? 564 | @pos = @prev_pos 565 | @prev_pos = nil 566 | @match = nil 567 | self 568 | end 569 | 570 | # Returns +true+ iff the scan pointer is at the beginning of the line. 571 | # 572 | # s = StringScanner.new("test\ntest\n") 573 | # s.bol? # => true 574 | # s.scan(/te/) 575 | # s.bol? # => false 576 | # s.scan(/st\n/) 577 | # s.bol? # => true 578 | # s.terminate 579 | # s.bol? # => true 580 | # 581 | def bol? 582 | (@pos == 0) || (@string[@pos-1] == "\n") 583 | end 584 | alias :beginning_of_line? :bol? 585 | 586 | # Return the n-th subgroup in the most recent match. 587 | # 588 | # s = StringScanner.new("Fri Dec 12 1975 14:39") 589 | # s.scan(/(\w+) (\w+) (\d+) /) # -> "Fri Dec 12 " 590 | # s[0] # -> "Fri Dec 12 " 591 | # s[1] # -> "Fri" 592 | # s[2] # -> "Dec" 593 | # s[3] # -> "12" 594 | # s.post_match # -> "1975 14:39" 595 | # s.pre_match # -> "" 596 | # 597 | def [](n) 598 | raise TypeError, "Bad argument #{n.inspect}" unless n.respond_to? :to_int 599 | @match.nil? ? nil : @match[n] 600 | end 601 | 602 | # Return the pre-match (in the regular expression sense) of the last scan. 603 | # 604 | # s = StringScanner.new('test string') 605 | # s.scan(/\w+/) # -> "test" 606 | # s.scan(/\s+/) # -> " " 607 | # s.pre_match # -> "test" 608 | # s.post_match # -> "string" 609 | # 610 | def pre_match 611 | if matched? 612 | p = @string.size - @match.string.size + @match.begin(0) 613 | @string[0...p] 614 | end 615 | end 616 | 617 | # Return the post-match (in the regular expression sense) of the last scan. 618 | # 619 | # s = StringScanner.new('test string') 620 | # s.scan(/\w+/) # -> "test" 621 | # s.scan(/\s+/) # -> " " 622 | # s.pre_match # -> "test" 623 | # s.post_match # -> "string" 624 | # 625 | def post_match 626 | @match.post_match if matched? 627 | end 628 | 629 | private 630 | 631 | def _scan(pattern, succptr, getstr, headonly) 632 | raise TypeError, "wrong argument type #{pattern.class.name} (expected Regexp)" unless 633 | Regexp === pattern 634 | 635 | @match = nil 636 | rest = @pos == 0 ? @string : self.rest 637 | 638 | if headonly 639 | headonly_pattern = Regexp.new('\A' + pattern.source, pattern.options) 640 | @match = headonly_pattern.match rest 641 | else 642 | @match = pattern.match rest 643 | end 644 | 645 | return nil unless @match 646 | 647 | m = rest[0, @match.end(0)] 648 | 649 | if succptr 650 | @prev_pos = @pos 651 | @pos += m.size 652 | end 653 | 654 | getstr ? m : m.size 655 | end 656 | 657 | end 658 | --------------------------------------------------------------------------------