[
  {
    "path": ".gitignore",
    "content": ".rake_tasks~\npkg/*\nbuild/\n.DS_Store\n.repl_history\n"
  },
  {
    "path": ".travis.yml",
    "content": "language: objective-c\nosx_image: xcode8\nbefore_install:\n  - sudo motion update\n  - motion repo\nscript:\n  - bundle exec rake clean\n  - bundle exec rake spec\n  - bundle exec rake clean\n  - bundle exec rake spec target=10\n  - bundle exec rake clean\n  - bundle exec rake spec osx=true\n"
  },
  {
    "path": ".yardopts",
    "content": "lib/**/*.rb\nmotion/**/*.rb"
  },
  {
    "path": "CHANGELOG.md",
    "content": "## Unreleased\n\n[Commit history](https://github.com/rubymotion-community/BubbleWrap/compare/v1.9.7...master)\n\n## 1.9.7\n\n[Commit history](https://github.com/rubymotion-community/BubbleWrap/compare/v1.9.6...v1.9.7)\n\n* [iOS] Fix iOS 11 photo library crash ([#493](https://github.com/rubymotion-community/BubbleWrap/pull/493))\n* [iOS] Updated Device.simulator? to work in iOS 13+ ([#499](https://github.com/rubymotion-community/BubbleWrap/pull/499))\n* [iOS] Add MobileCoreServices to list of required frameworks. Fixes issue with missing KUTTypeMovie constant.\n\n## 1.9.6\n\n[Commit history](https://github.com/rubymotion-community/BubbleWrap/compare/v1.9.5...v1.9.6)\n\n* [iOS] Add Device.force_touch? ([#478](https://github.com/rubymotion-community/BubbleWrap/pull/478))\n* [iOS] Fixes for iOS 10 ([#489](https://github.com/rubymotion-community/BubbleWrap/pull/489))\n\n## 1.9.5\n\n[Commit history](https://github.com/rubymotion-community/BubbleWrap/compare/v1.9.4...v1.9.5)\n\n* Fixed 'simulator?' so it returns the correct value, when running ios 8 or below on device. ([#481](https://github.com/rubymotion-community/BubbleWrap/pull/481))\n\n## ~~1.9.3~~ 1.9.4\n\n[Commit history](https://github.com/rubymotion-community/BubbleWrap/compare/v1.9.2...v1.9.4)\n\n* Fixed `Device.simulator?` for iOS 9. ([#473](https://github.com/rubymotion-community/BubbleWrap/pull/473))\n\n## 1.9.2\n\n[Commit history](https://github.com/rubymotion-community/BubbleWrap/compare/v1.9.1...v1.9.2)\n\n* Added `DependentDeferrable`. ([#469](https://github.com/rubymotion-community/BubbleWrap/pull/469))\n\n## 1.9.1\n\n[Commit history](https://github.com/rubymotion-community/BubbleWrap/compare/v1.9.0...v1.9.1)\n\n* Fix issue in loading iOS 8 constants for CoreLocation\n\n## 1.9.0\n\n[Commit history](https://github.com/rubymotion-community/BubbleWrap/compare/v1.8.0...v1.9.0)\n\n* Add support for `keyboardType` on first input field of `BW::UIAlertView` ([#406](https://github.com/rubymotion-community/BubbleWrap/pull/406))\n* Implement #+ and #- for NSIndexPath to incr/decr row ([#420](https://github.com/rubymotion-community/BubbleWrap/pull/420))\n* Extract CoreMotion classes to make it easier to use and more maintainable ([#454](https://github.com/rubymotion-community/BubbleWrap/pull/454))\n* Motion extract classes ([#454](https://github.com/rubymotion-community/BubbleWrap/pull/454))\n* Added RSS fields: creator, category, encoded  ([#461](https://github.com/rubymotion-community/BubbleWrap/pull/461))\n* KVO `observe!` and ability to pass multiple key paths to `observe` ([#460](https://github.com/rubymotion-community/BubbleWrap/pull/460))\n* `App.short_version` ([#466](https://github.com/rubymotion-community/BubbleWrap/pull/466))\n* Bump the required version of iOS to >= 7 ([#424](https://github.com/rubymotion-community/BubbleWrap/pull/424))\n* Bump the required version of iOS to >= 7 ([#424](https://github.com/rubymotion-community/BubbleWrap/pull/424))\n* Fixes to CoreLocation ([#422](https://github.com/rubymotion-community/BubbleWrap/pull/422)) & ([#432](https://github.com/rubymotion-community/BubbleWrap/pull/432))\n* Adds default text option to BW::UIAlertView ([#467](https://github.com/rubymotion-community/BubbleWrap/pull/467))\n* Bump the minimum required RubyMotion version to `3.12`.\n\n## ... nothing to see here... move along.\n\n## 1.4.0\n\n[Commit history](https://github.com/rubymotion-community/BubbleWrap/compare/v1.3.0...v1.4.0)\n\n* Added `BW::Mail` for sending emails ([#247](https://github.com/rubymotion-community/BubbleWrap/pull/247))\n* Added `BW::SMS` for sending SMS/iMessages ([#287](https://github.com/rubymotion-community/BubbleWrap/pull/287))\n* Added `App::Persistence.delete` ([#286](https://github.com/rubymotion-community/BubbleWrap/pull/286))\n* Added `BW::HTTP::Response#error`, which is an `NSError` instance ([#284](https://github.com/rubymotion-community/BubbleWrap/pull/284))\n* Added `BW::HTTP::Query#cancel` to chancel URL requests ([#278](https://github.com/rubymotion-community/BubbleWrap/pull/278))\n* Added `App.info_plist` ([#273](https://github.com/rubymotion-community/BubbleWrap/pull/273/))\n* Added `BW::Device.interface_orientation` ([#265](https://github.com/rubymotion-community/BubbleWrap/pull/265))\n* Added `OPTIONS` request method to `BW::HTTP` ([#260](https://github.com/rubymotion-community/BubbleWrap/pull/260))\n* Added `Time.iso8601_with_timezone` ([#255](https://github.com/rubymotion-community/BubbleWrap/pull/255))\n* Added `:encoding` option to `BW::HTTP` ([#251](https://github.com/rubymotion-community/BubbleWrap/pull/251))\n* Moved `BW::Reactor::Timer` and `BW::Reactor::PeriodicTimer` from `NSTimer` to GCD `Dispatch::Source.timer` ([#242](https://github.com/rubymotion-community/BubbleWrap/pull/242))\n\t* Option `:common_modes` for BW::Reactor::PeriodicTimer has been deprecated, it's not needed anymore.\n* Fixed App#window (and thus `BW::Camera`) to work with iOS7 modals ([#305](https://github.com/rubymotion-community/BubbleWrap/pull/305))\n* Fixed patches on `String` to be on `NSString` ([#292](https://github.com/rubymotion-community/BubbleWrap/pull/292))\n* Fixed `BW::HTTP` success heuristic to match RFC 2616 ([#282](https://github.com/rubymotion-community/BubbleWrap/pull/282))\n* Fixed `BW::HTTP` to correctly identify `false` parameters ([#261](https://github.com/rubymotion-community/BubbleWrap/issues/261) [#262](https://github.com/rubymotion-community/BubbleWrap/pull/262))\n* Fixed `BW::Reactor` to correctly handle unregistered procs ([#253](https://github.com/rubymotion-community/BubbleWrap/pull/253))\n* Fixed `BW::localized_string` to mirror Cocoa API by returning the `key` if no localization exists ([#181](https://github.com/rubymotion-community/BubbleWrap/pull/181))\n* Addressed a few memory related problems ([#270](https://github.com/rubymotion-community/BubbleWrap/pull/270) [#275](https://github.com/rubymotion-community/BubbleWrap/pull/275) [#276](https://github.com/rubymotion-community/BubbleWrap/pull/276))\n\n## 1.3.0\n\n[Commit history](https://github.com/rubymotion-community/BubbleWrap/compare/v1.2.0...v1.3.0)\n\n* Added OS X support for RubyMotion 2.0 ([#233](https://github.com/rubymotion-community/BubbleWrap/pull/233)). BubbleWrap *drops support* for RubyMotion 1.x.\n* Changed `BW::UIBarButtonItem` internals; `.build` is now deprecated and forwards to `.new` ([#226](https://github.com/rubymotion-community/BubbleWrap/pull/226))\n* Changed `HTTP` to present credentials with an `Authorization` header *before* any requests are made, unless the `:present_credentials` option `== false` ([#199](https://github.com/rubymotion-community/BubbleWrap/pull/199))\n* Fixed `HTTP` to not append a question-mark (`?`) at the end of URL requests with empty `:payload`s ([#221](https://github.com/rubymotion-community/BubbleWrap/pull/221))\n* Fixed `HTTP` to correctly parameterize an array of hashes, Rails-style ([#219](https://github.com/rubymotion-community/BubbleWrap/pull/219))\n\n## 1.2.0\n\n[Commit history](https://github.com/rubymotion-community/BubbleWrap/compare/v1.1.5...v1.2.0)\n\n* Added `BW::UIBarButtonItem`, a factory-esque wrapper for `UIBarButtonItem` ([#202](https://github.com/rubymotion-community/BubbleWrap/pull/202))\n* Added `BW::Font`, a wrapper for creating `UIFont` objects ([#206](https://github.com/rubymotion-community/BubbleWrap/pull/206))\n* Added `BW::Reactor::Eventable#off`, to remove `BW::Reactor` callbacks ([#205](https://github.com/rubymotion-community/BubbleWrap/pull/205))\n* Added `BW::Constants.get`, which is a class to help wrapper creation ([#203](https://github.com/rubymotion-community/BubbleWrap/pull/203))\n* Added `BW::Location.get_once` to grab only one location ([#197](https://github.com/rubymotion-community/BubbleWrap/pull/197))\n* Added `App#environment`, to detect the current RubyMotion environment ([#191](https://github.com/rubymotion-community/BubbleWrap/pull/191))\n* Added `:follow_urls` option to `BW::HTTP` ([#192](https://github.com/rubymotion-community/BubbleWrap/pull/192))\n* Added `:common_modes` option to `BW::Reactor`to change the runloop mode (#190)\n* Added `:no_redirect` option to `BW::HTTP` ([#187](https://github.com/rubymotion-community/BubbleWrap/pull/187))\n* Added `:cookies` option to `BW::HTTP` ([#204](https://github.com/rubymotion-community/BubbleWrap/pull/204))\n\n## 1.1.5\n\n[Commit history](https://github.com/rubymotion-community/BubbleWrap/compare/v1.1.4...v1.1.5)\n\n* Fix `BW::Camera` view-controller selection process to pickup a window's `presentedViewController` ([#183](https://github.com/rubymotion-community/BubbleWrap/pull/183))\n* Fix strings parsed in `BW::JSON` to be mutable ([#175](https://github.com/rubymotion-community/BubbleWrap/pull/175))\n* Add option for `:credential_persistence`/`NSURLCredentialPersistence` in `BW::HTTP` ([#166](https://github.com/rubymotion-community/BubbleWrap/pull/166))\n* Change `Device.wide_screen?` to `Device.long_screen?` ([#159](https://github.com/rubymotion-community/BubbleWrap/pull/159))\n* String escaping fixes to `BW::HTTP` ([#160](https://github.com/rubymotion-community/BubbleWrap/pull/160) [#161](https://github.com/rubymotion-community/BubbleWrap/pull/161) [#162](https://github.com/rubymotion-community/BubbleWrap/pull/162))\n* Add `Device.sdk_version`\n\n## 1.1.4\n\n[Commit history](https://github.com/rubymotion-community/BubbleWrap/compare/v1.1.3...v1.1.4)\n\n* Support RubyMotion 1.24 or above (https://github.com/rubymotion-community/BubbleWrap/pull/143)\n* Fixed a problem with `when` events not properly handling multiple targets per event. Now defaults to one target per event with an option to append multiple targets.\n\n## 1.1.3\n\n[Commit history](https://github.com/rubymotion-community/BubbleWrap/compare/v1.1.2...v1.1.3)\n\n\n## 1.1.2\n\n[Commit history](https://github.com/rubymotion-community/BubbleWrap/compare/v1.1.1...v1.1.2)\n\n* Fixed a problem with the load path.\n* Added `format:` to the HTTP wrapper with [5 supported formats supported](https://github.com/rubymotion-community/BubbleWrap/pull/109) that sets the content type accordingly.\n* Default HTTP Content-Type for `POST` like requests is back to being\n  form url encoded.\n\n## 1.1.1\n\n[Commit history](https://github.com/rubymotion-community/BubbleWrap/compare/v1.1.0...v1.1.1)\n\n### Enhancements\n\n* Added support for symbols as selectors to the KVO module.\n* Improved the RSSParser by providing a delegate to handle errors.\n\n### Bug Fixes\n\n* Fixed a bug with the way JSON payloads were handled in the HTTP\n  wrapper.\n* Fixed a bug with the way the headers and content types were handled in\n  the HTTP wrapper.\n\n## 1.1.0\n\n[Commit history](https://github.com/rubymotion-community/BubbleWrap/compare/v1.0.0...v1.1.0)\n\n* Added `BubbleWrap::Reactor`,  a simplified implementation of the Event Machine API on top of GCD.\n* Added upload support to the HTTP wrapper.\n* Added `BubbleWrap.create_uuid` to generate a uuid string.\n* Added a program progress proc option to the HTTP wrapper.\n* Added a RSS parser.\n* Added a camera wrapper.\n* Split the various wrappers in self contained and requirable libraries.\n* Added a wrapper around the location/gps APIs.\n* Added a merge method to the persistence layer so multiple values can\n  be saved at once.\n* Added a way to create `UIColor` instances using a hex string: `'#FF8A19'.to_color` or color keyword: `'blue'.to_color`, `'dark_gray'.to_color`.\n\n## 1.0.0\n\n[Commit history](https://github.com/rubymotion-community/BubbleWrap/compare/v0.4.0...v1.0.0)\n\n* Improved the integration with RubyMotion build system.\n* Improved test suite.\n* Added better documentation, including on how to work on the internals.\n* Added a KVO DSL to observe objects.\n* Renamed `Device.screen.widthForOrientation` to Device.screen.width_for_orientation` and `Device.screen.heightForOrientation` to `Device.screen.height_for_orientation`.\n* The `HTTP` wrapper now encodes arrays in params in a way that's compatible with Rails.\n\n## 0.4.0\n\n[Commit history](https://github.com/rubymotion-community/BubbleWrap/compare/v0.3.1...v0.4.0)\n\n* Refactored the code and test suite to be more modular and to handle\n  dependencies. One can now require only a subset of BW such as `require 'bubble-wrap/core'` or 'bubble-wrap/http'\n\n## 0.3.1\n\n[Commit history](https://github.com/rubymotion-community/BubbleWrap/compare/v0.3.0...v0.3.1)\n\n* Added App.run_after(delay){ }\n* HTTP responses return true to ok? when the status code is 20x.\n\n## 0.3.0\n\n[Commit history](https://github.com/rubymotion-community/BubbleWrap/compare/v0.2.1...v0.3.0)\n\n* Major refactoring preparing for 1.0\n\n## 0.2.1\n\n[Commit history](https://github.com/rubymotion-community/BubbleWrap/compare/v0.2.0...v0.2.1)\n\n* Minor fix in the way the dependencies are set (had to monkey patch\n  RubyMotion)\n\n## 0.2.0\n\n[Commit history](https://github.com/rubymotion-community/BubbleWrap/compare/v0.1.2...v0.2.0)\n\n* Added network activity notification to the HTTP wrapper.\n* fixed a bug in the `NSNotificationCenter` wrapper.\n\n## 0.1.2\n\nStart packaging this lib as a gem.\n"
  },
  {
    "path": "GEM.md",
    "content": "# Creating a RubyMotion gem with BubbleWrap\n\nLet's say we want to develop a simple library gem that lists the\npeople in a user's addressbook.\n\nLet's start by initializing an empty gem directory:\n\n```\n$ gem install bundler\n$ bundle gem bw-addressbook\n```\n\nAdd BubbleWrap and Rake to your gem's dependencies in `bw-addressbook.gemspec`:\n\n```ruby\nGem::Specification.new do |gem|\n  gem.add_dependency 'bubble-wrap'\n  gem.add_development_dependency 'rake'\nend\n```\n\nThen run `bundler`:\n```\n$ bundle\nFetching gem metadata from https://rubygems.org/..\nUsing rake (0.9.2.2) \nInstalling bubble-wrap (0.4.0) \nUsing bw-addressbook (0.0.1) from source at /Users/jnh/Dev/tmp/bw-addressbook \nUsing bundler (1.1.4) \nYour bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.\n```\n\nModify your `lib/bw-addressbook.rb` to include:\n\n```ruby\nrequire 'bw-addressbook/version'\nBW.require 'motion/address_book.rb'\n```\n\nEdit your project's `Rakefile` to include:\n\n```ruby\n#!/usr/bin/env rake\n$:.unshift(\"/Library/RubyMotion/lib\")\nrequire 'motion/project'\nrequire \"bundler/gem_tasks\"\nBundler.setup\nBundler.require\nrequire 'bubble-wrap/test'\n```\n\nAt this point we should have a working RubyMotion environment able to\ncompile our code as we write it.\n\nLet's start by creating a spec for our address book gem in `spec/address_book_spec.rb`:\n\n```ruby\ndescribe AddressBook do\n  describe '.list' do\n    it 'returns an Enumerable' do\n      AddressBook.list.is_a?(Enumerable).should == true\n    end\n  end\nend\n```\n\nNow if you run `rake spec` you can watch the spec fail:\n\n```\n2012-06-07 11:19:35.506 Untitled[14987:f803] *** Terminating app due to uncaught exception 'NameError', reason: 'uninitialized constant AddressBook (NameError)'\n*** First throw call stack:\n(0x8f6022 0x286cd6 0x140054 0x291f 0x2645 0x1)\nterminate called throwing an exception\n```\n\nLet's go and define ourselves an `AddressBook` class in `motion/address_book.rb`:\n\n```ruby\nclass AddressBook\nend\n```\n\nYou'll now get a spec failure:\n\n```\nNoMethodError: undefined method `list' for AddressBook:Class\n\tspec.rb:156:in `block in run_spec_block': .list - returns an Enumerable\n\t 4:in `execute_block'\n\tspec.rb:156:in `run_spec_block'\n\tspec.rb:171:in `run'\n```\n\nWell, we'd better go and define it then, eh?\n\n```\nclass AddressBook\n  def self.list\n    []\n  end\nend\n```\n\nI'm going to leave it here for now, but you're welcome to take a look at the \nfully working demonstration project on [Github](http://github.com/jamesotron/bw-addressbook-demo).\n"
  },
  {
    "path": "GETTING_STARTED.md",
    "content": "# Getting Started with BubbleWrap\n\nA short guide to starting a RubyMotion application using the bubble wrappers.\n\n## A quick note about RubyMotion\n\n[RubyMotion](http://www.rubymotion.com/) is a commercial product available from\n[HipByte SPRL](http://www.hipbyte.com/). RubyMotion needs a recent (10.6 or newer)\nversion of Mac OS X and Apple's Xcode tools installed. If you don't have a working\nRubyMotion install take a look at the [getting started guide](http://www.rubymotion.com/developer-center/guides/getting-started/).\n\n## Create a new RubyMotion project\n\nRubyMotion ships with the `motion` command-line tool to handle creating projects,\nupdating and creating support tickets.\n\n```\n$ motion create bw-demo\n    Create bw-demo\n    Create bw-demo/.gitignore\n    Create bw-demo/Rakefile\n    Create bw-demo/app\n    Create bw-demo/app/app_delegate.rb\n    Create bw-demo/resources\n    Create bw-demo/spec\n    Create bw-demo/spec/main_spec.rb\n```\n\nThis gives us an empty project (and one failing spec).  That's fine for now.\n\n## Set up Bundler\n\n[Bundler](http://www.gembundler.com/) is a project to manage your project's\nRubyGem dependencies. You can get by without it if you want to, but it will\nsave time and hassle as the number of BubbleWrap gems grows over time.\n\n```\n$ gem install bundler\n```\n\nCreate a new file called `Gemfile` in your project directory and add the\nfollowing:\n\n```ruby\nsource :rubygems\ngem 'bubble-wrap', '~> 1.0.0'\ngem 'rake'\n```\n\nThen run `bundle` from the command-line in the same directory and Bundler\nshould install BubbleWrap and Rake for you.\n\n\n## Adding BubbleWrap\n\nNow that we have BubbleWrap installed we need to configure the project to use\nit - the easiest way is to add Bundler to our build-environment and tell it \nto take care of everything for us.\n\nEdit your project's `Rakefile` and add the following just under `require 'motion/project'`:\n\n```ruby\nrequire 'bundler'\nBundler.setup\nBundler.require\n```\n\nNow when you build your project by running `rake` you will see the BubbleWrap files\nbeing compiled into your project.\n\n## Customising BubbleWrap requires\n\nBy default BubbleWrap will include all the bubble wrappers into your project, but often\nyou won't need them all - perhaps you only want to use BubbleWrap's `http` wrappers?\nYou can change your bubble-wrap line in your `Gemfile` as follows:\n\n```ruby\ngem 'bubble-wrap', '~> 1.0.0', :require => 'bubble-wrap/http'\n```\n\nIf you just want core, the change is similarly easy:\n\n```ruby\ngem 'bubble-wrap', '~> 1.0.0', :require => 'bubble-wrap/core'\n```\n\nAlso, if you don't want any of BubbleWrap loaded by default, and you just want to use\nit's ability to add projects to your build system you can change it to:\n\n```ruby\ngem 'bubble-wrap', '~> 1.0.0', :require => 'bubble-wrap/loader'\n```\n\nAnd modify your `Rakefile` to include one or more `BW.require` lines:\n\n```ruby\nBW.require '/path/to/some/files/**/*.rb'\n```\n\nFor more information in using `BW.require` take a look at\n[the bubblewrap hacking guide](HACKING.md).\n\n## Go forth and conquer!\n\nThe developers wish to thank you for using BubbleWrap.\nPlease feel free to open issues or pull requests on \n[GitHub](https://www.github.com/mattetti/BubbleWrap), join our\n[mailing list](https://groups.google.com/forum/#!forum/bubblewrap)\nor join us on `#bubblewrap` on `irc.freenode.net`.\n"
  },
  {
    "path": "Gemfile",
    "content": "source 'https://rubygems.org'\n\n# Specify your gem's dependencies in bubble-wrap.gemspec\ngemspec\n"
  },
  {
    "path": "HACKING.md",
    "content": "# Hacking on BubbleWrap\n\n## A library in two parts\n\nRubyMotion forces a certain background-radiation of schizophrenia\ndue to the fact that it's build tools run using the system ruby\nvia Rake.  BubbleWrap manipulates the build environment in order\nto make it possible to include itself (and other code) into the\nbuild process from outside the project hierarchy.\n\n### Part the first: `lib/`\n\nThis is where [RubyGems](http://rubygems.org) goes looking for\ncode when you call\n\n```ruby\nrequire 'bubble-wrap'\n```\n\nWhen `bubble-wrap` is required it immediately requires `bubble-wrap/loader`\nwhich sets up the infrastructure needed to manipulate the `Rakefile` build process.\nOnce that is done we can freely call\n\n```ruby\nBubbleWrap.require 'motion/core/**/*.rb'\n```\n\n`BubbleWrap.require` (or simply `BW.require`) is used to include\nlibrary code into the Rake build process used by RubyMotion. \n`BW.require` is similar to ruby's standard `require` method with\ntwo major changes:\n\n  - it can take a file pattern as used by [`Dir.glob`](http://ruby-doc.org/core-1.9.3/Dir.html#method-c-glob).\n  - it can be passed a block to manipulate dependencies.\n\nIf a block is passed to `BW.require` it is evaluated in the context\nof `BW::Requirement` and thus has access to all it's class methods.\nThe most common use cases are setting file dependencies:\n\n```ruby\nBW.require('motion/core/**/*.rb') do\n  file('motion/core/device/screen.rb').depends_on 'motion/core/device.rb'\nend\n```\n\nand specifying frameworks that need to be included at build time:\n\n```ruby\nBW.require('motion/**/*.rb') do\n  file('motion/address_book.rb').uses_framework 'Addressbook'\nend\n```\n\n### Part the second: `motion/`\n\nInside the `motion` directory you'll see the actual implementation code\nwhich is compiled into RubyMotion projects that are using BubbleWrap.\n\n  - `motion/core` contains \"core\" extension, things that the developers\n    reasonably think should be included in every BubbleWrap using project.\n    Careful consideration should be taken when making changes to the \n    contents and test coverage (in `spec/core`) must be updated to match.\n    This can be included in your project by requiring `bubble-wrap` or \n    `bubble-wrap/core` in your project `Rakefile`.\n  - `motion/http` contains the \"http\" extension.  This can be included\n    by requiring `bubble-wrap/http` in your project `Rakefile`.\n  - `motion/test_suite_delegate` contains a simple `AppDelegate` which\n    can be used to enable the `rake spec` to run when developing a\n    BubbleWrap gem.  Using `require 'bubble-wrap/test'` will include\n    it in the build process and also configure the app delegate to point\n    to `TestSuiteDelegate`. See the [BubbleWrap gem guide](gem.html) for\n    more information.\n\n#### Your project here\n\nIf you think that your project would be of interest to the large number \nof RubyMotion users that use BubbleWrap in their daily development then\nfeel free to fork [the repository on GitHub](https://github.com/mattetti/BubbleWrap)\nand send us a pull request.\n\nYou should place your implementation files in a subdirectory of `motion`\n(eg `motion/my_awesome_project`), your tests in a subdirectory of `spec`\n(eg `spec/my_awesome_project`) and you can create a require file in\n`lib/bubble-wrap` for example `lib/bubble-wrap/my_awesome_project.rb`:\n\n```ruby\nrequire 'bubble-wrap/loader'\nBW.require 'motion/my_awesome_project.rb'\n```\n\nPeople will then be able to use it by adding:\n\n```ruby\nrequire 'bubble-wrap/my_awesome_project'\n```\n\nto their project's `Rakefile`\n\n## Go forth and conquer!\n\nThe developers wish to thank you so much for taking the time\nto improve BubbleWrap and by extension the RubyMotion\necosystem. You're awesome!\n"
  },
  {
    "path": "LICENSE",
    "content": "LICENCE\n\nMIT: http://mattaimonetti.mit-license.org\n\n------------------------------------------------\n\nThe MIT License (MIT)\nCopyright © 2012 Matt Aimonetti <matt.aimonetti@gmail.com>\n\nPermission is hereby granted, free of charge, to any person obtaining a copy\nof this software and associated documentation files (the “Software”), to deal\nin the Software without restriction, including without limitation the rights\nto use, copy, modify, merge, publish, distribute, sublicense, and/or sell\ncopies of the Software, and to permit persons to whom the Software is\nfurnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in\nall copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR\nIMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,\nFITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE\nAUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER\nLIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,\nOUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN\nTHE SOFTWARE.\n"
  },
  {
    "path": "README.md",
    "content": "# BubbleWrap for RubyMotion\n\nA collection of (tested) helpers and wrappers used to wrap Cocoa Touch and AppKit code and provide more Ruby like APIs.\n\n[![Code Climate](https://codeclimate.com/github/rubymotion/BubbleWrap.svg)](https://codeclimate.com/github/rubymotion/BubbleWrap)\n[![Build Status](https://travis-ci.org/rubymotion/BubbleWrap.svg?branch=master)](https://travis-ci.org/rubymotion/BubbleWrap)\n[![Gem Version](https://badge.fury.io/rb/bubble-wrap.png)](http://badge.fury.io/rb/bubble-wrap)\n\n## Installation\n\n```ruby\ngem install bubble-wrap\n```\n\n## Setup\n\n1. Edit the `Rakefile` of your RubyMotion project and add the following require line:\n\n```ruby\nrequire 'bubble-wrap'\n```\n\nIf you use Bundler:\n\n```ruby\ngem 'bubble-wrap', '~> 1.9.7'\n```\n\nBubbleWrap is split into multiple modules so that you can easily choose which parts are included at compile-time.\n\nIf you wish to only include the `RSS Parser` wrapper:\n\n```ruby\nrequire 'bubble-wrap/rss_parser'\n```\n\nIf you wish to only include the `Reactor` wrapper:\n\n```ruby\nrequire 'bubble-wrap/reactor'\n```\n\nIf you wish to only include the UI-related wrappers:\n\n```ruby\nrequire 'bubble-wrap/ui'\n```\n\nIf you wish to only include the `Camera` wrapper:\n\n```ruby\nrequire 'bubble-wrap/camera'\n```\n\nIf you wish to only include the `Location` wrapper:\n\n```ruby\nrequire 'bubble-wrap/location'\n```\n\nIf you wish to only include the `Media` wrapper:\n\n```ruby\nrequire 'bubble-wrap/media'\n```\n\nIf you wish to only include the `Mail` wrapper:\n\n```ruby\nrequire 'bubble-wrap/mail'\n```\n\nIf you wish to only include the `SMS` wrapper:\n\n```ruby\nrequire 'bubble-wrap/sms'\n```\n\nIf you wish to only include the `Motion` (CoreMotion) wrapper:\n\n```ruby\nrequire 'bubble-wrap/motion'\n```\n\nIf you wish to only include the `NetworkIndicator` wrapper:\n\n```ruby\nrequire 'bubble-wrap/network-indicator'\n```\n\nIf you want to include everything (ie kitchen sink mode) you can save time and do:\n\n```ruby\nrequire 'bubble-wrap/all'\n```\n\nYou can also do this directly in your `Gemfile` like so:\n\n```ruby\ngem 'bubble-wrap', require: %w[bubble-wrap/core bubble-wrap/location, bubble-wrap/reactor]\n```\n\nNote: **DON'T** use `app.files =` in your Rakefile to set up your files once you've required BubbleWrap.\nMake sure to append onto the array or use `+=`.\n\n2. Now, you can use BubbleWrap extension in your app:\n\n```ruby\nclass AppDelegate\n  def application(application, didFinishLaunchingWithOptions:launchOptions)\n    puts \"#{App.name} (#{App.documents_path})\"\n    true\n  end\nend\n```\n\nNote: You can also vendor this repository but the recommended way is to\nuse the versioned gem.\n\n\n## Core\n\n### Misc\n\nUUID generator:\n```ruby\nBubbleWrap.create_uuid\n=> \"68ED21DB-82E5-4A56-ABEB-73650C0DB701\"\n```\n\nLocalization (using `NSBundle.mainBundle.localizedStringForKey`):\n```ruby\nBubbleWrap.localized_string(:foo, 'fallback')\n=> \"fallback\"\n```\n\nColor conversion:\n```ruby\nBubbleWrap.rgba_color(23, 45, 12, 0.4)\n=> #<UIDeviceRGBColor:0x6db6ed0>\nBubbleWrap.rgb_color(23, 45, 12)\n=> #<UIDeviceRGBColor:0x8ca88b0>\n'blue'.to_color\n=> #<UICachedDeviceRGBColor:0xda535c0>\n'dark_gray'.to_color\n=> #<UICachedDeviceWhiteColor:0x8bb5be0>\n'#FF8A19'.to_color\n=> #<UIDeviceRGBColor:0x8d54110>\n'#88FF8A19'.to_color # ARGB format\n=> #<UIDeviceRGBColor:0xca0fe00>\n```\n\nDebug flag:\n```ruby\nBubbleWrap.debug?\n=> false\nBubbleWrap.debug = true\n=> true\nBubbleWrap.debug?\n=> true\n```\n\n### App\n\nA module with useful methods related to the running application\n\n```ruby\n> App.documents_path\n# \"/Users/mattetti/Library/Application Support/iPhone Simulator/5.0/Applications/EEC6454E-1816-451E-BB9A-EE18222E1A8F/Documents\"\n> App.resources_path\n# \"/Users/mattetti/Library/Application Support/iPhone Simulator/5.0/Applications/EEC6454E-1816-451E-BB9A-EE18222E1A8F/testSuite_spec.app\"\n> App.name\n# \"testSuite\"\n> App.identifier\n# \"io.bubblewrap.testSuite\"\n> App.alert(\"BubbleWrap is awesome!\")\n# creates and shows an alert message.\n> App.alert(\"BubbleWrap is awesome!\", {cancel_button_title: \"I know it is!\", message: \"Like, seriously awesome.\"})\n# creates and shows an alert message with optional parameters.\n> App.run_after(0.5) {  p \"It's #{Time.now}\"   }\n# Runs the block after 0.5 seconds.\n> App.open_url(\"http://matt.aimonetti.net\")\n> App.open_url(\"tel://123456789\")\n# Opens the url using the device's browser. Can also open custom URL schemas (accepts a string url or an instance of `NSURL`.)\n> App.can_open_url(\"tel://\")\n# Returns whether the app can open a given URL resource.\n> App::Persistence['channels'] # application specific persistence storage\n# ['NBC', 'ABC', 'Fox', 'CBS', 'PBS']\n> App::Persistence['channels'] = ['TF1', 'France 2', 'France 3']\n# ['TF1', 'France 2', 'France 3']\n> App.environment\n# 'test'\n```\n\nOther available methods:\n\n* `App.notification_center`\n* `App.user_cache`\n* `App.states`\n* `App.frame`\n* `App.delegate`\n* `App.shared`\n* `App.window`\n* `App.current_locale`\n* `App.release?`\n* `App.test?`\n* `App.development?`\n\n\n### Device\n\nA collection of useful methods about the current device:\n\nExamples:\n\n```ruby\n> Device.iphone?\n# true\n> Device.ipad?\n# false\n> Device.camera.front?\n# true\n> Device.camera.rear?\n# true\n> Device.orientation\n# :portrait\n> Device.interface_orientation\n# :portrait\n> Device.simulator?\n# true\n> Device.ios_version\n# \"6.0\"\n> Device.retina?\n# false\n> Device.screen.width\n# 320\n> Device.screen.height\n# 480\n> Device.screen.width_for_orientation(:landscape_left)\n# 480\n> Device.screen.height_for_orientation(:landscape_left)\n# 320\n> Device.vendor_identifier\n# <NSUUID>\n```\n\n### Camera\n\nAdded interface for better camera access:\n\n```ruby\n# Uses the front camera\nBW::Device.camera.front.picture(media_types: [:movie, :image]) do |result|\n  image_view = UIImageView.alloc.initWithImage(result[:original_image])\nend\n\n# Uses the rear camera\nBW::Device.camera.rear.picture(media_types: [:movie, :image]) do |result|\n  image_view = UIImageView.alloc.initWithImage(result[:original_image])\nend\n\n# Uses the photo library\nBW::Device.camera.any.picture(media_types: [:movie, :image]) do |result|\n  image_view = UIImageView.alloc.initWithImage(result[:original_image])\nend\n\n# Lets the user edit the photo (with access to the edited and original photos)\nBW::Device.camera.any.picture(allows_editing: true, media_types: [:image]) do |result|\n  edited_image_view = UIImageView.alloc.initWithImage(result[:edited_image])\n  original_image_view = UIImageView.alloc.initWithImage(result[:original_image])\nend\n\n# Capture a low quality movie with a limit of 10 seconds\nBW::Device.camera.front.picture(media_types: [:movie], video_quality: :low, video_maximum_duration: 10) do |result|\n  video_file_path = result[:media_url]\nend\n```\n\nOptions include:\n\n- `:allows_editing` - Boolean; whether a user can edit the photo/video before picking\n- `:animated` - Boolean; whether to display the camera with an animation (default true)\n- `:on_dismiss` - Lambda; called instead of the default dismissal logic\n- `:media_types` - Array; containing any of `[:movie, :image]`\n- `:video_quality` - Symbol; one of `:high`, `:medium`, `low`, `\"640x480\".to_sym`, `iframe1280x720`, or `iframe960x540`. Defaults to `:medium`\n- `:video_maximum_duration` - Integer; limits movie recording length. Defaults to 600.\n\n### JSON\n\n`BW::JSON` wraps `NSJSONSerialization` available in iOS5 and offers the same API as Ruby's JSON std lib. For apps building for iOS4, we suggest a different JSON alternative, like [AnyJSON](https://github.com/mattt/AnyJSON).\n\n```ruby\nBW::JSON.generate({'foo' => 1, 'bar' => [1,2,3], 'baz' => 'awesome'})\n=> \"{\\\"foo\\\":1,\\\"bar\\\":[1,2,3],\\\"baz\\\":\\\"awesome\\\"}\"\nBW::JSON.parse \"{\\\"foo\\\":1,\\\"bar\\\":[1,2,3],\\\"baz\\\":\\\"awesome\\\"}\"\n=> {\"foo\"=>1, \"bar\"=>[1, 2, 3], \"baz\"=>\"awesome\"}\n```\n\n### NSIndexPath\n\nHelper methods added to give `NSIndexPath` a bit more of a Ruby\ninterface.\n\n```ruby\nindex_path = table_view.indexPathForCell(cell)\nindex_path + 1 # NSIndexPath for next cell in the same section\n=> #<NSIndexPath:0x120db8e0>\n```\n\n### NSNotificationCenter\n\nHelper methods to give NSNotificationCenter a Ruby-like interface:\n\n```ruby\ndef viewWillAppear(animated)\n  @foreground_observer = App.notification_center.observe UIApplicationWillEnterForegroundNotification do |notification|\n    loadAndRefresh\n  end\n\n  @reload_observer = App.notification_center.observe 'ReloadNotification' do |notification|\n    loadAndRefresh\n  end\nend\n\ndef viewWillDisappear(animated)\n  App.notification_center.unobserve @foreground_observer\n  App.notification_center.unobserve @reload_observer\nend\n\ndef reload\n  App.notification_center.post 'ReloadNotification'\nend\n```\n\n\n### NSUserDefaults\n\nHelper methods added to the class repsonsible for user preferences used\nby the `App::Persistence` module shown below.\n\n### Persistence\n\nOffers a way to persist application specific information using a very\nsimple interface:\n\n``` ruby\n> App::Persistence['channels'] # application specific persistence storage\n# ['NBC', 'ABC', 'Fox', 'CBS', 'PBS']\n> App::Persistence['channels'] = ['TF1', 'France 2', 'France 3']\n# ['TF1', 'France 2', 'France 3']\n> App::Persistence.delete('channels')\n# ['TF1', 'France 2', 'France 3']\n> App::Persistence['something__new'] # something previously never stored\n# nil\n> App::Persistence.all\n# {'all':'values', 'stored':'by', 'bubblewrap':'as a hash!'}\n```\n\n### Observers\n**Since: > version 0.4**\n\nYou can observe for object's changes and trigger blocks:\n\n``` ruby\nclass ExampleViewController < UIViewController\n  include BW::KVO\n\n  def viewDidLoad\n    @label = UILabel.alloc.initWithFrame [[20,20],[280,44]]\n    @label.text = \"\"\n    view.addSubview @label\n\n    observe(@label, :text) do |old_value, new_value|\n      puts \"Hello from viewDidLoad!\"\n    end\n  end\n\n  def viewDidAppear(animated)\n    observe(@label, :text) do |old_value, new_value|\n      puts \"Hello from viewDidAppear!\"\n    end\n  end\n\nend\n```\n\nYou can remove observers using `unobserve` method.\n\n**Since: > version 1.9.0**\n\nOptionally, multiple key paths can be passed to the `observer` method:\n\n``` ruby\nclass ExampleViewController < UIViewController\n  include BW::KVO\n\n  def viewDidLoad\n    @label = UILabel.alloc.initWithFrame [[20,20],[280,44]]\n    @label.text = \"\"\n    view.addSubview @label\n\n    observe(@label, [:text, :textColor]) do |old_value, new_value, key_path|\n      puts \"Hello from viewDidLoad for #{key_path}!\"\n    end\n  end\nend\n```\n\nAlso you can use `observe!` method to register observer that will immediately\nreturn initial value. Note that in this case only new value will be passed to\nthe block.\n\n\n### String\n\nThe Ruby `String` class was extended to add `#camelize` and\n`#underscore` methods.\n\n```ruby\n> \"matt_aimonetti\".camelize\n=> \"MattAimonetti\"\n> \"MattAimonetti\".underscore\n=> \"matt_aimonetti\"\n```\n\n### Time\n\nThe `Time` Ruby class was added a class level method to convert a\niso8601 formatted string into a Time instance.\n\n```ruby\n> Time.iso8601(\"2012-05-31T19:41:33Z\")\n=> 2012-05-31 21:41:33 +0200\n```\n\n## Location\n\nInterface for Ruby-like GPS and compass access (the CoreLocation framework):\n\n```ruby\n> BW::Location.enabled? # Whether location services are enabled on the device\n=> true\n> BW::Location.authorized? # If your app is authorized to use location services\n=> false\n```\n\n```ruby\nBW::Location.get(purpose: 'We need to use your GPS because...') do |result|\n  p \"From Lat #{result[:from].latitude}, Long #{result[:from].longitude}\"\n  p \"To Lat #{result[:to].latitude}, Long #{result[:to].longitude}\"\nend\n```\n*Note: `result[:from]` will return `nil` the first time location services are started.*\n\nThe `:previous` key in the `BW::Location.get()` result hash will always return an array of zero or more additional `CLLocation` objects aside from the locations returned from the `:to` and `:from` hash keys.  While in most scenarios this array will be empty, per [Apple's Documentation](https://developer.apple.com/library/IOs/documentation/CoreLocation/Reference/CLLocationManagerDelegate_Protocol/index.html#//apple_ref/occ/intfm/CLLocationManagerDelegate/locationManager:didUpdateLocations:) if there are deferred updates or multiple locations that arrived before they could be delivered, multiple locations will be returned in an order of oldest to newest.\n\n\n```ruby\nBW::Location.get_compass do |result|\n  p result[:magnetic_heading] # Heading towards magnetic north\n  p result[:true_heading] # Heading towards true north\n  p result[:accuracy] # Potential error between magnetic and true heading\n  p result[:timestamp] # Timestamp of the heading calculation\nend\n```\n\n`BW::Location.get_significant` is also available, for monitoring significant location changes.\n\n`BW::Location` also supports `get_once`-style methods, which will return the first result before ending the search:\n\n```ruby\nBW::Location.get_once(desired_accuracy: :three_kilometers, ...) do |result|\n  if result.is_a?(CLLocation)\n    p result.coordinate.latitude\n    p result.coordinate.longitude\n  else\n    p \"ERROR: #{result[:error]}\"\n  end\nend\n\nBW::Location.get_compass_once do |heading|\n  p result[:magnetic_heading]\n  p result[:true_heading]\n  p result[:accuracy]\n  p result[:timestamp]\nend\n```\n\n### iOS 8 Location Requirements\n\niOS 8 introduced stricter location services requirements. Although BubbleWrap will handle most of this for you automatically, you are required to add a few key/value pairs to the `Info.plist`. Add these two lines to your `Rakefile` (with your descriptions, obviously):\n\n```ruby\napp.info_plist['NSLocationAlwaysUsageDescription'] = 'Description'\napp.info_plist['NSLocationWhenInUseUsageDescription'] = 'Description'\n```\n\n*Note: you need both keys to use `get_once`, so it's probably best to just include both no matter what.* See [Apple's documentation](https://developer.apple.com/library/ios/documentation/General/Reference/InfoPlistKeyReference/Articles/CocoaKeys.html#//apple_ref/doc/uid/TP40009251-SW18) on iOS 8 location services requirements for more information.\n\n## Motion\n\nInterface for the accelerometer, gyroscope, and magnetometer sensors (the\nCoreMotion framework).  You can access each sensor individually, or you can get\ndata from all of them at once using the `BW::Motion.device` interface, which\ndelegates to `CMMotionManager#deviceMotion`.\n\nEach sensor has an `every` and `once` method. `every` expects a time interval,\nand you will need to retain the object it returns and call `#stop` on it when\nyou are done with the data.\n\nThe `every` and `once` methods can accept a `:queue` option. The default value\nis a queue that runs on the main loop, so that UI updates can be processed in\nthe block. This is useful, but not recommended by Apple, since the events can\ncome in at a high rate. If you want to use a background queue, you can either\nspecify an NSOperationQueue object, or you can use one of these symbols:\n\n- `:main` - `NSOperationQueue.mainQueue`, this is the default value.\n- `:background` - BubbleWrap will create a new `NSOperationQueue`.\n- `:current` - BubbleWrap will use `NSOperationQueue.currentQueue`.\n\nIf you pass a string instead, a new queue will be created and its `name`\nproperty will be set to that string.\n\nThe `CMDeviceMotion` interface (`BW::Motion.device`) accepts a `:reference`\noption, which specifies the `CMAttitudeReferenceFrame`.  The default value is\nthe same as the one that `CMMotionManager` uses, which is returned by the\n`CMMotionManager#attitudeReferenceFrame` method.  This option should be passed\nto the `repeat`, `every` or `once` methods.\n\n###### Accelerometer\n```ruby\nBW::Motion.accelerometer.available?\nBW::Motion.accelerometer.data  # returns CMAccelerometerData object or nil\n\n# ask the CMMotionManager to update every 5 seconds\nBW::Motion.accelerometer.every(5) do |result|\n  # result contains the following data (from CMAccelerometerData#acceleration):\n  p result[:data]  # the CMAccelerometerData object\n  p result[:acceleration]  # the CMAcceleration struct\n  p result[:x]  # acceleration in the x direction\n  p result[:y]  #          \"          y direction\n  p result[:z]  #          \"          z direction\nend\n\n# every, start, and repeat all need to be stopped later.\nBW::Motion.accelerometer.stop\n\n# repeat, but don't set the interval\nBW::Motion.accelerometer.repeat do |result|\nend\n\n# you can specify a :queue where the operations will be executed.  See above for details\nBW::Motion.accelerometer.every(5, queue: :background) { |result| ... }\nBW::Motion.accelerometer.every(5, queue: :main) { |result| ... }\nBW::Motion.accelerometer.every(5, queue: :current) { |result| ... }\nBW::Motion.accelerometer.every(5, queue: 'my queue') { |result| ... }\n\nBW::Motion.accelerometer.once do |result|\n  # ...\nend\n```\n\n###### Gyroscope\n```ruby\nBW::Motion.gyroscope.available?\nBW::Motion.gyroscope.data  # returns CMGyroData object or nil\n\n# ask the CMMotionManager to update every second.\nBW::Motion.gyroscope.every(1) do |result|\n  # result contains the following data (from CMGyroData#rotationRate):\n  p result[:data]  # the CMGyroData object\n  p result[:rotation]  # the CMRotationRate struct\n  p result[:x]  # rotation in the x direction\n  p result[:y]  #        \"        y direction\n  p result[:z]  #        \"        z direction\nend\nBW::Motion.gyroscope.stop\n\nBW::Motion.gyroscope.once do |result|\n  # ...\nend\n```\n\n###### Magnetometer\n```ruby\nBW::Motion.magnetometer.available?\nBW::Motion.magnetometer.data  # returns CMMagnetometerData object or nil\n\n# ask the CMMotionManager to update every second\nBW::Motion.magnetometer.every(1) do |result|\n  # result contains the following data (from CMMagnetometerData#magneticField):\n  p result[:data]  # the CMMagnetometerData object\n  p result[:field]  # the CMMagneticField struct\n  p result[:x]  # magnetic field in the x direction\n  p result[:y]  #           \"           y direction\n  p result[:z]  #           \"           z direction\nend\nBW::Motion.magnetometer.stop\n\nBW::Motion.magnetometer.once do |result|\n  # ...\nend\n```\n\n###### Device Motion\n\nThis is an amalgam of all the motion sensor data.\n\n```ruby\nBW::Motion.device.available?\nBW::Motion.device.data  # returns CMDeviceMotion object or nil\n\nBW::Motion.device.every(1) do |result|\n  # result contains the following data:\n  p result[:data]  # the CMDeviceMotion object\n  # orientation data, from CMDeviceMotion#attitude\n  p result[:attitude]  # the CMAttitude struct\n  p result[:roll]\n  p result[:pitch]\n  p result[:yaw]\n  # rotation data, from CMDeviceMotion#rotationRate\n  p result[:rotation]  # the CMRotationRate struct\n  p result[:rotation_x]\n  p result[:rotation_y]\n  p result[:rotation_z]\n  # gravity+acceleration vector, from CMDeviceMotion#gravity\n  p result[:gravity]  # the CMAcceleration struct\n  p result[:gravity_x]\n  p result[:gravity_y]\n  p result[:gravity_z]\n  # just the acceleration vector, from CMDeviceMotion#userAcceleration\n  p result[:acceleration]  # the CMAcceleration struct\n  p result[:acceleration_x]\n  p result[:acceleration_y]\n  p result[:acceleration_z]\n  # the magnetic data, from CMDeviceMotion#magneticField\n  p result[:magnetic]  # the CMCalibratedMagneticField struct\n  p result[:magnetic_field]  # the CMMagneticField struct from the CMCalibratedMagneticField\n  p result[:magnetic_x]\n  p result[:magnetic_y]\n  p result[:magnetic_z]\n  p result[:magnetic_accuracy]  # this will be a symbol, :low, :medium, :high, or nil if the magnetic data is uncalibrated\n\n  # less useful data from CMAttitude, unless you're into the whole linear algebra thing:\n  p result[:matrix]  # CMAttitude#rotationMatrix\n  p result[:quarternion]  # CMAttitude#quarternion\nend\n\n# the reference frame should be one of the CMAttitudeReferenceFrame constants...\nref = CMAttitudeReferenceFrameXArbitraryZVertical\n# ... or one of these symbols: :arbitrary_z, :corrected_z, :magnetic_north, :true_north\nref = :corrected_z\nBW::Motion.device.every(1, queue: :background, reference: ref) { |result| ... }\n\nBW::Motion.device.once do |result|\n  # ...\nend\n```\n\n## Media\n\nAdded wrapper for playing remote and local media. Available are `modal` and custom presentation styles:\n\n```ruby\n# Plays in your custom frame\nlocal_file = NSURL.fileURLWithPath(File.join(NSBundle.mainBundle.resourcePath, 'test.mp3'))\nBW::Media.play(local_file) do |media_player|\n  media_player.view.frame = [[10, 100], [100, 100]]\n  self.view.addSubview media_player.view\nend\n\n# Plays in an independent modal controller\nBW::Media.play_modal(\"http://www.hrupin.com/wp-content/uploads/mp3/testsong_20_sec.mp3\")\n```\n\n## Mail\n\nWrapper for showing an in-app mail composer view.\n\nYou should always determine if the device your app is running on is configured to send mail before displaying a mail composer window. `BW::Mail.can_send_mail?` will return `true` or `false`.\n\n```ruby\n# Opens as a modal in the current UIViewController\nBW::Mail.compose(\n  delegate: self, # optional, defaults to rootViewController\n  to: [ \"tom@example.com\" ],\n  cc: [ \"itchy@example.com\", \"scratchy@example.com\" ],\n  bcc: [ \"jerry@example.com\" ],\n  html: false,\n  subject: \"My Subject\",\n  message: \"This is my message. It isn't very long.\",\n  animated: false\n) do |result, error|\n  result.sent?      # => boolean\n  result.canceled?  # => boolean\n  result.saved?     # => boolean\n  result.failed?    # => boolean\n  error             # => NSError\nend\n```\n\n## SMS\n\nWrapper for showing an in-app message (SMS) composer view.\n\nYou should always determine if the device your app is running on can send SMS messages before displaying a SMS composer window. `BW::SMS.can_send_sms?` will return `true` or `false`.\n\n```ruby\n# Opens as a modal in the current UIViewController\n    BW::SMS.compose (\n    {\n       delegate: self, # optional, will use root view controller by default\n       to: [ \"1(234)567-8910\" ],\n       message: \"This is my message. It isn't very long.\",\n       animated: false\n    }) {|result, error|\n       result.sent?      # => boolean\n       result.canceled?  # => boolean\n       result.failed?    # => boolean\n       error             # => NSError\n      }\n```\n\n## NetworkIndicator\n\nWrapper for showing and hiding the network indicator (the status bar spinner).\n\n```ruby\n    BW::NetworkIndicator.show  # starts the spinner\n    BW::NetworkIndicator.hide  # stops it\n\n    # the nice thing is if you call 'show' multiple times, the 'hide' method will\n    # not have any effect until you've called it the same number of times.\n    BW::NetworkIndicator.show\n    # ...somewhere else\n    BW::NetworkIndicator.show\n\n    # ...down the line\n    BW::NetworkIndicator.hide\n    # indicator is still visible\n\n    BW::NetworkIndicator.hide\n    # NOW the indicator is hidden!\n\n    # If you *really* want to hide the indicator immediately, you can call `reset!`\n    # but this is in no way encouraged.\n    BW::NetworkIndicator.reset!\n\n    # and for completeness, a check to see if the indicator is visible\n    BW::NetworkIndicator.visible?\n```\n\n## UI\n\n### Gestures\n\nExtra methods on `UIView` for working with gesture recognizers. A gesture recognizer can be added using a normal Ruby block, like so:\n\n```ruby\n    view.when_tapped do\n      UIView.animateWithDuration(1,\n        animations:lambda {\n          # animate\n          # @view.transform = ...\n        })\n    end\n```\n\nThere are similar methods for `pinched`, `rotated`, `swiped`, `panned`, and `pressed` (for long presses). All of the methods return the actual recognizer object, so it is possible to set the delegate if more fine-grained control is needed.\n\nIn order to prevent retain cycles due to strong references within the passed block, use the use_weak_callbacks flag so the blocks do not retain a strong reference to self:\n\n```ruby\nBubbleWrap.use_weak_callbacks = true\n```\n\n### UIViewController\n\nA custom method was added to `UIViewController` to return the content\nframe of a view controller.\n\n### UIControl / UIButton\n\nHelper methods to give `UIButton` a Ruby-like interface. Ex:\n\n```ruby\nbutton.when(UIControlEventTouchUpInside) do\n  self.view.backgroundColor = UIColor.redColor\nend\n```\n\nThe `#when` method also accepts bitwise combinations of events:\n\n```ruby\nbutton.when(UIControlEventTouchUpInside | UIControlEventTouchUpOutside) do\n  self.view.backgroundColor = UIColor.redColor\nend\n```\n\nYou can use symbols for events (but won't work with the bitwise operator):\n\n```ruby\nbutton.when(:touch_up_inside) do\n  self.view.backgroundColor = UIColor.redColor\nend\n\nbutton.when(:value_changed) do\n  self.view.backgroundColor = UIColor.blueColor\nend\n```\n\nSet the use_weak_callbacks flag so the blocks do not retain a strong reference to self:\n\n```ruby\nBubbleWrap.use_weak_callbacks = true\n```\n\n### UIBarButtonItem\n\n`BW::UIBarButtonItem` is a subclass of `UIBarButtonItem` with an natural Ruby syntax.\n\n#### Constructors\n\nInstead specifying a target-action pair, each constructor method accepts an optional block.  When the button is tapped, the block is executed.\n\n```ruby\nBW::UIBarButtonItem.system(:save) do\n  # ...\nend\n\ntitle = \"Friends\"\nBW::UIBarButtonItem.styled(:plain, title) do\n  # ...\nend\n\nimage = UIImage.alloc.init\nBW::UIBarButtonItem.styled(:bordered, image) do\n  # ...\nend\n\nimage     = UIImage.alloc.init\nlandscape = UIImage.alloc.init\nBW::UIBarButtonItem.styled(:bordered, image, landscape) do\n  # ...\nend\n\nview = UIView.alloc.init\nBW::UIBarButtonItem.custom(view) do\n  # ...\nend\n# NOTE: The block is attached to the view as a single tap gesture recognizer.\n```\n\nThe `.new` constructor provides a flexible, builder-style syntax.\n\n```ruby\noptions = { :system => :save }\nBW::UIBarButtonItem.new(options) do\n  # ...\nend\n\noptions = { :styled => :plain, :title => \"Friends\" }\nBW::UIBarButtonItem.new(options) do\n  # ...\nend\n\noptions = { :styled => :bordered, :image => UIImage.alloc.init }\nBW::UIBarButtonItem.new(options) do\n  # ...\nend\n\noptions = {\n  :styled    => :bordered,\n  :image     => UIImage.alloc.init,\n  :landscape => UIImage.alloc.init\n}\nBW::UIBarButtonItem.new(options) do\n  # ...\nend\n\noptions = { :custom => UIView.alloc.init }\nBW::UIBarButtonItem.new(options) do\n  # ...\nend\n# NOTE: The block is attached to the view as a single tap gesture recognizer.\n```\n\n#### Button types\n\nThe `.styled` button types are:\n\n```ruby\n:plain\n:bordered\n:done\n```\n\nAnd the `.system` button types are:\n\n```ruby\n:done\n:cancel\n:edit\n:save\n:add\n:flexible_space\n:fixed_space\n:compose\n:reply\n:action\n:organize\n:bookmarks\n:search\n:refresh\n:stop\n:camera\n:trash\n:play\n:pause\n:rewind\n:fast_forward\n:undo\n:redo\n:page_curl\n```\n\n### UIActivityViewController\n\n`BW::UIActivityViewController` is a subclass of `UIActivityViewController` with an natural Ruby syntax.\n\nYou can initiate a `UIActivityViewController` with or without a completion handler block. For more information on `UIActivityViewController`s, see [Apple's documentation](https://developer.apple.com/library/ios/documentation/uikit/reference/UIActivityViewController_Class/Reference/Reference.html).\n\n```ruby\n# Without a completion handler\nBW::UIActivityViewController.new(\n  items: \"Some Text\", # or [\"Some Text\", NSURL.URLWithString('http://www.rubymotion.com')] or a UIImage\n  animated: true, # Defaults to true\n  excluded: :add_to_reading_list # One item or an array\n)\n\n# With completion handler\nBW::UIActivityViewController.new(\n  items: \"Some Text\",\n  animated: true,\n  excluded: [:add_to_reading_list, :print, :air_drop]\n) do |activity_type, completed|\n  puts \"completed with activity: #{activity_type} - finished?: #{completed}\"\nend\n```\n\nBuilt in activities that can be passed to the `excluded` option are defined as `UIActivity` class `UIActivityType` constants:\n\n```ruby\n:post_to_facebook\n:post_to_twitter\n:post_to_weibo\n:message\n:mail\n:print\n:copy_to_pasteboard\n:assign_to_contact\n:save_to_camera_roll\n:add_to_reading_list\n:post_to_flickr\n:post_to_vimeo\n:post_to_tencent_weibo\n:air_drop\n```\n\n## RSS Parser\n**Since: > version 1.0.0**\n\nThe RSS Parser provides an easy interface to consume RSS feeds in an\nasynchronous (non blocking) way.\n\n\n```ruby\nfeed_parser = BW::RSSParser.new(\"http://feeds2.feedburner.com/sdrbpodcast\")\nfeed_parser.parse do |item|\n  # called asynchronously as items get parsed\n  p item.title\nend\n```\n\nThe yielded RSS item is of type `RSSParser::RSSItem` and has the\nfollowing attributes:\n\n* title\n* description\n* link\n* guid\n* pubDate\n* enclosure\n\nThe item can be converted into a hash by calling `to_hash` on it.\n\n### Delegate\n**Since: > version 1.0.0**\n\nYou can also designate a delegate to the parser and implement change\nstate callbacks:\n\n```ruby\nfeed_parser = BW::RSSParser.new(\"http://feeds.feedburner.com/sdrbpodcast\")\nfeed_parser.delegate = self\nfeed_parser.parse do |item|\n  p item.title\nend\n\n# Delegate method\ndef when_parser_initializes\n  p \"The parser is ready!\"\nend\n\ndef when_parser_parses\n  p \"The parser started parsing the document\"\nend\n\ndef when_parser_is_done\n  p \"The feed is entirely parsed, congratulations!\"\nend\n\ndef when_parser_errors\n  p \"The parser encountered an error\"\n  ns_error = feed_parser.parserError\n  p ns_error.localizedDescription\nend\n```\n\nThese delegate methods are optional, however, you might find the\n`when_parser_is_done` callback useful if you collected all the items and\nwant to process all at once for instance.\n\n### Parsing a remote content or actual data\n\nYou have the choice to initialize a parser instance with a string\nrepresenting an URL, an instance of `NSURL` or my specifying that the\npassed param is some data to parse directly.\n\n```ruby\n# string representing an url:\nfeed_parser = BW::RSSParser.new(\"http://feeds2.feedburner.com/sdrbpodcast\")\n# a NSURL instance:\nurl =  NSURL.alloc.initWithString(\"http://matt.aimonetti.net/atom.xml\")\nfeed_parser = BW::RSSParser.new(url)\n# Some data\nfeed = File.read('atom.xml')\nfeed_parser = BW::RSSParser.new(feed, true)\n```\n\n\n## Reactor\n**Since: > version 1.0.0**\n\n`BW::Reactor` is a simplified, mostly complete implementation of\nthe [Event Machine](http://rubyeventmachine.com/) API.  In fact\n`BW::Reactor` is aliased to `EM` in the runtime environment.\n\n### Deferables\n\nBubbleWrap provides both a `Deferrable` mixin and a `DefaultDeferrable`\nclass, which simply mixes in deferrable behaviour if you don't want to\nimplement your own.\n\nA deferrable is an object with four states: unknown, successful, failure\nand timeout.  When you initially create a deferrable it is in an unknown\nstate, however you can assign callbacks to be run when the object\nchanges to either successful or failure state.\n\nUsing `delegate`, `errback_delegate` and `callback_delegate` you can link\ndeferrables together.\n\nBy default, callbacks will be made on the thread that the deferrable\nsucceeds/fails on. For multithreaded environments, it can be useful to use\nEM::ThreadAwareDeferrable so that callbacks will be made on the threads they\nare declared on.\n\n#### Success\n\n```ruby\n> d = EM::DefaultDeferrable.new\n=> #<BW::Reactor::DefaultDeferrable:0x6d859a0>\n> d.callback { |what| puts \"Great #{what}!\" }\n=> [#<Proc:0x6d8a1e0>]\n> d.succeed \"justice\"\nGreat justice!\n=> nil\n```\n\n#### Failure\n\n```ruby\n> d = EM::DefaultDeferrable.new\n=> #<BW::Reactor::DefaultDeferrable:0x8bf3ee0>\n> d.errback { |what| puts \"Great #{what}!\" }\n=> [#<Proc:0x8bf3ef0>]\n> d.fail \"sadness\"\nGreat sadness!\n=> nil\n```\n#### Delegate\n\n```ruby\n> d = EM::DefaultDeferrable.new\n=> #<BW::Reactor::DefaultDeferrable:0x8bf3ee0>\n> delegate = EM::DefaultDeferrable.new\n=> #<BW::Reactor::DefaultDeferrable:0x8bf5910>\n> d.delegate delegate\n=> #<BW::Reactor::DefaultDeferrable:0x8bf3ee0>\n> delegate.callback { |*args| puts args }\n=> [#<Proc:0x8bf3ef0>]\n> d.succeed :passed\n=> nil\n=> [:passed]\n```\n\n#### DependentDeferrable\n\n`DependentDeferrable` depends on children deferrables. A `DependentDeferrable`\nsucceeds only when every child succeeds and fails immediately when any child\nfails\n\n```ruby\n> d1 = EM::DefaultDeferrable.new\n=> #<BubbleWrap::Reactor::DefaultDeferrable:0x10c713750>\n> d2 = EM::DefaultDeferrable.new\n=> #<BubbleWrap::Reactor::DefaultDeferrable:0x10370bb10>\n> d = EM::DependentDeferrable.on(d1, d2)\n=> #<BubbleWrap::Reactor::DependentDeferrable:0x106c17b80>\n> d.callback {|a, b| puts \"a: #{a} b: #{b}\"}\n=> [#<Proc:0x103075210>]\n> d1.succeed 'one', 'one more'\n> d2.succeed :two\na: [\"one\", \"one more\"] b: [:two]\n```\n\n#### ThreadAwareDeferrable\n\n```ruby\n> d = EM::ThreadAwareDeferrable.new\n=> #<BW::Reactor::ThreadAwareDeferrable:0x8bf3ee0>\n\n> queue = Dispatch::Queue.new(:deferrable.to_s)\n> queue.async do\n>   d.callback do |*args|\n>     Dispatch::Queue.current == queue\n>     => true # this is normally false\n>   end\n> end\n> d.succeed true\n```\n\n#### Timeout\n\n```ruby\n> d = EM::DefaultDeferrable.new\n=> #<BW::Reactor::DefaultDeferrable:0x8bf5910>\n> d.errback { puts \"Great scott!\" }\n=> [#<Proc:0x8bf6350>]\n> d.timeout 2\n=> #<BW::Reactor::Timer:0x6d920a0 @timer=#<__NSCFTimer:0x6d91990>>\n# wait...\n> Great scott!\n```\n\n### Timers\n\n*All timers can be cancelled using `EM.cancel_timer`.*\n\n#### One-shot timers\n\n```ruby\n> EM.add_timer 1.0 do\n>   puts \"Great scott!\"\n> end\n=> 146335904\n> Great scott!\n```\n\n#### Periodic timers\n\n```ruby\n> count = 0\n=> 0\n> timer = EM.add_periodic_timer 1.0 do\n>   count = count + 1\n>   puts \"Great scott!\"\n>   (count < 10) || EM.cancel_timer(timer)\n> end\n=> 146046832\n> Great scott!\nGreat scott!\nGreat scott!\nGreat scott!\nGreat scott!\nGreat scott!\nGreat scott!\nGreat scott!\nGreat scott!\nGreat scott!\n```\n\n### Scheduling operations\n\nYou can use `EM.schedule` to schedule blocks to be executed\nasynchronously.  BubbleWrap deviates from the EventMachine\nAPI here in that it also provides `EM.schedule_on_main` which\nmakes sure that the task is run asynchronously, but on the\napplication's main thread - this is necessary if you are\nupdating the user interface.\n\n```ruby\n> EM.schedule { puts Thread.current.object_id }\n146027920\n=> nil\n> EM.schedule_on_main { puts Thread.current.object_id }\n112222480\n=> nil\n```\n\n### Deferrable operations\n\nYou can also use `EM.defer` in much the same way as `EM.schedule`\nwith one important difference, you can pass in a second `proc`\nwhich will be called when the first has completed, and be passed\nit's result as an argument. Just like `EM.schedule`, `EM.defer`\nalso has an `EM.defer_on_main` version.\n\n```ruby\n> operation = proc { 88 }\n=> #<Proc:0x6d763c0>\n> callback = proc { |speed| puts speed >= 88 ? \"Time travel!\" : \"Conventional travel!\" }\n=> #<Proc:0x8bd3910>\n> EM.defer(operation, callback)\n=> nil\nTime travel!\n```\n\n### Events\n\nAlthough not part of the EventMachine API, BubbleWrap provides\nan `Eventable` mixin for use instrumenting objects with simple\nevent triggering behaviour. `BW::Reactor` uses this\nbehind the scenes in several places, and as it's a very handy\nidiom it is available as a public API.\n\n```ruby\n> o = Class.new { include EM::Eventable }.new\n=> #<#<Class:0xab63f00>:0xab64430>\n> o.on(:november_5_1955) { puts \"Ow!\" }\n=> [#<Proc:0xad9bf00>]\n> flux = proc{ puts \"Flux capacitor!\" }\n=> #<Proc:0xab630f0>\n> o.on(:november_5_1955, &flux)\n=> [#<Proc:0xad9bf00>, #<Proc:0xab630f0>]\n> o.trigger(:november_5_1955)\nOw!\nFlux capacitor!\n=> [nil, nil]\n> o.off(:november_5_1955, &flux)\n=> #<Proc:0xab630f0>\n> o.trigger(:november_5_1955)\nOw!\n=> [nil]\n> o.on(:november_5_1955) { puts \"Ow!\" }\n> o.on(:november_5_1955) { puts \"Another Ow!\" }\n> o.off(:november_5_1955)\n=> nil\n```\n\n# Contributing\n\nDo you have a suggestion for a specific wrapper? Feel free to open an\nissue/ticket and tell us about what you are after. If you have a\nwrapper/helper you are using and are thinking that others might enjoy,\nplease send a pull request with tests. If you need help writing the tests,\nsend the pull request anyways and we'll try to help you out with that.\n\n1. Create an issue in GitHub to make sure your PR will be accepted\n2. Fork the BubbleWrap repository\n3. Create your feature branch (`git checkout -b my-new-feature`)\n4. Commit your changes (`git commit -am 'Add some feature'`)\n5. Write tests for your changes and ensure they pass locally (`bundle exec rake spec && bundle exec rake spec osx=true`)\n6. Push to the branch (`git push origin my-new-feature`)\n7. Create new Pull Request\n"
  },
  {
    "path": "Rakefile",
    "content": "$:.unshift(\"/Library/RubyMotion/lib\")\n$:.unshift(\"~/.rubymotion/rubymotion-templates\")\nrequire 'motion/project/template/gem/gem_tasks'\nif ENV['osx']\n  require 'motion/project/template/osx'\nelse\n  require 'motion/project/template/ios'\nend\nBundler.setup\nBundler.require\n\nrequire 'bubble-wrap/all'\nrequire 'bubble-wrap/test'\n\nmodule Motion\n  module Project\n    class Config\n      def spec_files=(spec_files)\n        @spec_files = spec_files\n      end\n    end\n  end\nend\n\nMotion::Project::App.setup do |app|\n  app.name = 'testSuite'\n  app.identifier = 'io.bubblewrap.testSuite'\n  app.specs_dir = './spec/motion'\n  app.spec_files\n  if Motion::Project::App.osx?\n    app.spec_files -= Dir.glob(\"./spec/motion/**/ios/**.rb\")\n    [\"font\", \"motion\", \"location\", \"media\", \"ui\", \"mail\", \"sms\", \"network-indicator\"].each do |package|\n      app.spec_files -= Dir.glob(\"./spec/motion/#{package}/**/*.rb\")\n    end\n  else\n    app.info_plist['NSLocationAlwaysUsageDescription'] = 'Description'\n    app.info_plist['NSLocationWhenInUseUsageDescription'] = 'Description'\n\n    app.spec_files -= Dir.glob(\"./spec/motion/**/osx/**.rb\")\n  end\n\n  app.version       = '1.2.3'\n  app.short_version = '3.2.1'\nend\n\nnamespace :spec do\n  task :lib do\n    sh \"bacon #{Dir.glob(\"spec/lib/**/*_spec.rb\").join(' ')}\"\n  end\n\n  task :motion => 'spec'\n\n  task :all => [:lib, :motion]\nend\n"
  },
  {
    "path": "bubble-wrap.gemspec",
    "content": "# -*- encoding: utf-8 -*-\nrequire File.expand_path('../lib/bubble-wrap/version', __FILE__)\n\nGem::Specification.new do |gem|\n  gem.authors       = ['Matt Aimonetti', 'Francis Chong', 'James Harton', 'Clay Allsopp', 'Dylan Markow', 'Jan Weinkauff', 'Marin Usalj']\n  gem.email         = ['mattaimonetti@gmail.com', 'francis@ignition.hk', 'james@sociable.co.nz', 'clay.allsopp@gmail.com', 'dylan@dylanmarkow.com', 'jan@dreimannzelt.de', 'marin2211@gmail.com']\n  gem.description   = 'RubyMotion wrappers and helpers (Ruby for iOS and OS X) - Making Cocoa APIs more Ruby like, one API at a time. Fork away and send your pull request.'\n  gem.summary       = 'RubyMotion wrappers and helpers (Ruby for iOS and OS X) - Making Cocoa APIs more Ruby like, one API at a time. Fork away and send your pull request.'\n  gem.homepage      = 'https://github.com/rubymotion-community/BubbleWrap'\n  gem.license       = 'MIT'\n\n  gem.files         = `git ls-files`.split($\\)\n  gem.test_files    = gem.files.grep(%r{^(test|spec|lib_spec|features)/})\n  gem.name          = 'bubble-wrap'\n  gem.require_paths = ['lib']\n  gem.version       = BubbleWrap::VERSION\n\n  gem.extra_rdoc_files = gem.files.grep(%r{motion})\n\n  gem.add_development_dependency 'mocha', '~> 0.11'\n  gem.add_development_dependency 'mocha-on-bacon', '~> 0.2'\n  gem.add_development_dependency 'bacon', '~> 1.2'\n  gem.add_development_dependency 'rake', '~> 10.0'\n  gem.add_development_dependency 'webstub', '~> 1.1'\nend\n"
  },
  {
    "path": "lib/bubble-wrap/all.rb",
    "content": "require File.expand_path('../loader', __FILE__)\n[\n  'core',\n  'font',\n  'location',\n  'mail',\n  'media',\n  'motion',\n  'network-indicator',\n  'reactor',\n  'rss_parser',\n  'sms',\n  'ui',\n].each { |sub|\n\trequire File.expand_path(\"../#{sub}\", __FILE__)\n}\n"
  },
  {
    "path": "lib/bubble-wrap/camera.rb",
    "content": "require 'bubble-wrap/loader'\n\nBubbleWrap.require_ios(\"camera\") do\n  BubbleWrap.require('motion/util/constants.rb')\n  BubbleWrap.require('motion/core/device.rb')\n  BubbleWrap.require('motion/core/device/*.rb')\n  BubbleWrap.require('motion/core/device/ios/*.rb') do\n    file('motion/core/device/ios/camera_wrapper.rb').depends_on 'motion/core/device/ios/camera.rb'\n    file('motion/core/device/ios/camera.rb').depends_on ['motion/core/ios/device.rb', 'motion/util/constants.rb']\n    file('motion/core/device/ios/camera.rb').uses_framework 'MobileCoreServices'\n    file('motion/core/device/ios/screen.rb').depends_on 'motion/core/ios/device.rb'\n  end\nend\n"
  },
  {
    "path": "lib/bubble-wrap/core.rb",
    "content": "require 'bubble-wrap/version' unless defined?(BubbleWrap::VERSION)\nrequire 'bubble-wrap/loader'\n\nBubbleWrap.require('motion/core.rb')\nBubbleWrap.require('motion/core/*.rb') do\n  file('motion/core/pollute.rb').depends_on 'motion/core/ns_index_path.rb'\nend\nBubbleWrap.require('motion/core/device/*.rb')\n\nBubbleWrap.require_ios do\n  BubbleWrap.require('motion/core/ios/**/*.rb')\n  BubbleWrap.require('motion/core/device/ios/**/*.rb')\n\n  require 'bubble-wrap/camera'\n  require 'bubble-wrap/ui'\nend\n\nBubbleWrap.require_osx do\n  BubbleWrap.require('motion/core/osx/**/*.rb')\n  BubbleWrap.require('motion/core/device/osx/**/*.rb')\nend\n"
  },
  {
    "path": "lib/bubble-wrap/ext/motion_project_app.rb",
    "content": "module BubbleWrap\n  module Ext\n    module BuildTask\n\n      def self.extended(base)\n        base.instance_eval do\n          def setup_with_bubblewrap(*args, &block)\n            bw_config = proc do |app|\n              ::BubbleWrap.before_config(app)\n              block.call(app) unless block.nil?\n            end\n\n            setup_without_bubblewrap *args, &bw_config\n          end\n          alias :setup_without_bubblewrap :setup\n          alias :setup :setup_with_bubblewrap\n        end\n      end\n\n    end\n\n    module Config\n      def config_with_bubblewrap\n        config_without_bubblewrap.tap do |c|\n          ::BubbleWrap.after_config(c)\n        end\n      end\n\n      def self.extended(base)\n        singleton_class = class << base; self; end\n        singleton_class.send :alias_method, :config_without_bubblewrap, :config\n        singleton_class.send :alias_method, :config, :config_with_bubblewrap\n      end\n    end\n\n    module Platforms\n      def osx?\n        self.respond_to?(:template) && self.template == :osx\n      end\n    end\n  end\nend\n\nMotion::Project::App.extend(BubbleWrap::Ext::BuildTask)\n\nMotion::Project::App.extend(BubbleWrap::Ext::Platforms)\n\nMotion::Project::App.extend(BubbleWrap::Ext::Config)\n"
  },
  {
    "path": "lib/bubble-wrap/ext.rb",
    "content": "require 'bubble-wrap/ext/motion_project_app'\n"
  },
  {
    "path": "lib/bubble-wrap/font.rb",
    "content": "require 'bubble-wrap/loader'\nBubbleWrap.require_ios(\"font\") do\n  BubbleWrap.require('motion/font/font.rb')\nend\n"
  },
  {
    "path": "lib/bubble-wrap/loader.rb",
    "content": "unless defined?(Motion::Project::Config)\n  raise 'This file must be required within a RubyMotion project Rakefile.'\nend\n\nunless defined?(BubbleWrap::LOADER_PRESENT)\n  require 'bubble-wrap/version' unless defined?(VERSION)\n  if Gem::Version.new(Motion::Version) < Gem::Version.new(BubbleWrap::MIN_MOTION_VERSION)\n    raise \"BubbleWrap #{BubbleWrap::VERSION} requires at least RubyMotion #{BubbleWrap::MIN_MOTION_VERSION}\"\n  end\n\n  require 'bubble-wrap/ext'\n  require 'bubble-wrap/requirement'\n\n  module BubbleWrap\n\n    LOADER_PRESENT = true\n    module_function\n\n    def root\n      File.expand_path('../../../', __FILE__)\n    end\n\n    def require(file_spec, &block)\n      Requirement.scan(caller.first, file_spec, &block)\n    end\n\n    def require_ios(requirement = nil, &callback)\n      if !Motion::Project::App.osx?\n        callback.call\n      else\n        puts \"bubble-wrap/#{requirement} requires iOS to use.\" if requirement\n      end\n    end\n\n    def require_osx(requirement = nil, &callback)\n      if Motion::Project::App.osx?\n        callback.call\n      else\n        puts \"bubble-wrap/#{requirement} requires OS X to use.\" if requirement\n      end\n    end\n\n    def before_config(app)\n      app.files = ::BubbleWrap::Requirement.files(app.files)\n      app.files_dependencies ::BubbleWrap::Requirement.files_dependencies\n      app.frameworks = ::BubbleWrap::Requirement.frameworks(app.frameworks)\n    end\n\n    def after_config(config)\n      return unless ::BubbleWrap::Requirement.frameworks.include?(\"CoreLocation\")\n      BubbleWrap.require_ios do\n        ios8_files = 'motion/ios/8/location_constants.rb'\n        if config.send(:deployment_target).to_f >= 8.0\n          ::BubbleWrap.require(ios8_files)\n          before_config(config)\n        else\n          config.files = config.files.reject {|s|\n            s.include?(ios8_files)\n          }\n        end\n      end\n    end\n  end\n\n  BW = BubbleWrap unless defined?(BW)\n\n  BW.require 'motion/shortcut.rb'\n  BW.require 'lib/bubble-wrap/version.rb'\n  BW.require 'motion/util/deprecated.rb'\n\nend\n"
  },
  {
    "path": "lib/bubble-wrap/location.rb",
    "content": "require 'bubble-wrap/loader'\n\nBubbleWrap.require_ios(\"location\") do\n  BubbleWrap.require('motion/util/constants.rb')\n  BubbleWrap.require('motion/core/string.rb')\n  BubbleWrap.require('motion/location/**/*.rb') do\n    file('motion/location/pollute.rb').depends_on 'motion/location/location.rb'\n    file('motion/location/location.rb').depends_on 'motion/util/constants.rb'\n    file('motion/location/location.rb').uses_framework('CoreLocation')\n  end\nend\n"
  },
  {
    "path": "lib/bubble-wrap/mail.rb",
    "content": "require 'bubble-wrap/loader'\n\nBubbleWrap.require_ios(\"mail\") do\n  BubbleWrap.require('motion/core/app.rb')\n  BubbleWrap.require('motion/mail/**/*.rb') do\n    file('motion/mail/mail.rb').depends_on('motion/mail/result.rb')\n    file('motion/mail/mail.rb').uses_framework('MessageUI')\n  end\nend\n"
  },
  {
    "path": "lib/bubble-wrap/media.rb",
    "content": "require 'bubble-wrap/loader'\n\nBubbleWrap.require_ios(\"media\") do\n  BubbleWrap.require('motion/core/app.rb')\n  BubbleWrap.require('motion/core/ios/app.rb')\n  BubbleWrap.require('motion/core/ios/device.rb')\n  BubbleWrap.require('motion/core/string.rb')\n  BubbleWrap.require('motion/core/ns_notification_center.rb')\n  BubbleWrap.require('motion/media/**/*.rb') do\n    file('motion/media/media.rb').depends_on('motion/media/player.rb')\n    file('motion/media/player.rb').depends_on 'motion/core/string.rb'\n    file('motion/media/player.rb').uses_framework('MediaPlayer')\n  end\nend\n"
  },
  {
    "path": "lib/bubble-wrap/motion.rb",
    "content": "require 'bubble-wrap/loader'\n\nBubbleWrap.require_ios('motion') do\n  BubbleWrap.require('motion/util/constants.rb')\n  BubbleWrap.require('motion/core/string.rb')\n  BubbleWrap.require('motion/motion/**/*.rb') do\n    file('motion/motion/motion.rb').depends_on 'motion/util/constants.rb'\n\n    file('motion/motion/accelerometer.rb').depends_on('motion/motion/motion.rb')\n    file('motion/motion/device_motion.rb').depends_on('motion/motion/motion.rb')\n    file('motion/motion/gyroscope.rb').depends_on('motion/motion/motion.rb')\n    file('motion/motion/magnetometer.rb').depends_on('motion/motion/motion.rb')\n\n    file('motion/motion/motion.rb').uses_framework('CoreMotion')\n  end\nend\n"
  },
  {
    "path": "lib/bubble-wrap/network-indicator.rb",
    "content": "require 'bubble-wrap/loader'\n\nBubbleWrap.require_ios(\"network-indicator\") do\n  BubbleWrap.require('motion/core/app.rb')\n  BubbleWrap.require('motion/network-indicator/**/*.rb')\nend\n"
  },
  {
    "path": "lib/bubble-wrap/reactor.rb",
    "content": "require 'bubble-wrap/loader'\n\nBW.require 'motion/reactor.rb'\nBW.require 'motion/reactor/**/*.rb' do\n  file('motion/reactor.rb').depends_on Dir.glob('motion/reactor/**/*.rb')\n  file('motion/reactor/timer.rb').depends_on 'motion/reactor/eventable.rb'\n  file('motion/reactor/periodic_timer.rb').depends_on 'motion/reactor/eventable.rb'\n  file('motion/reactor/deferrable.rb').depends_on ['motion/reactor/timer.rb', 'motion/reactor/future.rb']\n  file('motion/reactor/default_deferrable.rb').depends_on 'motion/reactor/deferrable.rb'\nend\n"
  },
  {
    "path": "lib/bubble-wrap/requirement/path_manipulation.rb",
    "content": "module BubbleWrap\n  class Requirement\n    module PathManipulation\n\n      def convert_caller_to_root_path(path)\n        path = convert_caller_to_path path\n        path = convert_to_absolute_path path\n        strip_up_to_last_lib path\n      end\n\n      def convert_caller_to_path(string)\n        chunks = string.split(':')\n        if chunks.size >= 3\n          string = chunks[0..-3].join(':')\n          string = File.dirname(string)\n        end\n        string\n      end\n\n      def convert_to_absolute_path(path)\n        File.expand_path(path)\n      end\n\n      def strip_up_to_last_lib(path)\n        if path =~ /\\/lib$/\n          path = path.gsub(/\\/lib$/, \"\")\n        else\n          path = path.split('lib')\n          path = if path.size > 1\n                   path[0..-2].join('lib')\n                 else\n                   path[0]\n                 end\n          path = path[0..-2] if path[-1] == '/'\n        end\n        path\n      end\n\n      def convert_to_relative(path,root)\n        path = path.gsub(root,'')\n        path = path[1..-1] if path[0] == '/'\n        path\n      end\n\n    end\n  end\nend\n"
  },
  {
    "path": "lib/bubble-wrap/requirement.rb",
    "content": "require 'bubble-wrap/requirement/path_manipulation'\n\nmodule BubbleWrap\n  class Requirement\n    extend PathManipulation\n    include PathManipulation\n\n    attr_accessor :file, :root\n    attr_writer :file_dependencies\n\n    def initialize(file,root)\n      self.file = file\n      self.root = root\n    end\n\n    def relative\n      convert_to_relative(file, root)\n    end\n\n    def depends_on(file_or_paths)\n      paths = file_or_paths.respond_to?(:each) ? file_or_paths : [ file_or_paths ]\n      self.file_dependencies += paths.map do |f|\n        f = self.class.file(f) unless f.is_a? Requirement\n        f unless f.file == file\n      end.compact\n      self.file_dependencies.uniq!(&:to_s)\n    end\n\n    def uses_framework(framework_name)\n      self.frameworks << framework_name\n    end\n\n    def dependencies\n      return {} if file_dependencies.empty?\n      { file => file_dependencies.map(&:to_s) }\n    end\n\n    def to_s\n      file\n    end\n\n    def file_dependencies\n      @file_dependencies ||= []\n    end\n\n    def frameworks\n      @frameworks ||= []\n    end\n\n    class << self\n\n      attr_accessor :paths\n\n      def scan(caller_location, file_spec, &block)\n        root = convert_caller_to_root_path caller_location\n        self.paths ||= {}\n        Dir.glob(File.expand_path(file_spec, root)).each do |file|\n          p = new(file,root)\n          self.paths[p.relative] = p\n          p.depends_on('motion/shortcut.rb') unless p.relative == 'motion/shortcut.rb'\n        end\n        self.class_eval(&block) if block\n      end\n\n      def file(relative)\n        paths.fetch(relative)\n      end\n\n      def files(app_files=nil)\n        files = paths.values.map(&:to_s)\n        files += app_files if app_files\n        files.uniq\n      end\n\n      def clear!\n        paths.select! { |k,v| v.relative == 'motion/shortcut.rb' }\n      end\n\n      def files_dependencies\n        deps = {}\n        paths.each_value do |file|\n          deps.merge! file.dependencies\n        end\n        deps\n      end\n\n      def frameworks(app_frameworks=nil)\n        frameworks = ['Foundation', 'CoreGraphics'] +\n          paths.values.map(&:frameworks)\n        frameworks += app_frameworks if app_frameworks\n        frameworks.flatten.compact.sort.uniq\n      end\n\n    end\n  end\nend\n"
  },
  {
    "path": "lib/bubble-wrap/rss_parser.rb",
    "content": "require 'bubble-wrap/loader'\nBubbleWrap.require('motion/rss_parser.rb')\n"
  },
  {
    "path": "lib/bubble-wrap/sms.rb",
    "content": "require 'bubble-wrap/loader'\n\nBubbleWrap.require_ios(\"sms\") do\n  BubbleWrap.require('motion/core/app.rb')\n  BubbleWrap.require('motion/sms/**/*.rb') do\n    file('motion/sms/sms.rb').depends_on('motion/sms/result.rb')\n    file('motion/sms/sms.rb').uses_framework('MessageUI')\n  end\nend\n"
  },
  {
    "path": "lib/bubble-wrap/test.rb",
    "content": "require 'webstub'\nrequire 'bubble-wrap/loader'\nBW.require 'motion/util/*.rb'\nBW.require 'motion/test_suite_delegate.rb'\n\nMotion::Project::App.setup do |app|\n  app.development do\n    if Motion::Project::App.osx?\n      app.delegate_class = 'TestSuiteOSXDelegate'\n    else\n      app.delegate_class = 'TestSuiteDelegate'\n    end\n  end\nend\n"
  },
  {
    "path": "lib/bubble-wrap/ui.rb",
    "content": "require 'bubble-wrap/loader'\n\nBubbleWrap.require_ios(\"ui\") do\n  BubbleWrap.require('motion/util/constants.rb')\n  BubbleWrap.require('motion/ui/**/*.rb') do\n    file('motion/ui/pollute.rb').depends_on %w(\n      motion/ui/ui_control_wrapper.rb\n      motion/ui/ui_view_wrapper.rb\n      motion/ui/ui_view_controller_wrapper.rb\n    )\n  end\nend\n"
  },
  {
    "path": "lib/bubble-wrap/version.rb",
    "content": "module BubbleWrap\n  VERSION = '1.9.7' unless defined?(BubbleWrap::VERSION)\n  MIN_MOTION_VERSION = '3.12'\nend\n"
  },
  {
    "path": "lib/bubble-wrap.rb",
    "content": "require 'bubble-wrap/version' unless defined?(BubbleWrap::VERSION)\nrequire File.expand_path('../bubble-wrap/core', __FILE__)\n"
  },
  {
    "path": "motion/core/app.rb",
    "content": "# Provides a module to store global states\n#\nmodule BubbleWrap\n  module App\n    include BubbleWrap::Deprecated\n\n    module_function\n\n    # Returns the application's document directory path where users might be able to upload content.\n    # @return [String] the path to the document directory\n    def documents_path\n      NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, true)[0]\n    end\n\n    # Returns the application resource path where resource located\n    # @return [String] the application main bundle resource path\n    def resources_path\n      NSBundle.mainBundle.resourcePath\n    end\n\n    # Returns the default notification center\n    # @return [NSNotificationCenter] the default notification center\n    def notification_center\n      NSNotificationCenter.defaultCenter\n    end\n\n    def user_cache\n      NSUserDefaults.standardUserDefaults\n    end\n    deprecated :user_cache, \"2.0.0\"\n\n    # Executes a block after a certain delay\n    # Usage example:\n    #   App.run_after(0.5) {  p \"It's #{Time.now}\"   }\n    def run_after(delay,&block)\n      NSTimer.scheduledTimerWithTimeInterval( delay,\n                                              target: block,\n                                              selector: \"call:\",\n                                              userInfo: nil,\n                                              repeats: false)\n    end\n\n    @states = {}\n\n    def states\n      @states\n    end\n\n    def info_plist\n      NSBundle.mainBundle.infoDictionary\n    end\n\n    def name\n      info_plist['CFBundleDisplayName']\n    end\n\n    def identifier\n      NSBundle.mainBundle.bundleIdentifier\n    end\n\n    def version\n      info_plist['CFBundleVersion']\n    end\n\n    def short_version\n      info_plist['CFBundleShortVersionString']\n    end\n\n    # @return [NSLocale] locale of user settings\n    def current_locale\n      languages = NSLocale.preferredLanguages\n      if languages.count > 0\n        return NSLocale.alloc.initWithLocaleIdentifier(languages.first)\n      else\n        return NSLocale.currentLocale\n      end\n    end\n\n    # the current application environment : development, test, release\n    def environment\n      RUBYMOTION_ENV\n    end\n\n    def development?\n      environment == 'development'\n    end\n\n    def test?\n      environment == 'test'\n    end\n\n    def release?\n      environment == 'release'\n    end\n\n    def osx?\n      Kernel.const_defined?(:NSApplication)\n    end\n\n    def ios?\n      Kernel.const_defined?(:UIApplication)\n    end\n  end\n\nend\n::App = BubbleWrap::App unless defined?(::App)\n"
  },
  {
    "path": "motion/core/device/ios/camera.rb",
    "content": "# Provides a nice DSL for interacting with the standard\n# UIImagePickerController\n#\nmodule BubbleWrap\n  module Device\n    class Camera\n      module Error\n        SOURCE_TYPE_NOT_AVAILABLE=0\n        INVALID_MEDIA_TYPE=1\n        MEDIA_TYPE_NOT_AVAILABLE=2\n        INVALID_CAMERA_LOCATION=3\n        CANCELED=1000\n      end\n\n      Constants.register UIImagePickerControllerSourceTypePhotoLibrary, UIImagePickerControllerSourceTypeCamera,\n          UIImagePickerControllerSourceTypeSavedPhotosAlbum\n\n      Constants.register UIImagePickerControllerQualityTypeHigh,\n          UIImagePickerControllerQualityTypeMedium,\n          UIImagePickerControllerQualityTypeLow,\n          UIImagePickerControllerQualityType640x480,\n          UIImagePickerControllerQualityTypeIFrame1280x720,\n          UIImagePickerControllerQualityTypeIFrame960x540\n\n      MEDIA_TYPE_HASH = {movie: KUTTypeMovie, image: KUTTypeImage}\n\n      CAMERA_LOCATIONS = [:front, :rear, :none]\n\n      # The camera location; if :none, then we can't use source_type: :camera\n      # in #picture\n      # [:front, :rear, :none]\n      attr_accessor :location\n\n      def self.front\n        return nil if not UIImagePickerController.isCameraDeviceAvailable(UIImagePickerControllerCameraDeviceFront)\n        @front ||= Camera.new(:front)\n      end\n\n      def self.rear\n        return nil if not UIImagePickerController.isCameraDeviceAvailable(UIImagePickerControllerCameraDeviceRear)\n        @rear ||= Camera.new(:rear)\n      end\n\n      def self.available?\n        UIImagePickerController.isSourceTypeAvailable(UIImagePickerControllerSourceTypeCamera)\n      end\n\n      # For uploading photos from the library.\n      class << self\n        def any\n          @any ||= Camera.new\n        end\n        alias_method \"photo_library\", \"any\"\n      end\n\n      def popover_from(view)\n        @popover_in_view = view\n        self\n      end\n\n      def initialize(location = :none)\n        self.location = location\n      end\n\n      def location=(location)\n        if not CAMERA_LOCATIONS.member? location\n          raise Error::INVALID_CAMERA_LOCATION, \"#{location} is not a valid camera location\"\n        end\n        @location = location\n      end\n\n      def flash?\n        return false if self.location == :none\n        UIImagePickerController.isFlashAvailableForCameraDevice(camera_device)\n      end\n\n      # @param [Hash] options to open the UIImagePickerController with\n      # the form {\n      #   source_type: :photo_library, :camera, or :saved_photos_album; default :photo_library\n      #   media_types: [] containing :image and/or :movie; default [:image]\n      #   allows_editing: true/false; default false\n      #   animated: true/false; default true\n      #   on_dismiss: lambda; default nil\n      # }\n      #\n      # @param [UIViewController] view controller from which to present the image picker;\n      #   if nil, uses the keyWindow's rootViewController.\n      #\n      # @block for callback. takes one argument.\n      #   - On error or cancelled, is called with a hash {error: BW::Camera::Error::<Type>}\n      #   - On success, is called with a hash with a possible set of keys\n      #   [:media_type, :original_image, :edited_image, :crop_rect, :media_url,\n      #    :reference_url, :media_metadata]\n      #\n      # Example\n      # BW::Camera.picture(source_type: :photo_library, media_types: [:image]) do |result|\n      #   image_view = UIImageView.alloc.initWithImage(result[:original_image])\n      # end\n      def picture(options = {}, presenting_controller = nil, &block)\n        @callback = block\n        @callback.weak! if @callback && BubbleWrap.use_weak_callbacks?\n\n        @options = {\n          allows_editing: false,\n          animated: true,\n          on_dismiss: false,\n          media_types: [:image],\n          dismiss_completed: nil,\n          video_quality: :medium,\n          video_maximum_duration: 600\n        }.merge(options)\n\n        # If we're using Camera.any, by default use photo library\n        if !@options.has_key?(:source_type) and self.location == :none\n          @options[:source_type] = :photo_library\n        # If we're using a real Camera, by default use the camera.\n        elsif !@options.has_key?(:source_type)\n          @options[:source_type] = :camera\n        end\n\n        source_type_readable = options[:source_type]\n        source_type = Constants.get(\"UIImagePickerControllerSourceType\", @options[:source_type])\n        if not Camera.source_type_available?(source_type)\n          error(Error::SOURCE_TYPE_NOT_AVAILABLE) and return\n        end\n\n        media_types = @options[:media_types].collect { |mt| symbol_to_media_type(mt) }\n        if media_types.member? nil\n          error(Error::INVALID_MEDIA_TYPE) and return\n        end\n\n        media_types.each { |media_type|\n          if not Camera.media_type_available?(media_type, for_source_type: source_type)\n            error(Error::MEDIA_TYPE_NOT_AVAILABLE) and return\n          end\n        }\n\n        self.picker.delegate = self\n        self.picker.sourceType = source_type\n        self.picker.mediaTypes = media_types\n        self.picker.allowsEditing = @options[:allows_editing]\n        self.picker.videoQuality = Constants.get(\"UIImagePickerControllerQualityType\", @options[:video_quality])\n        self.picker.videoMaximumDuration = @options[:video_maximum_duration]\n\n        if source_type_readable == :camera && ![:front, :rear].member?(self.location)\n          raise Error::INVALID_CAMERA_LOCATION, \"Can't use camera location #{self.location} with source type :camera\"\n        end\n\n        if source_type_readable == :camera\n          self.picker.cameraDevice = camera_device\n        end\n\n        presenting_controller ||= App.window.rootViewController.presentedViewController # May be nil, but handles use case of container views\n        presenting_controller ||= App.window.rootViewController\n\n        # use popover for iPad (ignore on iPhone)\n        if Device.ipad? and source_type==UIImagePickerControllerSourceTypePhotoLibrary and @popover_in_view\n          @popover = UIPopoverController.alloc.initWithContentViewController(picker)\n          @popover.presentPopoverFromRect(@popover_in_view.bounds, inView:@popover_in_view, permittedArrowDirections:UIPopoverArrowDirectionAny, animated:@options[:animated])\n        else\n          presenting_controller.presentViewController(self.picker, animated:@options[:animated], completion: lambda {})\n        end\n      end\n\n      # iPad popover is dismissed\n      def popoverControllerDidDismissPopover(popoverController)\n        @popover = nil\n      end\n\n      ##########\n      # UIImagePickerControllerDelegate Methods\n      def imagePickerControllerDidCancel(picker)\n        error(Error::CANCELED)\n        dismiss\n      end\n\n      # Takes the default didFinishPickingMediaWithInfo hash,\n      # transforms the keys to be nicer symbols of :this_form\n      # instead of UIImagePickerControllerThisForm, and then sends it\n      # to the callback\n      def imagePickerController(picker, didFinishPickingMediaWithInfo: info)\n        callback_info = {}\n\n        info.keys.each { |k|\n          nice_key = k.gsub(\"UIImagePickerController\", \"\").underscore.to_sym\n          val = info[k]\n          callback_info[nice_key] = val\n        }\n\n        if media_type = callback_info[:media_type]\n          callback_info[:media_type] = media_type_to_symbol(media_type)\n        end\n\n        @callback.call(callback_info)\n        dismiss\n        # iPad popover? close it\n        if @popover\n          @popover.dismissPopoverAnimated(@options[:animated])\n          @popover = nil\n        end\n      end\n\n      ##########\n      # Short Helper Methods\n      def picker\n        @picker_klass ||= UIImagePickerController\n        @picker ||= @picker_klass.alloc.init\n      end\n\n      def dismiss\n        if @options[:on_dismiss]\n          @options[:on_dismiss].call(self.picker)\n          return\n        end\n\n        self.picker.dismissViewControllerAnimated(@options[:animated], completion: @options[:dismiss_completed])\n      end\n\n      # @param [UIImagePickerControllerSourceType] source_type to check\n      def self.source_type_available?(source_type)\n        UIImagePickerController.isSourceTypeAvailable(source_type)\n      end\n\n      # @param [String] (either KUTTypeMovie or KUTTypeImage)\n      # @param [UIImagePickerControllerSourceType]\n      def self.media_type_available?(media_type, for_source_type: source_type)\n        UIImagePickerController.availableMediaTypesForSourceType(source_type).member? media_type\n      end\n\n      private\n      def camera_device\n        Constants.get(\"UIImagePickerControllerCameraDevice\", self.location)\n      end\n\n      # ex media_type_to_symbol(KUTTypeMovie) => :movie\n      def media_type_to_symbol(media_type)\n        MEDIA_TYPE_HASH.invert[media_type]\n      end\n\n      # ex symbol_to_media_type(:movie) => :KUTTypeMovie\n      def symbol_to_media_type(symbol)\n        MEDIA_TYPE_HASH[symbol]\n      end\n\n      def error(type)\n        @callback.call({ error: type })\n      end\n    end\n  end\nend\n::Camera = BubbleWrap::Device::Camera unless defined?(::Camera)\n"
  },
  {
    "path": "motion/core/device/ios/camera_wrapper.rb",
    "content": "module BubbleWrap\n  module Device\n    module CameraWrapper\n\n      module_function\n\n      # The front-facing camera used to capture media\n      # @return [Device::Camera, NilClass] a Camera will be returned if there is a front camera, nil otherwise\n      def front\n        @front ||= BubbleWrap::Device::Camera.front\n      end\n\n      # Verifies that the device running has a front facing camera.\n      # @return [TrueClass, FalseClass] true will be returned if the device has a front facing camera, false otherwise.\n      def front?\n        !!front\n      end\n\n      # The rear-facing camera used to capture media\n      # @return [Device::Camera, NilClass] a Camera will be returned if there is a rear camera, nil otherwise\n      def rear\n        @rear ||= BubbleWrap::Device::Camera.rear\n      end\n\n      # Verifies that the device running has a rear facing camera.\n      # @return [TrueClass, FalseClass] true will be returned if the device has a rear facing camera, false otherwise.\n      def rear?\n        !!rear\n      end\n\n      # A Device::Camera used to capture media; by default it will use the :photo_library source type\n      # See Device::Camera docs for more source type options.\n      # @return [Device::Camera] a Camera will always be returned.\n      def any\n        @any ||= BubbleWrap::Device::Camera.any\n      end\n      # alias for any\n      def photo_library\n        any\n      end\n\n      # Should always return true, since picking images from *some* source is always possible\n      # @return [TrueClass]\n      def any?\n        !!any\n      end\n\n      # Verifies that the device running has a physical camera.\n      # @return [TrueClass, FalseClass] true will be returned if the device has a physical camera, false otherwise.\n      def available?\n        BubbleWrap::Device::Camera.available?\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "motion/core/device/ios/screen.rb",
    "content": "module BubbleWrap\n  module Device\n    module Screen\n\n      module_function\n\n      # Certifies that the device running the app has a Retina display\n      # @return [TrueClass, FalseClass] true will be returned if the device has a Retina display, false otherwise.\n      def retina?(screen=UIScreen.mainScreen)\n        if screen.respondsToSelector('displayLinkWithTarget:selector:') && screen.scale == 2.0\n          true\n        else\n          false\n        end\n      end\n\n      # Figure out the current physical orientation of the device\n      # @return [:portrait, :portrait_upside_down, :landscape_left, :landscape_right, :face_up, :face_down, :unknown]\n      def orientation(device_orientation=UIDevice.currentDevice.orientation, fallback=true)\n        case device_orientation\n        when UIDeviceOrientationPortrait then :portrait\n        when UIDeviceOrientationPortraitUpsideDown then :portrait_upside_down\n        when UIDeviceOrientationLandscapeLeft then :landscape_left\n        when UIDeviceOrientationLandscapeRight then :landscape_right\n        when UIDeviceOrientationFaceUp then :face_up\n        when UIDeviceOrientationFaceDown then :face_down\n        else\n          # In some cases, the accelerometer can't get an accurate read of orientation so we fall back on the orientation of\n          # the status bar.\n          if fallback && (device_orientation != UIApplication.sharedApplication.statusBarOrientation)\n            orientation(UIApplication.sharedApplication.statusBarOrientation)\n          else\n            :unknown\n          end\n        end\n      end\n\n      # Figure out the current orientation of the interface\n      # @return [:portrait, :portrait_upside_down, :landscape_left, :landscape_right]\n      def interface_orientation(device_orientation=UIDevice.currentDevice.orientation, fallback=true)\n        case device_orientation\n        when UIInterfaceOrientationPortrait then :portrait\n        when UIInterfaceOrientationPortraitUpsideDown then :portrait_upside_down\n        when UIInterfaceOrientationLandscapeLeft then :landscape_left\n        when UIInterfaceOrientationLandscapeRight then :landscape_right\n        else\n          # In some cases, the accelerometer can't get an accurate read of orientation so we fall back on the orientation of\n          # the status bar.\n          if fallback && (device_orientation != UIApplication.sharedApplication.statusBarOrientation)\n            orientation(UIApplication.sharedApplication.statusBarOrientation)\n          else\n            :unknown\n          end\n        end\n      end\n\n      # The width of the device's screen.\n      # The real resolution is dependant on the scale\n      # factor (see `retina?`) but the coordinate system\n      # is in non-retina pixels. You can get pixel\n      # accuracy by using half-coordinates.\n      # This is a Float\n      def width\n        UIScreen.mainScreen.bounds.size.width\n      end\n\n      # The height of the device's screen.\n      # The real resolution is dependant on the scale\n      # factor (see `retina?`) but the coordinate system\n      # is in non-retina pixels. You can get pixel\n      # accuracy by using half-coordinates.\n      # This is a Float\n      def height\n        UIScreen.mainScreen.bounds.size.height\n      end\n\n      # The same as `.width` and `.height` but\n      # compensating for screen rotation (which\n      # can do your head in).\n      def width_for_orientation(o=orientation)\n        return height if (o == :landscape_left) || (o == :landscape_right)\n        width\n      end\n\n      # The same as `.width` and `.height` but\n      # compensating for screen rotation (which\n      # can do your head in).\n      def height_for_orientation(o=orientation)\n        return width if (o == :landscape_left) || (o == :landscape_right)\n        height\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "motion/core/device/osx/screen.rb",
    "content": "module BubbleWrap\n  module Device\n    module Screen\n\n      module_function\n\n      # Certifies that the device running the app has a Retina display\n      # @return [TrueClass, FalseClass] true will be returned if the device has a Retina display, false otherwise.\n      def retina?(screen=NSScreen.mainScreen)\n        if screen.respondsToSelector('backingScaleFactor') && screen.backingScaleFactor == 2.0\n          true\n        else\n          false\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "motion/core/device/screen.rb",
    "content": "module BubbleWrap\n  module Device\n    module Screen\n\n    end\n  end\nend\n"
  },
  {
    "path": "motion/core/device.rb",
    "content": "module BubbleWrap\n  module Device\n    module_function\n\n    # Shameless shorthand for accessing BubbleWrap::Screen\n    def screen\n      BubbleWrap::Device::Screen\n    end\n\n    # Delegates to BubbleWrap::Screen.retina?\n    def retina?\n      screen.retina?\n    end\n  end\nend\n::Device = BubbleWrap::Device unless defined?(::Device)\n"
  },
  {
    "path": "motion/core/ios/app.rb",
    "content": "module BubbleWrap\n  module App\n    module_function\n\n    # Opens an url (string or instance of `NSURL`)\n    # in the device's web browser or in the correspondent app for custom schemas\n    # Usage Example:\n    #   App.open_url(\"http://matt.aimonetti.net\")\n    #   App.open_url(\"fb://profile\")\n    def open_url(url)\n      unless url.is_a?(NSURL)\n        url = NSURL.URLWithString(url)\n      end\n      UIApplication.sharedApplication.openURL(url)\n    end\n\n    # Returns whether an app can open a given URL resource (string or instance of `NSURL`)\n    # Useful to check if certain apps are installed before calling to their custom schemas.\n    # Usage Example:\n    #   App.open_url(\"fb://profile\") if App.can_open_url(\"fb://\")\n    def can_open_url(url)\n      unless url.is_a?(NSURL)\n        url = NSURL.URLWithString(url)\n      end\n      UIApplication.sharedApplication.canOpenURL(url)\n    end\n\n    # Displays a UIAlertView.\n    #\n    # title - The title as a String.\n    # args  - The title of the cancel button as a String, or a Hash of options.\n    #         (Default: { cancel_button_title: 'OK' })\n    #         cancel_button_title - The title of the cancel button as a String.\n    #         message             - The main message as a String.\n    # block - Yields the alert object if a block is given, and does so before the alert is shown.\n    #\n    # Returns an instance of BW::UIAlertView\n    def alert(title, *args, &block)\n      options = { cancel_button_title: 'OK' }\n      options.merge!(args.pop) if args.last.is_a?(Hash)\n\n      if args.size > 0 && args.first.is_a?(String)\n        options[:cancel_button_title] = args.shift\n      end\n\n      options[:title]               = title\n      options[:buttons]             = options[:cancel_button_title]\n      options[:cancel_button_index] = 0 # FIXME: alerts don't have \"Cancel\" buttons\n\n      alert = UIAlertView.default(options)\n\n      yield(alert) if block_given?\n\n      alert.show\n      alert\n    end\n\n    # Return application frame\n    def frame\n      UIScreen.mainScreen.applicationFrame\n    end\n\n    # Main Screen bounds. Useful when starting the app\n    def bounds\n      UIScreen.mainScreen.bounds\n    end\n\n    # Application Delegate\n    def delegate\n      UIApplication.sharedApplication.delegate\n    end\n\n    # the Application object.\n    def shared\n      UIApplication.sharedApplication\n    end\n\n    def windows\n      UIApplication.sharedApplication.windows\n    end\n\n    # the Application Window\n    def window\n      normal_windows = App.windows.select { |w|\n        w.windowLevel == UIWindowLevelNormal\n      }\n\n      key_window = normal_windows.select {|w|\n        w == UIApplication.sharedApplication.keyWindow\n      }.first\n\n      key_window || normal_windows.first\n    end\n  end\nend\n"
  },
  {
    "path": "motion/core/ios/device.rb",
    "content": "module BubbleWrap\n  module Device\n    module_function\n\n    # Verifies that the device running the app is an iPhone.\n    # @return [TrueClass, FalseClass] true will be returned if the device is an iPhone, false otherwise.\n    def iphone?(idiom=UIDevice.currentDevice.userInterfaceIdiom)\n      idiom == UIUserInterfaceIdiomPhone\n    end\n\n    # Verifies that the device running the app is an iPad.\n    # @return [TrueClass, FalseClass] true will be returned if the device is an iPad, false otherwise.\n    def ipad?(idiom=UIDevice.currentDevice.userInterfaceIdiom)\n      idiom == UIUserInterfaceIdiomPad\n    end\n\n    # Verifies that the device having a long screen (4 inch iPhone/iPod)\n    # @return [TrueClass, FalseClass] true will be returned if the device is an iPhone/iPod with 4 inche screen, false otherwise.\n    def long_screen?(idiom=UIDevice.currentDevice.userInterfaceIdiom, screen_height=UIScreen.mainScreen.bounds.size.height)\n      iphone?(idiom) && screen_height == 568.0\n    end\n\n    # Use this to make a DSL-style call for picking images\n    # @example Device.camera.front\n    # @return [Device::Camera::CameraWrapper]\n    def camera\n      BubbleWrap::Device::CameraWrapper\n    end\n\n    # Verifies that the device running has a front facing camera.\n    # @return [TrueClass, FalseClass] true will be returned if the device has a front facing camera, false otherwise.\n    def front_camera?(picker=UIImagePickerController)\n      p \"This method (front_camera?) is DEPRECATED. Transition to using Device.camera.front?\"\n      picker.isCameraDeviceAvailable(UIImagePickerControllerCameraDeviceFront)\n    end\n\n    # Verifies that the device running has a rear facing camera.\n    # @return [TrueClass, FalseClass] true will be returned if the device has a rear facing camera, false otherwise.\n    def rear_camera?(picker=UIImagePickerController)\n      p \"This method (rear_camera?) is DEPRECATED. Transition to using Device.camera.rear?\"\n      picker.isCameraDeviceAvailable(UIImagePickerControllerCameraDeviceRear)\n    end\n\n    # Return whether app is being run in simulator\n    # @return [TrueClass, FalseClass] true will be returned if simulator, false otherwise.\n    def simulator?\n      @simulator_state ||= begin\n        if ios_version.to_i >= 9\n          NSBundle.mainBundle.bundlePath !~ /^(\\/private)?\\/var/\n        else\n          !(UIDevice.currentDevice.model =~ /simulator/i).nil?\n        end\n      end\n    end\n\n    def force_touch?\n      if defined?(UIForceTouchCapabilityAvailable) && defined?(UIScreen.mainScreen.traitCollection) && defined?(UIScreen.mainScreen.traitCollection.forceTouchCapability)\n        UIScreen.mainScreen.traitCollection.forceTouchCapability == UIForceTouchCapabilityAvailable\n      else\n        false\n      end\n    end\n\n    # Returns the IOS SDK version currently running (i.e. \"5.1\" or \"6.0\" etc)\n    # @return [String] the IOS SDK version currently running\n    def ios_version\n      UIDevice.currentDevice.systemVersion\n    end\n\n    # Returns an identifier unique to the vendor across the vendors app.\n    # @return [NSUUID]\n    def vendor_identifier\n      UIDevice.currentDevice.identifierForVendor\n    end\n\n    # Delegates to BubbleWrap::Screen.orientation\n    def orientation\n      screen.orientation\n    end\n\n    # Delegates to BubbleWrap::Screen.interface_orientation\n    def interface_orientation\n      screen.interface_orientation\n    end\n  end\nend\n"
  },
  {
    "path": "motion/core/ios/ns_index_path.rb",
    "content": "module NSIndexPathWrap\n\n  def +(aNumber)\n    self.class.indexPathForRow(row+aNumber, inSection:section)\n  end\n\n  def -(aNumber)\n    self.class.indexPathForRow(row-aNumber, inSection:section)\n  end\n\nend\n"
  },
  {
    "path": "motion/core/json.rb",
    "content": "module BubbleWrap\n\n  # Handles JSON encoding and decoding in a similar way Ruby 1.9 does.\n  module JSON\n\n    class ParserError < StandardError; end\n\n    # Parses a string or data object and converts it in data structure.\n    #\n    # @param [String, NSData] str_data the string or data to serialize.\n    # @raise [ParserError] If the parsing of the passed string/data isn't valid.\n    # @return [Hash, Array, NilClass] the converted data structure, nil if the incoming string isn't valid.\n    #\n    # TODO: support options like the C Ruby module does\n    def self.parse(str_data, &block)\n      return nil unless str_data\n      data = str_data.respond_to?('dataUsingEncoding:') ? str_data.dataUsingEncoding(NSUTF8StringEncoding) : str_data\n      opts = NSJSONReadingMutableContainers | NSJSONReadingMutableLeaves | NSJSONReadingAllowFragments\n      error = Pointer.new(:id)\n      obj = NSJSONSerialization.JSONObjectWithData(data, options:opts, error:error)\n      raise ParserError, error[0].description if error[0]\n      if block_given?\n        yield obj\n      else\n        obj\n      end\n\n    end\n\n    def self.generate(obj)\n      NSJSONSerialization.dataWithJSONObject(obj, options:0, error:nil).to_str\n    end\n\n  end\nend\n"
  },
  {
    "path": "motion/core/kvo.rb",
    "content": "# Usage example:\n#\n# class ExampleViewController < UIViewController\n#     include BubbleWrap::KVO\n#\n#     def viewDidLoad\n#         @label = UILabel.alloc.initWithFrame [[20,20],[280,44]]\n#         @label.text = \"\"\n#         view.addSubview @label\n#\n#         observe(@label, :text) do |old_value, new_value|\n#             puts \"Changed #{old_value} to #{new_value}\"\n#         end\n#     end\n#\n# end\n#\n# @see https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html#//apple_ref/doc/uid/10000177i\nmodule BubbleWrap\n  module KVO\n    class Registry\n      COLLECTION_OPERATIONS = [ NSKeyValueChangeInsertion, NSKeyValueChangeRemoval, NSKeyValueChangeReplacement ]\n      OPTION_MAP = {\n        new: NSKeyValueChangeNewKey,\n        old: NSKeyValueChangeOldKey\n      }\n\n      attr_reader :callbacks, :keys\n\n      def initialize(value_keys = [:old, :new])\n        @keys = value_keys.inject([]) do |acc, key|\n          value_change_key = OPTION_MAP[key]\n\n          if value_change_key.nil?\n            raise RuntimeError, \"Unknown value change key #{key}. Possible keys: #{OPTION_MAP.keys}\"\n          end\n\n          acc << value_change_key\n        end\n\n        @callbacks = Hash.new do |hash, key|\n          hash[key] = Hash.new do |subhash, subkey|\n            subhash[subkey] = Array.new\n          end\n        end\n      end\n\n      def add(target, key_path, &block)\n        return if target.nil? || key_path.nil? || block.nil?\n\n        block.weak! if BubbleWrap.use_weak_callbacks?\n\n        callbacks[target][key_path.to_s] << block\n      end\n\n      def remove(target, key_path)\n        return if target.nil? || key_path.nil?\n\n        key_path = key_path.to_s\n\n        callbacks[target].delete(key_path)\n\n        # If there no key_paths left for target, remove the target key\n        if callbacks[target].empty?\n          callbacks.delete(target)\n        end\n      end\n\n      def registered?(target, key_path)\n        callbacks[target].has_key? key_path.to_s\n      end\n\n      def remove_all\n        callbacks.clear\n      end\n\n      def each_key_path\n        callbacks.each do |target, key_paths|\n          key_paths.each_key do |key_path|\n            yield target, key_path\n          end\n        end\n      end\n\n      private\n\n      def observeValueForKeyPath(key_path, ofObject: target, change: change, context: context)\n        key_paths = callbacks[target] || {}\n        blocks = key_paths[key_path] || []\n\n        args = change.values_at(*keys)\n        args << key_path\n\n        blocks.each do |block|\n          block.call(*args)\n        end\n      end\n    end\n\n    DEFAULT_OPTIONS = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld\n    IMMIDEATE_OPTIONS = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionInitial\n\n    def observe(target = self, key_paths, &block)\n      key_paths = [key_paths].flatten\n\n      key_paths.each do |key_path|\n        if not observers_registry.registered?(target, key_path)\n          target.addObserver(observers_registry, forKeyPath:key_path, options:DEFAULT_OPTIONS, context:nil)\n        end\n\n        # Add block even if observer is registed, so multiplie blocks can be invoked.\n        observers_registry.add(target, key_path, &block)\n      end\n    end\n\n    def observe!(target = self, key_paths, &block)\n      key_paths = [key_paths].flatten\n\n      key_paths.each do |key_path|\n        registered = immediate_observers_registry.registered?(target, key_path)\n\n        immediate_observers_registry.add(target, key_path, &block)\n\n        # We need to first register the block, and then call addObserver, because\n        # observeValueForKeyPath will fire immedeately.\n        if not registered\n          target.addObserver(immediate_observers_registry, forKeyPath:key_path, options: IMMIDEATE_OPTIONS, context:nil)\n        end\n      end\n    end\n\n    def unobserve(target = self, key_paths)\n      remove_from_registry_if_exists(target, key_paths, observers_registry)\n    end\n\n    def unobserve!(target = self, key_paths)\n      remove_from_registry_if_exists(target, key_paths, immediate_observers_registry)\n    end\n\n    def unobserve_all\n      observers_registry.each_key_path do |target, key_path|\n        target.removeObserver(observers_registry, forKeyPath:key_path)\n      end\n\n      immediate_observers_registry.each_key_path do |target, key_path|\n        target.removeObserver(immediate_observers_registry, forKeyPath:key_path)\n      end\n\n      observers_registry.remove_all\n      immediate_observers_registry.remove_all\n    end\n\n    private\n    def observers_registry\n      @observers_registry ||= Registry.new\n    end\n\n    def immediate_observers_registry\n      @immediate_observers_registry ||= Registry.new([:new])\n    end\n\n    def remove_from_registry_if_exists(target, key_paths, registry)\n      key_paths = [key_paths].flatten\n\n      key_paths.each do |key_path|\n        if registry.registered?(target, key_path)\n          target.removeObserver(registry, forKeyPath:key_path)\n          registry.remove(target, key_path)\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "motion/core/ns_index_path.rb",
    "content": "module NSIndexPathWrap\n\n  # Gives access to an index at a given position.\n  # @param [Integer] position to use to fetch the index\n  # @return [Integer] the index for the given position\n  def [](position)\n    raise ArgumentError unless position.is_a?(Integer)\n    indexAtPosition(position)\n  end\n\n  # Provides an iterator taking a block following the common Ruby idiom.\n  # @param [Block]\n  # @return [NSIndexPath] the iterated object itself\n  def each\n    i = 0\n    until i == self.length\n      yield self.indexAtPosition(i)\n      i += 1\n    end\n    self\n  end\n\nend\n"
  },
  {
    "path": "motion/core/ns_notification_center.rb",
    "content": "class NSNotificationCenter\n  def observers\n    @observers ||= []\n  end\n\n  def observe(name, object=nil, &proc)\n    proc.weak! if proc && BubbleWrap.use_weak_callbacks?\n    observer = self.addObserverForName(name, object:object, queue:NSOperationQueue.mainQueue, usingBlock:proc)\n    observers << observer\n    observer\n  end\n\n  def unobserve(observer)\n    return unless observers.include?(observer)\n    removeObserver(observer)\n    observers.delete(observer)\n  end\n\n  def post(name, object=nil, info=nil)\n    self.postNotificationName(name, object: object, userInfo: info)\n  end\nend\n"
  },
  {
    "path": "motion/core/ns_url_request.rb",
    "content": "class NSURLRequest\n\n  # Provides a to_s method so we can use inspect in instances and get\n  # valuable information.\n  def to_s\n    \"#<#{self.class}:#{self.object_id} - url: #{self.URL.description},\n    headers: #{self.allHTTPHeaderFields.inspect},\n    cache policy: #{self.cachePolicy}, Pipelining: #{self.HTTPShouldUsePipelining}, main doc url: #{mainDocumentURL},\\\n    timeout: #{self.timeoutInterval}, network service type: #{self.networkServiceType} >\"\n  end\n\nend\n"
  },
  {
    "path": "motion/core/ns_user_defaults.rb",
    "content": "# Reopens the NSUserDefaults class to add Array like accessors\n# @see https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/nsuserdefaults_Class/Reference/Reference.html\nclass NSUserDefaults\n\n  # Retrieves the object for the passed key\n  def [](key)\n    self.objectForKey(key.to_s)\n  end\n\n  # Sets the value for a given key and save it right away.\n  def []=(key, val)\n    self.setObject(val, forKey: key.to_s)\n    self.synchronize\n  end\nend\n"
  },
  {
    "path": "motion/core/osx/app.rb",
    "content": "module BubbleWrap\n  module App\n    module_function\n\n    # Opens an url (string or instance of `NSURL`)\n    # in the default web browser\n    # Usage Example:\n    #   App.open_url(\"http://www.rubymotion.com\")\n    def open_url(url)\n      url = NSURL.URLWithString(url) unless url.is_a?(NSURL)\n      NSWorkspace.sharedWorkspace.openURL(url)\n    end\n\n    # Application Delegate\n    def delegate\n      shared.delegate\n    end\n\n    # the Application object.\n    def shared\n      NSApplication.sharedApplication\n    end\n\n  end\nend\n"
  },
  {
    "path": "motion/core/osx/device.rb",
    "content": "module BubbleWrap\n  module Device\n    module_function\n\n  end\nend\n"
  },
  {
    "path": "motion/core/persistence.rb",
    "content": "# Persistence module built on top of NSUserDefaults\nmodule BubbleWrap\n  module Persistence\n    module_function\n\n    def app_key\n      @app_key ||= BubbleWrap::App.identifier\n    end\n\n    def []=(key, value)\n      storage.setObject(value, forKey: storage_key(key))\n      storage.synchronize\n    end\n\n    def [](key)\n      value = storage.objectForKey storage_key(key)\n\n      # RubyMotion currently has a bug where the strings returned from\n      # standardUserDefaults are missing some methods (e.g. to_data).\n      # And because the returned object is slightly different than a normal\n      # String, we can't just use `value.is_a?(String)`\n      value.class.to_s == 'String' ? value.dup : value\n    end\n\n    def merge(values)\n      values.each do |key, value|\n        storage.setObject(value, forKey: storage_key(key))\n      end\n      storage.synchronize\n    end\n\n    def delete(key)\n      value = storage.objectForKey storage_key(key)\n      storage.removeObjectForKey(storage_key(key))\n      storage.synchronize\n      value\n    end\n\n    def storage\n      NSUserDefaults.standardUserDefaults\n    end\n\n    def storage_key(key)\n      \"#{app_key}_#{key}\"\n    end\n\n    def all\n      hash = storage.dictionaryRepresentation.select{|k,v| k.start_with?(app_key) }\n      new_hash = {}\n      hash.each do |k,v|\n        new_hash[k.sub(\"#{app_key}_\", '')] = v\n      end\n      new_hash\n    end\n  end\n\nend\n::Persistence = BubbleWrap::Persistence unless defined?(::Persistence)\n"
  },
  {
    "path": "motion/core/pollute.rb",
    "content": "[\n  [NSIndexPath, NSIndexPathWrap]\n].each do |base, wrapper|\n    base.send(:include, wrapper)\nend\n"
  },
  {
    "path": "motion/core/string.rb",
    "content": "module BubbleWrap\n  # This module contains simplified version of the `camelize` and\n  # `underscore` methods from ActiveSupport, since these are such\n  # common operations when dealing with the Cocoa API.\n  module String\n\n    # Convert 'snake_case' into 'CamelCase'\n    def camelize(uppercase_first_letter = true)\n      string = self.dup\n      string.gsub!(/(?:_|(\\/))([a-z\\d]*)/i) do\n        new_word = $2.downcase\n        new_word[0] = new_word[0].upcase\n        new_word = \"/#{new_word}\" if $1 == '/'\n        new_word\n      end\n      if uppercase_first_letter && uppercase_first_letter != :lower\n        string[0] = string[0].upcase\n      else\n        string[0] = string[0].downcase\n      end\n      string.gsub!('/', '::')\n      string\n    end\n\n    # Convert 'CamelCase' into 'snake_case'\n    def underscore\n      word = self.dup\n      word.gsub!(/::/, '/')\n      word.gsub!(/([A-Z\\d]+)([A-Z][a-z])/,'\\1_\\2')\n      word.gsub!(/([a-z\\d])([A-Z])/,'\\1_\\2')\n      word.tr!(\"-\", \"_\")\n      word.downcase!\n      word\n    end\n\n    def to_url_encoded(encoding = nil, legacy = false)\n      if legacy\n        stringByAddingPercentEscapesUsingEncoding(encoding || NSUTF8StringEncoding)\n      else\n        encoding ||= KCFStringEncodingUTF8\n        encoding = CFStringConvertNSStringEncodingToEncoding(encoding) unless CFStringIsEncodingAvailable(encoding)\n        CFURLCreateStringByAddingPercentEscapes(nil, self, nil, \"!*'();:@&=+$,/?%#[]\", encoding)\n      end\n    end\n\n    def to_url_decoded(encoding = nil, legacy = false)\n      if legacy\n        stringByReplacingPercentEscapesUsingEncoding(encoding || NSUTF8StringEncoding)\n      else\n        if encoding\n          encoding = CFStringConvertNSStringEncodingToEncoding(encoding) unless CFStringIsEncodingAvailable(encoding)\n          CFURLCreateStringByReplacingPercentEscapesUsingEncoding(nil, self, nil, encoding)\n        else\n          CFURLCreateStringByReplacingPercentEscapes(nil, self, nil)\n        end\n      end\n    end\n\n    def to_encoded_data(encoding = NSUTF8StringEncoding)\n      dataUsingEncoding encoding\n    end\n\n    def to_color\n      # First check if it is a color keyword\n      keyword_selector = \"#{self.camelize(:lower)}Color\"\n      color_klass = App.osx? ? NSColor : UIColor\n      return color_klass.send(keyword_selector) if color_klass.respond_to? keyword_selector\n\n      # Next attempt to convert from hex\n      hex_color = self.gsub(\"#\", \"\")\n      case hex_color.size\n        when 3\n          colors = hex_color.scan(%r{[0-9A-Fa-f]}).map!{ |el| (el * 2).to_i(16) }\n        when 6\n          colors = hex_color.scan(%r<[0-9A-Fa-f]{2}>).map!{ |el| el.to_i(16) }\n        when 8\n          colors = hex_color.scan(%r<[0-9A-Fa-f]{2}>).map!{ |el| el.to_i(16) }\n        else\n          raise ArgumentError\n      end\n      if colors.size == 3\n        BubbleWrap.rgb_color(colors[0], colors[1], colors[2])\n      elsif colors.size == 4\n        BubbleWrap.rgba_color(colors[1], colors[2], colors[3], colors[0])\n      else\n        raise ArgumentError\n      end\n    end\n\n  end\nend\n\nNSString.send(:include, BubbleWrap::String)\n"
  },
  {
    "path": "motion/core/time.rb",
    "content": "class Time\n\n  def self.iso8601(time)\n    if time.include?(\".\")\n      # Fractional Seconds\n      if time.include?('Z')\n        iso8601_with_fractional_seconds(time)\n      else\n        iso8601_with_fractional_seconds_and_timesone(time)\n      end\n    else\n      # Non Fractional Seconds\n      if time.include?('Z')\n        iso8601_zulu(time)\n      else\n        iso8601_with_timezone(time)\n      end\n    end\n  end\n\n  def self.iso8601_zulu(time)\n    cached_date_formatter(\"yyyy-MM-dd'T'HH:mm:ss'Z'\").\n      dateFromString(time)\n  end\n\n  def self.iso8601_with_timezone(time)\n    cached_date_formatter(\"yyyy-MM-dd'T'HH:mm:ssZZZZZ\").\n      dateFromString(time)\n  end\n\n  def self.iso8601_with_fractional_seconds(time)\n    cached_date_formatter(\"yyyy-MM-dd'T'HH:mm:ss.SSS'Z'\").\n      dateFromString(time)\n  end\n\n  def self.iso8601_with_fractional_seconds_and_timesone(time)\n    cached_date_formatter(\"yyyy-MM-dd'T'HH:mm:ss.SSSZZZZZ\").\n      dateFromString(time)\n  end\n\n  private\n\n  def self.cached_date_formatter(dateFormat)\n    Thread.current[:date_formatters] ||= {}\n    Thread.current[:date_formatters][dateFormat] ||=\n      NSDateFormatter.alloc.init.tap do |formatter|\n        formatter.dateFormat = dateFormat\n        formatter.timeZone   = NSTimeZone.timeZoneWithAbbreviation \"UTC\"\n      end\n  end\n\nend\n"
  },
  {
    "path": "motion/core.rb",
    "content": "module BubbleWrap\n  module_function\n\n  # @return [UIcolor]\n  def rgb_color(r,g,b)\n    rgba_color(r,g,b,1)\n  end\n\n  # @return [UIcolor]\n  def rgba_color(r,g,b,a)\n    r,g,b = [r,g,b].map { |i| i / 255.0}\n    if a > 1.0\n      a = a / 255.0\n    end\n    if App.osx?\n      NSColor.colorWithDeviceRed(r, green: g, blue: b, alpha: a)\n    else\n      UIColor.colorWithRed(r, green: g, blue:b, alpha:a)\n    end\n  end\n\n  def localized_string(key, value=nil)\n    NSBundle.mainBundle.localizedStringForKey(key, value:value, table:nil)\n  end\n\n  # I had issues with #p on the device, this is a temporary workaround\n  def p(arg)\n    NSLog arg.inspect\n  end\n\n  def create_uuid\n    uuid = CFUUIDCreate(nil)\n    CFUUIDCreateString(nil, uuid)\n  end\n\nend\n"
  },
  {
    "path": "motion/font/font.rb",
    "content": "module BubbleWrap\n  module Font\n    module_function\n\n    def bold(size = nil)\n      Font.new(:bold, size)\n    end\n\n    def system(size = nil)\n      Font.new(:system, size)\n    end\n\n    def italic(size = nil)\n      Font.new(:italic, size)\n    end\n\n    # Example\n    # Font.new(<# UIFont >)\n    # Font.new(\"Helvetica\")\n    # Font.new(\"Helvetica\", 12)\n    # Font.new(\"Helvetica\", size: 12)\n    # Font.new(name: \"Helvetica\", size: 12)\n    def new(params = {}, *args)\n      if params.is_a?(UIFont)\n        return params\n      end\n      _font = nil\n\n      if params.is_a?(NSString)\n        params = {name: params}\n      end\n\n      if args && !args.empty?\n        case args[0]\n        when NSDictionary\n          params.merge!(args[0])\n        else\n          params.merge!({size: args[0]})\n        end\n      end\n      params[:size] ||= UIFont.systemFontSize\n\n      case params[:name].to_sym\n      when :system\n        _font = UIFont.systemFontOfSize(params[:size].to_f)\n      when :bold\n        _font = UIFont.boldSystemFontOfSize(params[:size].to_f)\n      when :italic\n        _font = UIFont.italicSystemFontOfSize(params[:size].to_f)\n      else\n        begin\n          _font = UIFont.fontWithName(params[:name], size: params[:size])\n        rescue\n        end\n      end\n\n      if !_font\n        raise \"Invalid font for parameters: #{params.inspect} args #{args.inspect}\"\n      end\n\n      _font\n    end\n\n    class << self\n      alias_method :named, :new\n    end\n\n    # I.e. for UINavigationBar#titleTextAttributes\n    def attributes(params = {})\n      _attributes = {}\n\n      _attributes[UITextAttributeFont] = Font.new(params[:font]) if params[:font]\n      _attributes[UITextAttributeTextColor] = params[:color].to_color if params[:color]\n      _attributes[UITextAttributeTextShadowColor] = params[:shadow_color].to_color if params[:shadow_color]\n      _attributes[UITextAttributeTextShadowOffset] = begin\n        x = params[:shadow_offset][:x]\n        y = params[:shadow_offset][:y]\n        offset = UIOffsetMake(x,y)\n        NSValue.valueWithUIOffset(offset)\n      end if params[:shadow_offset]\n\n      _attributes\n    end\n  end\nend\n"
  },
  {
    "path": "motion/ios/8/location_constants.rb",
    "content": "module BW\n  # New additions to CLAuthorizationStatus in ios8\n  # see: https://developer.apple.com/library/prerelease/ios/documentation/CoreLocation/Reference/CLLocationManager_Class/index.html#//apple_ref/c/tdef/CLAuthorizationStatus\n  Constants.register(\n    KCLAuthorizationStatusAuthorized,\n    KCLAuthorizationStatusAuthorizedWhenInUse,\n    KCLAuthorizationStatusAuthorizedAlways\n  )\n\n  module Location\n    module_function\n\n    def authorized?\n      [\n        BW::Constants.get(\"KCLAuthorizationStatus\", :authorized),\n        BW::Constants.get(\"KCLAuthorizationStatus\", :authorized_always),\n        BW::Constants.get(\"KCLAuthorizationStatus\", :authorized_when_in_use)\n      ].include?(CLLocationManager.authorizationStatus)\n    end\n  end\nend\n"
  },
  {
    "path": "motion/location/location.rb",
    "content": "# Provides a nice DSL for interacting with the standard\n# CLLocationManager\n#\nmodule BubbleWrap\n  module CLLocationWrap\n\n    def latitude\n      self.coordinate.latitude\n    end\n\n    def longitude\n      self.coordinate.longitude\n    end\n  end\n\n  module Location\n    module Error\n      DISABLED=0\n      PERMISSION_DENIED=1\n      NETWORK_FAILURE=2\n      LOCATION_UNKNOWN=3\n    end\n\n    Constants.register KCLLocationAccuracyBestForNavigation, KCLLocationAccuracyBest,\n        KCLLocationAccuracyNearestTenMeters, KCLLocationAccuracyHundredMeters,\n        KCLLocationAccuracyKilometer, KCLLocationAccuracyThreeKilometers\n\n    module_function\n    # Start getting locations\n    # @param [Hash] options = {\n    #   authorization_type: :always/:when_in_use to trigger the type of authorization you want\n    #     default == uses :always\n    #   significant: true/false; whether to listen for significant location changes or\n    #     all location changes (see Apple docs for info); default == false\n    #   distance_filter:  minimum change in distance to be updated about, in meters;\n    #     default == uses KCLDistanceFilterNone,\n    #   desired_accuracy: minimum accuracy for updates to arrive;\n    #     any of :best_for_navigation, :best, :nearest_ten_meters,\n    #     :hundred_meters, :kilometer, or :three_kilometers; default == :best\n    #   purpose: string to display when the system asks user for location,\n    #   retries: if location cant be found. how many errors do we retry; default == 5\n    #   calibration: if the OS should display the heading calibration to the user; default == false\n    # }\n    # @block for callback. takes one argument, `result`.\n    #   - On error or cancelled, is called with a hash {error: BW::Location::Error::<Type>}\n    #   - On success, is called with a hash {to: #<CLLocation>, from: #<CLLocation>, previous: [#<CLLocation>,...]}\n    #   -- :previous will return an Array of CLLocation objects, ordered from oldest to newest, excluding the \n    #        locations :to and :from, returning an empty Array if no additional locations were provided\n    #\n    # Example\n    # BW::Location.get(distance_filter: 10, desired_accuracy: :nearest_ten_meters) do |result|\n    #   result[:to].class == CLLocation\n    #   result[:from].class == CLLocation\n    #   result[:previous].class == NSArray<CLLocation>\n    #   p \"Lat #{result[:to].latitude}, Long #{result[:to].longitude}\"\n    # end\n    def get(options = {}, &block)\n      @callback = block\n      @callback.weak! if @callback && BubbleWrap.use_weak_callbacks?\n      @options = {\n        authorization_type: :always,\n        significant: false,\n        distance_filter: KCLDistanceFilterNone,\n        desired_accuracy: KCLLocationAccuracyBest,\n        retries: 5,\n        once: false,\n        calibration: false\n      }.merge(options)\n\n      @options[:significant] = false if @options[:significant].nil?\n      @retries = 0\n      @from_location = nil\n\n      if not enabled?\n        error(Error::DISABLED) and return\n      end\n\n      self.location_manager\n\n      if self.location_manager.respondsToSelector('requestAlwaysAuthorization')\n        @options[:authorization_type] == :always ? self.location_manager.requestAlwaysAuthorization : self.location_manager.requestWhenInUseAuthorization\n      end\n\n\n      self.location_manager.distanceFilter = @options[:distance_filter]\n      self.location_manager.desiredAccuracy = Constants.get(\"KCLLocationAccuracy\", @options[:desired_accuracy])\n      self.location_manager.purpose = @options[:purpose] if @options[:purpose]\n\n      @initialized = true\n      start\n    end\n\n    def get_significant(options = {}, &block)\n      get(options.merge(significant: true), &block)\n    end\n\n    # Get the first returned location based on your options\n    # @param [Hash] options = {\n    #   significant: true/false; whether to listen for significant location changes or\n    #     all location changes (see Apple docs for info); default == false\n    #   distance_filter:  minimum change in distance to be updated about, in meters;\n    #     default == uses KCLDistanceFilterNone,\n    #   desired_accuracy: minimum accuracy for updates to arrive;\n    #     any of :best_for_navigation, :best, :nearest_ten_meters,\n    #     :hundred_meters, :kilometer, or :three_kilometers; default == :best\n    #   purpose: string to display when the system asks user for location,\n    #   retries: if location cant be found. how many errors do we retry; default == 5\n    # }\n    # @block for callback. takes one argument, `result`.\n    #   - On error or cancelled, is called with a hash {error: BW::Location::Error::<Type>}\n    #   - On success, it returns a CLLocation\n    #\n    #\n    # Example\n    # BW::Location.get_once(desired_accuracy: :three_kilometers, purpose: 'We need to use your GPS to show you how fun RM is') do |result|\n    #   if result.is_a?(CLLocation)\n    #     p \"Lat #{result.latitude}, Long #{result.longitude}\"\n    #   else\n    #     p \"ERROR: #{result[:error]\"\n    #   end\n    # end\n    def get_once(options = {}, &block)\n      get(options.merge(once: true), &block)\n    end\n\n    def get_compass(options = {}, &block)\n      get(options.merge(compass: true), &block)\n    end\n\n    def get_compass_once(options = {}, &block)\n      get_compass(options.merge(once: true), &block)\n    end\n\n    # Start getting locations\n    def start\n      return unless initialized?\n      if @options[:significant]\n        self.location_manager.startMonitoringSignificantLocationChanges\n      elsif @options[:compass]\n        self.location_manager.startUpdatingHeading\n      else\n        self.location_manager.startUpdatingLocation\n      end\n    end\n\n    # Stop getting locations\n    def stop\n      return unless @options\n      if @options[:significant]\n        self.location_manager.stopMonitoringSignificantLocationChanges\n      elsif @options[:compass]\n        self.location_manager.stopUpdatingHeading\n      else\n        self.location_manager.stopUpdatingLocation\n      end\n    end\n\n    def location_manager\n      @location_manager ||= CLLocationManager.alloc.init\n      @location_manager.delegate ||= self\n      @location_manager\n    end\n\n    # returns true/false whether services, or limited services, are enabled for the _device_\n    def enabled?\n      CLLocationManager.locationServicesEnabled\n    end\n\n    # returns true/false if CLLocationManager has been initialized with the provided or default options\n    def initialized?\n      @initialized ||= false\n    end\n\n    # returns true/false whether services are enabled for the _app_\n    def authorized?\n      [\n        BW::Constants.get(\"KCLAuthorizationStatus\", :authorized)\n      ].include?(CLLocationManager.authorizationStatus)\n    end\n\n    def error(type)\n      @callback && @callback.call({ error: type })\n      @callback = nil\n      self.location_manager.stopUpdatingLocation\n    end\n\n    ##########\n    # CLLocationManagerDelegate Methods\n    def locationManager(manager, didUpdateLocations:locations)\n      if @options[:once]\n        @callback && @callback.call(locations.last)\n        @callback = proc { |result| }\n        stop\n      else\n        size = locations.count\n        result = {to: locations.last, \n          from: ( (size > 1) ? locations.last(2).first : @from_location ), \n          previous: ( (size > 2) ? locations.first(size - 2) : [] )\n        }\n        @from_location = result[:to]\n        @callback && @callback.call(result)\n      end\n    end\n\n    def locationManager(manager, didUpdateHeading:newHeading)\n      heading = {\n        magnetic_heading: newHeading.magneticHeading,\n        true_heading: newHeading.trueHeading,\n        accuracy: newHeading.headingAccuracy,\n        timestamp: newHeading.timestamp,\n      }\n\n      if @options[:once]\n        @callback && @callback.call(heading)\n        @callback = proc { |result| }\n        stop\n      else\n        @callback && @callback.call(heading)\n      end\n    end\n\n    def locationManager(manager, didFailWithError:error)\n      if error.domain == KCLErrorDomain\n        case error.code\n        when KCLErrorDenied\n          error(Error::PERMISSION_DENIED)\n        when KCLErrorLocationUnknown\n          # Docs specify that this is a temporary error,\n          # so we stop/start updating to try again.\n          @retries += 1\n          if @retries > @options[:retries]\n            error(Error::LOCATION_UNKNOWN)\n          else\n            stop\n            start\n          end\n        when KCLErrorNetwork\n          error(Error::NETWORK_FAILURE)\n        end\n      end\n    end\n\n    def locationManager(manager, didChangeAuthorizationStatus:status)\n      case status\n      when KCLAuthorizationStatusRestricted\n        error(Error::PERMISSION_DENIED)\n      when KCLAuthorizationStatusDenied\n        error(Error::PERMISSION_DENIED)\n      end\n    end\n\n    def locationManagerShouldDisplayHeadingCalibration(manager)\n      @options[:calibration] ? @options[:calibration] : false\n    end\n  end\nend\n::Location = BubbleWrap::Location unless defined?(::Location)\n"
  },
  {
    "path": "motion/location/pollute.rb",
    "content": "[\n  [CLLocation, BubbleWrap::CLLocationWrap],\n].each do |base, wrapper|\n    base.send(:include, wrapper)\nend\n"
  },
  {
    "path": "motion/mail/mail.rb",
    "content": "module BubbleWrap\n  module Mail\n\n    module_function\n\n    # Base method to create your in-app mail\n    # ---------------------------------------\n    # EX\n    #   BW::Mail.compose(\n    #     delegate: self, # optional, will use root view controller by default\n    #     to: [ \"tom@example.com\" ],\n    #     cc: [ \"itchy@example.com\", \"scratchy@example.com\" ],\n    #     bcc: [ \"jerry@example.com\" ],\n    #     html: false,\n    #     subject: \"My Subject\",\n    #     message: \"This is my message. It isn't very long.\",\n    #     animated: false\n    #   ) do |result, error|\n    #     result.sent?      # => boolean\n    #     result.canceled?  # => boolean\n    #     result.saved?     # => boolean\n    #     result.failed?    # => boolean\n    #     error             # => NSError\n    #   end\n    def compose(options = {}, &callback)\n      options = {\n        delegate: App.window.rootViewController,\n        animated: true,\n        html: false,\n        to: [],\n        cc: [],\n        bcc: [],\n        subject: 'Contact'\n      }.merge(options)\n\n      @delegate = options[:delegate]\n      @mailer_is_animated = options[:animated]\n      @callback = callback\n      @callback.weak! if @callback && BubbleWrap.use_weak_callbacks?\n\n      @mail_controller = create_mail_controller(options)\n\n      @delegate.presentViewController(@mail_controller, animated: @mailer_is_animated, completion: options[:completion])\n    end\n\n    def create_mail_controller(options = {})\n      unless can_send_mail?\n        controller = UIAlertController.alertControllerWithTitle(\"Email\", message:\"Cannot compose an email. Please run on device.\", preferredStyle:UIAlertControllerStyleAlert)\n        controller.addAction(UIAlertAction.actionWithTitle:\"OK\",style:UIAlertActionStyleDefault, handler:@callback)\n        return controller\n      end\n\n      mail_controller = MFMailComposeViewController.alloc.init\n\n      mail_controller.mailComposeDelegate = self\n      mail_controller.setToRecipients(Array(options[:to]))\n      mail_controller.setCcRecipients(Array(options[:cc]))\n      mail_controller.setBccRecipients(Array(options[:bcc]))\n      mail_controller.setSubject(options[:subject])\n      mail_controller.setMessageBody(options[:message], isHTML: !!options[:html])\n\n      mail_controller\n    end\n\n    def can_send_mail?\n      !!MFMailComposeViewController.canSendMail\n    end\n\n    # Event when the MFMailComposeViewController is closed\n    # -------------------------------------------------------------\n    # the callback is fired if it was present in the constructor\n\n    def mailComposeController(controller, didFinishWithResult: result, error: error)\n      @delegate.dismissModalViewControllerAnimated(@mailer_is_animated)\n      @callback.call Result.new(result, error) if @callback\n    end\n  end\nend\n"
  },
  {
    "path": "motion/mail/result.rb",
    "content": "module BubbleWrap\n  module Mail\n    class Result\n      attr_accessor :result, :error\n\n      def initialize(result, error)\n        self.result = result\n        self.error = error\n      end\n\n      def sent?\n        self.result == MFMailComposeResultSent\n      end\n\n      def canceled?\n        self.result == MFMailComposeResultCancelled\n      end\n\n      def saved?\n        self.result == MFMailComposeResultSaved\n      end\n\n      def failed?\n        self.result == MFMailComposeResultFailed || self.error\n      end\n\n    end\n  end\nend\n"
  },
  {
    "path": "motion/media/media.rb",
    "content": "module BubbleWrap\n  module Media\n    module_function\n\n    def play_modal(*args, &block)\n      Media::Player.new.retain.send(:play_modal, *args, &block)\n    end\n\n    def play(*args, &block)\n      Media::Player.new.retain.send(:play, *args, &block)\n    end\n  end\nend\n\n::Media = BubbleWrap::Media unless defined?(::Media)\n"
  },
  {
    "path": "motion/media/player.rb",
    "content": "module BubbleWrap\n  module Media\n    module Error\n      class InvalidPlayerType < StandardError; end\n      class NilPlayerCallback < StandardError; end\n    end\n\n    class Player\n      attr_reader :media_player\n      ############\n      # Playing Media\n\n      # Plays media in the system-default modal controller\n      # Takes same parameters as #play\n      # NOTE: If you don't supply a :controller option,\n      # the rootViewController will be used.\n      def play_modal(content_url, options = {})\n        play(content_url, options.merge(modal: true))\n      end\n\n      # @param [String, NSURL] content_url is either a local or remote string\n      #   for the location of the media you're playing.\n      #   NOTE: if you're playing a remote file, your server needs to support\n      #   range requests for that URL.\n      #\n      # @param [Hash] options to open the MPMoviePlayerController with\n      # the form {\n      #   ### These are properties of MPMoviePlayerController\n      #   allows_air_play: true/false; default false,\n      #   control_style: [MPMovieControlStyle]; default MPMovieControlStyleDefault,\n      #   end_playback_time: [Integer] end time (in seconds) for media; default is -1,\n      #   initial_playback_time: [Integer] start time (in seconds) for media; default is -1,\n      #   movie_source_type: [MPMovieSourceType] a \"hint\" so the player knows how to load the data type;\n      #     either MPMovieSourceTypeFile or MPMovieSourceTypeStreaming; default is MPMovieSourceTypeUnknown\n      #     which may delay playback,\n      #   repeat_mode: [MPMovieRepeatMode] how the player repeats at the end of playback; defautl is\n      #     MPMovieRepeatModeNone\n      #   scaling_mode: [MPMovieScalingMode] scaling mode for movies; default is MPMovieScalingModeAspectFit\n      #   should_autoplay: true/false; default true,\n      #   use_application_audio_session: true/false; default true.\n      #\n      #   ### These are properties of just the ::Player\n      #   delay_play: true/false, default false. If false then you have to manually call\n      #     @media_player.play in your code\n      #   modal: true/false; default false,\n      #   controller: [UIViewController] used to present the player modally;\n      #     default uses root view controller of window\n      # }\n      #\n      # @block for when setup is done; use this block to present\n      #   @media_player.view if options[:modal] == false\n      #\n      # EX\n      #  From a local URL:\n      #    file = File.join(App.resources_path, 'test.mp3')\n      #    BW::Media::Player.play(NSURL.fileURLWithPath(file)) do |media_player|\n      #      media_player.view.frame = some_view.bounds\n      #      self.view.addSubview media_player.view\n      #    end\n      #\n      #  From a remote URL:\n      #    BW::Media::Player.play(\"http://www.hrupin.com/wp-content/uploads/mp3/testsong_20_sec.mp3\") do |media_player|\n      #      media_player.view.frame = some_view.bounds\n      #      self.view.addSubview media_player.view\n      #    end\n      def play(content_url, options = {}, &block)\n        options = {\n          delay_play: false\n        }.merge(options)\n\n        display_modal = !!options[:modal]\n\n        klass = display_modal ? MPMoviePlayerViewController : MPMoviePlayerController\n\n        content_url = content_url.is_a?(NSURL) ? content_url : NSURL.URLWithString(content_url)\n        @media_player = klass.alloc.init\n\n        self.media_player.prepareToPlay if not display_modal\n\n        set_player_options(options)\n        self.media_player.setContentURL(content_url)\n\n        NSNotificationCenter.defaultCenter.observe MPMoviePlayerPlaybackDidFinishNotification do |notification|\n          h = notification.userInfo\n          error = h[\"error\"]\n          if error\n            p \"BW::Media::Player error: #{error.userInfo.inspect}\"\n            p \"Code: #{error.code}, Domain: #{error.domain}\"\n          end\n        end\n\n        if display_modal\n          @presenting_controller = options[:controller]\n          @presenting_controller ||= App.window.rootViewController\n          if Device.ios_version < \"7.0\"\n            @presenting_controller.presentMoviePlayerViewControllerAnimated(@media_player)\n          else\n            @presenting_controller.presentViewController(@media_player, animated:true, completion:nil)\n          end\n        else\n          if block.nil?\n            raise Error::NilPlayerCallback, \"no block callback given in #play; you need\\\n                                              to supply one if options[:modal] == false\"\n          end\n          block.call(@media_player)\n        end\n\n        if not display_modal and not options[:delay_play]\n          @media_player.play\n        end\n      end\n\n      # Stops playback for a Media::Player\n      def stop\n        if @media_player.is_a? MPMoviePlayerViewController\n          if Device.ios_version < \"7.0\"\n            @presenting_controller.dismissMoviePlayerViewControllerAnimated\n          else\n            @presenting_controller.dismissViewControllerAnimated(true, completion:nil)\n          end\n          @presenting_controller = nil\n        else\n          @media_player.stop\n        end\n        @media_player = nil\n      end\n\n      def media_player\n        if @media_player.is_a? MPMoviePlayerViewController\n          return @media_player.moviePlayer\n        end\n        @media_player\n      end\n\n      private\n      # RubyMotion has a problem calling some objective-c methods at runtime, so we can't\n      # do any cool `camelize` tricks.\n      def set_player_options(options)\n        self.media_player.allowsAirPlay = options[:allows_air_play] if options.has_key? :allows_air_play\n        self.media_player.controlStyle = options[:control_style] if options.has_key? :control_style\n        self.media_player.endPlaybackTime = options[:end_playback_time] if options.has_key? :end_playback_time\n        self.media_player.initialPlaybackTime = options[:initial_playback_time] if options.has_key? :initial_playback_time\n        self.media_player.movieSourceType = options[:movie_source_type] if options.has_key? :movie_source_type\n        self.media_player.repeatMode = options[:repeat_mode] if options.has_key? :repeat_mode\n        self.media_player.scalingMode = options[:scaling_mode] if options.has_key? :scaling_mode\n        self.media_player.shouldAutoplay = options[:should_autoplay] if options.has_key? :should_autoplay\n        self.media_player.useApplicationAudioSession = options[:use_application_audio_session] if options.has_key? :use_application_audio_session\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "motion/motion/accelerometer.rb",
    "content": "module BubbleWrap\n  module Motion\n    class Accelerometer < GenericMotionInterface\n\n      def start(options={}, &handler)\n        if options.key?(:interval)\n          @manager.accelerometerUpdateInterval = options[:interval]\n        end\n\n        if handler\n          queue = convert_queue(options[:queue])\n          @manager.startAccelerometerUpdatesToQueue(queue, withHandler: internal_handler(handler))\n        else\n          @manager.startAccelerometerUpdates\n        end\n\n        return self\n      end\n\n      private def handle_result(result_data, error, handler)\n        if result_data\n          result = {\n            data: result_data,\n            acceleration: result_data.acceleration,\n            x: result_data.acceleration.x,\n            y: result_data.acceleration.y,\n            z: result_data.acceleration.z,\n          }\n        else\n          result = nil\n        end\n\n        handler.call(result, error)\n      end\n\n      def available?\n        @manager.accelerometerAvailable?\n      end\n\n      def active?\n        @manager.accelerometerActive?\n      end\n\n      def data\n        @manager.accelerometerData\n      end\n\n      def stop\n        @manager.stopAccelerometerUpdates\n      end\n\n    end\n\n  end\nend\n"
  },
  {
    "path": "motion/motion/device_motion.rb",
    "content": "module BubbleWrap\n  module Motion\n    class DeviceMotion < GenericMotionInterface\n\n      def start(options={}, &handler)\n        if options.key?(:interval)\n          @manager.deviceMotionUpdateInterval = options[:interval]\n        end\n\n        if options.key?(:reference)\n          reference_frame = convert_reference_frame(options[:reference])\n        else\n          reference_frame = nil\n        end\n\n        if handler\n          queue = convert_queue(options[:queue])\n\n          if reference_frame\n              @manager.startDeviceMotionUpdatesUsingReferenceFrame(reference_frame, toQueue: queue, withHandler: internal_handler(handler))\n          else\n              @manager.startDeviceMotionUpdatesToQueue(queue, withHandler: internal_handler(handler))\n          end\n        else\n          if reference_frame\n            @manager.startDeviceMotionUpdatesUsingReferenceFrame(reference_frame)\n          else\n            @manager.startDeviceMotionUpdates\n          end\n        end\n\n        return self\n      end\n\n      private def handle_result(result_data, error, handler)\n        if result_data\n          result = {\n            data: result_data,\n            attitude: result_data.attitude,\n            rotation: result_data.rotationRate,\n            gravity: result_data.gravity,\n            acceleration: result_data.userAcceleration,\n            magnetic: result_data.magneticField,\n          }\n\n          if result_data.attitude\n            result.merge!({\n              roll: result_data.attitude.roll,\n              pitch: result_data.attitude.pitch,\n              yaw: result_data.attitude.yaw,\n              matrix: result_data.attitude.rotationMatrix,\n              quaternion: result_data.attitude.quaternion,\n            })\n          end\n\n          if result_data.rotationRate\n            result.merge!({\n              rotation_x: result_data.rotationRate.x,\n              rotation_y: result_data.rotationRate.y,\n              rotation_z: result_data.rotationRate.z,\n            })\n          end\n\n          if result_data.gravity\n            result.merge!({\n              gravity_x: result_data.gravity.x,\n              gravity_y: result_data.gravity.y,\n              gravity_z: result_data.gravity.z,\n            })\n          end\n\n          if result_data.userAcceleration\n            result.merge!({\n              acceleration_x: result_data.userAcceleration.x,\n              acceleration_y: result_data.userAcceleration.y,\n              acceleration_z: result_data.userAcceleration.z,\n            })\n          end\n\n          if result_data.magneticField\n            case result_data.magneticField.accuracy\n            when CMMagneticFieldCalibrationAccuracyLow\n              accuracy = :low\n            when CMMagneticFieldCalibrationAccuracyMedium\n              accuracy = :medium\n            when CMMagneticFieldCalibrationAccuracyHigh\n              accuracy = :high\n            end\n\n            result.merge!({\n              field: result_data.magneticField.field,\n              magnetic_x: result_data.magneticField.field.x,\n              magnetic_y: result_data.magneticField.field.y,\n              magnetic_z: result_data.magneticField.field.z,\n              magnetic_accuracy: accuracy,\n            })\n          end\n        else\n          result = nil\n        end\n\n        handler.call(result, error)\n      end\n\n      def convert_reference_frame(reference_frame)\n        case reference_frame\n        when :arbitrary_z\n          CMAttitudeReferenceFrameXArbitraryZVertical\n        when :corrected_z\n          CMAttitudeReferenceFrameXArbitraryCorrectedZVertical\n        when :magnetic_north\n          CMAttitudeReferenceFrameXMagneticNorthZVertical\n        when :true_north\n          CMAttitudeReferenceFrameXTrueNorthZVertical\n        else\n          reference_frame\n        end\n      end\n\n      def available?\n        @manager.deviceMotionAvailable?\n      end\n\n      def active?\n        @manager.deviceMotionActive?\n      end\n\n      def data\n        @manager.deviceMotion\n      end\n\n      def stop\n        @manager.stopDeviceMotionUpdates\n      end\n\n    end\n\n  end\nend\n"
  },
  {
    "path": "motion/motion/gyroscope.rb",
    "content": "module BubbleWrap\n  module Motion\n    class Gyroscope < GenericMotionInterface\n\n      def start(options={}, &handler)\n        if options.key?(:interval)\n          @manager.gyroUpdateInterval = options[:interval]\n        end\n\n        if handler\n          queue = convert_queue(options[:queue])\n          @manager.startGyroUpdatesToQueue(queue, withHandler: internal_handler(handler))\n        else\n          @manager.startGyroUpdates\n        end\n\n        return self\n      end\n\n      private def handle_result(result_data, error, handler)\n        if result_data\n          result = {\n            data: result_data,\n            rotation: result_data.rotationRate,\n            x: result_data.rotationRate.x,\n            y: result_data.rotationRate.y,\n            z: result_data.rotationRate.z,\n          }\n        else\n          result = nil\n        end\n\n        handler.call(result, error)\n      end\n\n      def available?\n        @manager.gyroAvailable?\n      end\n\n      def active?\n        @manager.gyroActive?\n      end\n\n      def data\n        @manager.gyroData\n      end\n\n      def stop\n        @manager.stopGyroUpdates\n      end\n\n    end\n\n  end\nend\n"
  },
  {
    "path": "motion/motion/magnetometer.rb",
    "content": "module BubbleWrap\n  module Motion\n    class Magnetometer < GenericMotionInterface\n\n      def start(options={}, &handler)\n        if options.key?(:interval)\n          @manager.magnetometerUpdateInterval = options[:interval]\n        end\n\n        if handler\n          queue = convert_queue(options[:queue])\n          @manager.startMagnetometerUpdatesToQueue(queue, withHandler: internal_handler(handler))\n        else\n          @manager.startMagnetometerUpdates\n        end\n\n        return self\n      end\n\n      private def handle_result(result_data, error, handler)\n        if result_data\n          result = {\n            data: result_data,\n            field: result_data.magneticField,\n            x: result_data.magneticField.x,\n            y: result_data.magneticField.y,\n            z: result_data.magneticField.z,\n          }\n        else\n          result = nil\n        end\n\n        handler.call(result, error)\n      end\n\n      def available?\n        @manager.magnetometerAvailable?\n      end\n\n      def active?\n        @manager.magnetometerActive?\n      end\n\n      def data\n        @manager.magnetometerData\n      end\n\n      def stop\n        @manager.stopMagnetometerUpdates\n      end\n\n    end\n\n  end\nend\n"
  },
  {
    "path": "motion/motion/motion.rb",
    "content": "# Provides a nice DSL for interacting with the standard CMMotionManager from\n# CoreMotion\nmodule BubbleWrap\n  # These module methods provide the main interface.  It uses a shared manager\n  # (per Apple's recommendation), and they all have a common set of supported\n  # methods:\n  #     available?\n  #     active?\n  #     repeat(opts)\n  #     once(opts)\n  #     every(time_interval, opts)\n  #\n  # @example\n  #     if BW::Motion.accelerometer.available?\n  #       BW::Motion.accelerometer.every(5) do |result|\n  #         # see the README for the keys that are available in result.\n  #       end\n  #     end\n  #\n  # If you insist on using your own manager, or you want more than one\n  # BW::Motion::Whatever running at the same time, you'll need to instantiate\n  # them yourself.\n  #\n  # @example\n  #     mgr = CMMotionManager.alloc.init\n  #     accel = BW::Motion::Accelerometer.new(mgr)\n  #     accel.once do |result_data|\n  #     end\n  #     # => BW::Motion::accelerometer.once do |result_data| ... end\n  module Motion\n    module_function\n\n    def manager\n      @manager ||= CMMotionManager.alloc.init\n    end\n\n    def accelerometer\n      @accelerometer ||= Accelerometer.new(self.manager)\n    end\n\n    def gyroscope\n      @gyroscope ||= Gyroscope.new(self.manager)\n    end\n\n    def magnetometer\n      @magnetometer ||= Magnetometer.new(self.manager)\n    end\n\n    def device\n      @device ||= DeviceMotion.new(self.manager)\n    end\n  end\n\n\n  module Motion\n    module Error\n    end\n\n    class GenericMotionInterface\n\n      def initialize(manager)\n        @manager = manager\n      end\n\n      def repeat(options={}, &blk)\n        raise \"A block is required\" unless blk\n        blk.weak! if BubbleWrap.use_weak_callbacks?\n\n        self.start(options, &blk)\n        return self\n      end\n\n      def every(time=nil, options={}, &blk)\n        raise \"A block is required\" unless blk\n        blk.weak! if BubbleWrap.use_weak_callbacks?\n\n        if time.is_a?(NSDictionary)\n          options = time\n        elsif time\n          options = options.merge(interval: time)\n        end\n\n        self.start(options, &blk)\n        return self\n      end\n\n      def once(options={}, &blk)\n        raise \"A block is required\" unless blk\n        blk.weak! if BubbleWrap.use_weak_callbacks?\n\n        @called_once = false\n        every(options) do |result, error|\n          unless @called_once\n            @called_once = true\n            blk.call(result, error)\n          end\n          self.stop\n        end\n\n        return self\n      end\n\n      private def convert_queue(queue_name)\n        case queue_name\n        when :main, nil\n          return NSOperationQueue.mainQueue\n        when :background\n          queue = NSOperationQueue.new\n          queue.name = 'com.bubble-wrap.core-motion.background-queue'\n          return queue\n        when :current\n          return NSOperationQueue.currentQueue\n        when String\n          queue = NSOperationQueue.new\n          queue.name = queue_name\n          return queue\n        else\n          queue_name\n        end\n      end\n\n      private def internal_handler(handler)\n        retval = -> (result_data, error) do\n          handle_result(result_data, error, handler)\n        end\n        retval.weak! if BubbleWrap.use_weak_callbacks?\n        retval\n      end\n\n    end\n\n  end\nend\n"
  },
  {
    "path": "motion/network-indicator/network-indicator.rb",
    "content": "module BubbleWrap\n  module NetworkIndicator\n    DELAY = 0.2\n\n    module_function\n\n    def counter\n      @counter ||= 0\n    end\n\n    def show\n      if Dispatch::Queue.current.to_s == 'com.apple.main-thread'\n        @counter = self.counter + 1\n        self.update_spinner\n      else\n        Dispatch::Queue.main.async do\n          self.show\n        end\n      end\n    end\n\n    def hide\n      if Dispatch::Queue.current.to_s == 'com.apple.main-thread'\n        @counter = [self.counter - 1, 0].max\n        if self.counter == 0\n          if @hide_indicator_timer\n            @hide_indicator_timer.invalidate\n          end\n          @hide_indicator_timer = NSTimer.timerWithTimeInterval(DELAY - 0.01, target: self, selector: :update_spinner_timer, userInfo: nil, repeats: false)\n          NSRunLoop.mainRunLoop.addTimer(@hide_indicator_timer, forMode:NSRunLoopCommonModes)\n        end\n      else\n        Dispatch::Queue.main.async do\n          self.hide\n        end\n      end\n    end\n\n    def update_spinner_timer\n      update_spinner\n    end\n\n    def update_spinner\n      if Dispatch::Queue.current.to_s == 'com.apple.main-thread'\n        if @hide_indicator_timer\n          @hide_indicator_timer.invalidate\n          @hide_indicator_timer = nil\n        end\n        UIApplication.sharedApplication.networkActivityIndicatorVisible = (@counter > 0)\n      else\n        Dispatch::Queue.main.async do\n          self.update_spinner\n        end\n      end\n    end\n\n    def visible?\n      UIApplication.sharedApplication.networkActivityIndicatorVisible?\n    end\n\n    def reset!\n      @counter = 0\n      self.update_spinner\n    end\n\n  end\nend\n"
  },
  {
    "path": "motion/reactor/default_deferrable.rb",
    "content": "module BubbleWrap\n  module Reactor\n    # A basic class which includes Deferrable when all\n    # you need is a deferrable without any added behaviour.\n    class DefaultDeferrable\n      include ::BubbleWrap::Reactor::Deferrable\n    end\n  end\nend\n"
  },
  {
    "path": "motion/reactor/deferrable.rb",
    "content": "module BubbleWrap\n  module Reactor\n    # Provides a mixin for deferrable jobs.\n    module Deferrable\n\n      # def self.included(base)\n      #   base.extend ::BubbleWrap::Reactor::Future\n      # end\n\n      # Specify a block to be executed if and when the Deferrable object\n      # receives a status of :succeeded. See set_deferred_status for more\n      # information.\n      # Calling this method on a Deferrable object whose status is not yet\n      # known will cause the callback block to be stored on an internal\n      # list. If you call this method on a Deferrable whose status is\n      # :succeeded, the block will be executed immediately, receiving\n      # the parameters given to the prior set_deferred_status call.\n      def callback(&blk)\n        return unless blk\n        @deferred_status ||= :unknown\n        if @deferred_status == :succeeded\n          execute_block(&blk)\n        elsif @deferred_status != :failed\n          @callbacks ||= []\n          @callbacks.unshift blk\n        end\n      end\n\n      # Cancels an outstanding timeout if any. Undoes the action of timeout.\n      def cancel_timeout\n        @deferred_timeout ||= nil\n        if @deferred_timeout\n          @deferred_timeout.cancel\n          @deferred_timeout = nil\n        end\n      end\n\n      # Specify a block to be executed if and when the Deferrable object\n      # receives a status of :failed. See set_deferred_status for more\n      # information.\n      def errback(&blk)\n        return unless blk\n        @deferred_status ||= :unknown\n        if @deferred_status == :failed\n          execute_block(&blk)\n          blk.call(*@deferred_args)\n        elsif @deferred_status != :succeeded\n          @errbacks ||= []\n          @errbacks.unshift blk\n        end\n      end\n\n      def execute_block(&blk)\n        return unless blk\n        blk.call(*@deferred_args)\n      end\n\n      def delegate(delegate)\n        callback_delegate(delegate)\n        errback_delegate(delegate)\n        self\n      end\n\n      def errback_delegate(delegate)\n        errback do |*args|\n          delegate.fail *args\n        end\n        self\n      end\n\n      def callback_delegate(delegate)\n        callback do |*args|\n          delegate.succeed *args\n        end\n        self\n      end\n\n      # Sugar for set_deferred_status(:failed, …)\n      def fail(*args)\n        set_deferred_status :failed, *args\n      end\n      alias set_deferred_failure fail\n\n      # Sets the “disposition” (status) of the Deferrable object. See also\n      # the large set of sugarings for this method. Note that if you call\n      # this method without arguments, no arguments will be passed to the\n      # callback/errback. If the user has coded these with arguments,\n      # then the user code will throw an argument exception. Implementors\n      # of deferrable classes must document the arguments they will supply\n      # to user callbacks.\n      # OBSERVE SOMETHING VERY SPECIAL here: you may call this method even\n      # on the INSIDE of a callback. This is very useful when a\n      # previously-registered callback wants to change the parameters that\n      # will be passed to subsequently-registered ones.\n      # You may give either :succeeded or :failed as the status argument.\n      # If you pass :succeeded, then all of the blocks passed to the object\n      # using the callback method (if any) will be executed BEFORE the\n      # set_deferred_status method returns. All of the blocks passed to the\n      # object using errback will be discarded.\n      # If you pass :failed, then all of the blocks passed to the object\n      # using the errback method (if any) will be executed BEFORE the\n      # set_deferred_status method returns. All of the blocks passed to the\n      # object using # callback will be discarded.\n      # If you pass any arguments to set_deferred_status in addition to the\n      # status argument, they will be passed as arguments to any callbacks\n      # or errbacks that are executed. It’s your responsibility to ensure\n      # that the argument lists specified in your callbacks and errbacks match\n      # the arguments given in calls to set_deferred_status, otherwise Ruby\n      # will raise an ArgumentError.\n      def set_deferred_status(status, *args)\n        cancel_timeout\n        @errbacks ||= nil\n        @callbacks ||= nil\n        @deferred_status = status\n        @deferred_args = args\n        case @deferred_status\n        when :succeeded\n          if @callbacks\n            while cb = @callbacks.pop\n              execute_block(&cb)\n            end\n          end\n          @errbacks.clear if @errbacks\n        when :failed\n          if @errbacks\n            while eb = @errbacks.pop\n              execute_block(&eb)\n            end\n          end\n          @callbacks.clear if @callbacks\n        end\n      end\n\n      # Sugar for set_deferred_status(:succeeded, …)\n      def succeed(*args)\n        set_deferred_status :succeeded, *args\n      end\n      alias set_deferred_success succeed\n\n      # Setting a timeout on a Deferrable causes it to go into the failed\n      # state after the Timeout expires (passing no arguments to the object’s\n      # errbacks). Setting the status at any time prior to a call to the\n      # expiration of the timeout will cause the timer to be cancelled.\n      def timeout(seconds)\n        cancel_timeout\n        me = self\n        @deferred_timeout = Timer.new(seconds) {me.fail}\n      end\n\n      def deferred_status\n        @deferred_status ||= :unknown\n      end\n\n      def deferred_args\n        @deferred_args\n      end\n\n    end\n  end\nend\n"
  },
  {
    "path": "motion/reactor/dependent_deferrable.rb",
    "content": "module BubbleWrap\n  module Reactor\n    class DependentDeferrable < DefaultDeferrable\n      # args are Deferrable(s) which the returned Deferrable depends on.\n      # returns a Deferrable that depends on args.\n      # which:\n      #   succeeds only when every Deferrable in args succeeds\n      #   fails immediately when any Deferrable in args fails\n      # Have to be careful that #deferred_args for DependentDeferrable is a list of #deferred_args from its children Deferrable(s).\n      def self.on(*args)\n        deferrable = self.new\n        @children_deferrables = args\n        @children_deferrables.each do |e|\n          e.callback do |result|\n            if @children_deferrables.all? {|a| a.deferred_status == :succeeded}\n              deferrable.succeed(*@children_deferrables.map(&:deferred_args))\n            end\n          end\n          e.errback do |result|\n            deferrable.fail(*e.deferred_args)\n          end\n        end\n        deferrable\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "motion/reactor/eventable.rb",
    "content": "module BubbleWrap\n  module Reactor\n    # A simple mixin that adds events to your object.\n    module Eventable\n\n      # When `event` is triggered the block will execute\n      # and be passed the arguments that are passed to\n      # `trigger`.\n      def on(event, method = nil, &blk)\n        events = _events_for_key(event)\n        method_or_block = method ? method : blk\n        events.push method_or_block\n      end\n\n      # When `event` is triggered, do not call the given\n      # block any more\n      def off(event, method = nil, &blk)\n        events = _events_for_key(event)\n        if method\n          events.delete_if { |m| m.receiver == method.receiver and m.name == method.name }\n        elsif blk\n          events.delete_if { |b| b == blk }\n        else\n          __events__[event] = Array.new\n        end\n        blk\n      end\n\n      # Trigger an event\n      def trigger(event, *args)\n        blks = _events_for_key(event).clone\n        blks.map do |blk|\n          blk.call(*args)\n        end\n      end\n\n      private\n\n      def __events__\n        @__events__ ||= Hash.new\n      end\n\n      def _events_for_key(event)\n        __events__[event] ||= Array.new\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "motion/reactor/future.rb",
    "content": "module BubbleWrap\n  module Reactor\n    module Future\n\n      # A future is a sugaring of a typical deferrable usage.\n      def future arg, cb=nil, eb=nil, &blk\n        arg = arg.call if arg.respond_to?(:call)\n\n        if arg.respond_to?(:set_deferred_status)\n          if cb || eb\n            arg.callback(&cb) if cb\n            arg.errback(&eb) if eb\n          else\n            arg.callback(&blk) if blk\n          end\n        end\n\n        arg\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "motion/reactor/periodic_timer.rb",
    "content": "module BubbleWrap\n  module Reactor\n    # Creates a repeating timer.\n    class PeriodicTimer\n      include Eventable\n\n      attr_accessor :interval\n\n      # Create a new timer that fires after a given number of seconds\n      def initialize(interval, *args, &blk)\n        callback = args.first.respond_to?(:call) ? args.first : blk\n        raise ArgumentError, \"No callback or block supplied to periodic timer\" unless callback\n        callback.weak! if callback && BubbleWrap.use_weak_callbacks?\n\n        options = args.last.is_a?(Hash) ? args.last : {}\n        if options[:common_modes]\n          NSLog \"[DEPRECATED - Option :common_modes] a Run Loop Mode is no longer needed.\"\n        end\n\n        self.interval = interval\n\n        leeway = interval\n        queue  = Dispatch::Queue.current\n        @timer = Dispatch::Source.timer(leeway, interval, 0.0, queue) do\n          callback.call\n          trigger(:fired)\n        end\n      end\n\n      # Cancel the timer\n      def cancel\n        @timer.cancel!\n        trigger(:cancelled)\n      end\n\n    end\n  end\nend\n"
  },
  {
    "path": "motion/reactor/queue.rb",
    "content": "module BubbleWrap\n  module Reactor\n    # A GCD scheduled, linear queue.\n    #\n    # This class provides a simple “Queue” like abstraction on top of the\n    # GCD scheduler.\n    #\n    # Useful as an API sugar for stateful protocols\n    #\n    #  q = BubbleWrap::Reactor::Queue.new\n    #  q.push('one', 'two', 'three')\n    #  3.times do\n    #    q.pop{ |msg| puts(msg) }\n    #  end\n    class Queue\n\n      # Create a new queue\n      def initialize\n        @items = []\n      end\n\n      # Is the queue empty?\n      def empty?\n        @items.empty?\n      end\n\n      # The size of the queue\n      def size\n        @items.size\n      end\n\n      # Push items onto the work queue. The items will not appear in the queue\n      # immediately, but will be scheduled for addition.\n      def push(*items)\n        ::BubbleWrap::Reactor.schedule do\n          @items.push(*items)\n          @popq.shift.call @items.shift until @items.empty? || @popq.empty?\n        end\n      end\n\n      # Pop items off the queue, running the block on the work queue. The pop\n      # will not happen immediately, but at some point in the future, either\n      # in the next tick, if the queue has data, or when the queue is populated.\n      def pop(*args, &blk)\n        cb = proc do\n          blk.call(*args)\n        end\n        ::BubbleWrap::Reactor.schedule do\n          if @items.empty?\n            @popq << cb\n          else\n            cb.call @items.shift\n          end\n        end\n        nil # Always returns nil\n      end\n\n    end\n  end\nend\n"
  },
  {
    "path": "motion/reactor/thread_aware_deferrable.rb",
    "content": "module BubbleWrap\n  module Reactor\n    class ThreadAwareDeferrable < DefaultDeferrable\n      include ::BubbleWrap::Reactor::Deferrable\n\n\n      # need to store the the queue in callback / errback\n      def callback(&blk)\n        return unless blk\n        cache_block_queue(&blk)\n        super(&blk)\n      end\n\n      def errback(&blk)\n        return unless blk\n        cache_block_queue(&blk)\n        super(&blk)\n      end\n\n      def execute_block(&blk)\n        return unless blk\n        queue = @queue_cache.delete(blk.object_id)\n        return unless queue\n        queue.async do\n          blk.call(*@deferred_args)\n        end\n      end\n\n      def cache_block_queue(&blk)\n        return unless blk\n        @queue_cache ||= {}\n        @queue_cache[blk.object_id] = Dispatch::Queue.current\n      end\n    end\n  end\nend\n\n"
  },
  {
    "path": "motion/reactor/timer.rb",
    "content": "module BubbleWrap\n  module Reactor\n    # Creates a one-time timer.\n    class Timer\n      include Eventable\n\n      # Create a new timer that fires after a given number of seconds\n      def initialize(leeway, callback=nil, &blk)\n        queue  = Dispatch::Queue.current\n        @timer = Dispatch::Source.timer(leeway, Dispatch::TIME_FOREVER, 0.0, queue) do |src|\n          begin\n            (callback || blk).call\n            trigger(:fired)\n          ensure\n            src.cancel!\n          end\n        end\n      end\n\n      # Cancel the timer\n      def cancel\n        @timer.cancel! if @timer\n        trigger(:cancelled)\n        true\n      end\n\n    end\n  end\nend\n"
  },
  {
    "path": "motion/reactor.rb",
    "content": "module BubbleWrap\n  module Reactor\n    module_function\n\n    # Always returns true - for compatibility with EM\n    def reactor_running?\n      true\n    end\n    alias reactor_thread? reactor_running?\n\n    # Call `callback` or the passed block in `interval` seconds.\n    # Returns a timer signature that can be passed into\n    # `cancel_timer`\n    def add_timer(interval, callback=nil, &blk)\n      @timers ||= {}\n      timer = Timer.new(interval,callback,&blk)\n      timer.on(:fired) do\n        @timers.delete(timer.object_id)\n      end\n      timer.on(:cancelled) do\n        @timers.delete(timer.object_id)\n      end\n      @timers[timer.object_id] = timer\n      timer.object_id\n    end\n\n    # Cancel a timer by passing in either a Timer object or\n    # a timer id (as returned by `add_timer` and\n    # `add_periodic_timer`).\n    def cancel_timer(timer)\n      return timer.cancel if timer.respond_to?(:cancel)\n      @timers ||= {}\n      return @timers[timer].cancel if @timers[timer]\n      false\n    end\n\n    # Call `callback` or the passed block every `interval` seconds.\n    # Returns a timer signature that can be passed into\n    # `cancel_timer`\n    # Optionally supply a callback as a second argument instead of a block\n    # (as per EventMachine API)\n    # Optionally supply :common_modes => true in args to schedule the timer\n    # for the runloop \"common modes\" (NSRunLoopCommonModes) instead of\n    # the default runloop mode.\n    def add_periodic_timer(interval, *args, &blk)\n      @timers ||= {}\n      timer = PeriodicTimer.new(interval,*args,&blk)\n      timer.on(:cancelled) do\n        @timers.delete(timer)\n      end\n      @timers[timer.object_id] = timer\n      timer.object_id\n    end\n\n    # Defer is for integrating blocking operations into the reactor's control\n    # flow.\n    # Call defer with one or two blocks, the second block is optional.\n    #     operation = proc do\n    #       # perform a long running operation here\n    #       \"result\"\n    #     end\n    #     callback = proc do |result|\n    #       # do something with the result here, such as trigger a UI change\n    #     end\n    #     BubbleWrap::Reactor.defer(operation,callback)\n    # The action of `defer` is to take the block specified in the first\n    # parameter (the \"operation\") and schedule it for asynchronous execution\n    # on a GCD concurrency queue. When the operation completes the result (if any)\n    # is passed into the callback (if present).\n    def defer(op=nil,cb=nil,&blk)\n      schedule do\n        result = (op||blk).call\n        schedule(result, &cb) if cb\n      end\n    end\n\n    # A version of `defer` which schedules both the operator\n    # and callback operations on the application's main thread.\n    def defer_on_main(op=nil,cb=nil,&blk)\n      schedule_on_main do\n        result = (op||blk).call\n        schedule_on_main(result, &cb) if cb\n      end\n    end\n\n    # Schedule a block for execution on the reactor queue.\n    def schedule(*args, &blk)\n      @queue ||= ::Dispatch::Queue.concurrent(\"#{NSBundle.mainBundle.bundleIdentifier}.reactor\")\n      blk.weak! if blk && BubbleWrap.use_weak_callbacks?\n\n      cb = proc do\n        blk.call(*args)\n      end\n      @queue.async &cb\n      nil\n    end\n\n    # Schedule a block for execution on your application's main thread.\n    # This is useful as UI updates need to be executed from the main\n    # thread.\n    def schedule_on_main(*args, &blk)\n      blk.weak! if blk && BubbleWrap.use_weak_callbacks?\n      cb = proc do\n        blk.call(*args)\n      end\n      ::Dispatch::Queue.main.async &cb\n    end\n\n  end\nend\n\n::EM = ::BubbleWrap::Reactor unless defined?(::EM) # Yes I dare!\n"
  },
  {
    "path": "motion/rss_parser.rb",
    "content": "# Asynchronous and non blocking RSS Parser based on NSXMLParser.\n# The parser will progressively parse a feed and yield each item to the provided block.\n#\n# The parser can also trigger delegate methods, you can define the following delegate method on the receiving object:\n# def when_parser_initializes; end\n# def when_parser_parses; end\n# def when_parser_is_done; end\n# def when_parser_errors; end\n#\n# @see https://developer.apple.com/library/ios/#documentation/Cocoa/Reference/Foundation/Classes/NSXMLParser_Class/Reference/Reference.html\n#\n# @usage example:\n#   feed = RSSParser.new(URL)\n#   feed.delegate = self\n#   feed.parse do |item|\n#     print item.link\n#   end\n#   def when_parser_is_done\n#     App.alert('parsing complete')\n#   end\n#\nmodule BubbleWrap\n  class RSSParser\n\n    attr_accessor :parser, :source, :doc, :debug, :delegate, :parser_error\n    attr_reader :state\n\n    # RSSItem is a simple class that holds all of RSS items.\n    # Extend this class to display/process the item differently.\n    class RSSItem\n      attr_accessor :title, :description, :link, :guid, :pubDate, :creator, :category, :encoded, :enclosure\n\n      def initialize\n        @title, @description, @link, @pubDate, @guid, @creator, @category, @encoded = '', '', '', '', ''\n      end\n\n      def to_hash\n        {\n          :title        => title,\n          :description  => description,\n          :link         => link,\n          :pubDate      => pubDate,\n          :guid         => guid,\n          :enclosure    => enclosure,\n          :category     => category,\n          :encoded      => encoded,\n        }\n      end\n    end\n\n    def initialize(input, data=false)\n      if data\n        data_to_parse = input.respond_to?(:to_data) ? input.to_data : input\n        @source = data_to_parse\n        @source_type = :data\n      else\n        url = input.is_a?(NSURL) ? input : NSURL.alloc.initWithString(input)\n        @source = url\n        @source_type = :url\n      end\n      self\n    end\n\n    def state=(new_state)\n      @state = new_state\n      callback_meth = \"when_parser_#{new_state}\"\n      if self.delegate && self.delegate.respond_to?(callback_meth)\n        self.delegate.send(callback_meth)\n      end\n    end\n\n    # Starts the parsing and send each parsed item through its block.\n    #\n    # Usage:\n    #   feed.parse do |item|\n    #     puts item.link\n    #   end\n    def parse(&block)\n      @block = block\n\n      if @source_type == :url\n        @parser = NSXMLParser.alloc.initWithContentsOfURL(@source)\n      else\n        @parser = NSXMLParser.alloc.initWithData(@source)\n      end\n\n      @parser.shouldProcessNamespaces = true\n      @parser.delegate ||= self\n      self.state = :initializes\n      @parser.parse\n    end\n\n    # Delegate getting called when parsing starts\n    def parserDidStartDocument(parser)\n      puts \"starting parsing..\" if debug\n      self.state = :parses\n    end\n\n    # Delegate being called when an element starts being processed\n    def parser(parser, didStartElement:element, namespaceURI:uri, qualifiedName:name, attributes:attrs)\n      if element == 'item'\n        @current_item = RSSItem.new\n      elsif element == 'enclosure'\n        @current_item.enclosure = attrs\n      end\n      @current_element = element\n    end\n\n    # as the parser finds characters, this method is being called\n    def parser(parser, foundCharacters:string)\n      if @current_element && @current_item && @current_item.respond_to?(@current_element)\n        el = @current_item.send(@current_element)\n        el << string if el.respond_to?(:<<)\n      end\n    end\n\n    # method called when an element is done being parsed\n    def parser(parser, didEndElement:element, namespaceURI:uri, qualifiedName:name)\n      if element == 'item'\n        @block.call(@current_item) if @block\n      else\n        @current_element = nil\n      end\n    end\n\n    # method called when the parser encounters an error\n    # error can be retrieved with parserError\n    def parser(parser, parseErrorOccurred:parse_error)\n      puts \"parseErrorOccurred\" if debug\n      @parser_error = parse_error\n\n      self.state = :errors\n    end\n\n    # delegate getting called when the parsing is done\n    # If a block was set, it will be called on each parsed items\n    def parserDidEndDocument(parser)\n      puts \"done parsing\" if debug\n      self.state = :is_done unless self.state == :errors\n    end\n\n    def parserError\n      @parser_error || @parser.parserError\n    end\n\n    # TODO: implement\n    # parser:validationErrorOccurred:\n    # parser:foundCDATA:\n\n  end\nend\n"
  },
  {
    "path": "motion/shortcut.rb",
    "content": "# Make sure that\n# Both BubbleWrap and BW are defined.  This file is depended on by everything\n# else in BubbleWrap, so don't stuff anything in here unless you know why\n# you're doing it.\nmodule BubbleWrap\n  SETTINGS = {}\n\n  def self.debug=(val)\n    BubbleWrap::SETTINGS[:debug] = val\n  end\n\n  def self.debug?\n    BubbleWrap::SETTINGS[:debug]\n  end\n\n  def self.use_weak_callbacks=(val)\n    BubbleWrap::SETTINGS[:use_weak_callbacks] = val\n  end\n\n  def self.use_weak_callbacks?\n    BubbleWrap::SETTINGS[:use_weak_callbacks]\n  end\n\n  def version\n    BubbleWrap::VERSION\n  end\nend\n\nBW = BubbleWrap unless defined?(BW)\n"
  },
  {
    "path": "motion/sms/result.rb",
    "content": "module BubbleWrap\n  module SMS\n    class Result\n      attr_accessor :result\n\n      def initialize(result)\n        self.result = result\n      end\n\n      def sent?\n        self.result == MessageComposeResultSent\n      end\n\n      def canceled?\n        self.result == MessageComposeResultCancelled\n      end\n\n      def failed?\n        self.result == MessageComposeResultFailed\n      end\n\n    end\n  end\nend\n"
  },
  {
    "path": "motion/sms/sms.rb",
    "content": "module BubbleWrap\n  module SMS\n\n    module_function\n\n    # Base method to create your in-app mail\n    # ---------------------------------------\n    # EX\n    # BW::SMS.compose (\n    # {\n    #   delegate: self, # optional, will use root view controller by default\n    #   to: [ \"1(234)567-8910\" ],\n    #   message: \"This is my message. It isn't very long.\",\n    #   animated: false\n    # }) {|result, error|\n    #   result.sent?      # => boolean\n    #   result.canceled?  # => boolean\n    #   result.failed?    # => boolean\n    #   error             # => NSError\n    #   }\n\n    def compose(options = {}, &callback)\n      @delegate = options[:delegate] || App.window.rootViewController\n      @callback = callback\n      @callback.weak! if @callback && BubbleWrap.use_weak_callbacks?\n\n      @message_controller = create_message_controller(options)\n      @message_is_animated = options[:animated] == false ? false : true\n      @delegate.presentModalViewController(@message_controller, animated: @message_is_animated)\n    end\n\n    def create_message_controller(options = {})\n      message_controller = MFMessageComposeViewController.alloc.init\n      message_controller.messageComposeDelegate = self\n      message_controller.body = options[:message]\n      message_controller.recipients = Array(options[:to])\n      message_controller\n    end\n\n    def can_send_sms?\n      !!MFMessageComposeViewController.canSendText\n    end\n\n    # Event when the MFMessageComposeViewController is closed\n    # -------------------------------------------------------------\n    # the callback is fired if it was present in the constructor\n\n    def messageComposeViewController(controller, didFinishWithResult: result)\n      @delegate.dismissModalViewControllerAnimated(@message_is_animated)\n      @callback.call Result.new(result) if @callback\n    end\n  end\nend\n"
  },
  {
    "path": "motion/test_suite_delegate.rb",
    "content": "class TestSuiteDelegate\n  attr_accessor :window\n\n  def application(application, didFinishLaunchingWithOptions:launchOptions)\n    @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)\n    @window.rootViewController = UIViewController.alloc.init\n    @window.makeKeyAndVisible\n    true\n  end\nend\n\nclass TestSuiteOSXDelegate\n  def applicationDidFinishLaunching(notification)\n    buildMenu\n    buildWindow\n  end\n\n  def buildWindow\n    @mainWindow = NSWindow.alloc.initWithContentRect([[240, 180], [480, 360]],\n      styleMask: NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask,\n      backing: NSBackingStoreBuffered,\n      defer: false)\n    @mainWindow.title = \"BubbleWrap Tests\"\n  end\n\n  def buildMenu\n    @mainMenu = NSMenu.new\n\n    appName = \"BubbleWrap Tests\"\n    addMenu(appName) do\n      addItemWithTitle(\"About #{appName}\", action: 'orderFrontStandardAboutPanel:', keyEquivalent: '')\n      addItem(NSMenuItem.separatorItem)\n      addItemWithTitle('Preferences', action: 'openPreferences:', keyEquivalent: ',')\n      addItem(NSMenuItem.separatorItem)\n      servicesItem = addItemWithTitle('Services', action: nil, keyEquivalent: '')\n      NSApp.servicesMenu = servicesItem.submenu = NSMenu.new\n      addItem(NSMenuItem.separatorItem)\n      addItemWithTitle(\"Hide #{appName}\", action: 'hide:', keyEquivalent: 'h')\n      item = addItemWithTitle('Hide Others', action: 'hideOtherApplications:', keyEquivalent: 'H')\n      item.keyEquivalentModifierMask = NSCommandKeyMask|NSAlternateKeyMask\n      addItemWithTitle('Show All', action: 'unhideAllApplications:', keyEquivalent: '')\n      addItem(NSMenuItem.separatorItem)\n      addItemWithTitle(\"Quit #{appName}\", action: 'terminate:', keyEquivalent: 'q')\n    end\n\n    NSApp.helpMenu = addMenu('Help') do\n      addItemWithTitle(\"#{appName} Help\", action: 'showHelp:', keyEquivalent: '?')\n    end.menu\n\n    NSApp.mainMenu = @mainMenu\n  end\n\n  private\n\n  def addMenu(title, &b)\n    item = createMenu(title, &b)\n    @mainMenu.addItem item\n    item\n  end\n\n  def createMenu(title, &b)\n    menu = NSMenu.alloc.initWithTitle(title)\n    menu.instance_eval(&b) if b\n    item = NSMenuItem.alloc.initWithTitle(title, action: nil, keyEquivalent: '')\n    item.submenu = menu\n    item\n  end\nend\n"
  },
  {
    "path": "motion/ui/pollute.rb",
    "content": "# Please, no more!  It'll hurt BubbleWrap's compatibility with other libraries.\n[\n  [UIControl,         BW::UIControlWrapper],\n  [UIView,            BW::UIViewWrapper],\n  [UIViewController,  BW::UIViewControllerWrapper],\n].each do |base, wrapper|\n    base.send(:include, wrapper)\nend\n"
  },
  {
    "path": "motion/ui/ui_activity_view_controller_wrapper.rb",
    "content": "module BW\n  class UIActivityViewController < ::UIActivityViewController\n    class << self\n      def new(options = {}, presenting_controller = nil, &block)\n        options = {\n          activities: nil,\n          animated: true\n        }.merge(options)\n\n        if options[:item] || options[:items]\n          items = Array(options[:item] || options[:items])\n        else\n          raise ArgumentError, \"You must specify at least one item - #{options.inspect}\"\n        end\n\n        vc = alloc.initWithActivityItems(items, applicationActivities:options[:activities])\n        vc.excludedActivityTypes = BW::Constants.get(\"UIActivityType\", Array(options[:excluded])) if options[:excluded]\n\n        unless block.nil?\n          block.weak! if BubbleWrap.use_weak_callbacks?\n          vc.setCompletionHandler block\n        end\n\n        presenting_controller ||= App.window.rootViewController.presentedViewController # May be nil, but handles use case of container views\n        presenting_controller ||= App.window.rootViewController\n\n        presenting_controller.presentViewController(vc, animated:options[:animated], completion: lambda {})\n        vc\n      end\n\n    end\n  end\n\n  # UIActivityTypes\n  Constants.register(\n    UIActivityTypePostToFacebook,\n    UIActivityTypePostToTwitter,\n    UIActivityTypePostToWeibo,\n    UIActivityTypeMessage,\n    UIActivityTypeMail,\n    UIActivityTypePrint,\n    UIActivityTypeCopyToPasteboard,\n    UIActivityTypeAssignToContact,\n    UIActivityTypeSaveToCameraRoll,\n    UIActivityTypeAddToReadingList,\n    UIActivityTypePostToFlickr,\n    UIActivityTypePostToVimeo,\n    UIActivityTypePostToTencentWeibo,\n    UIActivityTypeAirDrop\n  )\nend\n"
  },
  {
    "path": "motion/ui/ui_alert_view.rb",
    "content": "module BW\n  class UIAlertView < ::UIAlertView\n    @callbacks = [\n      :will_present,\n      :did_present,\n      :on_system_cancel,\n      :enable_first_other_button?,\n      :on_click,\n      :will_dismiss,\n      :did_dismiss\n    ]\n\n    KEYBOARD_TYPES = {\n      default: UIKeyboardTypeDefault,\n      ascii: UIKeyboardTypeASCIICapable,\n      numbers_punctuation: UIKeyboardTypeNumbersAndPunctuation,\n      url: UIKeyboardTypeURL,\n      number_pad: UIKeyboardTypeNumberPad,\n      phone_pad: UIKeyboardTypePhonePad,\n      name_phone_pad: UIKeyboardTypeNamePhonePad,\n      email_address: UIKeyboardTypeEmailAddress,\n      email: UIKeyboardTypeEmailAddress, # Duplicate to help developers\n      decimal_pad: UIKeyboardTypeDecimalPad,\n      twitter: UIKeyboardTypeTwitter,\n      web_search: UIKeyboardTypeWebSearch,\n      alphabet: UIKeyboardTypeASCIICapable\n    }\n\n    class << self\n      attr_reader :callbacks\n\n      def new(options = {}, &block)\n        view = alloc.initWithTitle(options[:title],\n          message: options[:message],\n          delegate: nil,\n          cancelButtonTitle: nil,\n          otherButtonTitles: nil\n        )\n\n        Array(options[:buttons]).each { |title| view.addButtonWithTitle(title) }\n\n        view.style               = options[:style]\n        view.delegate            = view\n        view.cancel_button_index = options[:cancel_button_index]\n\n        view.instance_variable_set(:@handlers, {})\n        block.weak! if block && BubbleWrap.use_weak_callbacks?\n\n        options[:on_click] ||= block\n\n        callbacks.each do |callback|\n          view.send(callback, &options[callback]) if options[callback]\n        end\n\n        view\n      end\n\n      def default(options = {}, &block)\n        options = {buttons: \"OK\"}.merge!(options)\n        options[:style] = :default\n        new(options, &block)\n      end\n\n      def plain_text_input(options = {}, &block)\n        options = {buttons: [\"Cancel\", \"OK\"],\n                   cancel_button_index: 0}.merge!(options)\n        options[:style] = :plain_text_input\n        new(options, &block).tap do |view|\n          view.textFieldAtIndex(0).tap do |tf|\n            tf.text = options[:text] if options[:text]\n            tf.placeholder = options[:placeholder] if options[:placeholder]\n            tf.keyboardType = (KEYBOARD_TYPES[options[:keyboard_type]] || options[:keyboard_type]) if options[:keyboard_type]\n          end\n        end\n      end\n\n      def secure_text_input(options = {}, &block)\n        options = {buttons: [\"Cancel\", \"OK\"],\n                   cancel_button_index: 0}.merge!(options)\n        options[:style] = :secure_text_input\n        new(options, &block)\n      end\n\n      def login_and_password_input(options = {}, &block)\n        options = {buttons: [\"Cancel\", \"Log in\"],\n                   cancel_button_index: 0}.merge!(options)\n        options[:style] = :login_and_password_input\n        new(options, &block)\n      end\n    end\n\n    def style\n      alertViewStyle\n    end\n\n    def style=(value)\n      self.alertViewStyle = Constants.get(\"UIAlertViewStyle\", value) if value\n    end\n\n    def cancel_button_index\n      cancelButtonIndex\n    end\n\n    def cancel_button_index=(value)\n      self.cancelButtonIndex = value if value\n    end\n\n    ###############################################################################################\n\n    attr_accessor :clicked_button\n    protected     :clicked_button=\n\n    class ClickedButton\n      def initialize(alert, index)\n        @index  = index\n        @title  = alert.buttonTitleAtIndex(index)\n        @cancel = alert.cancelButtonIndex == index\n      end\n\n      attr_reader :index, :title\n      def cancel?; @cancel end\n    end\n\n    ###############################################################################################\n\n    attr_reader :handlers\n    protected   :handlers\n\n    callbacks.each do |callback|\n      define_method(callback) do |&block|\n        return handlers[callback] unless block\n\n        handlers[callback] = block if block\n        self\n      end\n    end\n\n    # UIAlertViewDelegate protocol ################################################################\n\n    def willPresentAlertView(alert)\n      alert.clicked_button = nil\n      handlers[:will_present].call(alert) if handlers[:will_present]\n    end\n\n    def didPresentAlertView(alert)\n      alert.clicked_button = nil\n      handlers[:did_present].call(alert) if handlers[:did_present]\n    end\n\n    def alertViewCancel(alert)\n      alert.clicked_button = nil\n      handlers[:on_system_cancel].call(alert) if handlers[:on_system_cancel]\n    end\n\n    def alertViewShouldEnableFirstOtherButton(alert)\n      alert.clicked_button = nil\n      handlers[:enable_first_other_button?].call(alert) if handlers[:enable_first_other_button?]\n    end\n\n    def alertView(alert, clickedButtonAtIndex:index)\n      alert.clicked_button = ClickedButton.new(alert, index)\n      handlers[:on_click].call(alert) if handlers[:on_click]\n    end\n\n    def alertView(alert, willDismissWithButtonIndex:index)\n      alert.clicked_button = ClickedButton.new(alert, index)\n      handlers[:will_dismiss].call(alert) if handlers[:will_dismiss]\n    end\n\n    def alertView(alert, didDismissWithButtonIndex:index)\n      alert.clicked_button = ClickedButton.new(alert, index)\n      handlers[:did_dismiss].call(alert) if handlers[:did_dismiss]\n    end\n\n    ###############################################################################################\n\n    def plain_text_field\n      textFieldAtIndex(0) if style == UIAlertViewStylePlainTextInput\n    end\n\n    def secure_text_field\n      textFieldAtIndex(0) if style == UIAlertViewStyleSecureTextInput\n    end\n\n    def login_text_field\n      textFieldAtIndex(0) if style == UIAlertViewStyleLoginAndPasswordInput\n    end\n\n    def password_text_field\n      textFieldAtIndex(1) if style == UIAlertViewStyleLoginAndPasswordInput\n    end\n  end\n\n  Constants.register(\n    UIAlertViewStyleDefault,\n    UIAlertViewStylePlainTextInput,\n    UIAlertViewStyleSecureTextInput,\n    UIAlertViewStyleLoginAndPasswordInput\n  )\nend\n"
  },
  {
    "path": "motion/ui/ui_bar_button_item.rb",
    "content": "module BW\n  class UIBarButtonItem < ::UIBarButtonItem\n    class << self\n      def styled(type, *objects, &block)\n        if block.nil?\n          action = nil\n        else\n          block.weak! if BubbleWrap.use_weak_callbacks?\n          action = :call\n        end\n        object = objects.size == 1 ? objects.first : objects\n        style  = Constants.get(\"UIBarButtonItemStyle\", type)\n\n        item = if object.is_a?(String)\n          alloc.initWithTitle(object,\n            style:style,\n            target:block,\n            action:action\n          )\n        elsif object.is_a?(UIImage)\n          alloc.initWithImage(object,\n            style:style,\n            target:block,\n            action:action\n          )\n        elsif object.is_a?(Array) && object.size == 2 && object.all? { |o| o.is_a?(UIImage) }\n          alloc.initWithImage(object[0],\n            landscapeImagePhone:object[1],\n            style:style,\n            target:block,\n            action:action\n          )\n        else\n          raise ArgumentError, \"invalid object - #{object.inspect}\"\n        end\n\n        item.instance_variable_set(:@target, block)\n        item\n      end\n\n      def system(type, &block)\n        if block.nil?\n          action = nil\n        else\n          block.weak! if BubbleWrap.use_weak_callbacks?\n          action = :call\n        end\n        system_item = Constants.get(\"UIBarButtonSystemItem\", type)\n\n        item = alloc.initWithBarButtonSystemItem(system_item, target:block, action:action)\n        item.instance_variable_set(:@target, block)\n        item\n      end\n\n      def custom(view, &block)\n        view.when_tapped(true, &block) if block\n        alloc.initWithCustomView(view)\n      end\n\n      def new(options = {}, &block)\n        if options[:styled]\n          args = options.values_at(:title, :image, :landscape).compact\n          return styled(options[:styled], *args, &block)\n        end\n\n        return system(options[:system], &block) if options[:system]\n\n        return custom(options[:custom], &block) if options[:custom]\n        return custom(options[:view],   &block) if options[:view]\n\n        raise ArgumentError, \"invalid options - #{options.inspect}\"\n      end\n\n      def build(options = {}, &block)\n        NSLog \"[DEPRECATED - BW::UIBarButtonItem.build] please use .new instead.\"\n        new(options, &block)\n      end\n    end\n  end\n\n  Constants.register(\n    UIBarButtonItemStylePlain,\n    UIBarButtonItemStyleBordered,\n    UIBarButtonItemStyleDone,\n\n    UIBarButtonSystemItemDone,\n    UIBarButtonSystemItemCancel,\n    UIBarButtonSystemItemEdit,\n    UIBarButtonSystemItemSave,\n    UIBarButtonSystemItemAdd,\n    UIBarButtonSystemItemFlexibleSpace,\n    UIBarButtonSystemItemFixedSpace,\n    UIBarButtonSystemItemCompose,\n    UIBarButtonSystemItemReply,\n    UIBarButtonSystemItemAction,\n    UIBarButtonSystemItemOrganize,\n    UIBarButtonSystemItemBookmarks,\n    UIBarButtonSystemItemSearch,\n    UIBarButtonSystemItemRefresh,\n    UIBarButtonSystemItemStop,\n    UIBarButtonSystemItemCamera,\n    UIBarButtonSystemItemTrash,\n    UIBarButtonSystemItemPlay,\n    UIBarButtonSystemItemPause,\n    UIBarButtonSystemItemRewind,\n    UIBarButtonSystemItemFastForward,\n    UIBarButtonSystemItemUndo,\n    UIBarButtonSystemItemRedo,\n    UIBarButtonSystemItemPageCurl,\n  )\nend\n"
  },
  {
    "path": "motion/ui/ui_control_wrapper.rb",
    "content": "module BubbleWrap\n  module UIControlWrapper\n    def when(events, options = {}, &block)\n      events = BW::Constants.get(\"UIControlEvent\", events)\n\n      @callback ||= {}\n      @callback[events] ||= []\n\n      unless options[:append]\n        @callback[events] = []\n        removeTarget(nil, action: nil, forControlEvents: events)\n      end\n\n      @callback[events] << block\n      block.weak! if BubbleWrap.use_weak_callbacks?\n      addTarget(@callback[events].last, action:'call', forControlEvents: events)\n    end\n  end\n\n  Constants.register(\n    UIControlEventTouchDown,\n    UIControlEventTouchDownRepeat,\n    UIControlEventTouchDragInside,\n    UIControlEventTouchDragOutside,\n    UIControlEventTouchDragEnter,\n    UIControlEventTouchDragExit,\n    UIControlEventTouchUpInside,\n    UIControlEventTouchUpOutside,\n    UIControlEventTouchCancel,\n\n    UIControlEventValueChanged,\n\n    UIControlEventEditingDidBegin,\n    UIControlEventEditingChanged,\n    UIControlEventEditingDidEnd,\n    UIControlEventEditingDidEndOnExit,\n\n    UIControlEventAllTouchEvents,\n    UIControlEventAllEditingEvents,\n    # UIControlEventApplicationReserved,\n    # UIControlEventSystemReserved,\n    UIControlEventAllEvents\n  )\nend\n"
  },
  {
    "path": "motion/ui/ui_view_controller_wrapper.rb",
    "content": "module BubbleWrap\n  module UIViewControllerWrapper\n    # Short hand to get the content frame\n    #\n    # Return content frame: the application frame - navigation bar frame\n    def content_frame\n      app_frame = App.frame\n      navbar_height = self.navigationController.nil? ?\n        0 : self.navigationController.navigationBar.frame.size.height\n      CGRectMake(0, 0, app_frame.size.width, app_frame.size.height - navbar_height)\n    end\n  end\nend\n"
  },
  {
    "path": "motion/ui/ui_view_wrapper.rb",
    "content": "module BubbleWrap\n  module UIViewWrapper\n    def when_tapped(enableInteraction=true, &proc)\n      add_gesture_recognizer_helper(UITapGestureRecognizer.alloc.initWithTarget(self, action:'handle_gesture:'), enableInteraction, proc)\n    end\n\n    def when_pinched(enableInteraction=true, &proc)\n      add_gesture_recognizer_helper(UIPinchGestureRecognizer.alloc.initWithTarget(self, action:'handle_gesture:'), enableInteraction, proc)\n    end\n\n    def when_rotated(enableInteraction=true, &proc)\n      add_gesture_recognizer_helper(UIRotationGestureRecognizer.alloc.initWithTarget(self, action:'handle_gesture:'), enableInteraction, proc)\n    end\n\n    def when_swiped(enableInteraction=true, &proc)\n      add_gesture_recognizer_helper(UISwipeGestureRecognizer.alloc.initWithTarget(self, action:'handle_gesture:'), enableInteraction, proc)\n    end\n\n    def when_panned(enableInteraction=true, &proc)\n      add_gesture_recognizer_helper(UIPanGestureRecognizer.alloc.initWithTarget(self, action:'handle_gesture:'), enableInteraction, proc)\n    end\n\n    def when_screen_edge_panned(enableInteraction=true, &proc)\n      add_gesture_recognizer_helper(UIScreenEdgePanGestureRecognizer.alloc.initWithTarget(self, action:'handle_gesture:'), enableInteraction, proc)\n    end\n\n    def when_pressed(enableInteraction=true, &proc)\n      add_gesture_recognizer_helper(UILongPressGestureRecognizer.alloc.initWithTarget(self, action:'handle_gesture:'), enableInteraction, proc)\n    end\n\n    def self.deprecated_methods\n      %w(whenTapped whenPinched whenRotated whenSwiped whenPanned whenPressed)\n    end\n\n    deprecated_methods.each do |method|\n      define_method(method) do |enableInteraction = true, &proc|\n        NSLog \"[DEPRECATED - #{method}] please use #{method.underscore} instead.\"\n        send(method.underscore, enableInteraction, &proc)\n      end\n    end\n\n    private\n\n    def handle_gesture(recognizer)\n      @recognizers[recognizer].call(recognizer)\n    end\n\n    # Adds the recognizer and keeps a strong reference to the Proc object.\n    def add_gesture_recognizer_helper(recognizer, enableInteraction, proc)\n      setUserInteractionEnabled true if enableInteraction && !isUserInteractionEnabled\n      self.addGestureRecognizer(recognizer)\n\n      @recognizers = {} unless @recognizers\n      proc.weak! if !proc.nil? && BubbleWrap.use_weak_callbacks?\n      @recognizers[recognizer] = proc\n\n      recognizer\n    end\n  end\nend\n"
  },
  {
    "path": "motion/util/constants.rb",
    "content": "# Stupid hack because the RubyMotion dependency detection has a bug.\nmodule BubbleWrap\n  module Constants\n    module_function\n\n    # Looks like RubyMotion only adds UIKit constants\n    # at compile time. If you don't use these\n    # directly in your code, they don't get added\n    # to Kernel and Constants.get crashes.\n    # Examples\n    # Constants.register UIReturnKeyDone, UIReturnKeyNext\n    def register(*ui_constants)\n      # do nothing, just get the constants in the code\n    end\n\n    # @param [String] base of the constant\n    # @param [Integer, NSArray, String, Symbol] the suffix of the constant\n    #   when NSArray, will return the bitmask of all suffixes in the array\n    # @return [Integer] the constant for this base and suffix\n    # Examples\n    # get(\"UIReturnKey\", :done) => UIReturnKeyDone == 9\n    # get(\"UIReturnKey\", \"done\") => UIReturnKeyDone == 9\n    # get(\"UIReturnKey\", 9) => 9\n    # get(\"UIImagePickerControllerSourceType\", [\"photo_library\", \"camera\", \"saved_photos_album\"]) => 3\n    # get(\"UIActivityType\", [:air_drop, :print]) => [\"com.apple.UIKit.activity.AirDrop\", \"com.apple.UIKit.activity.Print\"]\n    def get(base, *values)\n      if values.is_a? NSArray\n        value = values.size == 1 ? values.first : values.flatten\n      else\n        value = values\n      end\n\n      case value\n      when Numeric\n        value.to_i\n      when NSArray\n        unless get(base, value.first).is_a? Fixnum\n          value.map { |v| get(base, v) }\n        else\n          value.reduce { |i, j|\n            get(base, i) | get(base, j)\n          }\n        end\n      else\n        value = value.to_s.camelize\n        Kernel.const_get(\"#{base}#{value}\")\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "motion/util/deprecated.rb",
    "content": "module BubbleWrap\n  module Deprecated\n\n    class DeprecatedError < StandardError; end\n\n    def deprecated(method_sym, version)\n      unless method_sym.kind_of?(Symbol)\n        raise ArgumentError, \"deprecated() requires symbols for its first argument.\"\n      end\n\n      scope = nil\n      alias_scope = nil\n      if self.methods.include?(method_sym)\n        scope = :define_singleton_method\n        alias_scope = (class << self; self end)\n      elsif self.instance_methods.include?(method_sym)\n        scope = :define_method\n        alias_scope = self\n      else\n        raise ArgumentError, \"Method not found for deprecated() - #{method_sym}\"\n      end\n\n\n      send(scope, \"#{method_sym}_with_deprecation\", ->(*args, &block) {\n        fail = BubbleWrap.version.to_s >= version.to_s\n        if fail\n          raise DeprecatedError, \"#{method_sym} was deprecated and removed in BubbleWrap #{version}\"\n        else\n          NSLog \"#{method_sym} is deprecated -- it will be removed in BubbleWrap #{version}\"\n          send(\"#{method_sym}_without_deprecation\", *args, &block)\n        end\n      })\n\n      alias_scope.send(:alias_method, \"#{method_sym}_without_deprecation\", method_sym)\n      alias_scope.send(:alias_method, method_sym, \"#{method_sym}_with_deprecation\")\n    end\n\n    def self.included(base)\n      base.extend(self)\n    end\n  end\nend\n"
  },
  {
    "path": "resources/Localizable.strings",
    "content": "\"real_key\" = \"Real Key\";"
  },
  {
    "path": "resources/atom.xml",
    "content": "<?xml version=\"1.0\" encoding=\"UTF-8\"?>\r\n<?xml-stylesheet type=\"text/xsl\" media=\"screen\" href=\"/~d/styles/rss2enclosuresfull.xsl\"?><?xml-stylesheet type=\"text/css\" media=\"screen\" href=\"http://feeds.feedburner.com/~d/styles/itemcontent.css\"?><rss xmlns:itunes=\"http://www.itunes.com/dtds/podcast-1.0.dtd\" xmlns:media=\"http://search.yahoo.com/mrss/\" xmlns:creativeCommons=\"http://backend.userland.com/creativeCommonsRssModule\" xmlns:feedburner=\"http://rssnamespace.org/feedburner/ext/1.0\" version=\"2.0\">\r\n  <channel>\r\n    <title>SD Ruby Podcast</title>\r\n    <link>http://sdruby.org</link>\r\n    <description>SD Ruby. We are San Diego's Ruby community.</description>\r\n    <language>en-us</language>\r\n    <pubDate>Wed, 18 Apr 2012 18:48:30 +0000</pubDate>\r\n    <lastBuildDate>Wed, 18 Apr 2012 18:48:30 +0000</lastBuildDate>\r\n    <itunes:author>SD Ruby</itunes:author>\r\n    <itunes:keywords>ruby, rails, ruby on rails, programming, web, development, agile, railscast</itunes:keywords>\r\n    <itunes:image href=\"http://sdruby.org/images/application/logo_podcast.jpg\" />\r\n    <itunes:owner>\r\n      <itunes:name>SD Ruby</itunes:name>\r\n      <itunes:email>sandiegoruby@gmail.com</itunes:email>\r\n    </itunes:owner>\r\n    <itunes:block>no</itunes:block>\r\n    <itunes:category text=\"Technology\">\r\n      <itunes:category text=\"Software How-To\" />\r\n    </itunes:category>\r\n    <itunes:category text=\"Education\">\r\n      <itunes:category text=\"Training\" />\r\n    </itunes:category>\r\n    <atom10:link xmlns:atom10=\"http://www.w3.org/2005/Atom\" rel=\"self\" type=\"application/rss+xml\" href=\"http://feeds.feedburner.com/sdrbpodcast\" /><feedburner:info uri=\"sdrbpodcast\" /><atom10:link xmlns:atom10=\"http://www.w3.org/2005/Atom\" rel=\"hub\" href=\"http://pubsubhubbub.appspot.com/\" /><creativeCommons:license>http://creativecommons.org/licenses/by-nc/2.0/</creativeCommons:license><item>\r\n      <title>Episode 108: Fog: The Ruby Cloud Services Library</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/108.png?1334774910\" alt=\"Episode 108: Fog: The Ruby Cloud Services Library\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://github.com/bensie\"&gt;James Miller&lt;/a&gt; demonstrates how to get up and running with &lt;a href=\"http://fog.io\"&gt;Fog&lt;/a&gt;, including practical use cases, caveats, and how to make Fog better by contributing back.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://speakerdeck.com/u/bensie/p/fog\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Wed, 18 Apr 2012 18:48:30 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/eDNb8jJM2aY/108</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/108</guid>\r\n      <itunes:summary>James Miller demonstrates how to get up and running with Fog, including practical use cases, caveats, and how to make Fog better by contributing back.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>20:46</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/108</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/y1u0ypWP6nY/108_fog.m4v\" length=\"249048733\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/108_fog.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 107: Advanced Heroku with the Celadon Cedar Stack</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/107.png?1316447751\" alt=\"Episode 107: Advanced Heroku with the Celadon Cedar Stack\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Ryan Daigle and Blake Gentry explore all the new features in Heroku's Celadon Cedar stack.&lt;/p&gt;</description>\r\n      <pubDate>Mon, 19 Sep 2011 15:55:52 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/Og7X8w7YOVk/107</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/107</guid>\r\n      <itunes:summary>Ryan Daigle and Blake Gentry explore all the new features in Heroku's Celadon Cedar stack.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>48:55</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/107</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/BpeJGePeM4M/107_heroku.m4v\" length=\"357998175\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/107_heroku.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 106: JRuby: Ruby in the JVM. Why on Earth?!?</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/106.png?1310833271\" alt=\"Episode 106: JRuby: Ruby in the JVM. Why on Earth?!?\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;JRuby is a 100% pure-Java implementation of Ruby with high performance, real threading, and a vast array of libraries. Chris McCann explores how JRuby can provide real value for Rails developers.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/testflyjets/jruby-8610179\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Sat, 16 Jul 2011 16:21:11 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/TodMbv_vJmA/106</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/106</guid>\r\n      <itunes:summary>JRuby is a 100% pure-Java implementation of Ruby with high performance, real threading, and a vast array of libraries. Chris McCann explores how JRuby can provide real value for Rails developers.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>35:20</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/106</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/AA0wzZVUthQ/106_jruby.m4v\" length=\"212436028\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/106_jruby.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 105: Get Your Flow On: Mastering Your Development Workflow </title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/105.png?1311900690\" alt=\"Episode 105: Get Your Flow On: Mastering Your Development Workflow \" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Rob Kaufman explores tools and options for improving your development process: common issues with managing tasks, starting work, committing branches, and getting completed tasks verified and deployed to production. It's all about automating the little steps because details are too easy to forget.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/notch8/developer-flow\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Sat, 16 Jul 2011 16:12:09 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/Sc1mz2caJlQ/105</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/105</guid>\r\n      <itunes:summary>Rob Kaufman explores tools and options for improving your development process: common issues with managing tasks, starting work, committing branches, and getting completed tasks verified and deployed to production. It's all about automating the little steps because details are too easy to forget.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>32:13</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/105</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/Yr7W8EiU-DE/105_tool_sharpening.m4v\" length=\"140231987\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/105_tool_sharpening.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 104: Send SMS Messages and Make Phone Calls From Your App With Twilio</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/104.png?1311900709\" alt=\"Episode 104: Send SMS Messages and Make Phone Calls From Your App With Twilio\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Brian Levine shows how easy it is to add phone services to your application using Twilio. Twilio wraps powerful telecom infrastructure with a simple REST API and offers pay-as-you-go pricing with cloud scalability.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/Beans0063/twilio-8696979\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Sun, 06 Mar 2011 02:05:52 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/vHszYPjAw_Q/104</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/104</guid>\r\n      <itunes:summary>Brian Levine shows how easy it is to add phone services to your application using Twilio. Twilio wraps powerful telecom infrastructure with a simple REST API and offers pay-as-you-go pricing with cloud scalability.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>33:08</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/104</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/L_l17TIu1i4/104_twilio.m4v\" length=\"347744448\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/104_twilio.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 103: HTML5 Quick Start with Compass-html5-boilerplate</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/103.png?1299461057\" alt=\"Episode 103: HTML5 Quick Start with Compass-html5-boilerplate\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Peter Gumeson shows off his compass-html5-boilerplate gem, which uses Compass to make it easier to integrate the HTML5 Boilerplate template into Rails apps.&lt;/p&gt;</description>\r\n      <pubDate>Sun, 06 Mar 2011 02:05:16 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/RO4ccv-ct-4/103</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/103</guid>\r\n      <itunes:summary>Peter Gumeson shows off his compass-html5-boilerplate gem, which uses Compass to make it easier to integrate the HTML5 Boilerplate template into Rails apps.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>14:24</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/103</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/Pbu_hNThHzg/103_compass_html5_boilerplate.m4v\" length=\"136655850\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/103_compass_html5_boilerplate.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 102: Newick-Ruby: A Gem for Manipulating Newick Format Trees</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/102.png?1299460973\" alt=\"Episode 102: Newick-Ruby: A Gem for Manipulating Newick Format Trees\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Jonathan Badger explores Newick format trees, and how to manipulate them using his Newick-ruby gem.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.ttaxus.com/files/Newick-Ruby.pdf\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Sun, 06 Mar 2011 02:04:51 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/ppo3rCc9tX8/102</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/102</guid>\r\n      <itunes:summary>Jonathan Badger explores Newick format trees, and how to manipulate them using his Newick-ruby gem.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>9:39</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/102</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/wkwRYMnZgg4/102_newick.m4v\" length=\"113821834\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/102_newick.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 101: TRegexp: Friendly Regular Expressions</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/101.png?1299460926\" alt=\"Episode 101: TRegexp: Friendly Regular Expressions\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Ian Young introduces his first gem, a human-friendly DSL for regular expressions.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://iangreenleaf.github.com/sdruby-lightningtalk-tregexp/\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Sun, 06 Mar 2011 02:04:28 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/UFiITU0S0lA/101</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/101</guid>\r\n      <itunes:summary>Ian Young introduces his first gem, a human-friendly DSL for regular expressions.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>11:21</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/101</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/HvhOM4JzdZM/101_tregexp.m4v\" length=\"86706659\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/101_tregexp.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 100: Getting Oriented with Compass</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/100.png?1299377041\" alt=\"Episode 100: Getting Oriented with Compass\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Patrick Crowley shows how to turbo-charge your layouts with Compass, a lightweight stylesheet framework built on top of Haml and Sass.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/mokolabs/compass-8613728\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Sun, 06 Mar 2011 02:04:01 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/TPDJP2KUOFI/100</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/100</guid>\r\n      <itunes:summary>Patrick Crowley shows how to turbo-charge your layouts with Compass, a lightweight stylesheet framework built on top of Haml and Sass.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>16:04</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/100</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/kt5zOvrxclo/100_compass.m4v\" length=\"147408532\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/100_compass.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 099: Omniauth: Future Proof Your Authentication</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/99.png?1299460567\" alt=\"Episode 099: Omniauth: Future Proof Your Authentication\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Kevin Ball shows how the traditional model of username/password authentication is dying, and how Omniauth makes it all rainbows and sunshine going forward.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"hhttp://www.slideshare.net/kbal11/omniauth\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Sun, 06 Mar 2011 02:03:35 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/oLBUfJZJsvk/99</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/99</guid>\r\n      <itunes:summary>Kevin Ball shows how the traditional model of username/password authentication is dying, and how Omniauth makes it all rainbows and sunshine going forward.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>4:08</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/99</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/EpXCIpl8npU/099_omniauth.m4v\" length=\"29808643\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/099_omniauth.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 098: Give Yourself Some Backbone</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/98.png?1299376972\" alt=\"Episode 098: Give Yourself Some Backbone\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Ryan Weald gives a brief overview of Backbone.js and the advantages it has for your project.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/ryanweald/sdruby-backbone-lightning-talk\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Sun, 06 Mar 2011 02:02:52 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/vIT8vCIivzM/98</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/98</guid>\r\n      <itunes:summary>Ryan Weald gives a brief overview of Backbone.js and the advantages it has for your project.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>5:08</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/98</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/SUvE5D3QZrU/098_backbone.m4v\" length=\"56023503\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/098_backbone.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 097: Basic Reporting with Ruby and CouchDB</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/97.png?1299376937\" alt=\"Episode 097: Basic Reporting with Ruby and CouchDB\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Christopher Petersen shows how to create both basic reports on individual user behavior and aggregate reports across all user activity.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/cpetersen/couchdb-lightning-talk\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Sun, 06 Mar 2011 02:02:17 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/K73MUAm_WN0/97</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/97</guid>\r\n      <itunes:summary>Christopher Petersen shows how to create both basic reports on individual user behavior and aggregate reports across all user activity.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>8:15</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/97</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/8mNyfssuxIA/097_couchdb.m4v\" length=\"91283160\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/097_couchdb.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 096: Rounded Corners Everywhere</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/96.png?1299376892\" alt=\"Episode 096: Rounded Corners Everywhere\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Scott Olmsted explores Javascript libraries to simulate CSS3 decorations on browsers that don't support them. (Yeah, we're talking about you Internet Explorer.)&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/sandiegoscott/rounded-corners-everywhere\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Sun, 06 Mar 2011 02:01:33 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/BXoa54S9LBQ/96</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/96</guid>\r\n      <itunes:summary>Scott Olmsted explores Javascript libraries to simulate CSS3 decorations on browsers that don't support them. (Yeah, we're talking about you Internet Explorer.)\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>6:43</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/96</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/ji30xJWex04/096_rounded.m4v\" length=\"48039752\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/096_rounded.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 095: The State of MacRuby</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/95.png?1299374363\" alt=\"Episode 095: The State of MacRuby\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Matt Aimonetti discusses the latest developments with the MacRuby project, which aims to implement Ruby 1.9 directly on top of Mac OS X core technologies.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/mattetti/macruby-rubyconf-presentation-2010\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Sun, 06 Mar 2011 01:19:23 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/_HITPQuH4po/95</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/95</guid>\r\n      <itunes:summary>Matt Aimonetti discusses the latest developments with the MacRuby project, which aims to implement Ruby 1.9 directly on top of Mac OS X core technologies.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>47:03</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/95</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/Dt5nUGG_aoU/095_macruby.m4v\" length=\"569348477\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/095_macruby.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 094: Refactoring ActiveRecord Models</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/94.png?1299373810\" alt=\"Episode 094: Refactoring ActiveRecord Models\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;It's always important to keep your ActiveRecord models in tip-top shape. Ben Hughes explores several techniques for refactoring models and preventing them from getting out of hand.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.scribd.com/doc/48183030/Refactoring-ActiveRecord-Models\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Sun, 06 Mar 2011 01:10:11 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/3Jx5te7bxmo/94</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/94</guid>\r\n      <itunes:summary>It's always important to keep your ActiveRecord models in tip-top shape. Ben Hughes explores several techniques for refactoring models and preventing them from getting out of hand.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>40:56</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/94</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/dHiKuvzwo-I/094_refactoring_models.m4v\" length=\"376346924\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/094_refactoring_models.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 093: Are You Redis?</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/93.png?1297180702\" alt=\"Episode 093: Are You Redis?\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Emanuele Tozzato gives a brief introduction to Redis, then demonstrates real-life database performance optimization with redis-object and how to manage background jobs using Resque.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/etozzato/are-you-redis\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Tue, 08 Feb 2011 15:58:22 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/JX_ukhaJno4/93</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/93</guid>\r\n      <itunes:summary>Emanuele Tozzato gives a brief introduction to Redis, then demonstrates real-life database performance optimization with redis-object and how to manage background jobs using Resque.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>32:44</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/93</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/rtJVgqGzt1Q/093_redis.m4v\" length=\"258217737\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/093_redis.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 092: File Uploads With S3SwfUpload</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/92.png?1297273018\" alt=\"Episode 092: File Uploads With S3SwfUpload\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Nathan Colgate Clark gives a general overview of his &lt;a href=\"http://github.com/nathancolgate/s3-swf-upload-plugin\"&gt;Amazon S3 upload plugin&lt;/a&gt; (past, present, and future) and shares his thoughts on how it managed to stand out on GitHub.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://ow.ly/3zEW7\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Tue, 08 Feb 2011 15:57:56 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/lDd9GWexbQI/92</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/92</guid>\r\n      <itunes:summary>Nathan Colgate Clark gives a general overview of his Amazon S3 upload plugin (past, present, and future) and shares his thoughts on how it managed to stand out on GitHub.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>40:26</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/92</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/j6tI957Q-28/092_upload.m4v\" length=\"399674085\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/092_upload.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 091: Message Block</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/91.png?1297180648\" alt=\"Episode 091: Message Block\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Ben Hughes shows off &lt;a href=\"https://github.com/rubiety/message_block\"&gt;message_block&lt;/a&gt;, a gem which simplifies generating flash and error messages. Additionally, Ben discusses how to test gem development using Cucumber and Cukigem.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.scribd.com/doc/48397290/Message-Block\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Tue, 08 Feb 2011 15:57:28 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/S41NSn0NN0w/91</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/91</guid>\r\n      <itunes:summary>Ben Hughes shows off message_block, a gem which simplifies generating flash and error messages. Additionally, Ben discusses how to test gem development using Cucumber and Cukigem.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>15:12</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/91</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/oUBbkBnKDRs/091_messageblock.m4v\" length=\"137513207\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/091_messageblock.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 090: CoffeeScript</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/90.png?1297273134\" alt=\"Episode 090: CoffeeScript\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;People are drawn to Ruby by its beautiful syntax, and CoffeeScript brings that same joie de vivre to Javascript. John Lynch shares his experience using CoffeeScript for both web and mobile app development.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/johnthethird/coffeescript-presentation\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Tue, 08 Feb 2011 15:56:49 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/VbotFtzfkPY/90</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/90</guid>\r\n      <itunes:summary>People are drawn to Ruby by its beautiful syntax, and CoffeeScript brings that same joie de vivre to Javascript. John Lynch shares his experience using CoffeeScript for both web and mobile app development.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>21:05</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/90</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/axouieoJzFg/090_coffeescript.m4v\" length=\"152040742\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/090_coffeescript.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 089: Ruleby</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/89.png?1297271650\" alt=\"Episode 089: Ruleby\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Chris McCann demonstrates &lt;a href=\"https://github.com/codeaspects/ruleby\"&gt;Ruleby&lt;/a&gt;, a Ruby implementation of a rules engine based on the Rete algorithm.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/testflyjets/ruleby\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Tue, 08 Feb 2011 15:56:22 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/2_m28SeylQg/89</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/89</guid>\r\n      <itunes:summary>Chris McCann demonstrates Ruleby, a Ruby implementation of a rules engine based on the Rete algorithm.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>34:53</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/89</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/W3lKalQh1d0/089_ruleby.m4v\" length=\"205555126\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/089_ruleby.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 088: Trucker</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/88.png?1297180549\" alt=\"Episode 088: Trucker\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Patrick Crowley talks about Trucker, a new gem that makes it easier to migrate legacy data into Rails apps.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/mokolabs/trucker\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Tue, 08 Feb 2011 15:55:50 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/An_XbuMHkfE/88</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/88</guid>\r\n      <itunes:summary>Patrick Crowley talks about Trucker, a new gem that makes it easier to migrate legacy data into Rails apps.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>20:01</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/88</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/yJfrfyVFCp8/088_trucker.m4v\" length=\"136756713\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/088_trucker.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 087: Searching With Solr</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/87.png?1297287316\" alt=\"Episode 087: Searching With Solr\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Get up and running in no time with enterprise-grade search powered by Solr. Nick Zadrozny shows you what Solr can do, how it works, and how you can make the most of it in production.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/nzadrozny/solr-powr-enterprisegrade-search-for-your-app\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Tue, 08 Feb 2011 15:55:26 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/oYEqUWtIv5s/87</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/87</guid>\r\n      <itunes:summary>Get up and running in no time with enterprise-grade search powered by Solr. Nick Zadrozny shows you what Solr can do, how it works, and how you can make the most of it in production.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>32:40</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/87</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/IhPT689YexA/087_solr.m4v\" length=\"309658295\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/087_solr.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 086: Mobile Web Apps Using jQTouch</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/86.png?1297180488\" alt=\"Episode 086: Mobile Web Apps Using jQTouch\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Patrick Crowley dives into the world of mobile app development and shows how to use the jQTouch framework to quickly build awesome mobile-optimized web apps.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/mokolabs/mobile-web-apps-6864942\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Tue, 08 Feb 2011 15:54:48 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/_5J1_9rUrTY/86</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/86</guid>\r\n      <itunes:summary>Patrick Crowley dives into the world of mobile app development and shows how to use the jQTouch framework to quickly build awesome mobile-optimized web apps.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>34:49</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/86</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/xwXKkLVKudU/086_mobile.m4v\" length=\"189557939\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/086_mobile.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 085: Introduction to Node.js</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/85.png?1297180458\" alt=\"Episode 085: Introduction to Node.js\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Web standards advocate Edward O'Connor gives a quick introduction to Node.js, a new event-driven networking engine for JavaScript.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://edward.oconnor.cx/2010/07/nodejs#title\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Tue, 08 Feb 2011 15:54:18 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/s2tJukLTyHU/85</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/85</guid>\r\n      <itunes:summary>Web standards advocate Edward O'Connor gives a quick introduction to Node.js, a new event-driven networking engine for JavaScript.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>8:39</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/85</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/aEGexEqe8ao/085_node.m4v\" length=\"76007515\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/085_node.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 084: Quick and Cheap Usability for Programmers</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/84.png?1297180390\" alt=\"Episode 084: Quick and Cheap Usability for Programmers\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Megan O'Rorke introduces the general concept and goal of usability, and shares some quick and cheap usability resources for Ruby developers.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/leggomymego/intro-to-ux-for-programmers\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Tue, 08 Feb 2011 15:53:10 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/9a_IQGjyL7o/84</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/84</guid>\r\n      <itunes:summary>Megan O'Rorke introduces the general concept and goal of usability, and shares some quick and cheap usability resources for Ruby developers.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>12:55</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/84</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/Ixe8c1rg32M/084_usability.m4v\" length=\"125912010\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/084_usability.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 083: Testing Philosophies</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/83.png?1297180355\" alt=\"Episode 083: Testing Philosophies\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Rob Kaufman discusses common testing philosophies and methodologies in use by the Ruby community.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/notch8/testing-philosphies\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Tue, 08 Feb 2011 15:52:36 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/KpL9bhFSe_8/83</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/83</guid>\r\n      <itunes:summary>Rob Kaufman discusses common testing philosophies and methodologies in use by the Ruby community.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>16:47</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/83</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/kEJ94uxY52o/083_testing.m4v\" length=\"115479266\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/083_testing.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 082: Behavior Driven Development Using Ruby, Cucumber, and rSpec</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/82.png?1297367446\" alt=\"Episode 082: Behavior Driven Development Using Ruby, Cucumber, and rSpec\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://calicowebdev.com/\"&gt;Steve Ross&lt;/a&gt; gives an introduction to behavior driven development using Ruby, &lt;a href=\"http://cukes.info/\"&gt;Cucumber&lt;/a&gt;, and &lt;a href=\"http://relishapp.com/rspec\"&gt;rSpec&lt;/a&gt;. This is a practical how-to for developers who have not yet integrated behavior driven development into their workflow.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/sxross/sd-ruby-bdd-talk\"&gt;download slides&lt;/a&gt; and &lt;a href=\"http://calicowebdev.com/tblog/index.html\"&gt;view notes&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Tue, 08 Feb 2011 15:51:47 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/i4_Ddni6Oh4/82</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/82</guid>\r\n      <itunes:summary>Steve Ross gives an introduction to behavior driven development using Ruby, Cucumber, and rSpec. This is a practical how-to for developers who have not yet integrated behavior driven development into their workflow.\r\n\r\nBonus content: download slides and view notes from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>01:07:34</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/82</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/FRO4updi7Y8/082_bdd.m4v\" length=\"371602122\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/082_bdd.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 081: Rolling with Riak</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/81.png?1275694882\" alt=\"Episode 081: Rolling with Riak\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;John Lynch from &lt;a href=\"http://rigelgroupllc.com/\"&gt;Rigel Group&lt;/a&gt; gives an overview of &lt;a href=\"http://riak.basho.com/\"&gt;Riak&lt;/a&gt;, the newest kid on the NoSQL block, and explains why it should power your next web-scale app.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/johnthethird/rolling-with-riak\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 04 Jun 2010 23:41:23 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/gThcPsMJUWM/81</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/81</guid>\r\n      <itunes:summary>John Lynch from Rigel Group gives an overview of Riak, the newest kid on the NoSQL block, and explains why it should power your next web-scale app.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>28:25</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/81</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/rvgutic3qXU/081_riak.m4v\" length=\"221668986\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/081_riak.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 080: EventMachine, AMQP, and WebSockets: The Musical</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/80.png?1274895968\" alt=\"Episode 080: EventMachine, AMQP, and WebSockets: The Musical\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://dansimpson.name/\"&gt;Dan Simpson&lt;/a&gt; and &lt;a href=\"http://beyondthepath.com/\"&gt;Nick Zadrozny&lt;/a&gt; give an overview of cutting-edge, real-time programming for the web; and demonstrate an interactive, web-based sequencer.&lt;/p&gt;</description>\r\n      <pubDate>Wed, 26 May 2010 17:46:09 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/NEwnvbVjJrw/80</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/80</guid>\r\n      <itunes:summary>Dan Simpson and Nick Zadrozny give an overview of cutting-edge, real-time programming for the web; and demonstrate an interactive, web-based sequencer.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>23:47</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/80</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/XEHs1oQxHGw/080_musical.m4v\" length=\"269070177\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/080_musical.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 079: Artificial Stupidity: Adding Smarts to Yer Kode</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/79.png?1272132777\" alt=\"Episode 079: Artificial Stupidity: Adding Smarts to Yer Kode\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Machine learning and data mining have long been \"black arts\" because they require access to expensive computing clusters. &lt;a href=\"http://evilmartini.com/blog/\"&gt;Randall Thomas&lt;/a&gt; discusses machine learning and the problem spaces it can help solve.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://bit.ly/doz9El\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Sat, 24 Apr 2010 18:12:57 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/PDZil8l8oTc/79</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/79</guid>\r\n      <itunes:summary>Machine learning and data mining have long been \"black arts\" because they require access to expensive computing clusters. Randall Thomas discusses machine learning and the problem spaces it can help solve.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>48:48</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/79</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/YvCUkBfKjvw/079_machine.m4v\" length=\"320991581\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/079_machine.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 078: Scripting Android With Ruby </title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/78.png?1269011170\" alt=\"Episode 078: Scripting Android With Ruby \" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://markranallo.com\"&gt;Mark Ranallo&lt;/a&gt; will give an introduction to using &lt;a href=\"http://jruby.org/\"&gt;JRuby&lt;/a&gt; in conjunction with the &lt;a href=\"http://code.google.com/p/android-scripting/\"&gt;Android Scripting Environment&lt;/a&gt;.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://bit.ly/cCPtSi\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 19 Mar 2010 14:55:23 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/U19Hs2ybkRY/78</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/78</guid>\r\n      <itunes:summary>Mark Ranallo will give an introduction to using JRuby in conjunction with the Android Scripting Environment.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>33:49</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/78</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/1IBLeSaPE0c/078_android.m4v\" length=\"234505343\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/078_android.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 077: Building Your Own Hosting Environment</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/77.png?1265049703\" alt=\"Episode 077: Building Your Own Hosting Environment\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://nicbenders.com/\"&gt;Nic Benders&lt;/a&gt; will show you what's needed for building a hosting environment for production applications. The focus will be on selecting server hardware, picking a Linux Distro, and getting your Rails app running.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/nicbenders/building-your-own-rails-hosting-environment\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Mon, 01 Feb 2010 18:41:44 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/v8UbedOMZA8/77</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/77</guid>\r\n      <itunes:summary>Nic Benders will show you what's needed for building a hosting environment for production applications. The focus will be on selecting server hardware, picking a Linux Distro, and getting your Rails app running.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>39:44</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/77</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/M4mE_xAyEYQ/077_building.m4v\" length=\"140574484\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/077_building.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 076: OpenCyc</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/76.png?1264463598\" alt=\"Episode 076: OpenCyc\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://relevantlogic.com/\"&gt;Guyren Howe&lt;/a&gt; gives a way-too-brief demonstration of &lt;a href=\"http://www.opencyc.org/doc\"&gt;OpenCyc&lt;/a&gt;, an AI technology based on the world's most comprehensive general-purpose ontology. Along the way, he reveals how to talk to OpenCyc using &lt;a href=\"http://jruby.org/\"&gt;JRuby&lt;/a&gt;.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/gisborne/cyc-presentation\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Mon, 25 Jan 2010 23:50:51 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/nMzzlLJEpIw/76</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/76</guid>\r\n      <itunes:summary>Guyren Howe gives a way-too-brief demonstration of OpenCyc, an AI technology based on the world's most comprehensive general-purpose ontology. Along the way, he reveals how to talk to OpenCyc using JRuby.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>28:20</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/76</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/K8aaDqlwWws/076_opencyc.m4v\" length=\"222344033\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/076_opencyc.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 075: Heroku</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/75.png?1263841546\" alt=\"Episode 075: Heroku\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://github.com/bmizerany\"&gt;Blake Mizerany&lt;/a&gt; from &lt;a href=\"http://heroku.com/\"&gt;Heroku&lt;/a&gt; shows off the company's cloud platform for Ruby applications. The talk covers Heroku basics, &lt;a href=\"http://www.sinatrarb.com/\"&gt;Sinatra&lt;/a&gt;, Ruby development, and scaling.&lt;/p&gt;</description>\r\n      <pubDate>Mon, 18 Jan 2010 19:05:46 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/MSXivVF9p_Q/75</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/75</guid>\r\n      <itunes:summary>Blake Mizerany from Heroku shows off the company's cloud platform for Ruby applications. The talk covers Heroku basics, Sinatra, Ruby development, and scaling.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>01:03:50</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/75</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/l21Qh7MFylQ/074_heroku.m4v\" length=\"658653024\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/074_heroku.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 074: Moonshine: 190 Proof Deployment</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/74.png?1263230377\" alt=\"Episode 074: Moonshine: 190 Proof Deployment\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://jarinudom.com\"&gt;Jarin Udom&lt;/a&gt; shows how to set up a full Rails stack and deploy your app on a bare Ubuntu server in 10 minutes. Is he drunk? Find out!&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/jarinudom/deploying-rails-applications-with-moonshine\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Mon, 11 Jan 2010 17:19:38 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/30PSCVMk4K4/74</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/74</guid>\r\n      <itunes:summary>Jarin Udom shows how to set up a full Rails stack and deploy your app on a bare Ubuntu server in 10 minutes. Is he drunk? Find out!\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>13:21</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/74</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/bsbOvCwzWss/073_moonshine.m4v\" length=\"108035682\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/073_moonshine.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 073: Cappuccino</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/73.png?1262709648\" alt=\"Episode 073: Cappuccino\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://cappuccino.org/\"&gt;Cappuccino&lt;/a&gt; is a framework for building desktop-caliber applications that run in a web browser. Brian Chapados reviews the design concepts of Cappuccino and the underlying Objective-J language, shows how to get started, and builds a simple app for searching scientific literature databases.&lt;/p&gt;\r\n\r\n&lt;p&gt;This talk was part of the &lt;a href=\"http://djangosd.jottit.com\"&gt;DjangoSD&lt;/a&gt;/&lt;a href=\"http://sdruby.org\"&gt;SD Ruby&lt;/a&gt; mashup meeting.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/chapados/cappuccino-sdruby-20090806\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Tue, 05 Jan 2010 16:40:49 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/23cOw1dMK7I/73</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/73</guid>\r\n      <itunes:summary>Cappuccino is a framework for building desktop-caliber applications that run in a web browser. Brian Chapados reviews the design concepts of Cappuccino and the underlying Objective-J language, shows how to get started, and builds a simple app for searching scientific literature databases.\r\n\r\nThis talk was part of the DjangoSD/SD Ruby mashup meeting.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>19:00</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/73</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/5wrr9xGwRk4/072_cappuccino.m4v\" length=\"226305134\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/072_cappuccino.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 072: MongoDB</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/72.png?1262027446\" alt=\"Episode 072: MongoDB\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://spitfiresky.com\"&gt;Scott Motte&lt;/a&gt; introduces us to the document-oriented database &lt;a href=\"http://www.mongodb.org\"&gt;MongoDB&lt;/a&gt; and shows how to use MongoDB as an alternative to MySQL and ActiveRecord using the &lt;a href=\"http://github.com/jnunemaker/mongomapper\"&gt;MongoMapper&lt;/a&gt; gem.&lt;/p&gt;\r\n\r\n&lt;p&gt;This talk was part of the &lt;a href=\"http://djangosd.jottit.com\"&gt;DjangoSD&lt;/a&gt;/&lt;a href=\"http://sdruby.org\"&gt;SD Ruby&lt;/a&gt; mashup meeting.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/scottmotte/mongodb-1825613\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Mon, 28 Dec 2009 19:10:46 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/Jvc4kj9WAI8/72</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/72</guid>\r\n      <itunes:summary>Scott Motte introduces us to the document-oriented database MongoDB and shows how to use MongoDB as an alternative to MySQL and ActiveRecord using the MongoMapper gem.\r\n\r\nThis talk was part of the DjangoSD/SD Ruby mashup meeting.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>14:37</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/72</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/KWWZQrd7wxg/071_mongo.m4v\" length=\"144456322\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/071_mongo.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 071: Mess of Protocols</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/71.png?1261156586\" alt=\"Episode 071: Mess of Protocols\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://relevantlogic.com/\"&gt;Guyren Howe&lt;/a&gt; discusses how we use different applications and protocols to govern how we communicate: with whom, how privately, how immediately, whether through text/video/whatever, and so on. He proposes we decouple these ideas, which would simplify and enrich the ways we communicate.&lt;/p&gt;\r\n\r\n&lt;p&gt;This talk was part of the &lt;a href=\"http://djangosd.jottit.com\"&gt;DjangoSD&lt;/a&gt;/&lt;a href=\"http://sdruby.org\"&gt;SD Ruby&lt;/a&gt; mashup meeting.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/gisborne/thoughts-on-communications-in-the-21st-century\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 18 Dec 2009 17:16:27 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/HQef7kPfY-0/71</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/71</guid>\r\n      <itunes:summary>Guyren Howe discusses how we use different applications and protocols to govern how we communicate: with whom, how privately, how immediately, whether through text/video/whatever, and so on. He proposes we decouple these ideas, which would simplify and enrich the ways we communicate.\r\n\r\nThis talk was part of the DjangoSD/SD Ruby mashup meeting.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>16:56</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/71</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/rH5G3OrZy-c/070_messofprotocols.m4v\" length=\"196655681\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/070_messofprotocols.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 070: Processing Real-world HTML</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/70.png?1260810099\" alt=\"Episode 070: Processing Real-world HTML\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://edward.oconnor.cx\"&gt;Edward O'Connor&lt;/a&gt; from djangosd gives an overview of &lt;a href=\"http://code.google.com/p/html5lib/\"&gt;html5lib&lt;/a&gt;, a major-desktop-browser-compatible HTML parser and tokenizer for both Ruby and Python.&lt;/p&gt;\r\n\r\n&lt;p&gt;This talk was part of the &lt;a href=\"http://djangosd.jottit.com\"&gt;DjangoSD&lt;/a&gt;/&lt;a href=\"http://sdruby.org\"&gt;SD Ruby&lt;/a&gt; mashup meeting.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://edward.oconnor.cx/2009/08/djangosd-html5lib#title\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Mon, 14 Dec 2009 17:01:40 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/wpPLqIWJz24/70</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/70</guid>\r\n      <itunes:summary>Edward O'Connor from djangosd gives an overview of html5lib, a major-desktop-browser-compatible HTML parser and tokenizer for both Ruby and Python.\r\n\r\nThis talk was part of the DjangoSD/SD Ruby mashup meeting.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>07:43</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/70</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/n3eR90Xn3dw/069_html5lib.m4v\" length=\"65009081\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/069_html5lib.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 069: Rails 3: From Vaporware to Awesomeness in 12 Months</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/69.png?1259966961\" alt=\"Episode 069: Rails 3: From Vaporware to Awesomeness in 12 Months\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://yehudakatz.com/\"&gt;Yehuda Katz&lt;/a&gt; returns to San Diego with a report on the latest developments with the &lt;a href=\"http://rubyonrails.org/\"&gt;Ruby on Rails&lt;/a&gt; web application framework.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://bit.ly/rails3-sdruby\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 04 Dec 2009 22:49:22 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/eZdtwd6uh8s/69</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/69</guid>\r\n      <itunes:summary>Yehuda Katz returns to San Diego with a report on the latest developments with the Ruby on Rails web application framework.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>01:01:47</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/69</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/DGu8xAmFjH0/075_rails3.m4v\" length=\"391063520\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/075_rails3.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 068: Hybrid iPhone Apps with Titanium Mobile </title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/68.png?1259597435\" alt=\"Episode 068: Hybrid iPhone Apps with Titanium Mobile \" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://spitfiresky.com\"&gt;Scott Motte&lt;/a&gt; talks about iPhone development using the &lt;a href=\"http://www.appcelerator.com/products/titanium-mobile/\"&gt;Titanium Mobile&lt;/a&gt; framework, including how to build a basic app.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/scottmotte/titanium-mobile\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Mon, 30 Nov 2009 16:10:37 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/58OMq7vy2ys/68</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/68</guid>\r\n      <itunes:summary>Scott Motte talks about iPhone development using the Titanium Mobile framework, including how to build a basic app.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>17:34</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/68</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/1KY_DKIucUw/068_titanium.m4v\" length=\"189226588\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/068_titanium.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 067: Cooking up a Cloud (with Chef &amp; EC2)</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/67.png?1258745212\" alt=\"Episode 067: Cooking up a Cloud (with Chef &amp;amp; EC2)\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://github.com/ezmobius/chef-deploy\"&gt;Chef&lt;/a&gt; is a young and ambitious Ruby DSL built for easy configuration management. &lt;a href=\"http://beyondthepath.com/\"&gt;Nick Zadrozny&lt;/a&gt; will take a look at the philosophy of configuration management, the basics of Chef, and some recipes to build out a basic Rails server.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/beyondthepath/cooking-up-a-cloud-an-intro-to-chef-sdruby-20090903\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 20 Nov 2009 16:41:45 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/EndWleaMeu4/67</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/67</guid>\r\n      <itunes:summary>Chef is a young and ambitious Ruby DSL built for easy configuration management. Nick Zadrozny will take a look at the philosophy of configuration management, the basics of Chef, and some recipes to build out a basic Rails server.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>49:51</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/67</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/R42x6Owie6E/067_chef.m4v\" length=\"605513307\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/067_chef.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 066: Unit Testing: The Easy Way</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/66.png?1258137619\" alt=\"Episode 066: Unit Testing: The Easy Way\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;With &lt;a href=\"http://rspec.info\"&gt;Rspec&lt;/a&gt; and &lt;a href=\"http://cukes.info\"&gt;Cucumber&lt;/a&gt;, Ruby has brought testing to a whole new level, but tests still require a lot of work.&lt;/p&gt;\r\n\r\n&lt;p&gt;But things just got better. &lt;a href=\"http://www.spunlabs.com/\"&gt;Llewellyn Falco&lt;/a&gt; will show you new patterns and practices to dramatically decrease the amount of effort needed to test.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/llewellynfalco/unit-testing-the-easy-way\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 13 Nov 2009 18:40:20 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/A5d2qgTdRIw/66</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/66</guid>\r\n      <itunes:summary>With Rspec and Cucumber, Ruby has brought testing to a whole new level, but tests still require a lot of work.\r\n\r\nBut things just got better. Llewellyn Falco will show you new patterns and practices to dramatically decrease the amount of effort needed to test.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>70:16</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/66</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/96z9KceH3lg/066_testing.m4v\" length=\"723020053\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/066_testing.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 065: HotRuby + jQuery: Ruby in the Browser</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/65.png?1257785173\" alt=\"Episode 065: HotRuby + jQuery: Ruby in the Browser\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://strd6.com\"&gt;Daniel Moore&lt;/a&gt; will demonstrate using the &lt;a href=\"http://hotruby.yukoba.jp\"&gt;HotRuby&lt;/a&gt; VM to execute Ruby code natively in the browser. The demonstration shows how Ruby can call native JS libraries, like &lt;a href=\"http://jquery.com\"&gt;jQuery&lt;/a&gt;, to perform DOM manipulation.&lt;/p&gt;</description>\r\n      <pubDate>Mon, 09 Nov 2009 16:46:14 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/I_ae4XPmyzQ/65</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/65</guid>\r\n      <itunes:summary>Daniel Moore will demonstrate using the HotRuby VM to execute Ruby code natively in the browser. The demonstration shows how Ruby can call native JS libraries, like jQuery, to perform DOM manipulation.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>13:40</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/65</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/Qk8nwAI4uYo/065_hotruby.m4v\" length=\"84263833\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/065_hotruby.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 064: Ruby 1.9.1</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/64.png?1242953494\" alt=\"Episode 064: Ruby 1.9.1\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://bradlyfeeley.com/\"&gt;Bradly Feeley&lt;/a&gt; reviews the new features and syntax changes in the latest production ready Ruby release, &lt;a href=\"http://www.ruby-lang.org/en/news/2009/01/30/ruby-1-9-1-released/\"&gt;Ruby 1.9.1&lt;/a&gt;.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/bradly/ruby-19-introduction\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 22 May 2009 00:51:35 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/yY4i-VMTwe4/64</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/64</guid>\r\n      <itunes:summary>Bradly Feeley reviews the new features and syntax changes in the latest production ready Ruby release, Ruby 1.9.1.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>27:23</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/64</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/Q6kEwENf1FA/064_ruby191.m4v\" length=\"165820179\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/064_ruby191.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 063: Earning an \"A\" in YSlow</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/63.png?1242953407\" alt=\"Episode 063: Earning an &amp;quot;A&amp;quot; in YSlow\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://beyondthepath.com/\"&gt;Nick Zadrozny&lt;/a&gt; takes us on a tour of basic website performance optimization, covering 13 optimizations you shouldn't deploy your site without. He'll also go over some sample implementations for a typical Rails app, including a demo of how to serve your public assets from Amazon's new &lt;a href=\"http://aws.amazon.com/cloudfront/\"&gt;CloudFront CDN&lt;/a&gt;.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 22 May 2009 00:50:08 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/OUhvtNS_lKU/63</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/63</guid>\r\n      <itunes:summary>Nick Zadrozny takes us on a tour of basic website performance optimization, covering 13 optimizations you shouldn't deploy your site without. He'll also go over some sample implementations for a typical Rails app, including a demo of how to serve your public assets from Amazon's new CloudFront CDN.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>30:16</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/63</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/92NDWsAiQTI/063_yslow.m4v\" length=\"220394695\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/063_yslow.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 062: Track Magic</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/62.png?1242953303\" alt=\"Episode 062: Track Magic\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Jim Rea will demonstrate &lt;a href=\"http://www.provue.com/trackmagic/\"&gt;Track Magic&lt;/a&gt;, a  plugin for &lt;a href=\"http://www.provue.com/Panorama/\"&gt;Panorama&lt;/a&gt; that allows Rails developers to interact directly with Rails databases on the desktop without a browser.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 22 May 2009 00:48:24 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/hTCuQ86WOeE/62</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/62</guid>\r\n      <itunes:summary>Jim Rea will demonstrate Track Magic, a  plugin for Panorama that allows Rails developers to interact directly with Rails databases on the desktop without a browser.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>46:59</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/62</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/MPM3SHaBim8/062_trackmagic.m4v\" length=\"530974981\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/062_trackmagic.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 061: Tanning Bed</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/61.png?1242952972\" alt=\"Episode 061: Tanning Bed\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://notch8.com/\"&gt;Rob Kaufman&lt;/a&gt; demonstrates &lt;a href=\"http://notch8.github.com/tanning_bed/\"&gt;Tanning Bed&lt;/a&gt;, his new Solr search mixin for any Ruby class. Tanning Bed makes fewer assumptions about how you want to deal with the &lt;a href=\"http://lucene.apache.org/solr/\"&gt;Solr search engine&lt;/a&gt;, while still providing easy Ruby-like interfaces.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/notch8/tanning-bed\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 22 May 2009 00:42:53 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/xoazxtH3Ytw/61</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/61</guid>\r\n      <itunes:summary>Rob Kaufman demonstrates Tanning Bed, his new Solr search mixin for any Ruby class. Tanning Bed makes fewer assumptions about how you want to deal with the Solr search engine, while still providing easy Ruby-like interfaces.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>19:18</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/61</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/2TmSe5x_Cfk/061_tanningbed.m4v\" length=\"154418201\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/061_tanningbed.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 060: Migrating Legacy Data</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/60.png?1242792928\" alt=\"Episode 060: Migrating Legacy Data\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Let's face it. Migrating legacy data into a new app is a giant pain in the ass. It's slow, error prone, and a ton of work.&lt;/p&gt;\r\n\r\n&lt;p&gt;What if you could import old data into a new app before it's finished? Normalize fields? Clean up old data? And migrate often? &lt;a href=\"http://the.railsi.st\"&gt;Patrick Crowley&lt;/a&gt; shows the way.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/mokolabs/migrating-legacy-data\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Wed, 20 May 2009 04:15:28 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/4AzeoUj4aMo/60</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/60</guid>\r\n      <itunes:summary>Let's face it. Migrating legacy data into a new app is a giant pain in the ass. It's slow, error prone, and a ton of work.\r\n\r\nWhat if you could import old data into a new app before it's finished? Normalize fields? Clean up old data? And migrate often? Patrick Crowley shows the way.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>23:29</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/60</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/5BI76qfgW0g/060_legacy.m4v\" length=\"228305840\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/060_legacy.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 059: Rails Case Study</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/59.png?1242791813\" alt=\"Episode 059: Rails Case Study\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Guyren Howe is doing something a little different with his current project, trying to make it more user-modifiable and user friendly than your average intraweb application, and removing some of the redundancy in regular Rails code. The current state of the project will be discussed, along with whether the interesting parts should be open sourced.&lt;/p&gt;</description>\r\n      <pubDate>Wed, 20 May 2009 03:56:54 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/krp3iGtGLMI/59</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/59</guid>\r\n      <itunes:summary>Guyren Howe is doing something a little different with his current project, trying to make it more user-modifiable and user friendly than your average intraweb application, and removing some of the redundancy in regular Rails code. The current state of the project will be discussed, along with whether the interesting parts should be open sourced.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>13:13</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/59</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/QN9YITp49-s/059_casestudy.m4v\" length=\"112313358\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/059_casestudy.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 058: MacRuby: What's the big deal?</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/58.png?1242790820\" alt=\"Episode 058: MacRuby: What's the big deal?\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Last year, Apple released &lt;a href=\"http://www.macruby.org/\"&gt;MacRuby&lt;/a&gt;, an open source Ruby implementation written on top of the Objective-C runtime. Writing native MacOSX applications in Ruby without having to pay the cost of using a bridge is now a reality. This is an important milestone for Ruby, Apple and the Ruby community.&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://merbist.com/\"&gt;Matt Aimonetti&lt;/a&gt; will explain the implementation, show how to build desktop applications with MacRuby &amp;amp; &lt;a href=\"http://www.macruby.org/hotcocoa.html\"&gt;HotCocoa&lt;/a&gt;, and discuss why Ruby developers should add this new tool to their utility belt. Matt will also talk about the future of MacRuby.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/mattetti/macruby-when-objectivec-and-ruby-meet\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Wed, 20 May 2009 03:40:21 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/Bk39qx7g_hE/58</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/58</guid>\r\n      <itunes:summary>Last year, Apple released MacRuby, an open source Ruby implementation written on top of the Objective-C runtime. Writing native MacOSX applications in Ruby without having to pay the cost of using a bridge is now a reality. This is an important milestone for Ruby, Apple and the Ruby community.\r\n\r\nMatt Aimonetti will explain the implementation, show how to build desktop applications with MacRuby &amp;amp; HotCocoa, and discuss why Ruby developers should add this new tool to their utility belt. Matt will also talk about the future of MacRuby.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>58:19</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/58</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/ERoNglpPya8/058_macruby.m4v\" length=\"418559883\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/058_macruby.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 057: Facebooker Plugin</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/57.png?1242789673\" alt=\"Episode 057: Facebooker Plugin\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Michael Madrid discusses Styleact (a Facebook application) and how to develop Facebook apps using Ruby on Rails and the &lt;a href=\"http://facebooker.rubyforge.org/\"&gt;Facebooker plugin&lt;/a&gt;.&lt;/p&gt;</description>\r\n      <pubDate>Wed, 20 May 2009 03:21:15 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/nH2KI4KDoEU/57</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/57</guid>\r\n      <itunes:summary>Michael Madrid discusses Styleact (a Facebook application) and how to develop Facebook apps using Ruby on Rails and the Facebooker plugin.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>38:23</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/57</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/JxNxkYruLJ8/057_facebook.m4v\" length=\"424887377\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/057_facebook.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 056: Ruby 1.9 or Bust!</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/56.png?1240852816\" alt=\"Episode 056: Ruby 1.9 or Bust!\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://notch8.com/\"&gt;Rob Kaufman&lt;/a&gt; talks about his new &lt;a href=\"http://ruby19orbust.com/\"&gt;Ruby 1.9 or Bust&lt;/a&gt; project. The project is dedicated to updating popular Ruby gems to be Ruby 1.9 compatible, and is seeking donations to hire a full-time developer to work on porting gems.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/notch8/ruby-19-or-bust-presentation\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Mon, 27 Apr 2009 17:20:19 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/qx5IFHyBvOY/56</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/56</guid>\r\n      <itunes:summary>Rob Kaufman talks about his new Ruby 1.9 or Bust project. The project is dedicated to updating popular Ruby gems to be Ruby 1.9 compatible, and is seeking donations to hire a full-time developer to work on porting gems.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>21:45</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/56</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/Tb4Kdva_rwM/056_ruby19.m4v\" length=\"239418748\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/056_ruby19.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 055: MySQL, Postgres, and Rails</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/55.png?1236986586\" alt=\"Episode 055: MySQL, Postgres, and Rails\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Guyren Howe has considerable experience with both &lt;a href=\"http://mysql.com\"&gt;MySQL&lt;/a&gt; and &lt;a href=\"http://www.postgresql.org/\"&gt;PostgreSQL&lt;/a&gt;. He discusses the myriad technical and other advantages to using PostgreSQL instead of MySQL for, well, pretty much anything.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/gisborne/postgres-presentation-presentation/\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 10 Oct 2008 15:04:53 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/yNnISnQFPpY/55</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/55</guid>\r\n      <itunes:summary>Guyren Howe has considerable experience with both MySQL and PostgreSQL. He discusses the myriad technical and other advantages to using PostgreSQL instead of MySQL for, well, pretty much anything.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>27:55</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/55</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/C71fEqyzlZk/055_postgres.m4v\" length=\"215719712\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/055_postgres.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 054: Paperclip</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/54.png?1236986586\" alt=\"Episode 054: Paperclip\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://the.railsi.st\"&gt;Patrick Crowley&lt;/a&gt; talks about &lt;a href=\"http://www.thoughtbot.com/projects/paperclip\"&gt;Paperclip&lt;/a&gt;, a lightweight plugin for handling attachments.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/mokolabs/paperclip-presentation/\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Sat, 20 Sep 2008 15:31:00 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/zV5M8vii5dc/54</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/54</guid>\r\n      <itunes:summary>Patrick Crowley talks about Paperclip, a lightweight plugin for handling attachments.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>29:15</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/54</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/kEa6m5U8EKQ/054_paperclip.m4v\" length=\"236034129\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/054_paperclip.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 053: Ruby Arduino Development (RAD)</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/53.png?1236986586\" alt=\"Episode 053: Ruby Arduino Development (RAD)\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Sensors and servos in hand, &lt;a href=\"http://jdbarnhart.com\"&gt;JD Barnhart&lt;/a&gt; shows off the current state of &lt;a href=\"http://rad.rubyforge.org\"&gt;RAD&lt;/a&gt;, the Rails inspired framework bringing convention over configuration and Ruby sensibility to the &lt;a href=\"http://www.arduino.cc\"&gt;Arduino Physical computing platform&lt;/a&gt;.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/madrona/rad-demo-at-sd-ruby-presentation/\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 12 Sep 2008 21:17:43 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/sWlI7g_mFAA/53</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/53</guid>\r\n      <itunes:summary>Sensors and servos in hand, JD Barnhart shows off the current state of RAD, the Rails inspired framework bringing convention over configuration and Ruby sensibility to the Arduino Physical computing platform.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>22:59</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/53</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/2q1zO2J_kXM/053_rad.m4v\" length=\"188760803\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/053_rad.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 052: Shadow</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/52.png?1236986585\" alt=\"Episode 052: Shadow\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://jordanfowler.com\"&gt;Jordan Fowler&lt;/a&gt; reveals his new &lt;a href=\"http://github.com/TheBreeze/shadow/tree/master/\"&gt;Shadow plugin&lt;/a&gt; that tracks attribute and association changes to ActiveRecord objects, making it easy to create activity feeds for your application.&lt;/p&gt;</description>\r\n      <pubDate>Sat, 30 Aug 2008 19:48:45 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/TdraNLiTRF4/52</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/52</guid>\r\n      <itunes:summary>Jordan Fowler reveals his new Shadow plugin that tracks attribute and association changes to ActiveRecord objects, making it easy to create activity feeds for your application.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>17:55</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/52</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/lF-pzQ1oLiE/052_shadow.m4v\" length=\"198617810\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/052_shadow.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 051: Archaeopteryx</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/51.png?1236986585\" alt=\"Episode 051: Archaeopteryx\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://gilesbowkett.blogspot.com\"&gt;Giles Bowkett&lt;/a&gt; shows off his mega-cool MIDI scaffolding generator for creating dance music with Ruby.&lt;/p&gt;</description>\r\n      <pubDate>Thu, 21 Aug 2008 00:37:28 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/CXKyTQIaFTU/51</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/51</guid>\r\n      <itunes:summary>Giles Bowkett shows off his mega-cool MIDI scaffolding generator for creating dance music with Ruby.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>56:21</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/51</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/uk80ZkQ2BDo/051_archaeopteryx.m4v\" length=\"671928895\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/051_archaeopteryx.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 050: One-man Lightning Talk</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/50.png?1236986584\" alt=\"Episode 050: One-man Lightning Talk\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://glu.ttono.us\"&gt;Kevin Clark&lt;/a&gt; is back again and talking about his latest work: Rubinius, grammar parsing from ruby, kqueue, and massive build systems.&lt;/p&gt;</description>\r\n      <pubDate>Sat, 28 Jun 2008 00:00:24 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/WjbqpySrHOU/50</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/50</guid>\r\n      <itunes:summary>Kevin Clark is back again and talking about his latest work: Rubinius, grammar parsing from ruby, kqueue, and massive build systems.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>56:13</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/50</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/jUtjoTrsQP0/050_kfc.m4v\" length=\"454441938\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/050_kfc.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 049: Intro to EC2</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/49.png?1236986584\" alt=\"Episode 049: Intro to EC2\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://blog.assaydepot.com\"&gt;Chris Petersen&lt;/a&gt; gives an introduction to working with &lt;a href=\"http://aws.amazon.com/ec2\"&gt;Amazon EC2&lt;/a&gt;, and discusses the advantages and disadvantage to hosting your Ruby applications there.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/cpetersen/ec2\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 20 Jun 2008 00:19:27 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/mA99BIJMMXk/49</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/49</guid>\r\n      <itunes:summary>Chris Petersen gives an introduction to working with Amazon EC2, and discusses the advantages and disadvantage to hosting your Ruby applications there.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>22:21</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/49</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/R3yuw8KtfpY/049_ec2.m4v\" length=\"70473542\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/049_ec2.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 048: Ruby for Data Processing</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/48.png?1236986583\" alt=\"Episode 048: Ruby for Data Processing\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://chapados.org\"&gt;Brian Chapados&lt;/a&gt; talks about using ruby &amp;amp; rake to build a simple workflow to coordinate external processes.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/chapados/processing-data-with-ruby\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 13 Jun 2008 21:31:40 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/kyv0ZumVoIk/48</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/48</guid>\r\n      <itunes:summary>Brian Chapados talks about using ruby &amp;amp; rake to build a simple workflow to coordinate external processes.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>6:00</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/48</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/ywhIdPoJIzE/048_ruby_for_data.m4v\" length=\"49453445\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/048_ruby_for_data.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 047: Lazy Indexing</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/47.png?1236986583\" alt=\"Episode 047: Lazy Indexing\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://railsontherun.com/\"&gt;Matt Aimonetti&lt;/a&gt; shows how to quickly get your database queries to run faster when you are not a DBA and are running out of time.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/mattetti/lazy-indexing-466697/\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 13 Jun 2008 21:31:29 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/PKlUdC319gM/47</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/47</guid>\r\n      <itunes:summary>Matt Aimonetti shows how to quickly get your database queries to run faster when you are not a DBA and are running out of time.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>20:51</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/47</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/ZgG8EUxI2Yo/047_indexing.m4v\" length=\"152536980\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/047_indexing.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 046: RSpec in 15 minutes</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/46.png?1236986582\" alt=\"Episode 046: RSpec in 15 minutes\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://workingwithrails.com/person/11918-cliff-rhyne\"&gt;Cliff Rhyne&lt;/a&gt; describes in 15 minutes what it takes to get up and running with RSpec.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://docs.google.com/EmbedSlideshow?docid=dd7r668z_6f5xt2tf8\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Thu, 05 Jun 2008 16:57:23 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/UtDx7i1F-GI/46</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/46</guid>\r\n      <itunes:summary>Cliff Rhyne describes in 15 minutes what it takes to get up and running with RSpec.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>12:12</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/46</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/eUe94t38G3Y/046_rspec.m4v\" length=\"93226379\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/046_rspec.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 045: Merb</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/45.png?1236986582\" alt=\"Episode 045: Merb\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Much like Rails, Merb is a MVC web-framework written in Ruby. In this talk, &lt;a href=\"http://jordanfowler.com/\"&gt;Jordan Fowler&lt;/a&gt; introduces Merb and presents some of the distinctions and tradeoffs between the two frameworks.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://docs.google.com/Present?docid=dfxf4brr_15cqs63pxf\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Thu, 05 Jun 2008 16:32:34 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/Mi7D1sfIkb0/45</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/45</guid>\r\n      <itunes:summary>Much like Rails, Merb is a MVC web-framework written in Ruby. In this talk, Jordan Fowler introduces Merb and presents some of the distinctions and tradeoffs between the two frameworks.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>38:32</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/45</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/aT5niCg-DEI/045_merb.m4v\" length=\"358968540\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/045_merb.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 044: Ext JavaScript Library</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/44.png?1236986581\" alt=\"Episode 044: Ext JavaScript Library\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Adam Grant talks about his experiences using Ext, a new Javascript library for creating advanced UI controls (like grids, trees, progress bars, etc).&lt;/p&gt;</description>\r\n      <pubDate>Sat, 24 May 2008 00:23:28 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/X7a9zreIUQY/44</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/44</guid>\r\n      <itunes:summary>Adam Grant talks about his experiences using Ext, a new Javascript library for creating advanced UI controls (like grids, trees, progress bars, etc).</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>46:36</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/44</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/TTMNilIsRM4/044_ext_javascript.m4v\" length=\"393764584\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/044_ext_javascript.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 043: Facebook API</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/43.png?1236986581\" alt=\"Episode 043: Facebook API\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;John Bresnik discusses the ruby library for the Facebook API, Facebooker, based on his production experience building Facebook applications. Presentation includes general overview of the Facebook API, Facebooker specifics, as well as code examples.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.rubyguerrilla.com/slides/SDRUBY-fb.pdf\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Sat, 24 May 2008 00:19:48 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/lTi_bwR2lzg/43</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/43</guid>\r\n      <itunes:summary>John Bresnik discusses the ruby library for the Facebook API, Facebooker, based on his production experience building Facebook applications. Presentation includes general overview of the Facebook API, Facebooker specifics, as well as code examples.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>41:15</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/43</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/tsuoLAeAQmQ/043_facebooker.m4v\" length=\"408966339\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/043_facebooker.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 042: Deploying Rails on Slicehost</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/42.png?1236986580\" alt=\"Episode 042: Deploying Rails on Slicehost\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://superjared.com/\"&gt;Jared Kuolt&lt;/a&gt; shows how to quickly deploy your Rails application to a &lt;a href=\"http://www.slicehost.com/\"&gt;Slicehost&lt;/a&gt; VPS. This talk covers installing MySQL, Nginx, Rails and Mongrel, as well as deployment using Capistrano.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://superjared.com/static/talk.pdf\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Mon, 28 Apr 2008 18:29:54 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/krxe4kMV8ao/42</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/42</guid>\r\n      <itunes:summary>Jared Kuolt shows how to quickly deploy your Rails application to a Slicehost VPS. This talk covers installing MySQL, Nginx, Rails and Mongrel, as well as deployment using Capistrano.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>26:21</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/42</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/Cw3Qxc5Tv3k/042_slicehost.m4v\" length=\"165758132\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/042_slicehost.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 041: MySQL Clustering</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/41.png?1236986578\" alt=\"Episode 041: MySQL Clustering\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://notch8.com/\"&gt;Rob Kaufman&lt;/a&gt; takes on database scaling with MySQL. This talk will walk you through setting up a Master (read write) Slave (read only) MySQL network, as well as a Master Master, or Multi-Master network. The talk will also cover the whys and when of database clustering.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/notch8/mysql-sm/\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Mon, 28 Apr 2008 18:18:02 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/20WlEg_FTZQ/41</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/41</guid>\r\n      <itunes:summary>Rob Kaufman takes on database scaling with MySQL. This talk will walk you through setting up a Master (read write) Slave (read only) MySQL network, as well as a Master Master, or Multi-Master network. The talk will also cover the whys and when of database clustering.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>51:15</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/41</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/Djye25kiDJM/041_mysql.m4v\" length=\"308755287\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/041_mysql.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 040: Unobtrusive Javascript</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/40.png?1236986578\" alt=\"Episode 040: Unobtrusive Javascript\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://railsontherun.com/\"&gt;Matt Aimonetti&lt;/a&gt; lays down the law on best-practice Ajax, using behavior oriented javascript with progressive enhancement.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/matta/unobtrusive-javascript-sdrb-2007\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Mon, 21 Jan 2008 20:25:15 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/7lMqEqIdO28/40</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/40</guid>\r\n      <itunes:summary>Matt Aimonetti lays down the law on best-practice Ajax, using behavior oriented javascript with progressive enhancement.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>22:32</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/40</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/owCHqV6olgk/040_unobtrusive.m4v\" length=\"162975888\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/040_unobtrusive.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 039: ActionMailer in Action</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/39.png?1236986578\" alt=\"Episode 039: ActionMailer in Action\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://notch8.com/\"&gt;Rob Kaufman&lt;/a&gt; talks about using ActionMailer safely, how to keep out of the spam box and how to test your emails before your customers see them.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://www.slideshare.net/notch8/action-mailer-in-action/\"&gt;download the slides&lt;/a&gt; from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Thu, 10 Jan 2008 03:24:31 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/SzD-7NNUyEo/39</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/39</guid>\r\n      <itunes:summary>Rob Kaufman talks about using ActionMailer safely, how to keep out of the spam box and how to test your emails before your customers see them.\r\n\r\nBonus content: download the slides from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>25:06</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/39</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/0Mk7tp3Bm2E/039_actionmailer.m4v\" length=\"160561021\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/039_actionmailer.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 038: Haml and Sass in 15 minutes</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/38.png?1236986577\" alt=\"Episode 038: Haml and Sass in 15 minutes\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://the.railsi.st/\"&gt;Patrick Crowley&lt;/a&gt; gets you up to speed on using &lt;a href=\"http://haml.hamptoncatlin.com/docs/haml\"&gt;Haml&lt;/a&gt; and &lt;a href=\"http://haml.hamptoncatlin.com/docs/sass\"&gt;Sass&lt;/a&gt;. Haml is a drop in replacement for ERB that turns your views into beautiful code poetry. Sass does the same for CSS.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://the.railsi.st/slides/haml_and_sass.pdf\"&gt;download the slides&lt;/a&gt;  from this talk.&lt;/p&gt;</description>\r\n      <pubDate>Tue, 20 Nov 2007 20:22:59 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/qmZKDXuAWCA/38</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/38</guid>\r\n      <itunes:summary>Patrick Crowley gets you up to speed on using Haml and Sass. Haml is a drop in replacement for ERB that turns your views into beautiful code poetry. Sass does the same for CSS.\r\n\r\nBonus content: download the slides  from this talk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>31:46</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/38</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/qb6ERuCakoo/038_haml_sass.m4v\" length=\"146337312\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/038_haml_sass.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 037: Rails Sexy Charts</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/37.png?1236986577\" alt=\"Episode 037: Rails Sexy Charts\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://railsontherun.com/\"&gt;Matt Aimonetti&lt;/a&gt; demonstrates how to create awesome, Flash-powered charts by integrating Rails with &lt;a href=\"http://www.amcharts.com\"&gt;amCharts&lt;/a&gt;.&lt;/p&gt;\r\n\r\n&lt;p&gt;Bonus content: &lt;a href=\"http://railsontherun.com/assets/sexy_charts.zip\"&gt;download the sample app&lt;/a&gt; shown in this talk or read Matt's original &lt;a href=\"http://railsontherun.com/2007/10/4/sexy-charts-in-less-than-5-minutes\"&gt;Sexy charts in less than 5 minutes&lt;/a&gt; blog post.&lt;/p&gt;</description>\r\n      <pubDate>Tue, 13 Nov 2007 18:11:37 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/yZT9ZdgkPW8/37</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/37</guid>\r\n      <itunes:summary>Matt Aimonetti demonstrates how to create awesome, Flash-powered charts by integrating Rails with amCharts.\r\n\r\nBonus content: download the sample app shown in this talk or read Matt's original Sexy charts in less than 5 minutes blog post.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>41:53</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/37</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/4LwvACpzIc8/037_sexy_charts.m4v\" length=\"508683503\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/037_sexy_charts.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 036: The Return of Kevin Clark</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/36.png?1236986576\" alt=\"Episode 036: The Return of Kevin Clark\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://glu.ttono.us\"&gt;Kevin Clark&lt;/a&gt; takes a break from &lt;a href=\"http://www.powerset.com/\"&gt;Powerset&lt;/a&gt; to give a full-throttle talk on using &lt;a href=\"http://merb.rubyforge.org/files/README.html\"&gt;Merb&lt;/a&gt; as a JSON-RPC service, &lt;a href=\"http://god.rubyforge.org/\"&gt;god&lt;/a&gt;, &lt;a href=\"http://fooplanet.com/projects/gem2rpm/\"&gt;gem2rpm&lt;/a&gt;, and &lt;a href=\"http://seattlerb.rubyforge.org/heckle/\"&gt;heckle&lt;/a&gt;.&lt;/p&gt;</description>\r\n      <pubDate>Sat, 03 Nov 2007 00:02:56 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/FF07HCKeK-k/36</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/36</guid>\r\n      <itunes:summary>Kevin Clark takes a break from Powerset to give a full-throttle talk on using Merb as a JSON-RPC service, god, gem2rpm, and heckle.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>47:37</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/36</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/Mh6nMRiKJp4/036_god.m4v\" length=\"578914037\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/036_god.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 035: ActiveRecord Backup &amp; MimetypeFu</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/35.png?1236986576\" alt=\"Episode 035: ActiveRecord Backup &amp;amp; MimetypeFu\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://railsontherun.com/\"&gt;Matt Aimonetti&lt;/a&gt; demonstrates his newest plugins: &lt;a href=\"http://code.google.com/p/ar-backup/\"&gt;ActiveRecord Backup&lt;/a&gt; and &lt;a href=\"http://code.google.com/p/mimetype-fu/\"&gt;MimetypeFu&lt;/a&gt;.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 02 Nov 2007 23:48:28 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/5DVOiqxnld4/35</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/35</guid>\r\n      <itunes:summary>Matt Aimonetti demonstrates his newest plugins: ActiveRecord Backup and MimetypeFu.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>16:52</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/35</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/Zd-bdgj2iOc/035_railsplugins.m4v\" length=\"185091718\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/035_railsplugins.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 034: Intro to JRuby</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/34.png?1236986575\" alt=\"Episode 034: Intro to JRuby\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://chapados.org\"&gt;Brian Chapados&lt;/a&gt; shows how to install and work with the latest &lt;a href=\"http://jruby.codehaus.org\"&gt;JRuby&lt;/a&gt; release.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 02 Nov 2007 23:41:27 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/1va2KjO79fI/34</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/34</guid>\r\n      <itunes:summary>Brian Chapados shows how to install and work with the latest JRuby release.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>20:28</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/34</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/e3lwq6epP3g/034_jruby.m4v\" length=\"237618665\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/034_jruby.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 033: Life on Edge</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/33.png?1236986575\" alt=\"Episode 033: Life on Edge\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;If you're a Rails junkie, you'll want to develop on &lt;a href=\"http://wiki.rubyonrails.org/rails/pages/EdgeRails\"&gt;Edge Rails&lt;/a&gt;. Matt Clark explains how to get started and shares some of the challenges of working on Edge.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 02 Nov 2007 23:26:47 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/4fTReqQ34IM/33</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/33</guid>\r\n      <itunes:summary>If you're a Rails junkie, you'll want to develop on Edge Rails. Matt Clark explains how to get started and shares some of the challenges of working on Edge.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>22:35</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/33</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/u3PMkN1gm4I/033_edgerails.m4v\" length=\"204581652\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/033_edgerails.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 032: Capistrano</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/32.png?1236986574\" alt=\"Episode 032: Capistrano\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://notch8.com\"&gt;Rob Kaufman&lt;/a&gt; takes on &lt;a href=\"http://www.capify.org/\"&gt;Capistrano 2&lt;/a&gt;. What is it? How does it work? What's changed since version 1?&lt;/p&gt;</description>\r\n      <pubDate>Fri, 02 Nov 2007 23:08:49 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/5yxK1SwKocY/32</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/32</guid>\r\n      <itunes:summary>Rob Kaufman takes on Capistrano 2. What is it? How does it work? What's changed since version 1?</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>42:24</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/32</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/FMK2Z2RSxfc/032_capistrano.m4v\" length=\"382324797\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/032_capistrano.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 031: Seaside</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/31.png?1236986574\" alt=\"Episode 031: Seaside\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Roger Whitney explores &lt;a href=\"http://seaside.st/\"&gt;Seaside&lt;/a&gt;, the web application framework based on &lt;a href=\"http://en.wikipedia.org/wiki/Smalltalk\"&gt;Smalltalk&lt;/a&gt;.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 02 Nov 2007 22:54:11 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/TgcnDp9VZ1o/31</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/31</guid>\r\n      <itunes:summary>Roger Whitney explores Seaside, the web application framework based on Smalltalk.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>35:41</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/31</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/jVEY6FxfkZU/031_seaside.m4v\" length=\"243731718\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/031_seaside.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 030: Tuneshelf</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/30.png?1236986573\" alt=\"Episode 030: Tuneshelf\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://www.19villages.com/\"&gt;Dominic Damian&lt;/a&gt; talks about his experiences building &lt;a href=\"http://tuneshelf.com\"&gt;Tuneshelf&lt;/a&gt;, a web application that allows music fans to keep track of their favorite music albums.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 02 Nov 2007 22:41:54 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/nFAAK3804M0/30</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/30</guid>\r\n      <itunes:summary>Dominic Damian talks about his experiences building Tuneshelf, a web application that allows music fans to keep track of their favorite music albums.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>21:13</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/30</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/7NPYGQ46nug/030_tuneshelf.m4v\" length=\"235887349\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/030_tuneshelf.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 029: Big Stinking Piles (of data)</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/29.png?1236986572\" alt=\"Episode 029: Big Stinking Piles (of data)\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;What do you do when third-party data vendors don't speak REST? &lt;a href=\"http://notch8.com\"&gt;Rob Kaufman&lt;/a&gt; discuss real-world techniques for importing and exporting data. (This talk was also given at &lt;a href=\"http://conferences.oreillynet.com/rails2007/\"&gt;RailsConf 2007&lt;/a&gt;.)&lt;/p&gt;</description>\r\n      <pubDate>Fri, 02 Nov 2007 22:23:56 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/g8Fza6tLkQo/29</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/29</guid>\r\n      <itunes:summary>What do you do when third-party data vendors don't speak REST? Rob Kaufman discuss real-world techniques for importing and exporting data. (This talk was also given at RailsConf 2007.)</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>36:38</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/29</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/_Prlth5bqHc/029_stinking.m4v\" length=\"281555454\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/029_stinking.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 028: Simple Sidebar Plugin</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/28.png?1236986572\" alt=\"Episode 028: Simple Sidebar Plugin\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://www.aisleten.com/\"&gt;Ryan Felton&lt;/a&gt; shows how to use &lt;a href=\"http://mabs29.googlecode.com/svn/trunk/plugins/simple_sidebar/\"&gt;Simple Sidebar&lt;/a&gt; plugin to DRY up sidebar content in applications.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 02 Nov 2007 22:11:08 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/le7z5jw32k0/28</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/28</guid>\r\n      <itunes:summary>Ryan Felton shows how to use Simple Sidebar plugin to DRY up sidebar content in applications.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>24:25</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/28</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/lYk0aDIBOig/028_sidebars.m4v\" length=\"252290265\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/028_sidebars.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 027: Headliner and Styler</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/27.png?1236986571\" alt=\"Episode 027: Headliner and Styler\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://the.railsi.st\"&gt;Patrick Crowley&lt;/a&gt; talks about his newest plugins: &lt;a href=\"http://the.railsi.st/2007/5/3/headliner-dry-up-your-page-titles\"&gt;Headliner&lt;/a&gt; and &lt;a href=\"http://the.railsi.st/2007/5/3/styler-stylesheets-made-easy\"&gt;Styler&lt;/a&gt;.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 02 Nov 2007 20:22:36 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/NhWlFMmh6eM/27</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/27</guid>\r\n      <itunes:summary>Patrick Crowley talks about his newest plugins: Headliner and Styler.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>20:30</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/27</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/REEJCkNMddM/027_headliner_styler.m4v\" length=\"179633129\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/027_headliner_styler.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 026: ActsAsSolr</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/26.png?1236986571\" alt=\"Episode 026: ActsAsSolr\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://notch8.com\"&gt;Rob Kaufman&lt;/a&gt; shows how easy it is to integrate &lt;a href=\"http://lucene.apache.org/solr/\"&gt;Solr&lt;/a&gt; powered search into your Rails application using the &lt;a href=\"http://acts-as-solr.rubyforge.org/\"&gt;ActsAsSolr&lt;/a&gt; plugin.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 02 Nov 2007 20:22:04 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/X2a-F9gdZHA/26</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/26</guid>\r\n      <itunes:summary>Rob Kaufman shows how easy it is to integrate Solr powered search into your Rails application using the ActsAsSolr plugin.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>29:14</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/26</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/OhDGzoU6Z5U/026_acts_as_solr.m4v\" length=\"319626810\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/026_acts_as_solr.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 025: Ajax CSS Star Rating with ActsAsRateable</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/25.png?1236986570\" alt=\"Episode 025: Ajax CSS Star Rating with ActsAsRateable\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://www.aisleten.com/\"&gt;Ryan Felton&lt;/a&gt; shows off how to build an Ajax-powered, CSS star rater using the &lt;a href=\"http://juixe.com/svn/acts_as_rateable/\"&gt;ActsAsRateable&lt;/a&gt; plugin and Komodo Media's &lt;a href=\"http://komodomedia.com/blog/index.php/2007/01/20/css-star-rating-redux/\"&gt;CSS Star Rating Redux&lt;/a&gt; technique.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 02 Nov 2007 20:10:25 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/ItmTTNb1WwA/25</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/25</guid>\r\n      <itunes:summary>Ryan Felton shows off how to build an Ajax-powered, CSS star rater using the ActsAsRateable plugin and Komodo Media's CSS Star Rating Redux technique.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>20:23</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/25</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/P4FiPNtU1-c/025_ajax_rating.m4v\" length=\"229588670\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/025_ajax_rating.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 024: Using Ruby + Amazon SQS to build backdoors</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/24.png?1236986570\" alt=\"Episode 024: Using Ruby + Amazon SQS to build backdoors\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://chapados.org/\"&gt;Brian Chapados&lt;/a&gt; talks about using Ruby and Amazon's &lt;a href=\"http://www.amazon.com/Simple-Queue-Service-home-page/b?ie=UTF8&amp;amp;node=13584001\"&gt;Simple Que Service&lt;/a&gt; web service to build backdoors into systems.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 02 Nov 2007 19:57:43 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/JueEHJRf2UQ/24</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/24</guid>\r\n      <itunes:summary>Brian Chapados talks about using Ruby and Amazon's Simple Que Service web service to build backdoors into systems.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>18:09</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/24</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/oqEfmvxsREI/024_amazon_sqs.m4v\" length=\"204460968\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/024_amazon_sqs.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 023: RubyInline</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/23.png?1236986569\" alt=\"Episode 023: RubyInline\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://beyondthepath.com/\"&gt;Nick Zadrozny&lt;/a&gt; shows us how to use C in Ruby with &lt;a href=\"http://www.zenspider.com/ZSS/Products/RubyInline/\"&gt;RubyInline&lt;/a&gt;. As an example, Nick walks us through writing a password-generating application with the help of &lt;a href=\"http://www.diceware.com/\"&gt;Diceware&lt;/a&gt;.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 15 Jun 2007 19:57:11 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/V8jZy7Let4U/23</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/23</guid>\r\n      <itunes:summary>Nick Zadrozny shows us how to use C in Ruby with RubyInline. As an example, Nick walks us through writing a password-generating application with the help of Diceware.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>13:10</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/23</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/B0Cv0JJxm6A/023_ruby_inline.m4v\" length=\"81696531\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/023_ruby_inline.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 022: DRP</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/22.png?1236986568\" alt=\"Episode 022: DRP\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://drp.rubyforge.org/\"&gt;DRP&lt;/a&gt; stands for Directed Ruby Programming. In this episode, Warren Henning explains exactly what that means and why you might be interested in it.&lt;/p&gt;</description>\r\n      <pubDate>Thu, 31 May 2007 07:08:50 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/jkeztWIisZg/22</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/22</guid>\r\n      <itunes:summary>DRP stands for Directed Ruby Programming. In this episode, Warren Henning explains exactly what that means and why you might be interested in it.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>30:52</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/22</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/scXCWxPqbX8/022_drp.m4v\" length=\"70364812\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/022_drp.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 021: Merb</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/21.png?1236986567\" alt=\"Episode 021: Merb\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://vandev.com/\"&gt;Chris Van Pelt&lt;/a&gt; gives us an introduction to &lt;a href=\"http://merb.devjavu.com/\"&gt;Merb&lt;/a&gt;. Merb is a Ruby framework built by &lt;a href=\"http://brainspl.at/\"&gt;Ezra Zygmuntowicz&lt;/a&gt;.&lt;/p&gt;</description>\r\n      <pubDate>Mon, 23 Apr 2007 04:13:15 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/3Piw3qWMVVM/21</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/21</guid>\r\n      <itunes:summary>Chris Van Pelt gives us an introduction to Merb. Merb is a Ruby framework built by Ezra Zygmuntowicz.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>15:03</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/21</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/dibAMdj5emk/021_merb.m4v\" length=\"89568394\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/021_merb.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 020: ImageScience</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/20.png?1236986566\" alt=\"Episode 020: ImageScience\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;In this episode, Tom Werner covers some of the basics of the ruby image library &lt;a href=\"http://seattlerb.rubyforge.org/ImageScience.html\"&gt;ImageScience&lt;/a&gt;. ImageScience is a wrapper for the &lt;a href=\"http://sourceforge.net/projects/freeimage\"&gt;FreeImage&lt;/a&gt; graphics library, and is a lightweight alternative to &lt;a href=\"http://rmagick.rubyforge.org/\"&gt;RMagick&lt;/a&gt;.&lt;/p&gt;</description>\r\n      <pubDate>Sun, 25 Mar 2007 01:25:33 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/oIjleEQAyc4/20</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/20</guid>\r\n      <itunes:summary>In this episode, Tom Werner covers some of the basics of the ruby image library ImageScience. ImageScience is a wrapper for the FreeImage graphics library, and is a lightweight alternative to RMagick.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>19:43</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/20</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/2h0a34JIBVU/020_imagescience.mp4\" length=\"38338291\" type=\"video/mp4\" /><feedburner:origEnclosureLink>http://sdruby.org/video/020_imagescience.mp4</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 019: AWS S3</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/19.png?1236986565\" alt=\"Episode 019: AWS S3\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://www.jordanfowler.com/\"&gt;Jordan Fowler&lt;/a&gt; gives a presentation on Amazon's &lt;a href=\"http://www.amazon.com/gp/browse.html?node=16427261\"&gt;S3&lt;/a&gt; service. He also covers the &lt;a href=\"http://amazon.rubyforge.org/\"&gt;AWS::S3&lt;/a&gt; gem written by &lt;a href=\"http://www.vernix.org/marcel/\"&gt;Marcel Molina Jr.&lt;/a&gt;&lt;/p&gt;</description>\r\n      <pubDate>Sat, 03 Mar 2007 20:31:05 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/C22Zd5Y9egU/19</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/19</guid>\r\n      <itunes:summary>Jordan Fowler gives a presentation on Amazon's S3 service. He also covers the AWS::S3 gem written by Marcel Molina Jr.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>22:34</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/19</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/sz2o4LYQ-Ik/019_aws_s3.m4v\" length=\"112326427\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/019_aws_s3.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 018: StaffTool</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/18.png?1236986565\" alt=\"Episode 018: StaffTool\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;In this episode, &lt;a href=\"http://120db.com/\"&gt;Toby Sterrett&lt;/a&gt; gives us an update on his soon-to-be-released project StaffTool. StaffTool is a web-based church management application build with Ruby on Rails.&lt;/p&gt;</description>\r\n      <pubDate>Wed, 21 Feb 2007 23:24:41 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/JA6eggOlEr8/18</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/18</guid>\r\n      <itunes:summary>In this episode, Toby Sterrett gives us an update on his soon-to-be-released project StaffTool. StaffTool is a web-based church management application build with Ruby on Rails.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>12:52</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/18</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/rzteQL6xLT4/018_stafftool.m4v\" length=\"52593617\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/018_stafftool.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 017: ActsNaked</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/17.png?1236986563\" alt=\"Episode 017: ActsNaked\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;In today's episode, &lt;a href=\"http://mokolabs.com/\"&gt;Patrick Crowley&lt;/a&gt; introduces his first Rails plugin titled ActsNaked. It's an easy way to strip your models and keep them DRY.&lt;/p&gt;</description>\r\n      <pubDate>Thu, 15 Feb 2007 23:35:02 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/JsEylsDLYFc/17</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/17</guid>\r\n      <itunes:summary>In today's episode, Patrick Crowley introduces his first Rails plugin titled ActsNaked. It's an easy way to strip your models and keep them DRY.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>10:21</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/17</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/UAsX49_0GX8/017_actsnaked.m4v\" length=\"37218079\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/017_actsnaked.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 016: MasterView Templates</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/16.png?1236986563\" alt=\"Episode 016: MasterView Templates\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Deb Lewis showcases her project MasterView. MasterView is a template language for Rails.&lt;/p&gt;</description>\r\n      <pubDate>Tue, 16 Jan 2007 21:43:14 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/_wGEUxiViRE/16</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/16</guid>\r\n      <itunes:summary>Deb Lewis showcases her project MasterView. MasterView is a template language for Rails.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>25:53</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/16</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/px-iDPGss88/016_master_view.mp4\" length=\"102038568\" type=\"video/mp4\" /><feedburner:origEnclosureLink>http://sdruby.org/video/016_master_view.mp4</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 015: Rails for Legacy Applications</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/15.png?1236986562\" alt=\"Episode 015: Rails for Legacy Applications\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://mokolabs.com/\"&gt;Patrick Crowley&lt;/a&gt; covers some of the finer point of using Rails with legacy applications.&lt;/p&gt;</description>\r\n      <pubDate>Tue, 16 Jan 2007 21:43:08 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/PWRzhPafcHk/15</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/15</guid>\r\n      <itunes:summary>Patrick Crowley covers some of the finer point of using Rails with legacy applications.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>31:58</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/15</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/HVpiWqlXC_w/015_rails_for_legacy_apps.mp4\" length=\"109948536\" type=\"video/mp4\" /><feedburner:origEnclosureLink>http://sdruby.org/video/015_rails_for_legacy_apps.mp4</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 014: Ruby Idioms - Part 1</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/14.png?1236986562\" alt=\"Episode 014: Ruby Idioms - Part 1\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://www.cube6media.com\"&gt;Tom Werner&lt;/a&gt; gives us part 1 of his series on Ruby idoms.&lt;/p&gt;</description>\r\n      <pubDate>Tue, 16 Jan 2007 21:42:56 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/UXZ4J7fdkg8/14</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/14</guid>\r\n      <itunes:summary>Tom Werner gives us part 1 of his series on Ruby idoms.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>38:12</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/14</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/-_k1JDt5A4Y/014_ruby_idioms_1.mp4\" length=\"112940686\" type=\"video/mp4\" /><feedburner:origEnclosureLink>http://sdruby.org/video/014_ruby_idioms_1.mp4</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 013: Mocha and Stubba</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/13.png?1236986561\" alt=\"Episode 013: Mocha and Stubba\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://glu.ttono.us\"&gt;Kevin Clark&lt;/a&gt; shows us how to use mocks and stubs in your Ruby tests using &lt;a href=\"http://mocha.rubyforge.org\"&gt;Mocha and Stubba&lt;/a&gt;&lt;/p&gt;</description>\r\n      <pubDate>Mon, 06 Nov 2006 08:43:46 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/rvy5WXkSuKo/13</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/13</guid>\r\n      <itunes:summary>Kevin Clark shows us how to use mocks and stubs in your Ruby tests using Mocha and Stubba</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>27:20</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/13</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/VzsYDTosNb8/013_mocha_stubba.m4v\" length=\"141459556\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/013_mocha_stubba.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 012: Deployment</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/12.png?1236986560\" alt=\"Episode 012: Deployment\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://acorsys.com\"&gt;Dominic Damian&lt;/a&gt; talks about the different options available to you when deploying a Rails application. He covers areas such as servers, caching, database, and hosting.&lt;/p&gt;</description>\r\n      <pubDate>Mon, 06 Nov 2006 08:43:33 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/Xij2lUu2pb4/12</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/12</guid>\r\n      <itunes:summary>Dominic Damian talks about the different options available to you when deploying a Rails application. He covers areas such as servers, caching, database, and hosting.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>22:23</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/12</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/ydB2iMI93_U/012_deployment.m4v\" length=\"74318006\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/012_deployment.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 011: ActsAsTaggable Plugin</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/11.png?1236986560\" alt=\"Episode 011: ActsAsTaggable Plugin\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://mokolabs.com/\"&gt;Patrick Crowley&lt;/a&gt; discusses his experiences with acts&lt;em&gt;as&lt;/em&gt;taggable. He starts off outlining some of the differences between the &lt;a href=\"http://rubyforge.org/projects/taggable\"&gt;gem&lt;/a&gt; and the &lt;a href=\"http://dev.rubyonrails.com/svn/rails/plugins/acts_as_taggable\"&gt;plugin&lt;/a&gt;. Patrick then creates a simple app to show off some of the capabilities of the plugin.&lt;/p&gt;</description>\r\n      <pubDate>Mon, 06 Nov 2006 08:42:37 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/y7gGQHL1dO4/11</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/11</guid>\r\n      <itunes:summary>Patrick Crowley discusses his experiences with actsastaggable. He starts off outlining some of the differences between the gem and the plugin. Patrick then creates a simple app to show off some of the capabilities of the plugin.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>32:26</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/11</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/Mwej3cKfA_s/011_acts_as_taggable.m4v\" length=\"179779859\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/011_acts_as_taggable.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 010: REST Web Services with Rails</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/10.png?1236986559\" alt=\"Episode 010: REST Web Services with Rails\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://blog.integralimpressions.com\"&gt;Chris Abad&lt;/a&gt; talks a little bit about providing web services to your Rails application using some of the new REST features found in Edge Rails. At the end, he also throws in a bonus tip on using &lt;a href=\"http://techno-weenie.net\"&gt;Rick Olson's&lt;/a&gt; authentication plugin &lt;a href=\"http://svn.techno-weenie.net/projects/plugins/restful_authentication/\"&gt;restful_authentication&lt;/a&gt; to add authentication to your REST API.&lt;/p&gt;\r\n\r\n&lt;p&gt;Here are the &lt;a href=\"/pdfs/010_rest_2.pdf\"&gt;slides&lt;/a&gt;&lt;/p&gt;</description>\r\n      <pubDate>Tue, 03 Oct 2006 16:37:55 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/-GuJSbTOP_8/10</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/10</guid>\r\n      <itunes:summary>Chris Abad talks a little bit about providing web services to your Rails application using some of the new REST features found in Edge Rails. At the end, he also throws in a bonus tip on using Rick Olson's authentication plugin restful_authentication to add authentication to your REST API.\r\n\r\nHere are the slides</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>17:10</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/10</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/yBHgM0okP5c/010_rest_2.m4v\" length=\"50161545\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/010_rest_2.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 009: REST with Rails</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/9.png?1236986559\" alt=\"Episode 009: REST with Rails\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;In this episode, &lt;a href=\"http://glu.ttono.us\"&gt;Kevin Clark&lt;/a&gt; shows us some of the new REST features found in Edge Rails. He covers some basic concepts such as HTTP verbs, and also takes a look at using RESTful routes.&lt;/p&gt;\r\n\r\n&lt;p&gt;Here are the &lt;a href=\"/pdfs/009_rest.pdf\"&gt;slides&lt;/a&gt;&lt;/p&gt;</description>\r\n      <pubDate>Tue, 03 Oct 2006 16:34:31 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/lhXw4hZYGOg/9</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/9</guid>\r\n      <itunes:summary>In this episode, Kevin Clark shows us some of the new REST features found in Edge Rails. He covers some basic concepts such as HTTP verbs, and also takes a look at using RESTful routes.\r\n\r\nHere are the slides</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>15:56</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/9</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/9ecIaCTcTxA/009_rest.m4v\" length=\"68726923\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/009_rest.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 008: Chronic</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/8.png?1236986558\" alt=\"Episode 008: Chronic\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://www.cube6media.com/\"&gt;Tom Werner&lt;/a&gt; has released the ruby gem Chronic, a natural language date and time parser. In this episode he gives us a quick run through of some of the cool points.&lt;/p&gt;</description>\r\n      <pubDate>Tue, 03 Oct 2006 16:32:29 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/eJo5pHsijdQ/8</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/8</guid>\r\n      <itunes:summary>Tom Werner has released the ruby gem Chronic, a natural language date and time parser. In this episode he gives us a quick run through of some of the cool points.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>40:26</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/8</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/Ib00sPKdwGA/008_chronic.m4v\" length=\"161923972\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/008_chronic.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 007: Rails Authentication</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/7.png?1236986557\" alt=\"Episode 007: Rails Authentication\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://glu.ttono.us\"&gt;Kevin Clark&lt;/a&gt; gives us a brief overview of two of &lt;a href=\"http://techno-weenie.net\"&gt;Rick Olson's&lt;/a&gt; authentication plugins: &lt;a href=\"http://svn.techno-weenie.net/projects/plugins/acts_as_authenticated/\"&gt;acts&lt;em&gt;as&lt;/em&gt;authenticated&lt;/a&gt; and &lt;a href=\"http://svn.techno-weenie.net/projects/plugins/restful_authentication/\"&gt;restful_authentication&lt;/a&gt;.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 08 Sep 2006 17:24:08 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/91Scoai82nc/7</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/7</guid>\r\n      <itunes:summary>Kevin Clark gives us a brief overview of two of Rick Olson's authentication plugins: actsasauthenticated and restful_authentication.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>12:01</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/7</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/hXjZzA8fONQ/007_authentication.m4v\" length=\"29531997\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/007_authentication.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 006: Dynamic Domains</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/6.png?1236986556\" alt=\"Episode 006: Dynamic Domains\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://blog.integralimpressions.com\"&gt;Chris Abad&lt;/a&gt; introduces the new web service, &lt;a href=\"http://out.landi.sh\"&gt;Outlandish&lt;/a&gt;. He also shows us how they handle dynamic domains within Outlandish.&lt;/p&gt;\r\n\r\n&lt;p&gt;Here are the &lt;a href=\"/pdfs/006_dynamic_domains.pdf\"&gt;slides&lt;/a&gt;&lt;/p&gt;</description>\r\n      <pubDate>Fri, 08 Sep 2006 17:20:17 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/NmpU6EnpBbU/6</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/6</guid>\r\n      <itunes:summary>Chris Abad introduces the new web service, Outlandish. He also shows us how they handle dynamic domains within Outlandish.\r\n\r\nHere are the slides</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>38:29</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/6</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/uQfyx3cgIro/006_dynamic_domains.m4v\" length=\"56565951\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/006_dynamic_domains.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 005: ARTS Plugin</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/5.png?1236986556\" alt=\"Episode 005: ARTS Plugin\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Kevin Clark demos his new Rails plugin, &lt;a href=\"http://glu.ttono.us/articles/2006/05/29/guide-test-driven-rjs-with-arts\"&gt;ARTS&lt;/a&gt;, which allows you to test your RJS templates.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 08 Sep 2006 17:17:13 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/mfBR0Xxq4M4/5</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/5</guid>\r\n      <itunes:summary>Kevin Clark demos his new Rails plugin, ARTS, which allows you to test your RJS templates.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>47:32</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/5</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/3yzKNNWBPxM/005_arts.m4v\" length=\"131703235\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/005_arts.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 004: Numbers</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/4.png?1236986556\" alt=\"Episode 004: Numbers\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;&lt;a href=\"http://www.cube6media.com/\"&gt;Tom Werner&lt;/a&gt; wows us with the power of numbers in Ruby.&lt;/p&gt;</description>\r\n      <pubDate>Fri, 08 Sep 2006 17:14:30 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/gS7fvB1_Fow/4</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/4</guid>\r\n      <itunes:summary>Tom Werner wows us with the power of numbers in Ruby.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>35:48</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/4</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/eJ_G4P6su7k/004_numbers.m4v\" length=\"71755714\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/004_numbers.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 003: mkmf</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/3.png?1236986555\" alt=\"Episode 003: mkmf\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Kevin Clark takes us through some of the work he's doing on mkmf for his Summer of Rails project. We also get a quick preview of Kevin's RailsDay app, Advisr.&lt;/p&gt;</description>\r\n      <pubDate>Mon, 24 Jul 2006 20:54:52 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/BgLxy9HeHc0/3</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/3</guid>\r\n      <itunes:summary>Kevin Clark takes us through some of the work he's doing on mkmf for his Summer of Rails project. We also get a quick preview of Kevin's RailsDay app, Advisr.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>11:48</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/3</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/dbI2LVNIuSo/003_mkmf.m4v\" length=\"40705751\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/003_mkmf.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 002: Camping</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/2.png?1236986555\" alt=\"Episode 002: Camping\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Chris Van Pelt discussing Camping, a microframework built by Why the Lucky Stiff. Chris also gives us a preview of a cool little Camping app he built title Croppr.&lt;/p&gt;</description>\r\n      <pubDate>Mon, 24 Jul 2006 20:53:32 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/-3PX6sJ81kQ/2</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/2</guid>\r\n      <itunes:summary>Chris Van Pelt discussing Camping, a microframework built by Why the Lucky Stiff. Chris also gives us a preview of a cool little Camping app he built title Croppr.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>34:34</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/2</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/dXlEvP4DY-0/002_camping.m4v\" length=\"100531015\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/002_camping.m4v</feedburner:origEnclosureLink></item>\r\n    <item>\r\n      <title>Episode 001: Summer of Rails</title>\r\n      <description type=\"html\">&lt;p&gt;&lt;img src=\"http://sdruby.org/images/screenshots/large/1.png?1236986551\" alt=\"Episode 001: Summer of Rails\" /&gt;&lt;/p&gt;\r\n\r\n&lt;p&gt;Patrick Crowley discusses Summer of Rails. He also introduces his first Summer of Rails project, Graffletopia.&lt;/p&gt;</description>\r\n      <pubDate>Mon, 17 Jul 2006 23:32:03 +0000</pubDate>\r\n      \r\n      <link>http://feedproxy.google.com/~r/sdrbpodcast/~3/cKNXCGZBnBw/1</link>\r\n      <guid isPermaLink=\"false\">http://sdruby.org/podcast/1</guid>\r\n      <itunes:summary>Patrick Crowley discusses Summer of Rails. He also introduces his first Summer of Rails project, Graffletopia.</itunes:summary>\r\n      <itunes:explicit>no</itunes:explicit>\r\n      <itunes:duration>28:02</itunes:duration>\r\n    <feedburner:origLink>http://sdruby.org/podcast/1</feedburner:origLink><enclosure url=\"http://feedproxy.google.com/~r/sdrbpodcast/~5/1WGfYV2q-1o/001_summer_of_rails.m4v\" length=\"69675832\" type=\"video/x-m4v\" /><feedburner:origEnclosureLink>http://sdruby.org/video/001_summer_of_rails.m4v</feedburner:origEnclosureLink></item>\r\n  </channel>\r\n</rss>\r\n"
  },
  {
    "path": "samples/alert/.gitignore",
    "content": ".repl_history\nbuild\ntags\napp/pixate_code.rb\nresources/*.nib\nresources/*.momd\nresources/*.storyboardc\n.DS_Store\nnbproject\n.redcar\n#*#\n*~\n*.sw[po]\n.eprj\n.sass-cache\n.idea\n"
  },
  {
    "path": "samples/alert/Gemfile",
    "content": "source \"https://rubygems.org\"\n\ngem \"rake\"\ngem \"bubble-wrap\", :path => \"../../\"\n"
  },
  {
    "path": "samples/alert/Rakefile",
    "content": "# -*- coding: utf-8 -*-\n$:.unshift(\"/Library/RubyMotion/lib\")\nrequire 'motion/project/template/ios'\nrequire 'bundler'\nBundler.require\n\nMotion::Project::App.setup do |app|\n  # Use `rake config' to see complete project settings.\n  app.name = 'alert'\nend\n"
  },
  {
    "path": "samples/alert/app/app_delegate.rb",
    "content": "class AppDelegate\n  def application(application, didFinishLaunchingWithOptions:launchOptions)\n    @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)\n    @window.rootViewController = AlertViewController.alloc.init\n    @window.makeKeyAndVisible\n    true\n  end\nend\n"
  },
  {
    "path": "samples/alert/app/controllers/alert_view_controller.rb",
    "content": "class AlertViewController < UIViewController\n  attr_reader :text_view\n  attr_reader :buttons\n  attr_reader :alerts\n\n  def init\n    super.tap do\n      @text_view = build_text_view\n\n      @buttons = []\n      @alerts  = []\n\n      [:default, :plain_text_input, :secure_text_input, :login_and_password_input].each do |style|\n        @buttons << build_button(style.to_s)\n        @alerts  << built_alert(style)\n      end\n    end\n  end\n\n  def viewDidLoad\n    super\n\n    self.view.backgroundColor = UIColor.grayColor\n\n    self.view.addSubview(self.text_view)\n\n    self.buttons.each_with_index do |button, index|\n      self.view.addSubview(button)\n\n      button.when(UIControlEventTouchUpInside) { self.alerts[index].show }\n    end\n  end\n\n  def build_text_view\n    text_view = UITextView.alloc.initWithFrame([[0, 0], [320, 194]])\n    text_view.editable = false\n    text_view.text     = \"Waiting...\"\n    text_view\n  end\n\n  def build_button(title)\n    button = UIButton.buttonWithType(UIButtonTypeRoundedRect)\n    button.setTitle(title, forState:UIControlStateNormal)\n\n    rect = self.buttons.empty? ? CGRectMake(20, 150, 280, 44) : self.buttons.last.frame\n    button.frame = [[rect.origin.x, rect.origin.y + rect.size.height + 20], rect.size]\n\n    button\n  end\n\n  def built_alert(method)\n    options = {\n      :title        => method,\n      :will_present => build_callback(:will_present, method),\n      :did_present  => build_callback(:did_present, method),\n      :on_click     => build_callback(:on_click, method),\n      :will_dismiss => build_callback(:will_dismiss, method),\n      :did_dismiss  => build_callback(:did_dismiss, method)\n    }\n    BW::UIAlertView.send(method, options)\n  end\n\n  def build_callback(name, method)\n    lambda do |alert|\n      message = []\n      message << \"\\n\\n\" + method.to_s if name == :will_present\n      message << \"\\n\" + name.to_s\n      message << \"\\n\" + alert.clicked_button.inspect if alert.clicked_button\n\n      self.text_view.text += message.join\n      self.text_view.selectedRange = NSMakeRange(self.text_view.text.length, 0)\n    end\n  end\nend\n"
  },
  {
    "path": "samples/alert/spec/main_spec.rb",
    "content": "describe \"Application 'alert'\" do\n  before do\n    @app = UIApplication.sharedApplication\n  end\n\n  it \"has one window\" do\n    @app.windows.size.should == 1\n  end\nend\n"
  },
  {
    "path": "samples/camera/Gemfile",
    "content": "source \"https://rubygems.org\"\n\ngem \"rake\"\ngem \"bubble-wrap\", :path => \"../../\"\n"
  },
  {
    "path": "samples/camera/README.md",
    "content": "## Camera Demo\n\nA bubble-wrap demo to show how to use camera API.\n\n"
  },
  {
    "path": "samples/camera/Rakefile",
    "content": "# -*- coding: utf-8 -*-\n$:.unshift(\"/Library/RubyMotion/lib\")\nrequire 'motion/project/template/ios'\nrequire 'bubble-wrap/core'\nrequire 'bubble-wrap/camera'\n\nMotion::Project::App.setup do |app|\n  app.name = 'camera'\nend\n"
  },
  {
    "path": "samples/camera/app/app_delegate.rb",
    "content": "class AppDelegate\n  def application(application, didFinishLaunchingWithOptions:launchOptions)\n    @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)\n    camera_controller = CameraController.alloc.init\n    @window.rootViewController = camera_controller\n    @window.makeKeyAndVisible\n  end\nend\n"
  },
  {
    "path": "samples/camera/app/controllers/camera_controller.rb",
    "content": "class CameraController < UIViewController\n  attr_accessor :buttons\n\n  def init\n    super.tap do\n      @buttons = []\n    end\n  end\n\n  def viewDidLoad\n    super\n\n    self.view.addSubview(build_button(\"Library\", :any))\n    self.view.addSubview(build_button(\"Front\", :front)) if BW::Device.camera.front?\n    self.view.addSubview(build_button(\"Rear\", :rear))   if BW::Device.camera.rear?\n  end\n\n  def build_button(title, camera_method)\n    button = UIButton.buttonWithType(UIButtonTypeRoundedRect)\n    button.setTitle(title, forState:UIControlStateNormal)\n    button.sizeToFit\n\n    rect = self.buttons.empty? ? CGRectMake(0, 0, 0, 0) : self.buttons.last.frame\n\n    button.frame = [[rect.origin.x, rect.origin.y + rect.size.height + 10], button.frame.size]\n\n    button.when UIControlEventTouchUpInside do\n      BW::Device.camera.send(camera_method).picture(media_types: [:image]) do |result|\n        image_view = build_image_view(result[:original_image])\n        self.view.addSubview(image_view)\n\n        self.buttons.each { |button| self.view.bringSubviewToFront(button) }\n      end\n    end\n\n    self.buttons << button\n    button\n  end\n\n  def build_image_view(image)\n    image_view = UIImageView.alloc.initWithImage(image)\n    image_view.frame  = [CGPointZero, self.view.frame.size]\n    image_view.center = [self.view.frame.size.width / 2, self.view.frame.size.height / 2]\n    image_view\n  end\nend\n"
  },
  {
    "path": "samples/camera/spec/main_spec.rb",
    "content": "describe \"Application 'google_location'\" do\n  before do\n    @app = UIApplication.sharedApplication\n  end\n\n  it \"has one window\" do\n    @app.windows.size.should == 1\n  end\nend\n"
  },
  {
    "path": "samples/gesture/.gitignore",
    "content": ".repl_history\nbuild\nresources/*.nib\nresources/*.momd\nresources/*.storyboardc\n"
  },
  {
    "path": "samples/gesture/Gemfile",
    "content": "source \"https://rubygems.org\"\n\ngem 'rake'\ngem \"bubble-wrap\", :path => \"../../\"\ngem 'motion-dryer'\n"
  },
  {
    "path": "samples/gesture/README.md",
    "content": "## Gesture Demo\n\nA bubble-wrap demo to show how to use gesture API.\n\n![](http://f.cl.ly/items/0e1J3W0Y0p3n3S2a0k2K/%E8%9E%A2%E5%B9%95%E5%BF%AB%E7%85%A7%202012-07-16%20%E4%B8%8B%E5%8D%8802.15.30.png)"
  },
  {
    "path": "samples/gesture/Rakefile",
    "content": "# -*- coding: utf-8 -*-\n$:.unshift(\"/Library/RubyMotion/lib\")\nrequire 'motion/project/template/ios'\nrequire 'bundler'\nBundler.require :default\n\nMotion::Project::App.setup do |app|\n  app.name = 'gesture'\nend\n"
  },
  {
    "path": "samples/gesture/app/app_delegate.rb",
    "content": "class AppDelegate\n  attr_reader :window\n\n  def application(application, didFinishLaunchingWithOptions:launchOptions)\n    setup_window\n    true\n  end\n\n  # setup UI \n  def setup_window\n    @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)\n    @window.rootViewController = DrawingViewController.alloc.init\n    @window.rootViewController.wantsFullScreenLayout = true\n    @window.makeKeyAndVisible\n  end\nend"
  },
  {
    "path": "samples/gesture/app/controllers/drawing_view_controller.rb",
    "content": "class DrawingViewController < UIViewController\n  attr_reader :drawing_view\n\n  def loadView\n    screen = UIScreen.mainScreen.bounds\n    self.view = UIView.alloc.initWithFrame screen\n    self.view.backgroundColor = UIColor.whiteColor\n\n    @drawing_view = RectView.alloc.initWithFrame [[screen.size.width/2-50,screen.size.height/2-50], [100,100]]\n    self.view.addSubview @drawing_view\n  end\nend"
  },
  {
    "path": "samples/gesture/app/helpers/drawing_helper.rb",
    "content": "module DrawingHelper\nend"
  },
  {
    "path": "samples/gesture/app/views/drawing/gesture_view.rb",
    "content": "class GestureView < UIView\n  attr_accessor :rotation, :scale, :translation\n\n  def initWithCoder(coder)\n    super\n    setup\n    self\n  end\n\n  def initWithFrame(coder)\n    super\n    setup\n    self\n  end\n\n  ## UIGestureRecognizerDelegate\n\n  #  Note: this method allow rotate and pinch gesture happen at the same time\n  def gestureRecognizer(recognizer, shouldRecognizeSimultaneouslyWithGestureRecognizer:other_recognizer)\n    case recognizer\n    when @rotated_recognizer\n      if other_recognizer == @pinch_recognizer\n        return true\n      end\n    when @pinch_recognizer\n      if other_recognizer == @rotated_recognizer\n        return true\n      end\n    end\n    return false\n  end\n\n  private\n\n  def setup\n    self.layer.shouldRasterize = true\n    self.rotation = 0\n    self.scale = 1\n    setup_gesture\n  end\n\n  def setup_gesture\n    @panned_recognizer = self.whenPanned do |recognizer|\n      case(recognizer.state)\n      when UIGestureRecognizerStateBegan\n        @last_position = self.position\n      when UIGestureRecognizerStateChanged\n        self.translation = recognizer.translationInView(self.superview)\n        self.position = [@last_position.x + self.translation.x, @last_position.y + self.translation.y]\n      when UIGestureRecognizerStateEnded\n        @last_position = nil\n      end\n    end\n    @panned_recognizer.maximumNumberOfTouches = 1\n    @panned_recognizer.minimumNumberOfTouches = 1\n    @panned_recognizer.delegate = self\n\n    @rotated_recognizer = self.whenRotated do |recognizer|\n      case(recognizer.state)\n      when UIGestureRecognizerStateBegan\n        @last_rotation = self.rotation\n      when UIGestureRecognizerStateChanged\n        self.rotation = @last_rotation + recognizer.rotation\n        reset_transformation\n      when UIGestureRecognizerStateEnded\n        @last_rotation = nil\n      end\n    end\n    @rotated_recognizer.delegate = self\n\n    @pinch_recognizer = self.whenPinched do |recognizer|\n      case(recognizer.state)\n      when UIGestureRecognizerStateBegan\n        @last_scale = self.scale\n      when UIGestureRecognizerStateChanged\n        self.scale = @last_scale * recognizer.scale\n        reset_transformation\n      when UIGestureRecognizerStateEnded\n        @last_scale = nil\n      end\n    end\n    @pinch_recognizer.delegate = self\n  end\n\n  def reset_transformation\n    transform = CATransform3DIdentity\n    transform = CATransform3DRotate(transform, -1 * self.rotation, 0.0, 0.0, -1.0)\n    transform = CATransform3DScale(transform, self.scale, self.scale, 1.0)\n    self.layer.transform = transform\n  end\n\nend"
  },
  {
    "path": "samples/gesture/app/views/drawing/rect_view.rb",
    "content": "class RectView < GestureView\n  def drawRect(rect)\n    super(rect)\n\n    context = UIGraphicsGetCurrentContext()\n    CGContextSetAllowsAntialiasing(context, true);\n    CGContextSetShouldAntialias(context, true);\n    CGContextSetFillColorWithColor(context, UIColor.redColor.CGColor)\n    CGContextSetLineWidth(context, 10.0)\n    CGContextAddRect(context, [[0,0], [self.frame.size.width,self.frame.size.height]])\n    CGContextFillPath(context)\n  end\nend"
  },
  {
    "path": "samples/gesture/spec/main_spec.rb",
    "content": "describe \"Application 'gesture'\" do\n  before do\n    @app = UIApplication.sharedApplication\n  end\n\n  it \"has one window\" do\n    @app.windows.size.should == 1\n  end\nend\n"
  },
  {
    "path": "samples/location/.gitignore",
    "content": ".repl_history\nbuild\nresources/*.nib\nresources/*.momd\nresources/*.storyboardc\n"
  },
  {
    "path": "samples/location/Gemfile",
    "content": "source \"https://rubygems.org\"\n\ngem 'rake'\ngem \"bubble-wrap\", :path => \"../../\"\ngem 'bubble-wrap-http'\n"
  },
  {
    "path": "samples/location/README.md",
    "content": "## Location Demo\n\nA bubble-wrap demo to show how to use location API.\n\n"
  },
  {
    "path": "samples/location/Rakefile",
    "content": "# -*- coding: utf-8 -*-\n$:.unshift(\"/Library/RubyMotion/lib\")\nrequire 'motion/project/template/ios'\nrequire 'bubble-wrap/location'\nrequire 'bubble-wrap/core'\n\nrequire 'bundler'\nBundler.require\n\nMotion::Project::App.setup do |app|\n  app.name = 'location'\nend\n"
  },
  {
    "path": "samples/location/app/app_delegate.rb",
    "content": "class AppDelegate\n  def application(application, didFinishLaunchingWithOptions:launchOptions)\n    @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)\n    places_list_controller = PlacesListController.alloc.init\n    @window.rootViewController = places_list_controller\n    @window.makeKeyAndVisible\n  end\nend\n"
  },
  {
    "path": "samples/location/app/controllers/places_list_controller.rb",
    "content": "class PlacesListController < UITableViewController\n  attr_accessor :places_list\n\n  def viewDidLoad\n    @places_list = []\n    Places.load(self)\n    view.dataSource = view.delegate = self\n  end \n\n  def viewWillAppear(animated)\n    view.reloadData\n  end\n\n  def tableView(tableView, numberOfRowsInSection:section)\n    @places_list.size\n  end\n\n  def reloadData\n    view.reloadData\n  end\n\n  CellID = 'CellIdentifier'\n  def tableView(tableView, cellForRowAtIndexPath:indexPath)\n    cell = tableView.dequeueReusableCellWithIdentifier(CellID) || UITableViewCell.alloc.initWithStyle(UITableViewCellStyleSubtitle, reuseIdentifier:CellID)\n    placeItem= @places_list[indexPath.row]\n    cell.textLabel.text = placeItem\n    cell\n  end\n\nend\n"
  },
  {
    "path": "samples/location/app/models/places.rb",
    "content": "class Places\n  API_KEY = \"\"\n  #See google places documentation at https://developers.google.com/places/documentation/ to obtain a key\n\n  def self.load(places_list_controller)\n    BW::Location.get do |result|\n      BW::Location.stop\n      BubbleWrap::HTTP.get(\"https://maps.googleapis.com/maps/api/place/search/json?location=#{result[:to].latitude},#{result[:to].longitude}&radius=500&sensor=false&key=#{API_KEY}\") do |response|\n        names = BW::JSON.parse(response.body.to_str)[\"results\"].map{|r| r[\"name\"]}\n        places_list_controller.places_list = names  \n        places_list_controller.reloadData\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "samples/location/spec/main_spec.rb",
    "content": "describe \"Application 'google_location'\" do\n  before do\n    @app = UIApplication.sharedApplication\n  end\n\n  it \"has one window\" do\n    @app.windows.size.should == 1\n  end\nend\n"
  },
  {
    "path": "samples/media/.gitignore",
    "content": ".repl_history\nbuild\ntags\napp/pixate_code.rb\nresources/*.nib\nresources/*.momd\nresources/*.storyboardc\n.DS_Store\nnbproject\n.redcar\n#*#\n*~\n*.sw[po]\n.eprj\n.sass-cache\n.idea\n"
  },
  {
    "path": "samples/media/Gemfile",
    "content": "source \"https://rubygems.org\"\n\ngem \"rake\"\ngem \"bubble-wrap\", :path => \"../../\"\n"
  },
  {
    "path": "samples/media/Rakefile",
    "content": "# -*- coding: utf-8 -*-\n$:.unshift(\"/Library/RubyMotion/lib\")\nrequire 'motion/project/template/ios'\n\n$:.unshift(\"../../lib\")\nrequire 'bubble-wrap/media'\n\nMotion::Project::App.setup do |app|\n  # Use `rake config' to see complete project settings.\n  app.name = 'media'\nend\n"
  },
  {
    "path": "samples/media/app/app_delegate.rb",
    "content": "class AppDelegate\n  def application(application, didFinishLaunchingWithOptions:launchOptions)\n    @window = UIWindow.alloc.initWithFrame(UIScreen.mainScreen.bounds)\n    camera_controller = PlayController.alloc.init\n    @window.rootViewController = camera_controller\n    @window.makeKeyAndVisible\n  end\nend\n"
  },
  {
    "path": "samples/media/app/controllers/play_controller.rb",
    "content": "class PlayController < UIViewController\n  attr_accessor :buttons\n\n  def init\n    super.tap do\n      @buttons = []\n    end\n  end\n\n  def viewDidLoad\n    super\n\n    self.view.addSubview(build_button(\"Modal\", \"tapped_modal\"))\n    self.view.addSubview(build_button(\"Frame\", \"tapped_frame\"))\n    self.view.backgroundColor = UIColor.whiteColor\n  end\n\n  def build_button(title, callback)\n    button = UIButton.buttonWithType(UIButtonTypeRoundedRect)\n    button.setTitle(title, forState:UIControlStateNormal)\n    button.sizeToFit\n\n    rect = self.buttons.empty? ? CGRectMake(0, 0, 0, 0) : self.buttons.last.frame\n\n    button.frame = [[rect.origin.x, rect.origin.y + rect.size.height + 10], button.frame.size]\n    button.addTarget(self, action: callback, forControlEvents:UIControlEventTouchUpInside)\n\n    self.buttons << button\n    button\n  end\n\n  def local_file\n    NSURL.fileURLWithPath(File.join(NSBundle.mainBundle.resourcePath, 'test.mp3'))\n  end\n\n  def tapped_modal\n    BW::Media.play_modal(local_file)\n  end\n\n  def tapped_frame\n    BW::Media.play(local_file) do |media_player|\n      media_player.view.frame = [[10, 140], [self.view.frame.size.width - 20, 100]]\n      self.view.addSubview media_player.view\n    end\n  end\nend\n"
  },
  {
    "path": "samples/media/spec/main_spec.rb",
    "content": "describe \"Application 'media'\" do\n  before do\n    @app = UIApplication.sharedApplication\n  end\n\n  it \"has one window\" do\n    @app.windows.size.should == 1\n  end\nend\n"
  },
  {
    "path": "samples/osx/Gemfile",
    "content": "source \"https://rubygems.org\"\n\ngem \"rake\"\ngem 'bubble-wrap', :path => \"../../\"\ngem 'bubble-wrap-http'\n"
  },
  {
    "path": "samples/osx/Rakefile",
    "content": "# -*- coding: utf-8 -*-\n$:.unshift(\"/Library/RubyMotion/lib\")\nrequire 'motion/project/template/osx'\n\nrequire 'bundler'\nBundler.require :default\n\nMotion::Project::App.setup do |app|\n  # Use `rake config' to see complete project settings.\n  app.name = 'osx'\nend\n"
  },
  {
    "path": "samples/osx/app/app_delegate.rb",
    "content": "class AppDelegate\n  def applicationDidFinishLaunching(notification)\n    buildMenu\n    buildWindow\n  end\n\n  def buildWindow\n    @mainWindow = NSWindow.alloc.initWithContentRect([[240, 180], [480, 360]],\n      styleMask: NSTitledWindowMask|NSClosableWindowMask|NSMiniaturizableWindowMask|NSResizableWindowMask,\n      backing: NSBackingStoreBuffered,\n      defer: false)\n    @mainWindow.title = \"What Is My IP?\"\n    @mainWindow.orderFrontRegardless\n\n    @button = make_button(\"Find IP\")\n    @button.target = self\n    @button.action = \"fetch_ip\"\n\n    @label = make_label(\"_._._._\")\n\n    view = @mainWindow.contentView\n    view.addSubview(@button)\n    view.addSubview(@label)\n\n    views_hash = {\"button\" => @button, \"label\" => @label}\n\n    add_constraint \"|-[button(<=200)]-|\", to: view, views: views_hash\n\n    add_constraint \"|-[label]-|\", to: view, views: views_hash\n\n    add_constraint \"V:|-[button]-10-[label]-(>=20,<=60)-|\", to: view, views: views_hash\n  end\n\n  def fetch_ip\n    @button.title = \"Loading\"\n    BW::HTTP.get(\"http://jsonip.com\") do |response|\n      ip = BW::JSON.parse(response.body.to_str)[\"ip\"]\n      @label.stringValue = ip\n      @button.title = \"Find IP\"\n    end\n  end\n\n  def make_button(title)\n    button = NSButton.alloc.initWithFrame(CGRectZero)\n    button.title = title\n    button.buttonType = NSMomentaryLightButton\n    button.bezelStyle = NSRoundedBezelStyle\n    button.setTranslatesAutoresizingMaskIntoConstraints(false)\n    button\n  end\n\n  def make_label(text)\n    textField = NSTextField.alloc.initWithFrame(CGRectZero)\n    textField.stringValue = text\n    textField.alignment = NSCenterTextAlignment\n    textField.bezeled = false\n    textField.drawsBackground = false\n    textField.editable = false\n    textField.selectable = false\n    textField.setTranslatesAutoresizingMaskIntoConstraints(false)\n    textField\n  end\n\n  def add_constraint(ascii, params = {})\n    view = params[:to]\n    views_hash = params[:views]\n    view.addConstraints(NSLayoutConstraint.constraintsWithVisualFormat(ascii, options: 0, metrics: nil, views: views_hash))\n  end\nend\n"
  },
  {
    "path": "samples/osx/app/menu.rb",
    "content": "class AppDelegate\n  def buildMenu\n    @mainMenu = NSMenu.new\n\n    appName = NSBundle.mainBundle.infoDictionary['CFBundleName']\n    addMenu(appName) do\n      addItemWithTitle(\"About #{appName}\", action: 'orderFrontStandardAboutPanel:', keyEquivalent: '')\n      addItem(NSMenuItem.separatorItem)\n      addItemWithTitle('Preferences', action: 'openPreferences:', keyEquivalent: ',')\n      addItem(NSMenuItem.separatorItem)\n      servicesItem = addItemWithTitle('Services', action: nil, keyEquivalent: '')\n      NSApp.servicesMenu = servicesItem.submenu = NSMenu.new\n      addItem(NSMenuItem.separatorItem)\n      addItemWithTitle(\"Hide #{appName}\", action: 'hide:', keyEquivalent: 'h')\n      item = addItemWithTitle('Hide Others', action: 'hideOtherApplications:', keyEquivalent: 'H')\n      item.keyEquivalentModifierMask = NSCommandKeyMask|NSAlternateKeyMask\n      addItemWithTitle('Show All', action: 'unhideAllApplications:', keyEquivalent: '')\n      addItem(NSMenuItem.separatorItem)\n      addItemWithTitle(\"Quit #{appName}\", action: 'terminate:', keyEquivalent: 'q')\n    end\n\n    addMenu('File') do\n      addItemWithTitle('New', action: 'newDocument:', keyEquivalent: 'n')\n      addItemWithTitle('Open…', action: 'openDocument:', keyEquivalent: 'o')\n      addItem(NSMenuItem.separatorItem)\n      addItemWithTitle('Close', action: 'performClose:', keyEquivalent: 'w')\n      addItemWithTitle('Save…', action: 'saveDocument:', keyEquivalent: 's')\n      addItemWithTitle('Revert to Saved', action: 'revertDocumentToSaved:', keyEquivalent: '')\n      addItem(NSMenuItem.separatorItem)\n      addItemWithTitle('Page Setup…', action: 'runPageLayout:', keyEquivalent: 'P')\n      addItemWithTitle('Print…', action: 'printDocument:', keyEquivalent: 'p')\n    end\n\n    addMenu('Edit') do\n      addItemWithTitle('Undo', action: 'undo:', keyEquivalent: 'z')\n      addItemWithTitle('Redo', action: 'redo:', keyEquivalent: 'Z')\n      addItem(NSMenuItem.separatorItem)\n      addItemWithTitle('Cut', action: 'cut:', keyEquivalent: 'x')\n      addItemWithTitle('Copy', action: 'copy:', keyEquivalent: 'c')\n      addItemWithTitle('Paste', action: 'paste:', keyEquivalent: 'v')\n      item = addItemWithTitle('Paste and Match Style', action: 'pasteAsPlainText:', keyEquivalent: 'V')\n      item.keyEquivalentModifierMask = NSCommandKeyMask|NSAlternateKeyMask\n      addItemWithTitle('Delete', action: 'delete:', keyEquivalent: '')\n      addItemWithTitle('Select All', action: 'selectAll:', keyEquivalent: 'a')\n    end\n\n    fontMenu = createMenu('Font') do\n      addItemWithTitle('Show Fonts', action: 'orderFrontFontPanel:', keyEquivalent: 't')\n      addItemWithTitle('Bold', action: 'addFontTrait:', keyEquivalent: 'b')\n      addItemWithTitle('Italic', action: 'addFontTrait:', keyEquivalent: 'b')\n      addItemWithTitle('Underline', action: 'underline:', keyEquivalent: 'u')\n      addItem(NSMenuItem.separatorItem)\n      addItemWithTitle('Bigger', action: 'modifyFont:', keyEquivalent: '+')\n      addItemWithTitle('Smaller', action: 'modifyFont:', keyEquivalent: '-')\n    end\n\n    textMenu = createMenu('Text') do\n      addItemWithTitle('Align Left', action: 'alignLeft:', keyEquivalent: '{')\n      addItemWithTitle('Center', action: 'alignCenter:', keyEquivalent: '|')\n      addItemWithTitle('Justify', action: 'alignJustified:', keyEquivalent: '')\n      addItemWithTitle('Align Right', action: 'alignRight:', keyEquivalent: '}')\n      addItem(NSMenuItem.separatorItem)\n      addItemWithTitle('Show Ruler', action: 'toggleRuler:', keyEquivalent: '')\n      addItemWithTitle('Copy Ruler', action: 'copyRuler:', keyEquivalent: 'c')\n      addItemWithTitle('Paste Ruler', action: 'pasteRuler:', keyEquivalent: 'v')\n    end\n\n    addMenu('Format') do\n      addItem fontMenu\n      addItem textMenu\n    end\n\n    addMenu('View') do\n      item = addItemWithTitle('Show Toolbar', action: 'toggleToolbarShown:', keyEquivalent: 't')\n      item.keyEquivalentModifierMask = NSCommandKeyMask|NSAlternateKeyMask\n      addItemWithTitle('Customize Toolbar…', action: 'runToolbarCustomizationPalette:', keyEquivalent: '')\n    end\n\n    NSApp.windowsMenu = addMenu('Window') do\n      addItemWithTitle('Minimize', action: 'performMiniaturize:', keyEquivalent: 'm')\n      addItemWithTitle('Zoom', action: 'performZoom:', keyEquivalent: '')\n      addItem(NSMenuItem.separatorItem)\n      addItemWithTitle('Bring All To Front', action: 'arrangeInFront:', keyEquivalent: '')\n    end.menu\n\n    NSApp.helpMenu = addMenu('Help') do\n      addItemWithTitle(\"#{appName} Help\", action: 'showHelp:', keyEquivalent: '?')\n    end.menu\n\n    NSApp.mainMenu = @mainMenu\n  end\n\n  private\n\n  def addMenu(title, &b)\n    item = createMenu(title, &b)\n    @mainMenu.addItem item\n    item\n  end\n\n  def createMenu(title, &b)\n    menu = NSMenu.alloc.initWithTitle(title)\n    menu.instance_eval(&b) if b\n    item = NSMenuItem.alloc.initWithTitle(title, action: nil, keyEquivalent: '')\n    item.submenu = menu\n    item\n  end\nend\n"
  },
  {
    "path": "samples/osx/resources/Credits.rtf",
    "content": "{\\rtf0\\ansi{\\fonttbl\\f0\\fswiss Helvetica;}\n{\\colortbl;\\red255\\green255\\blue255;}\n\\paperw9840\\paperh8400\n\\pard\\tx560\\tx1120\\tx1680\\tx2240\\tx2800\\tx3360\\tx3920\\tx4480\\tx5040\\tx5600\\tx6160\\tx6720\\ql\\qnatural\n\n\\f0\\b\\fs24 \\cf0 Engineering:\n\\b0 \\\n\tSome people\\\n\\\n\n\\b Human Interface Design:\n\\b0 \\\n\tSome other people\\\n\\\n\n\\b Testing:\n\\b0 \\\n\tHopefully not nobody\\\n\\\n\n\\b Documentation:\n\\b0 \\\n\tWhoever\\\n\\\n\n\\b With special thanks to:\n\\b0 \\\n\tMom\\\n}\n"
  },
  {
    "path": "samples/osx/spec/main_spec.rb",
    "content": "describe \"Application 'osx'\" do\n  before do\n    @app = UIApplication.sharedApplication\n  end\n\n  it \"has one window\" do\n    @app.windows.size.should == 1\n  end\nend\n"
  },
  {
    "path": "spec/lib/bubble-wrap/ext/motion_project_app_spec.rb",
    "content": "require 'mocha-on-bacon'\nrequire File.expand_path('../../../motion_stub', __FILE__)\nrequire 'bubble-wrap'\n\ndescribe BubbleWrap::Ext::BuildTask do\n\n  before do\n    @subject = Class.new do\n      def self.setup; end\n      def self.configs\n        @configs ||= { :development => Object.new }\n      end\n    end\n    @subject.extend BubbleWrap::Ext::BuildTask\n    @default_frameworks = ['CoreGraphics', 'Foundation', 'UIKit']\n  end\n\n  describe '.extended' do\n    it 'responds to :setup_with_bubblewrap' do\n      @subject.respond_to?(:setup_with_bubblewrap).should == true\n    end\n\n    it 'responds to :setup_without_bubblewrap' do\n      @subject.respond_to?(:setup_without_bubblewrap).should == true\n    end\n\n    it 'replaces :setup with :setup_with_bubblewrap' do\n      @subject.method(:setup).should == @subject.method(:setup_with_bubblewrap)\n    end\n  end\n\n  describe '.setup_with_bubblewrap' do\n    before do\n      @config = @subject.configs[:development]\n      @config.stubs(:files=)\n      @config.stubs(:files)\n      @config.stubs(:files_dependencies)\n      @config.stubs(:frameworks)\n      @config.stubs(:frameworks=)\n      @subject.stubs(:config).returns(mock())\n      @subject.config.stubs(:validate)\n    end\n\n    it 'calls the passed-in block' do\n      block = proc { }\n      block.expects(:call).with(@config)\n      @subject.setup &block\n    end\n\n    describe 'when app.files is nil' do\n      it 'sets app.files' do\n        @config.stubs(:files).returns(nil)\n        files = BubbleWrap::Requirement.files\n        @config.expects(:files=).with(files)\n        @subject.setup\n      end\n    end\n\n    describe 'when app.files is empty' do\n      it 'sets app.files' do\n        @config.stubs(:files).returns([])\n        files = BubbleWrap::Requirement.files\n        @config.expects(:files=).with(files)\n        @subject.setup\n      end\n    end\n\n    describe 'when app.files has contents' do\n      it 'sets app.files' do\n        mock_files = ['a', 'b', 'c']\n        @config.stubs(:files).returns(mock_files)\n        files = BubbleWrap::Requirement.files + mock_files\n        @config.expects(:files=).with(files)\n        @subject.setup\n      end\n    end\n\n    it 'removes duplicates from app.files' do\n      files = ['a', 'a', 'b', 'b', 'c', 'c']\n      @config.stubs(:files).returns(files)\n      @config.expects(:files=).with(BubbleWrap::Requirement.files + files.uniq)\n      @subject.setup\n    end\n\n    it 'adds BW dependencies' do\n      @config.expects(:files_dependencies).with(BubbleWrap::Requirement.files_dependencies)\n      @subject.setup\n    end\n\n    describe 'when app.frameworks is empty' do\n      it 'sets the default frameworks' do\n        @config.stubs(:frameworks).returns(nil)\n        @config.expects(:frameworks=).with(@default_frameworks)\n        @subject.setup\n      end\n    end\n\n    describe 'when app.frameworks is empty' do\n      it 'sets the default frameworks' do\n        @config.stubs(:frameworks).returns([])\n        @config.expects(:frameworks=).with(@default_frameworks)\n        @subject.setup\n      end\n    end\n\n    describe 'when app.frameworks contains defaults' do\n      it 'sets the default frameworks' do\n        @config.stubs(:frameworks).returns(@default_frameworks)\n        @config.expects(:frameworks=).with(@default_frameworks)\n        @subject.setup\n      end\n    end\n\n    describe 'when app.frameworks contains non-defaults' do\n      it 'sets the default frameworks and the contents' do\n        @config.stubs(:frameworks).returns(['Addressbook'])\n        @config.expects(:frameworks=).with(['Addressbook'] + @default_frameworks)\n        @subject.setup\n      end\n    end\n\n    describe 'when BW::Requirement.frameworks has contents' do\n      it 'sets the default frameworks and the contents' do\n        BW.require('motion/core.rb') do\n          file('motion/core.rb').uses_framework('Addressbook')\n        end\n        @config.stubs(:frameworks).returns(nil)\n        @config.expects(:frameworks=).with(['Addressbook'] + @default_frameworks)\n        @subject.setup\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/lib/bubble-wrap/ext/motion_project_config_spec.rb",
    "content": "require 'mocha-on-bacon'\nrequire File.expand_path('../../../motion_stub', __FILE__)\nrequire 'bubble-wrap'\n\ndescribe BubbleWrap::Ext::ConfigTask do\n\n  before do\n    klass = Class.new do\n      def initialize\n        @files = [ '/fake/a', '/fake/b' ]\n        @dependencies = {}\n      end\n\n      def files_dependencies\n      end\n    end\n    klass.send(:include, BubbleWrap::Ext::ConfigTask)\n    @subject = klass.new\n  end\n\n  describe '.included' do\n    it 'aliases :files_dependencies to :files_dependencies_without_bubblewrap' do\n      @subject.respond_to?(:files_dependencies_without_bubblewrap).should == true\n    end\n\n    it 'aliass :files_dependencies_with_bubblewrap to :files_dependencies' do\n      @subject.method(:files_dependencies).should == @subject.method(:files_dependencies_with_bubblewrap)\n    end\n  end\n\n  describe '#path_matching_expression' do\n    it 'returns a regular expression' do\n      @subject.path_matching_expression.is_a?(Regexp).should == true\n    end\n  end\n\n  describe '#files_dependencies_with_bubblewrap' do\n    it 'should call #path_matching_expression' do\n      @subject.expects(:path_matching_expression).twice().returns(/^\\.?\\//)\n      @subject.files_dependencies_with_bubblewrap '/fake/a' => '/fake/b'\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/lib/bubble-wrap/requirement/path_manipulation_spec.rb",
    "content": "require File.expand_path('../../../../../lib/bubble-wrap/requirement/path_manipulation', __FILE__)\n\ndescribe BubbleWrap::Requirement::PathManipulation do\n\n  before do\n    @subject = Object.new\n    @subject.extend BubbleWrap::Requirement::PathManipulation\n  end\n\n  describe '#convert_caller_to_path' do\n    it 'strips off from the second-to-last colon' do\n      @subject.convert_caller_to_path(\"/fake/:path/foo:91:in `fake_method'\").\n        should == '/fake/:path'\n    end\n\n    it 'leaves plain old paths unmolested' do\n      @subject.convert_caller_to_path(\"/fake/path\").\n        should == '/fake/path'\n    end\n  end\n\n  describe '#convert_to_absolute_path' do\n    it 'converts relative paths to absolute paths' do\n      @subject.convert_to_absolute_path('foo')[0].should == '/'\n    end\n\n    it \"doesn't modify absolute paths\" do\n      @subject.convert_to_absolute_path('/foo').should == '/foo'\n    end\n  end\n\n  describe '#strip_up_to_last_lib' do\n    it 'strips off from the last lib' do\n      @subject.strip_up_to_last_lib('/fake/lib/dir/lib/foo').\n        should == '/fake/lib/dir'\n    end\n\n    it \"strips off only a trailing lib\" do\n      @subject.strip_up_to_last_lib('/fake/lib/dir/lib').\n        should == '/fake/lib/dir'\n    end\n\n    it \"doesn't modify the path otherwise\" do\n      @subject.strip_up_to_last_lib('/fake/path').\n        should == '/fake/path'\n    end\n  end\n\n  describe \"#convert_to_relative\" do\n    it 'strips off the root portion' do\n      @subject.convert_to_relative('/foo/bar/baz', '/foo').\n        should == 'bar/baz'\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/bubble-wrap/requirement_spec.rb",
    "content": "require File.expand_path('../../motion_stub', __FILE__)\nrequire 'bubble-wrap'\n\ndescribe BubbleWrap::Requirement do\n\n  before do\n    @subject = BubbleWrap::Requirement\n  end\n\n  describe '.scan' do\n    before do\n      @subject.clear!\n      @root_path = File.expand_path('../../../../', __FILE__)\n    end\n\n    it 'asking for a not-yet-found file raises an exception' do\n      should.raise(Exception) do\n        @subject.find('foo')\n      end\n    end\n\n    it 'finds the specified file' do\n      @subject.scan(@root_path, 'motion/core.rb')\n      @subject.paths.keys.should.include 'motion/core.rb'\n    end\n\n    it 'finds multiple files according to spec' do\n      @subject.scan(@root_path, 'motion/**/*.rb')\n      @subject.files.size.should > 1\n    end\n\n    it 'never depends on itself' do\n      @subject.scan(@root_path, 'motion/core.rb') do\n        file('motion/core.rb').depends_on 'motion/core.rb'\n      end\n      @subject.file('motion/core.rb').file_dependencies.should.not.include 'motion/core.rb'\n    end\n\n    it 'can depend on another file' do\n      @subject.scan(@root_path, 'motion/*.rb') do\n        file('motion/motion.rb').depends_on('motion/constants.rb')\n      end\n      @subject.file('motion/motion.rb').file_dependencies.should.include @subject.file('motion/constants.rb')\n    end\n\n    it 'can use a framework' do\n      @subject.scan(@root_path, 'motion/core.rb') do\n        file('motion/core.rb').uses_framework('FakeFramework')\n      end\n      @subject.file('motion/core.rb').frameworks.member?('FakeFramework').should == true\n    end\n\n    it \"figures out the root of the project\" do\n      @subject.scan(File.join(@root_path, 'lib/bubble-wrap.rb'), 'motion/core.rb')\n      @subject.paths.values.first.root.should == @root_path\n    end\n\n    describe '.frameworks' do\n      it 'includes UIKit by default' do\n        @subject.frameworks.member?('UIKit').should == true\n      end\n\n      it 'includes Foundation by default' do\n        @subject.frameworks.member?('Foundation').should == true\n      end\n\n      it 'includes CoreGraphics by default' do\n        @subject.frameworks.member?('CoreGraphics').should == true\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/lib/bubble-wrap_spec.rb",
    "content": "require 'mocha-on-bacon'\nrequire File.expand_path('../motion_stub.rb', __FILE__)\nrequire 'bubble-wrap'\n\ndescribe BubbleWrap do\n  describe '.root' do\n    it 'returns an absolute path' do\n      BubbleWrap.root[0].should == '/'\n    end\n  end\n\n  describe '.require' do\n    it 'delegates to Requirement.scan' do\n      BW::Requirement.expects(:scan)\n      BW.require('foo')\n    end\n\n    it 'finds files with relative paths' do\n      BW::Requirement.clear!\n      BW.require '../motion/core.rb'\n      BW::Requirement.files.member?(File.expand_path('../../../motion/core.rb', __FILE__)).should == true\n    end\n\n    it 'finds files with absolute paths' do\n      BW::Requirement.clear!\n      BW.require File.expand_path('../../../motion/core.rb', __FILE__)\n      BW::Requirement.files.member?(File.expand_path('../../../motion/core.rb', __FILE__)).should == true\n    end\n  end\nend\n"
  },
  {
    "path": "spec/lib/motion_stub.rb",
    "content": "# Create a fake Motion class hierarchy for testing.\nmodule Motion\n  module Project\n    class App\n      def self.setup\n      end\n    end\n\n    class Config\n      def files_dependencies\n      end\n    end\n  end\n  Version = \"1.24\"\nend\n"
  },
  {
    "path": "spec/motion/core/app_spec.rb",
    "content": "describe BubbleWrap::App do\n  describe '.documents_path' do\n    it 'should end in \"/Documents\"' do\n      App.documents_path[-10..-1].should == '/Documents'\n    end\n  end\n\n  describe '.resources_path' do\n    it 'should end in \"/testSuite.app\"' do\n      if App.osx?\n        App.resources_path.should =~ /\\/testSuite(_spec)?.app\\/Contents\\/Resources$/\n      else\n        App.resources_path.should =~ /\\/testSuite(_spec)?.app$/\n      end\n    end\n  end\n\n  describe '.notification_center' do\n    it 'should be a NSNotificationCenter' do\n      App.notification_center.should == NSNotificationCenter.defaultCenter\n    end\n  end\n\n  describe '.user_cache' do\n    it 'should be a NSUserDefaults' do\n      App.user_cache.should == NSUserDefaults.standardUserDefaults\n    end\n  end\n\n  describe '.states' do\n    it 'returns a hash' do\n      App.states.class.should == Hash\n    end\n    it \"returns the real instance variable\" do\n      App.states.should == App.instance_variable_get(:@states)\n    end\n  end\n\n  describe '.info_plist' do\n    it 'returns the information property list hash' do\n      App.info_plist.should == NSBundle.mainBundle.infoDictionary\n    end\n  end\n\n  describe '.name' do\n    it 'returns the application name' do\n      App.name.should == 'testSuite'\n    end\n  end\n\n  describe '.identifier' do\n    it 'returns the application identifier' do\n      App.identifier.should == 'io.bubblewrap.testSuite_spec'\n    end\n  end\n\n  describe '.version' do\n    it 'returns the application version' do\n      App.version.should == '1.2.3'\n    end\n  end\n\n  describe '.short_version' do\n    it 'returns the application short version' do\n      App.short_version.should == '3.2.1'\n    end\n  end\n\n  describe '.run_after' do\n    class DelayedRunAfterTest; attr_accessor :test_value end\n\n    it 'should run a block after the provided delay' do\n      @test_obj = DelayedRunAfterTest.new\n\n      App.run_after(0.1){ @test_obj.test_value = true }\n      wait_for_change(@test_obj, 'test_value') do\n        @test_obj.test_value.should == true\n      end\n    end\n\n  end\n\n  describe \".open_url\" do\n\n    it \"uses NSURL or converts NSString in NSURL and opens it\" do\n      if Kernel.const_defined?(:UIApplication)\n        application = UIApplication.sharedApplication\n      else\n        application = NSWorkspace.sharedWorkspace\n      end\n      def application.url; @url end\n      def application.openURL(url); @url = url end\n\n      url = NSURL.URLWithString('http://localhost')\n      App.open_url(url)\n      application.url.should.equal url\n\n      url = 'http://localhost'\n      App.open_url(url)\n      application.url.class.should.equal NSURL\n      application.url.description.should.equal url\n    end\n\n  end\n\n  describe \".environment\" do\n\n    it 'returns current application environment' do\n      App.environment.should.equal \"test\"\n    end\n\n  end\n\n  describe \".test? .release? .development?\" do\n\n    it 'tests if current application environment is test' do\n      App.test?.should.equal true\n    end\n\n    it 'tests if current application environment is release' do\n      App.release?.should.equal false\n    end\n\n    it 'tests if current application environment is development' do\n      App.development?.should.equal false\n    end\n\n  end\n\nend\n"
  },
  {
    "path": "spec/motion/core/device/ios/camera_spec.rb",
    "content": "def camera_picker\n  @camera.instance_variable_get(\"@picker\")\nend\n\ndef example_info\n  { UIImagePickerControllerMediaType => KUTTypeImage,\n    UIImagePickerControllerOriginalImage => UIImage.alloc.init,\n    UIImagePickerControllerMediaURL => NSURL.alloc.init}\nend\n\ndescribe BubbleWrap::Device::Camera do\n  before do\n    @controller = UIViewController.alloc.init\n    @controller.instance_eval do\n      def presentViewController(*args)\n        true\n      end\n    end\n    @camera = BW::Device::Camera.new\n  end\n\n  describe '.flash?' do\n    before do\n      UIImagePickerController.instance_eval do\n        def self.isCameraDeviceAvailable(c)\n          return true\n        end\n\n        def self.isFlashAvailableForCameraDevice(c)\n          return c == UIImagePickerControllerCameraDeviceFront\n        end\n      end\n    end\n\n    it 'should be true for front cameras' do\n      BW::Device::Camera.front.flash?.should == true\n    end\n\n    it 'should be false for rear cameras' do\n      BW::Device::Camera.rear.flash?.should == false\n    end\n  end\n\n  describe '.picture' do\n    it 'should have correct error for source_type camera' do\n      @camera.picture({source_type: :camera, media_types: [:image]}, @controller) do |result|\n        result[:error].should == BW::Camera::Error::SOURCE_TYPE_NOT_AVAILABLE\n        camera_picker.nil?.should == true\n      end\n\n      @camera.picture({source_type: :saved_photos_album, media_types: [:image]}, @controller) do |result|\n        result[:error].should == BW::Camera::Error::SOURCE_TYPE_NOT_AVAILABLE\n      end\n    end\n\n    describe 'under normal conditions' do\n      before do\n        class FakePickerClass\n          def self.isCameraDeviceAvailable(c)\n            c == UIImagePickerControllerCameraDeviceFront\n          end\n\n          def self.isSourceTypeAvailable(c)\n            c == UIImagePickerControllerSourceTypeCamera\n          end\n\n          def self.availableMediaTypesForSourceType(c)\n            [KUTTypeMovie, KUTTypeImage]\n          end\n\n          def dismissViewControllerAnimated(*args)\n            true\n          end\n\n          def self.method_missing(*args)\n            UIImagePickerController.send(*args)\n          end\n        end\n        @picker_klass = FakePickerClass\n      end\n\n      it 'should work' do\n        camera = BW::Device.camera.front\n        camera.instance_variable_set(\"@picker_klass\", @picker_klass)\n        image_view = nil\n        info = example_info\n\n        camera.picture(media_types: [:movie, :image]) do |result|\n          image_view = UIImageView.alloc.initWithImage(result[:original_image])\n        end\n\n        camera.picker\n        camera.imagePickerController(camera.instance_variable_get(\"@picker\"), didFinishPickingMediaWithInfo: info)\n        image_view.nil?.should == false\n      end\n\n      it 'should set popover' do\n        uiview = UIView.alloc\n        camera = BW::Device.camera.photo_library.popover_from(uiview)\n        camera.instance_variable_get(\"@popover_in_view\").should == uiview\n      end\n    end\n  end\n\n  describe '.imagePickerControllerDidCancel' do\n    it 'should yield the correct error when canceled' do\n      callback_ran = false\n\n      @camera.picture({source_type: :photo_library, media_types: [:image]}, @controller) do |result|\n        result[:error].should == BW::Camera::Error::CANCELED\n        callback_ran = true\n      end\n\n      @camera.imagePickerControllerDidCancel(camera_picker)\n      callback_ran.should == true\n    end\n  end\n\n  describe '.imagePickerController:didFinishPickingMediaWithInfo:' do\n    it 'should yield the correct results' do\n\n      info = example_info\n      callback_ran = false\n\n      @camera.picture({source_type: :photo_library, media_types: [:image]}, @controller) do |result|\n        result[:error].nil?.should == true\n        result.keys.should == [:media_type, :original_image, :media_url]\n        result[:media_type].should == :image\n        callback_ran = true\n      end\n\n      @camera.imagePickerController(camera_picker, didFinishPickingMediaWithInfo: info)\n      callback_ran.should == true\n    end\n  end\nend\n"
  },
  {
    "path": "spec/motion/core/device/ios/camera_wrapper_spec.rb",
    "content": "describe BubbleWrap::Device::CameraWrapper do\n\n  before do\n    BW::Device.camera.instance_variable_set(:@front, nil)\n    BW::Device.camera.instance_variable_set(:@rear, nil)\n  end\n\n  describe 'on device with only front facing camera' do\n    before do\n      UIImagePickerController.instance_eval do\n        def isCameraDeviceAvailable(c)\n          c == UIImagePickerControllerCameraDeviceFront\n        end\n        def isSourceTypeAvailable(c)\n          c == UIImagePickerControllerSourceTypeCamera\n        end\n      end\n    end\n\n    describe '.front?' do\n      it 'returns true' do\n        BW::Device.camera.front?.should == true\n      end\n    end\n\n    describe '.rear?' do\n      it 'returns false' do\n        BW::Device.camera.rear?.should == false\n      end\n    end\n\n    describe '.available?' do\n      it 'returns true' do\n        BW::Device.camera.available?.should == true\n      end\n    end\n  end\n\n  describe 'on device with only rear facing camera' do\n    before do\n      UIImagePickerController.instance_eval do\n        def isCameraDeviceAvailable(c)\n          c == UIImagePickerControllerCameraDeviceRear\n        end\n        def isSourceTypeAvailable(c)\n          c == UIImagePickerControllerSourceTypeCamera\n        end\n      end\n    end\n\n    describe '.front?' do\n      it 'returns false' do\n        BW::Device.camera.front?.should == false\n      end\n    end\n\n    describe '.rear?' do\n      it 'returns true' do\n        BW::Device.camera.rear?.should == true\n      end\n    end\n\n    describe '.available?' do\n      it 'returns true' do\n        BW::Device.camera.available?.should == true\n      end\n    end\n  end\n\n  describe 'on device with no physical camera' do\n    before do\n      UIImagePickerController.instance_eval do\n        def isCameraDeviceAvailable(c)\n          false\n        end\n        def isSourceTypeAvailable(c)\n          false\n        end\n      end\n    end\n\n    describe '.front?' do\n      it 'returns false' do\n        BW::Device.camera.front?.should == false\n      end\n    end\n\n    describe '.rear?' do\n      it 'returns false' do\n        BW::Device.camera.rear?.should == false\n      end\n    end\n\n    describe '.available?' do\n      it 'returns true' do\n        BW::Device.camera.available?.should == false\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/motion/core/device/ios/device_spec.rb",
    "content": "describe BubbleWrap::Device do\ndescribe \"iOS\" do\n  describe 'on iPhone' do\n    before do\n      @idiom = UIUserInterfaceIdiomPhone\n    end\n\n    describe '.iphone?' do\n      it 'returns true' do\n        BW::Device.iphone?(@idiom).should == true\n      end\n    end\n\n    describe '.ipad?' do\n      it 'returns false' do\n        BW::Device.ipad?(@idiom).should == false\n      end\n    end\n\n    describe '.long_screen?' do\n      it 'returns true if screen is wide' do\n        BW::Device.long_screen?(@idiom, 568.0).should == true\n      end\n\n      it 'returns false if screen is not wide' do\n        BW::Device.long_screen?(@idiom, 480.0).should == false\n      end\n    end\n  end\n\n  describe 'on iPad' do\n    before do\n      @idiom = UIUserInterfaceIdiomPad\n    end\n\n    describe '.iphone?' do\n      it 'returns false' do\n        BW::Device.iphone?(@idiom).should == false\n      end\n    end\n\n    describe '.ipad?' do\n      it 'returns true' do\n        BW::Device.ipad?(@idiom).should == true\n      end\n    end\n\n    describe '.long_screen?' do\n      it 'always not a widescreen' do\n        BW::Device.long_screen?(@idiom, 1024.0).should == false\n      end\n    end\n  end\n\n  describe '.simulator?' do\n    it 'returns true' do\n      BW::Device.simulator?.should == true\n    end\n  end\n\n  describe '.ios_version' do\n    it 'returns true' do\n      # exact value depends on system where specs run. 4.0 seems like a safe guess\n      BW::Device.ios_version.to_f.should > 4.0\n    end\n  end\n\n  describe '.vendor_identifier' do\n    it 'returns a value' do\n      BW::Device.vendor_identifier.should.not == nil\n    end\n  end\n\n  describe '.orientation' do\n    it 'delegates to BubbleWrap::Screen.orientation' do\n      BW::Device.orientation.should == BW::Device::Screen.orientation\n    end\n  end\nend\nend\n"
  },
  {
    "path": "spec/motion/core/device/ios/screen_spec.rb",
    "content": "describe BubbleWrap::Device::Screen do\ndescribe \"iOS\" do\n  describe 'on retina enabled screen' do\n    before do\n      @screen = Object.new.tap do |o|\n        def o.respondsToSelector(selector)\n          return true if selector == 'displayLinkWithTarget:selector:'\n          UIDevice.mainDevice.respondsToSelector(selector)\n        end\n        def o.scale\n          2.0\n        end\n        def o.method_missing(*args)\n          UIDevice.mainDevice.send(*args)\n        end\n      end\n    end\n\n    describe '.retina?' do\n      it 'returns true' do\n        BW::Device::Screen.retina?(@screen).should == true\n      end\n    end\n  end\n\n  describe 'on non-retina enabled screen' do\n    before do\n      @screen = Object.new.tap do |o|\n        def o.respondsToSelector(selector)\n          return false if selector == 'displayLinkWithTarget:selector:'\n          UIDevice.mainDevice.respondsToSelector(selector)\n        end\n        def o.scale\n          1.0\n        end\n        def o.method_missing(*args)\n          UIDevice.mainDevice.send(*args)\n        end\n      end\n    end\n\n    describe '.retina?' do\n      it 'returns false' do\n        BW::Device::Screen.retina?(@screen).should == false\n      end\n    end\n  end\n\n  describe '.orientation' do\n\n    describe 'portrait' do\n      it 'returns :portrait' do\n        BW::Device::Screen.orientation(UIDeviceOrientationPortrait).should == :portrait\n      end\n    end\n\n    describe 'portrait upside down' do\n      it 'returns :portrait_upside_down' do\n        BW::Device::Screen.orientation(UIDeviceOrientationPortraitUpsideDown).should == :portrait_upside_down\n      end\n    end\n\n    describe 'landscape left' do\n      it 'returns :landscape_left' do\n        BW::Device::Screen.orientation(UIDeviceOrientationLandscapeLeft).should == :landscape_left\n      end\n    end\n\n    describe 'landscape right' do\n      it 'returns :landscape_right' do\n        BW::Device::Screen.orientation(UIDeviceOrientationLandscapeRight).should == :landscape_right\n      end\n    end\n\n    describe 'face up' do\n      it 'returns :face_up' do\n        BW::Device::Screen.orientation(UIDeviceOrientationFaceUp).should == :face_up\n      end\n    end\n\n    describe 'face down' do\n      it 'returns :face_down' do\n        BW::Device::Screen.orientation(UIDeviceOrientationFaceDown).should == :face_down\n      end\n    end\n\n    describe 'unknown' do\n      it 'returns :unknown if fallback is false' do\n        BW::Device::Screen.orientation(UIDeviceOrientationUnknown, false).should == :unknown\n      end\n      it 'returns Status bar orientation if fallback not specified' do\n        BW::Device::Screen.orientation(UIDeviceOrientationUnknown).should == BW::Device::Screen.orientation(UIApplication.sharedApplication.statusBarOrientation)\n      end\n    end\n\n    describe 'any other input' do\n      it 'returns :unknown  if fallback is false' do\n        BW::Device::Screen.orientation('twiggy twiggy twiggy', false).should == :unknown\n      end\n      it 'returns Status bar orientation if fallback not specified' do\n        BW::Device::Screen.orientation('twiggy twiggy twiggy').should == BW::Device::Screen.orientation(UIApplication.sharedApplication.statusBarOrientation)\n      end\n    end\n  end\n\n  describe '.interface_orientation' do\n\n    describe 'portrait' do\n      it 'returns :portrait' do\n        BW::Device::Screen.interface_orientation(UIInterfaceOrientationPortrait).should == :portrait\n      end\n    end\n\n    describe 'portrait upside down' do\n      it 'returns :portrait_upside_down' do\n        BW::Device::Screen.interface_orientation(UIInterfaceOrientationPortraitUpsideDown).should == :portrait_upside_down\n      end\n    end\n\n    describe 'landscape left' do\n      it 'returns :landscape_left' do\n        BW::Device::Screen.interface_orientation(UIInterfaceOrientationLandscapeLeft).should == :landscape_left\n      end\n    end\n\n    describe 'landscape right' do\n      it 'returns :landscape_right' do\n        BW::Device::Screen.interface_orientation(UIInterfaceOrientationLandscapeRight).should == :landscape_right\n      end\n    end\n\n    describe 'unknown' do\n      it 'returns :unknown if fallback is false' do\n        BW::Device::Screen.interface_orientation(UIDeviceOrientationUnknown, false).should == :unknown\n      end\n      it 'returns Status bar orientation if fallback not specified' do\n        BW::Device::Screen.interface_orientation(UIDeviceOrientationUnknown).should == BW::Device::Screen.interface_orientation(UIApplication.sharedApplication.statusBarOrientation)\n      end\n    end\n\n    describe 'any other input' do\n      it 'returns :unknown  if fallback is false' do\n        BW::Device::Screen.interface_orientation('twiggy twiggy twiggy', false).should == :unknown\n      end\n      it 'returns Status bar orientation if fallback not specified' do\n        BW::Device::Screen.interface_orientation('twiggy twiggy twiggy').should == BW::Device::Screen.interface_orientation(UIApplication.sharedApplication.statusBarOrientation)\n      end\n    end\n  end\n\n  describe '.width' do\n    it 'returns the current device screen width' do\n      BW::Device::Screen.width.should == 320.0 if BW::Device.iphone?\n      BW::Device::Screen.width.should == 768.0 if BW::Device.ipad?\n    end\n  end\n\n  describe '.height' do\n    it 'returns the current device screen height' do\n      BW::Device::Screen.height.should == 480.0 if BW::Device.iphone?\n      BW::Device::Screen.height.should == 1024.0 if BW::Device.ipad?\n    end\n  end\n\n  describe '.width_for_orientation' do\n    describe ':landscape_left' do\n      it 'returns the current device screen height' do\n        BW::Device::Screen.width_for_orientation(:landscape_left).should == BW::Device::Screen.height\n      end\n    end\n\n    describe ':landscape_right' do\n      it 'returns the current device screen height' do\n        BW::Device::Screen.width_for_orientation(:landscape_right).should == BW::Device::Screen.height\n      end\n    end\n\n    describe 'default' do\n      it 'returns the current device screen width' do\n        BW::Device::Screen.width_for_orientation.should == BW::Device::Screen.width\n      end\n    end\n  end\n\n  describe '.height_for_orientation' do\n    describe ':landscape_left' do\n      it 'returns the current device screen width' do\n        BW::Device::Screen.height_for_orientation(:landscape_left).should == BW::Device::Screen.width\n      end\n    end\n\n    describe ':landscape_right' do\n      it 'returns the current device screen width' do\n        BW::Device::Screen.height_for_orientation(:landscape_right).should == BW::Device::Screen.width\n      end\n    end\n\n    describe 'default' do\n      it 'returns the current device screen height' do\n        BW::Device::Screen.height_for_orientation.should == BW::Device::Screen.height\n      end\n    end\n  end\nend\nend\n"
  },
  {
    "path": "spec/motion/core/device/osx/screen_spec.rb",
    "content": "describe BubbleWrap::Device::Screen do\n  describe \"OS X\" do\n    describe 'on retina enabled screen' do\n      before do\n        @screen = Object.new.tap do |o|\n          def o.respondsToSelector(selector)\n            return true if selector == 'backingScaleFactor'\n            NSScreen.mainScreen.respondsToSelector(selector)\n          end\n          def o.backingScaleFactor\n            2.0\n          end\n          def o.method_missing(*args)\n            NSScreen.mainScreen.send(*args)\n          end\n        end\n      end\n\n      describe '.retina?' do\n        it 'returns true' do\n          BW::Device::Screen.retina?(@screen).should == true\n        end\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/motion/core/device_spec.rb",
    "content": "describe BubbleWrap::Device do\n\n  describe '.screen' do\n    it 'return BubbleWrap::Screen' do\n      BW::Device.screen.should == BW::Device::Screen\n    end\n  end\n\n  describe '.retina?' do\n    it 'delegates to BubbleWrap::Screen.retina?' do\n      BW::Device.retina?.should == BW::Device::Screen.retina?\n    end\n  end\nend\n"
  },
  {
    "path": "spec/motion/core/ios/app_spec.rb",
    "content": "describe BubbleWrap::App do\n  describe \"iOS\" do\n    describe '.alert' do\n      after do\n        @alert.dismissWithClickedButtonIndex(@alert.cancelButtonIndex, animated: false)\n\n        wait 0.3 do\n        end\n      end\n\n      describe \"with only one string argument\" do\n        before do\n          @alert = App.alert('1.21 Gigawatts!')\n        end\n\n        it 'returns an alert' do\n          @alert.class.should == BW::UIAlertView\n        end\n\n        it 'is displaying the correct title' do\n          @alert.title.should == '1.21 Gigawatts!'\n        end\n\n        describe 'cancelButton' do\n          it 'is present' do\n            @alert.cancelButtonIndex.should == 0\n          end\n\n          it 'has the correct title' do\n            @alert.buttonTitleAtIndex(@alert.cancelButtonIndex).should == 'OK'\n          end\n        end\n      end\n\n      describe \"with only two string arguments\" do\n        before do\n          @alert = App.alert('1.21 Gigawatts!', 'Great Scott!')\n        end\n\n        it 'returns an alert' do\n          @alert.class.should == BW::UIAlertView\n        end\n\n        it 'is displaying the correct title' do\n          @alert.title.should == '1.21 Gigawatts!'\n        end\n\n        describe 'cancelButton' do\n          it 'is present' do\n            @alert.cancelButtonIndex.should == 0\n          end\n\n          it 'has the correct title' do\n            @alert.buttonTitleAtIndex(@alert.cancelButtonIndex).should == 'Great Scott!'\n          end\n        end\n      end\n\n      describe \"with variable args\" do\n        before do\n          @alert = App.alert('1.21 Gigawatts!', cancel_button_title: 'Great Scott!',\n                                                    message: 'Some random message')\n        end\n\n        it 'returns an alert' do\n          @alert.class.should == BW::UIAlertView\n        end\n\n        it 'is displaying the correct title' do\n          @alert.title.should == '1.21 Gigawatts!'\n        end\n\n        it 'is displaying the correct message' do\n          @alert.message.should == 'Some random message'\n        end\n\n        describe 'cancelButton' do\n          it 'is present' do\n            @alert.cancelButtonIndex.should == 0\n          end\n\n          it 'has the correct title' do\n            @alert.buttonTitleAtIndex(@alert.cancelButtonIndex).should == 'Great Scott!'\n          end\n        end\n      end\n\n      describe \"with a block\" do\n        before do\n          @alert = App.alert('1.21 Gigawatts!') do |alert|\n            alert.message = 'My message!!'\n          end\n        end\n\n        it 'returns an alert' do\n          @alert.class.should == BW::UIAlertView\n        end\n\n        it 'is displaying the correct title' do\n          @alert.title.should == '1.21 Gigawatts!'\n        end\n\n        it 'is displaying the correct message' do\n          @alert.message.should == 'My message!!'\n        end\n\n        describe 'cancelButton' do\n          it 'is present' do\n            @alert.cancelButtonIndex.should == 0\n          end\n\n          it 'has the correct title' do\n            @alert.buttonTitleAtIndex(@alert.cancelButtonIndex).should == 'OK'\n          end\n        end\n      end\n    end\n\n    describe '.frame' do\n      it 'returns Application Frame' do\n        App.frame.should == UIScreen.mainScreen.applicationFrame\n      end\n    end\n\n    describe '.bounds' do\n      it 'returns Main Screen bounds' do\n        App.bounds.should == UIScreen.mainScreen.bounds\n      end\n    end\n\n\n    describe '.delegate' do\n      it 'returns a TestSuiteDelegate' do\n        App.delegate.should == UIApplication.sharedApplication.delegate\n      end\n    end\n\n    describe '.shared' do\n      it 'returns UIApplication.sharedApplication' do\n        App.shared.should == UIApplication.sharedApplication\n      end\n    end\n\n    describe '.windows' do\n      it 'returns UIApplication.sharedApplication.windows' do\n        App.windows.should == UIApplication.sharedApplication.windows\n      end\n    end\n\n    describe '.window' do\n      # iOS 8 Makes the return of UIApplication.sharedApplication.keyWindow.class\n      # extremely volitile, especially when an alert view is shown.\n      # Removing this test for now.\n      #\n      # TODO - Fix this test so that it works consistently.\n      #\n      # it 'returns UIApplication.sharedApplication.keyWindow' do\n      #   App.window.class.should == UIApplication.sharedApplication.keyWindow.class\n      # end\n\n      describe 'with UIActionSheet' do\n\n        it 'returns the correct window' do\n          action_sheet = UIActionSheet.alloc.init\n          action_sheet.cancelButtonIndex = (action_sheet.addButtonWithTitle(\"Cancel\"))\n\n          old_window = App.window\n          action_sheet.showInView(App.window)\n          wait 1 do\n            App.window.should == old_window\n\n            action_sheet.dismissWithClickedButtonIndex(action_sheet.cancelButtonIndex, animated: false)\n\n            App.window.should == old_window\n          end\n        end\n      end\n    end\n\n    describe '.run_after' do\n      class DelayedRunAfterTest; attr_accessor :test_value end\n\n      it 'should run a block after the provided delay' do\n        @test_obj = DelayedRunAfterTest.new\n\n        App.run_after(0.1){ @test_obj.test_value = true }\n        wait_for_change(@test_obj, 'test_value') do\n          @test_obj.test_value.should == true\n        end\n      end\n\n    end\n\n    describe \".can_open_url\" do\n\n      it \"uses NSURL or converts NSString in NSURL and opens it\" do\n        application = UIApplication.sharedApplication\n        def application.url; @url end\n        def application.canOpenURL(url); @url = url; super; end\n\n        url = NSURL.URLWithString('http://localhost')\n        App.can_open_url(url)\n        application.url.should.equal url\n\n        url = 'http://localhost'\n        App.can_open_url(url)\n        application.url.class.should.equal NSURL\n        application.url.description.should.equal url\n      end\n\n      it \"returns false when it can't open the given url\" do\n        App.can_open_url(\"inexistent_schema://\").should.equal false\n      end\n\n      it \"returns true when it can open the given url\" do\n        App.can_open_url(\"http://google.com\").should.equal true\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/motion/core/ios/ns_index_path_spec.rb",
    "content": "describe \"NSIndexPathWrap\" do\n\n  before do\n      @index = NSIndexPath.indexPathForRow(0, inSection:3)\n  end\n\n  it \"should support #+ and #-\" do\n    @index = @index + 1\n    @index.row.should == 1\n    @index = @index + 1\n    @index.row.should == 2\n    @index = @index + 12\n    @index.row.should == 14\n    @index = @index - 3\n    @index.row.should == 11\n    @index = @index - 0\n    @index.row.should == 11\n  end\n\nend\n"
  },
  {
    "path": "spec/motion/core/json_spec.rb",
    "content": "describe \"JSON\" do\n\n  before do\n    @json_string = <<-EOS\n    {\n  \"public_gists\": 248,\n  \"type\": \"User\",\n  \"blog\": \"http://merbist.com\",\n  \"location\": \"San Diego, CA\",\n  \"followers\": 303,\n  \"company\": \"LivingSocial\",\n  \"html_url\": \"https://github.com/mattetti\",\n  \"created_at\": \"2008-01-31T22:56:31Z\",\n  \"email\": \"mattaimonetti@gmail.com\",\n  \"hireable\": true,\n  \"gravatar_id\": \"c69521d6e22fc0bbd69337ec8b1698df\",\n  \"bio\": \"\",\n  \"public_repos\": 137,\n  \"following\": 6,\n  \"name\": \"Matt Aimonetti\",\n  \"login\": \"mattetti\",\n  \"url\": \"https://api.github.com/users/mattetti\",\n  \"id\": 113,\n  \"avatar_url\": \"https://secure.gravatar.com/avatar/c69521d6e22fc0bbd69337ec8b1698df?d=https://a248.e.akamai.net/assets.github.com%2Fimages%2Fgravatars%2Fgravatar-140.png\"\n}\nEOS\n  end\n\n  describe \"parsing a basic JSON string without block\" do\n\n    before do\n      @parsed = BubbleWrap::JSON.parse(@json_string)\n    end\n\n    it \"doesn't crash when data is nil\" do\n      Proc.new { BW::JSON.parse(nil) }.should.not.raise Exception\n    end\n\n    it \"returns a mutable object\" do\n      Proc.new { @parsed[:blah] = 123 }.should.not.raise Exception\n    end\n\n    it \"should convert a top object into a Ruby hash\" do\n      obj = @parsed\n      obj.class.should == Hash\n      obj.keys.size.should == 19\n    end\n\n    it \"should properly convert integers values\" do\n      @parsed[\"id\"].is_a?(Integer).should == true\n    end\n\n    it \"should properly convert string values\" do\n      @parsed[\"login\"].is_a?(String).should == true\n    end\n\n    it \"should convert an array into a Ruby array\" do\n      obj = BubbleWrap::JSON.parse(\"[1,2,3]\")\n      obj.class.should == Array\n      obj.size.should == 3\n    end\n\n    it \"should parse String generated from NSData\" do\n      #A contrived example to produce NSString(s) created from NSData instances\n      text = NSString.alloc.initWithData(@json_string.to_data, encoding:NSUTF8StringEncoding)\n      parsed = BW::JSON.parse(text)\n      parsed['login'].should == 'mattetti'\n    end\n\n  end\n\n    describe \"parsing a basic JSON string with block\" do\n\n    before do\n      BubbleWrap::JSON.parse(@json_string) do |parsed|\n        @parsed = parsed\n      end\n    end\n\n    it \"should convert a top object into a Ruby hash\" do\n      obj = @parsed\n      obj.class.should == Hash\n      obj.keys.size.should == 19\n    end\n\n    it \"should properly convert integers values\" do\n      @parsed[\"id\"].is_a?(Integer).should == true\n    end\n\n    it \"should properly convert string values\" do\n      @parsed[\"login\"].is_a?(String).should == true\n    end\n\n    it \"should convert an array into a Ruby array\" do\n      obj = BubbleWrap::JSON.parse(\"[1,2,3]\")\n      obj.class.should == Array\n      obj.size.should == 3\n    end\n\n  end\n\n  describe \"generating a JSON string from an object\" do\n\n    before do\n      @obj = { foo: 'bar',\n               'bar' => 'baz',\n               baz: 123,\n               foobar: [1,2,3],\n               foobaz: {'a' => 1, 'b' => 2}\n            }\n    end\n\n    it \"should generate from a hash\" do\n      json = BubbleWrap::JSON.generate(@obj)\n      json.class == String\n      json.should == \"{\\\"foo\\\":\\\"bar\\\",\\\"bar\\\":\\\"baz\\\",\\\"baz\\\":123,\\\"foobar\\\":[1,2,3],\\\"foobaz\\\":{\\\"a\\\":1,\\\"b\\\":2}}\"\n    end\n\n    it \"should encode and decode and object losslessly\" do\n      json = BubbleWrap::JSON.generate(@obj)\n      obj = BubbleWrap::JSON.parse(json)\n\n      obj[\"foo\"].should == 'bar'\n      obj[\"bar\"].should == 'baz'\n      obj[\"baz\"].should == 123\n      obj[\"foobar\"].should == [1,2,3]\n      obj[\"foobaz\"].should == {\"a\" => 1, \"b\" => 2}\n\n      # TODO Find out why following line cause runtime error\n      # obj.keys.sort.should == @obj.keys.sort\n      # obj.values.sort.should == @obj.values.sort\n      obj.keys.sort { |a, b| a.to_s <=> b.to_s }.should == @obj.keys.sort { |a, b| a.to_s <=> b.to_s }\n      obj.values.sort { |a, b| a.to_s <=> b.to_s }.should == @obj.values.sort { |a, b| a.to_s <=> b.to_s }\n    end\n\n  end\n\n  describe \"rescuing from parser errors\" do\n    it \"should rescue from invalid data\" do\n      begin\n        BubbleWrap::JSON.parse(\"{\")\n        success = true\n      rescue BubbleWrap::JSON::ParserError\n        failure = true\n      end\n      success.should == nil\n      failure.should == true\n    end\n  end\n\nend\n\n"
  },
  {
    "path": "spec/motion/core/kvo_spec.rb",
    "content": "describe BubbleWrap::KVO do\n\n  class KvoExample\n    include BubbleWrap::KVO\n\n    attr_accessor :age\n    attr_accessor :label\n    attr_accessor :items\n\n    def initialize\n      @items = [ \"Apple\", \"Banana\", \"Chickpeas\" ]\n      @age = 1\n\n      @label = text_class.alloc.initWithFrame [[0,0],[320, 30]]\n\n      set_text \"Foo\"\n    end\n\n    # Test helper\n\n    def get_text\n      @label.send(text_method_name)\n    end\n\n    def set_text(text)\n      @label.send(\"#{text_method_name}=\", text)\n    end\n\n    def observe_label(&block)\n      observe(@label, text_method_name, &block)\n    end\n\n    def observe_label!(&block)\n      observe!(@label, text_method_name, &block)\n    end\n\n    def observe_collection(&block)\n      observe(self, :items, &block)\n    end\n\n    def unobserve_label\n      unobserve(@label, text_method_name)\n    end\n\n    def unobserve_label!\n      unobserve!(@label, text_method_name)\n    end\n\n    def text_method_name\n      App.osx? ? :stringValue : :text\n    end\n\n    def text_class\n      App.osx? ? NSTextField : UILabel\n    end\n\n    def update_collection\n      self.items += [ \"Rice\" ]\n    end\n\n    #  def unobserve_all\n    #unobserve(@label, \"text\")\n    #unobserve(self, \"items\")\n    #end\n\n  end\n\n  describe \"Registry\" do\n    before do\n      @example = BW::KVO::Registry.new\n    end\n\n    after do\n      @example = nil\n    end\n\n    # add\n\n    it \"should add an observer block\" do\n      target = Object.new\n      block = lambda { |old_value, new_value| }\n      @example.add(target, \"key_path\", &block)\n      @example.registered?(target, \"key_path\").should == true\n    end\n\n    it \"should not add an observer block if the key path is not present\" do\n      target = Object.new\n      block = lambda { |old_value, new_value| }\n      @example.add(target, nil, &block)\n      @example.registered?(target, nil).should == false\n    end\n\n    it \"should not add an observer block if the block is not present\" do\n      target = Object.new\n      @example.add(target, \"key_path\")\n      @example.registered?(target, \"key_path\").should == false\n    end\n\n    # remove\n\n    it \"should remove an observer block\" do\n      target = Object.new\n      block = lambda { |old_value, new_value| }\n      @example.add(target, \"key_path\", &block)\n      @example.remove(target, \"key_path\")\n      @example.registered?(target, \"key_path\").should == false\n    end\n\n    it \"should not remove an observer block if the target is not present\" do\n      target = Object.new\n      block = lambda { |old_value, new_value| }\n      @example.add(target, \"key_path\", &block)\n      @example.remove(nil, \"key_path\")\n      @example.registered?(target, \"key_path\").should == true\n    end\n\n    it \"should not remove an observer block if the key path is not present\" do\n      target = Object.new\n      block = lambda { |old_value, new_value| }\n      @example.add(target, \"key_path\", &block)\n      @example.remove(target, nil)\n      @example.registered?(target, \"key_path\").should == true\n    end\n\n    it \"should remove only one observer block\" do\n      target = Object.new\n      block = lambda { |old_value, new_value| }\n      @example.add(target, \"key_path1\", &block)\n      @example.add(target, \"key_path2\", &block)\n      @example.remove(target, \"key_path1\")\n      @example.registered?(target, \"key_path1\").should == false\n      @example.registered?(target, \"key_path2\").should == true\n    end\n\n    # remove all\n\n    it \"should remove all observer blocks\" do\n      target = Object.new\n      block = lambda { |old_value, new_value| }\n      @example.add(target, \"key_path1\", &block)\n      @example.add(target, \"key_path2\", &block)\n      @example.remove_all\n      @example.registered?(target, \"key_path1\").should == false\n      @example.registered?(target, \"key_path2\").should == false\n    end\n\n    it \"should remove target from targets if no observers remain\" do\n      target = Object.new\n      block = lambda { |old_value, new_value| }\n      @example.add(target, \"key_path\", &block)\n      @example.remove(target, \"key_path\")\n      @example.callbacks.length.should == 0\n    end\n\n    it \"should not remove target from targets if observers remain\" do\n      target = Object.new\n      block = lambda { |old_value, new_value| }\n      @example.add(target, \"key_path1\", &block)\n      @example.add(target, \"key_path2\", &block)\n      @example.remove(target, \"key_path1\")\n      @example.callbacks.length.should > 0\n    end\n\n  end\n\n  describe \"API\" do\n    before do\n      @example = KvoExample.new\n    end\n\n    after do\n      @example.unobserve_all\n      @example = nil\n    end\n\n    # observe\n\n    it \"should observe a key path\" do\n      observed = false\n      @example.observe_label do |old_value, new_value|\n        observed = true\n        old_value.should == \"Foo\"\n        new_value.should == \"Bar\"\n      end\n\n      @example.set_text \"Bar\"\n      observed.should == true\n    end\n\n    it \"should immediately observe a key path\" do\n      @example.set_text \"Foo\"\n\n      observed = false\n      @example.observe_label! do |new_value|\n        observed = true\n        new_value.should == \"Foo\"\n      end\n\n      observed.should == true\n    end\n\n    it \"should observe a key path with more than one block\" do\n      observed_one = false\n      observed_two = false\n      observed_three = false\n      @example.observe_label do |old_value, new_value|\n        observed_one = true\n      end\n      @example.observe_label do |old_value, new_value|\n        observed_two = true\n      end\n      @example.observe_label do |old_value, new_value|\n        observed_three = true\n      end\n\n      @example.set_text \"Bar\"\n      observed_one.should == true\n      observed_two.should == true\n      observed_three.should == true\n    end\n\n    # unobserve\n\n    it \"should unobserve a key path\" do\n      observed = false\n      @example.observe_label do |old_value, new_value|\n        observed = true\n      end\n      @example.unobserve_label\n\n      @example.set_text \"Bar\"\n      observed.should == false\n    end\n\n    it \"should unobserve immediate observer\" do\n      observed_times = 0\n      @example.observe_label do |old_value, new_value|\n        observed_times += 1\n      end\n      @example.unobserve_label!\n\n      @example.set_text \"Bar\"\n      observed_times.should == 1\n    end\n\n    # without target\n\n    it \"should observe a key path without a target\" do\n      observed = false\n      @example.observe :age do |old_value, new_value|\n        observed = true\n        old_value.should == 1\n        new_value.should == 2\n      end\n\n      @example.age = 2\n      observed.should == true\n    end\n\n    it \"should unobserve a key path without a target\" do\n      observed = false\n      @example.observe :age do |old_value, new_value|\n        observed = true\n      end\n      @example.unobserve :age\n\n      @example.age = 2\n      observed.should == false\n    end\n\n    # with multiple keypaths\n\n    it \"should observe multiple key paths\" do\n      observed = []\n      @example.observe [:age, :items] do |old_value, new_value|\n        observed << [old_value, new_value]\n      end\n\n      @example.age = 2\n      @example.age = 3\n      @example.update_collection\n\n      observed.should.be == [\n        [1, 2],\n        [2, 3],\n        [\n          [\"Apple\", \"Banana\", \"Chickpeas\"],\n          [\"Apple\", \"Banana\", \"Chickpeas\", \"Rice\"]\n        ]\n      ]\n    end\n\n    it \"should observe multiple key paths with key_path argument\" do\n      observed_changes = []\n      @example.observe [:age, :items] do |old_value, new_value, key_path|\n        observed_changes << { key_path => [old_value, new_value] }\n      end\n\n      @example.age = 2\n      @example.age = 3\n      @example.update_collection\n\n      observed_changes.should.be == [\n        {\"age\" => [1, 2]},\n        {\"age\" => [2, 3]},\n        {\n          \"items\" => [\n            [\"Apple\", \"Banana\", \"Chickpeas\"],\n            [\"Apple\", \"Banana\", \"Chickpeas\", \"Rice\"]\n          ]\n        }\n      ]\n    end\n\n    it \"should immediately observe multiple key paths\" do\n      observed_changes = []\n      @example.observe! [:age, :items] do |new_value|\n        observed_changes << new_value\n      end\n\n      @example.age = 2\n      @example.age = 3\n      @example.update_collection\n\n      observed_changes.should.be == [1, [\"Apple\", \"Banana\", \"Chickpeas\"], 2, 3, [\"Apple\", \"Banana\", \"Chickpeas\", \"Rice\"]]\n    end\n\n    it \"should immediately observe multiple key paths with key_path argument\" do\n      observed_changes = []\n      @example.observe! [:age, :items] do |new_value, key_path|\n        observed_changes << { key_path => new_value }\n      end\n\n      @example.age = 2\n      @example.age = 3\n      @example.update_collection\n\n      observed_changes.should.be == [\n        {\"age\" => 1},\n        {\"items\" => [\"Apple\", \"Banana\", \"Chickpeas\"]},\n        {\"age\" => 2},\n        {\"age\" => 3},\n        {\"items\" => [\"Apple\", \"Banana\", \"Chickpeas\", \"Rice\"]}\n      ]\n    end\n\n    it \"should unobserve multiple key paths\" do\n      observed = 0\n\n      @example.observe [:age, :items] do\n        observed += 1\n      end\n\n      @example.unobserve [:age]\n\n      @example.age = 2\n      @example.update_collection\n\n      observed.should.be == 1\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/motion/core/ns_index_path_spec.rb",
    "content": "describe \"NSIndexPathWrap\" do\n\n  before do\n    if App.osx?\n      @index = NSIndexPath.indexPathWithIndex(3)\n    else\n      @index = NSIndexPath.indexPathForRow(0, inSection:3)\n    end\n  end\n\n  it \"should be able to use an array like accessor\" do\n    @index[0].should == @index.indexAtPosition(0)\n  end\n\n  it \"should support the each iterator\" do\n    i = []\n    @index.each do |idx|\n      i << idx\n    end\n    if App.osx?\n      i.should == [3]\n    else\n      i.should == [3, 0]\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/motion/core/ns_notification_center_spec.rb",
    "content": "describe \"NSNotificationCenter\" do\n  SampleNotification = \"SampleNotification\"\n\n  after do\n    @observer = nil\n  end\n\n  after do\n    BW::App.notification_center.unobserve(@observer) if @observer\n  end\n\n  it \"return notification center\" do\n    BW::App.notification_center.should.not.be.nil\n  end\n\n  it \"add observer\" do\n    @notified = false\n    @observer = BW::App.notification_center.observe(SampleNotification) do |note|\n      @notified = true\n      note.should.is_a NSNotification\n      note.object.class.should == Time\n      note.userInfo.should.not.be.nil\n      note.userInfo[:status].should == \"ok\"\n    end\n\n    lambda {\n      BW::App.notification_center.post SampleNotification, Time.now, {:status => \"ok\"}\n    }.should.change { @notified }\n  end\n\n  it \"remove observer\" do\n    lambda {\n      @observer = BW::App.notification_center.observe(SampleNotification) {}\n      BW::App.notification_center.unobserve(@observer)\n    }.should.not.change { BW::App.notification_center.observers.size }\n  end\nend\n"
  },
  {
    "path": "spec/motion/core/osx/app_spec.rb",
    "content": "describe BubbleWrap::App do\n  describe \"OS X\" do\n\n    describe '.delegate' do\n      it 'returns a TestSuiteDelegate' do\n        App.delegate.should == NSApplication.sharedApplication.delegate\n      end\n    end\n\n    describe '.shared' do\n      it 'returns UIApplication.sharedApplication' do\n        App.shared.should == NSApplication.sharedApplication\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/motion/core/persistence_spec.rb",
    "content": "describe BubbleWrap::Persistence do\n\n  describe '.app_key' do\n\n    it \"caches the @app_key\" do\n      BubbleWrap::Persistence.instance_variable_get(:@app_key).should.equal nil\n      BubbleWrap::Persistence.app_key\n      BubbleWrap::Persistence.instance_variable_get(:@app_key).should.not.equal nil\n    end\n\n    it 'delegates to BubbleWrap::App.idenfitier' do\n      BubbleWrap::Persistence.app_key.should == BubbleWrap::App.identifier\n    end\n\n  end\n\n\n  describe \"storing objects\" do\n    it 'can persist simple objects' do\n      lambda do\n        BubbleWrap::Persistence['arbitraryNumber'] = 42\n      end.\n        should.not.raise(Exception)\n    end\n\n    it \"must call synchronize\" do\n      storage = NSUserDefaults.standardUserDefaults\n      def storage.synchronize; @sync_was_called = true; end\n\n      BubbleWrap::Persistence['arbitraryNumber'] = 42\n      storage.instance_variable_get(:@sync_was_called).should.equal true\n    end\n  end\n\n  describe \"storing multiple objects\" do\n    it 'can persist multiple objects' do\n      lambda do\n        BubbleWrap::Persistence.merge({\n          :anotherArbitraryNumber => 9001,\n          :arbitraryString => 'test string'\n        })\n      end.\n        should.not.raise(Exception)\n    end\n\n    it 'must call synchronize' do\n      storage = NSUserDefaults.standardUserDefaults\n      def storage.synchronize; @sync_was_called = true; end\n\n      BubbleWrap::Persistence.merge({\n        :anotherArbitraryNumber => 9001,\n        :arbitraryString => 'test string'\n      })\n      storage.instance_variable_get(:@sync_was_called).should.equal true\n    end\n  end\n\n  describe \"retrieving objects\" do\n    it 'can retrieve persisted objects' do\n      BubbleWrap::Persistence['arbitraryNumber'].should == 42\n      BubbleWrap::Persistence[:arbitraryString].should == 'test string'\n    end\n\n    it 'returns fully functional strings' do\n      BubbleWrap::Persistence[:arbitraryString].methods.should == 'test string'.methods\n    end\n  end\n\n  describe \"retrieving all objects\" do\n    it 'can retrieve a dictionary of all objects' do\n      all = BubbleWrap::Persistence.all\n      all.is_a?(Hash).should == true\n\n      compare_to = {}\n      compare_to[\"anotherArbitraryNumber\"] = 9001\n      compare_to[\"arbitraryNumber\"]        = 42\n      compare_to[\"arbitraryString\"]        = \"test string\"\n\n      all.should == compare_to\n    end\n  end\n\n  describe \"deleting object\" do\n    before do\n      BubbleWrap::Persistence['arbitraryString'] = 'foobarbaz'\n    end\n\n    it 'can delete persisted object' do\n      BubbleWrap::Persistence.delete(:arbitraryString).should == 'foobarbaz'\n      BubbleWrap::Persistence['arbitraryString'].should.equal nil\n    end\n\n    it 'returns nil when the object does not exist' do\n      BubbleWrap::Persistence.delete(:wrongKey).should == nil\n    end\n\n    it 'must call synchronize' do\n      storage = NSUserDefaults.standardUserDefaults\n      def storage.synchronize; @sync_was_called = true; end\n\n      BubbleWrap::Persistence.delete(:arbitraryString)\n\n      storage.instance_variable_get(:@sync_was_called).should.equal true\n    end\n\n  end\nend\n"
  },
  {
    "path": "spec/motion/core/string_spec.rb",
    "content": "describe BubbleWrap::String do\n\n  describe ::NSString do\n    it 'should include BubbleWrap::String' do\n      ::NSString.ancestors.member?(BubbleWrap::String).should == true\n    end\n  end\n\n  describe 'CamelCase input' do\n    describe '.camelize(true)' do\n      it \"doesn't change the value\" do\n        'CamelCase'.camelize(true).should == 'CamelCase'\n      end\n    end\n\n    describe '.camelize(false)' do\n      it 'lower cases the first character' do\n        'CamelCase'.camelize(false).should == 'camelCase'\n      end\n    end\n\n    describe '.camelize(:upper)' do\n      it \"doesn't change the value\" do\n        'CamelCase'.camelize(:upper).should == 'CamelCase'\n      end\n    end\n\n    describe '.camelize(:lower)' do\n      it 'lower cases the first character' do\n        'CamelCase'.camelize(:lower).should == 'camelCase'\n      end\n    end\n\n    describe '.underscore' do\n      it 'converts it to underscores' do\n        'CamelCase'.underscore.should == 'camel_case'\n      end\n    end\n  end\n\n  describe 'camelCase input' do\n    describe '.camelize(true)' do\n      it \"upper cases the first character\" do\n        'camelCase'.camelize(true).should == 'CamelCase'\n      end\n    end\n\n    describe '.camelize(false)' do\n      it \"doesn't change the value\" do\n        'camelCase'.camelize(false).should == 'camelCase'\n      end\n    end\n\n    describe '.camelize(:upper)' do\n      it \"upper cases the first character\" do\n        'camelCase'.camelize(:upper).should == 'CamelCase'\n      end\n    end\n\n    describe '.camelize(:lower)' do\n      it \"doesn't change the value\" do\n        'camelCase'.camelize(:lower).should == 'camelCase'\n      end\n    end\n\n    describe '.underscore' do\n      it 'converts it to underscores' do\n        'camelCase'.underscore.should == 'camel_case'\n      end\n    end\n  end\n\n  describe 'snake_case input' do\n    describe '.camelize(true)' do\n      it 'converts to CamelCase' do\n        'snake_case'.camelize(true).should == 'SnakeCase'\n      end\n    end\n\n    describe '.camelize(false)' do\n      it 'converts to camelCase' do\n        'snake_case'.camelize(false).should == 'snakeCase'\n      end\n    end\n\n    describe '.underscore' do\n      it \"doesn't change the value\" do\n        'snake_case'.underscore.should == 'snake_case'\n      end\n    end\n\n  end\n\n  before do\n    @blue_color = App.osx? ? NSColor.colorWithDeviceRed(0,green:0,blue:1,alpha:1) : UIColor.blueColor\n    r,g,b,a = [1, (0x8A.to_f/0xFF.to_f), (0x19/0xFF.to_f), (0x88.to_f/0xFF.to_f)]\n    @orange_color = App.osx? ? NSColor.colorWithDeviceRed(r, green:g, blue:b, alpha: 1.0) :\n                                UIColor.colorWithRed(r, green:g, blue:b, alpha:1.0)\n    @orange_alpha_color = App.osx? ? NSColor.colorWithDeviceRed(r, green:g, blue:b, alpha: a) :\n                                UIColor.colorWithRed(r, green:g, blue:b, alpha:a)\n  end\n\n  describe \"A UIColor should be created from a String with a hex color\" do\n    it \"with 8 digits\" do\n      @orange_color_from_hex = '88FF8A19'.to_color\n      @orange_color_from_hex.should == @orange_alpha_color\n    end\n\n    it \"with 8 digits and # sign\" do\n      @orange_color_from_hex = '#88FF8A19'.to_color\n      @orange_color_from_hex.should == @orange_alpha_color\n    end\n\n    it \"with 6 digits\" do\n      @orange_color_from_hex= '#FF8A19'.to_color\n      @orange_color_from_hex.should == @orange_color\n    end\n\n    it \"with 3 digits\" do\n      @blue_color_from_hex = '#00F'.to_color\n      @blue_color_from_hex.should ==  @blue_color\n    end\n\n    it \"with no # sign\" do\n      @orange_color_from_hex= 'FF8A19'.to_color\n      @orange_color_from_hex.should == @orange_color\n    end\n  end\n\n  describe \"a string with a color keyword (blue, red, lightText)\" do\n    it \"should return the corresponding color\" do\n      'blue'.to_color.should == (App.osx? ? NSColor.blueColor : UIColor.blueColor)\n    end\n\n    it \"should accept camelCase\" do\n      if App.osx?\n        'headerText'.to_color.should == NSColor.headerTextColor\n      else\n        'lightText'.to_color.should == UIColor.lightTextColor\n      end\n    end\n\n    it \"should accept snake_case\" do\n      'dark_gray'.to_color.should == (App.osx? ? NSColor.darkGrayColor : UIColor.darkGrayColor)\n    end\n  end\n\n  describe \"A UIColor should not be created from an invalid String\" do\n\n    it \"an invalid hex color\" do\n      should.raise( ArgumentError ) {\n        'XXX'.to_color\n      }\n    end\n\n    it \"a hex color with the wrong number of digits\" do\n      should.raise( ArgumentError ) {\n        'FFFF'.to_color\n      }\n    end\n\n  end\n\n  describe \"encoding\" do\n\n    before do\n      @raw_string = \"hey ho let's {go} http://bubblewrap.io&rocks!\"\n    end\n\n    it \"to_url_encoded\" do\n      real_encoded = CFURLCreateStringByAddingPercentEscapes(nil, @raw_string, nil, \"!*'();:@&=+$,/?%#[]\", KCFStringEncodingUTF8)\n      @raw_string.to_url_encoded.should.equal real_encoded\n    end\n\n    it \"handles other encodings\" do\n      utf16 = CFURLCreateStringByAddingPercentEscapes(nil, @raw_string, nil, \"!*'();:@&=+$,/?%#[]\", KCFStringEncodingUTF16)\n      @raw_string.to_url_encoded(KCFStringEncodingUTF16).should.equal utf16\n    end\n\n    it \"automatically selects available encodings\" do\n      encoding = if CFStringIsEncodingAvailable(NSUTF16StringEncoding)\n        NSUTF16StringEncoding\n      else\n        KCFStringEncodingUTF16\n      end\n\n      utf16 = CFURLCreateStringByAddingPercentEscapes(nil, @raw_string, nil, \"!*'();:@&=+$,/?%#[]\", encoding)\n      @raw_string.to_url_encoded(NSUTF16StringEncoding).should.equal utf16\n    end\n\n    it \"to_url_decoded\" do\n      encoded_string = \"hey%20ho%20let's%20%7Bgo%7D\"\n      real_decoded = CFURLCreateStringByReplacingPercentEscapes(nil, encoded_string, nil)\n      encoded_string.to_url_decoded.should.equal real_decoded\n    end\n\n    it \"to_url_decoded with encoding\" do\n      real_encoded = @raw_string.to_url_encoded(KCFStringEncodingUTF16)\n      real_decoded = CFURLCreateStringByReplacingPercentEscapes(nil, real_encoded, nil, KCFStringEncodingUTF16)\n      real_encoded.to_url_decoded.should.equal real_decoded\n    end\n\n    it \"should not contain disallowed characters\" do\n      encoded_string = @raw_string.to_url_encoded\n      encoded_string.should.not.match(/\\//)\n      encoded_string.should.not.match(/:/)\n      encoded_string.should.not.match(/!/)\n      encoded_string.should.not.match(/&/)\n    end\n\n    describe \"dataUsingEncoding\" do\n\n      it \"#to_url_encoded_data - utf8\" do\n        utf8 = @raw_string.dataUsingEncoding NSUTF8StringEncoding\n        @raw_string.to_encoded_data.should.equal utf8\n      end\n\n      it \"handles multiple encodings\" do\n        utf16 = @raw_string.dataUsingEncoding NSUTF16StringEncoding\n        @raw_string.to_encoded_data(NSUTF16StringEncoding).should.equal utf16\n      end\n\n    end\n\n\n  end\n\nend\n"
  },
  {
    "path": "spec/motion/core/time_spec.rb",
    "content": "describe \"Time\" do\n\n  describe \"Caching the date formatter\" do\n\n    it \"should reuse the created formatter\" do\n      100.times do\n        Time.iso8601(\"2011-04-11T13:22:21Z\")\n      end\n\n      Thread.current[:date_formatters].count.should.equal 1\n      Thread.current[:date_formatters][\"yyyy-MM-dd'T'HH:mm:ss'Z'\"].should.not.equal nil\n    end\n\n  end\n\n  describe \"auto-parsing of different iso8601 formats\" do\n    before do\n      @time = '1981-12-23T19:41:32-400'\n      @time_zulu = '1981-12-23T19:41:32Z'\n      @time_fractional_seconds = '1981-12-23T19:41:32.324-400'\n      @time_fractional_seconds_zulu = '1981-12-23T19:41:32.324Z'\n    end\n\n    it \"should parse a normal iso8601 time\" do\n      Time.iso8601(@time).instance_of?(Time).should == true\n    end\n\n    it \"should parse an iso8601 time with zulu timezone\" do\n      Time.iso8601(@time_zulu).instance_of?(Time).should == true\n    end\n\n    it \"should parse an iso8601 time with fractional time\" do\n      Time.iso8601(@time_fractional_seconds).instance_of?(Time).should == true\n    end\n\n    it \"should parse an iso8601 time with fractional time and zulu timezone\" do\n      Time.iso8601(@time_fractional_seconds_zulu).instance_of?(Time).should == true\n    end\n  end\n\n  describe \"parsing an iso8601 formatted time to a Time object\" do\n    before do\n      @time = Time.iso8601(\"2012-#{Time.now.month}-#{Time.now.day}T19:41:32Z\")\n      @time_with_timezone = Time.iso8601_with_timezone(\"1987-08-10T06:00:00+02:00\")\n      @time_with_fractional_seconds = Time.iso8601_with_fractional_seconds(\"2012-#{Time.now.month}-#{Time.now.day}T19:41:32.123Z\")\n      @time_with_fractional_seconds_and_timezone = Time.iso8601_with_fractional_seconds_and_timesone(\"2012-#{Time.now.month}-#{Time.now.day}T19:41:32.123+02:00\")\n    end\n\n    it \"should be a time\" do\n      @time.instance_of?(Time).should == true\n      @time_with_timezone.instance_of?(Time).should == true\n      @time_with_fractional_seconds.instance_of?(Time).should == true\n      @time_with_fractional_seconds_and_timezone.instance_of?(Time).should == true\n    end\n\n    # Crashes Buggy RubyMotion 1.18\n    it \"should be converted to the local timezone automatically\" do\n      local_zone = Time.now.zone\n      @time.zone.should == local_zone\n      @time_with_timezone.zone == local_zone\n      @time_with_fractional_seconds.zone.should == local_zone\n      @time_with_fractional_seconds_and_timezone.zone.should == local_zone\n    end\n\n    it \"should have a valid year\" do\n      @time.utc.year.should == 2012\n      @time_with_timezone.utc.year.should == 1987\n      @time_with_fractional_seconds.utc.year.should == 2012\n      @time_with_fractional_seconds_and_timezone.utc.year.should == 2012\n    end\n\n    it \"should have a valid month\" do\n      @time.utc.month.should == Time.now.month\n      @time_with_timezone.utc.month.should == 8\n      @time_with_fractional_seconds.utc.month.should == Time.now.month\n      @time_with_fractional_seconds_and_timezone.utc.month.should == Time.now.month\n    end\n\n    it \"should have a valid day\" do\n      @time.utc.day.should == Time.now.day\n      @time_with_timezone.utc.day.should == 10\n      @time_with_fractional_seconds.utc.day.should == Time.now.day\n      @time_with_fractional_seconds_and_timezone.utc.day.should == Time.now.day\n    end\n\n    it \"should have a valid hour\" do\n      @time.utc.hour.should == 19\n      @time_with_timezone.utc.hour.should == 4\n      @time_with_fractional_seconds.utc.hour.should == 19\n      @time_with_fractional_seconds_and_timezone.utc.hour.should == 17\n    end\n\n    it \"should have a valid minute\" do\n      @time.utc.min.should == 41\n      @time_with_timezone.utc.min.should == 0\n      @time_with_fractional_seconds.utc.min.should == 41\n      @time_with_fractional_seconds_and_timezone.utc.min.should == 41\n    end\n\n    it \"should have a valid second\" do\n      @time.utc.sec.should == 32\n      @time_with_timezone.utc.sec.should == 0\n      @time_with_fractional_seconds.utc.sec.should == 32\n      @time_with_fractional_seconds_and_timezone.utc.sec.should == 32\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/motion/core_spec.rb",
    "content": "describe 'BubbleWrap' do\n\n\n  describe \"debug flag\" do\n\n    after do\n       BubbleWrap.debug = false\n    end\n\n    it \"can be set\" do\n      BubbleWrap.debug = true\n      BubbleWrap.debug?.should.equal true\n    end\n\n    it \"can be unset\" do\n      BubbleWrap.debug = false\n      BubbleWrap.debug?.should.equal false\n    end\n\n  end\n\n  describe \"RGB color\" do\n\n    before do\n      @red = 23\n      @green = 45\n      @blue = 12\n    end\n\n    it \"creates color with rgb devided by 255 with alpha=1\" do\n      r,g,b,a = [(@red/255.0), (@green/255.0), (@blue/255.0), 1]\n      if App.osx?\n        color = NSColor.colorWithDeviceRed(r, green:g, blue:b, alpha: a)\n      else\n        color = UIColor.colorWithRed(r, green:g, blue:b, alpha:a)\n      end\n      BubbleWrap::rgb_color(@red, @green, @blue).should.equal color\n    end\n\n    it \"rgba_color uses the real alpha\" do\n      alpha = 0.4\n      r,g,b,a = [(@red/255.0), (@green/255.0), (@blue/255.0), alpha]\n      if App.osx?\n        color = NSColor.colorWithDeviceRed(r, green:g, blue:b, alpha: a)\n      else\n        color = UIColor.colorWithRed(r, green:g, blue:b, alpha:a)\n      end\n      BubbleWrap::rgba_color(@red, @green, @blue, alpha).should.equal color\n    end\n\n  end\n\n  describe \"Localized string\" do\n\n    it \"loads the string from NSBundle\" do\n      key = 'real_key'\n      value = 'Real Key'\n\n      BubbleWrap::localized_string(key, value).should == value\n    end\n\n    it \"returns the key if localization not found and no value is given\" do\n      key = 'fake_key'\n\n      BubbleWrap::localized_string(key).should == key\n    end\n\n  end\n\n  describe \"uuid\" do\n\n    it \"always creates the new UUID\" do\n      previous = BW.create_uuid\n      10.times do\n        uuid = BW.create_uuid\n        uuid.should.not.equal previous\n        uuid.size.should.equal 36\n        previous = uuid\n      end\n    end\n\n  end\n\nend\n"
  },
  {
    "path": "spec/motion/font/font_spec.rb",
    "content": "describe BubbleWrap::Font do\n  [[:system, \"systemFontOfSize:\"], [:bold, \"boldSystemFontOfSize:\"], [:italic, \"italicSystemFontOfSize:\"]].each do |font, method|\n    describe \".#{font}\" do\n      it \"should work\" do\n        f = BubbleWrap::Font.send(font, 16)\n        f.should == UIFont.send(method, 16)\n\n        f = BubbleWrap::Font.send(font)\n        f.should == UIFont.send(method, UIFont.systemFontSize)\n      end\n    end\n  end\n\n  describe \".make\" do\n    it \"should work with UIFont\" do\n      BubbleWrap::Font.new(UIFont.boldSystemFontOfSize(12)).should == UIFont.boldSystemFontOfSize(12)\n    end\n\n    it \"should work with string\" do\n      BubbleWrap::Font.new(\"Helvetica\").should == UIFont.fontWithName(\"Helvetica\", size: UIFont.systemFontSize)\n    end\n\n    it \"should work with string and size\" do\n      BubbleWrap::Font.new(\"Helvetica\", 18).should == UIFont.fontWithName(\"Helvetica\", size: 18)\n    end\n\n    it \"should work with string and hash\" do\n      BubbleWrap::Font.new(\"Helvetica\", size: 16).should == UIFont.fontWithName(\"Helvetica\", size: 16)\n    end\n\n    it \"should work with hash\" do\n      BubbleWrap::Font.new(name: \"Chalkduster\", size: 16).should == UIFont.fontWithName(\"Chalkduster\", size: 16)\n    end\n  end\n\n  describe \".named\" do\n    it \"should work\" do\n      BubbleWrap::Font.named(\"Helvetica\").should == UIFont.fontWithName(\"Helvetica\", size: UIFont.systemFontSize)\n    end\n  end\n\n  describe \".attributes\" do\n    it \"should work\" do\n      _attributes = BubbleWrap::Font.attributes(font: UIFont.systemFontOfSize(12), color: \"red\", shadow_color: \"blue\", shadow_offset: {x: 5, y: 10})\n\n      _attributes.should == {\n        UITextAttributeFont => UIFont.systemFontOfSize(12),\n        UITextAttributeTextColor => UIColor.redColor,\n        UITextAttributeTextShadowColor => UIColor.blueColor,\n        UITextAttributeTextShadowOffset => NSValue.valueWithUIOffset(UIOffsetMake(5, 10))\n      }\n    end\n  end\nend\n"
  },
  {
    "path": "spec/motion/location/location_spec.rb",
    "content": "describe \"CLLocationWrap\" do\n  it \"should have correct values for lat and lng\" do\n    coordinate = CLLocation.alloc.initWithLatitude(100, longitude: 50)\n    coordinate.latitude.should == 100\n    coordinate.longitude.should == 50\n  end\nend\n\n# monkey patch for testing\nclass BWCLHeading\n  attr_accessor :timestamp, :z, :y, :x, :headingAccuracy, :trueHeading, :magneticHeading\nend\n\nclass CLLocationManager\n  def self.enable(enable)\n    @enabled = enable\n  end\n\n  def self.authorize(authorize)\n    @authorized = authorize\n  end\n\n  def self.locationServicesEnabled\n    return true if @enabled.nil?\n    @enabled\n  end\n\n  def self.authorizationStatus\n    return KCLAuthorizationStatusNotDetermined if @authorized.nil?\n    @authorized\n  end\n\n  def startUpdatingLocation\n    @startUpdatingLocation = true\n  end\n\n  def stopUpdatingLocation\n    @stopUpdatingLocation = true\n  end\n\n  def startUpdatingHeading\n    @startUpdatingHeading = true\n  end\n\n  def stopUpdatingHeading\n    @stopUpdatingHeading = true\n  end\n\n  def startMonitoringSignificantLocationChanges\n    @startMonitoringSignificantLocationChanges = true\n  end\n\n  def stopMonitoringSignificantLocationChanges\n    @stopMonitoringSignificantLocationChanges = true\n  end\nend\n\ndef location_manager\n  BW::Location.instance_variable_get(\"@location_manager\")\nend\n\ndef reset\n  CLLocationManager.enable(true)\n  BW::Location.instance_variable_set(\"@callback\", nil)\n  BW::Location.instance_variable_set(\"@location_manager\", nil)\nend\n\ndescribe BubbleWrap::Location do\n  describe \".get\" do\n    before do\n      reset\n    end\n\n    it \"should set purpose using hash\" do\n      BW::Location.get(purpose: \"test\") do\n      end\n\n      location_manager.purpose.should == \"test\"\n    end\n\n    it \"should return false when not available\" do\n      CLLocationManager.authorize(KCLAuthorizationStatusNotDetermined)\n      BW::Location.authorized?.should == false\n\n      CLLocationManager.authorize(KCLAuthorizationStatusRestricted)\n      BW::Location.authorized?.should == false\n\n      CLLocationManager.authorize(KCLAuthorizationStatusDenied)\n      BW::Location.authorized?.should == false\n    end\n\n    it \"should return true when available\" do\n      CLLocationManager.authorize(KCLAuthorizationStatusAuthorized)\n      BW::Location.authorized?.should == true\n\n      # if Device.ios_version.to_f >= 8.0\n      #   CLLocationManager.authorize(KCLAuthorizationStatusAuthorizedWhenInUse)\n      #   BW::Location.authorized?.should == true\n      #\n      #   CLLocationManager.authorize(KCLAuthorizationStatusAuthorizedAlways)\n      #   BW::Location.authorized?.should == true\n      # end\n    end\n\n    it \"should throw error if not enabled\" do\n      CLLocationManager.enable(false)\n\n      BW::Location.get do |result|\n        result[:error].should == BW::Location::Error::DISABLED\n      end\n    end\n\n    it \"should throw error if permission denied\" do\n      BW::Location.get do |result|\n        result[:error].should == BW::Location::Error::PERMISSION_DENIED\n      end\n\n      error = NSError.errorWithDomain(KCLErrorDomain, code: KCLErrorDenied, userInfo: nil)\n      BW::Location.locationManager(location_manager, didFailWithError: error)\n    end\n\n    it \"should use significant update functions with :significant param\" do\n      BW::Location.get(significant: true) do |result|\n      end\n\n      location_manager.instance_variable_get(\"@startMonitoringSignificantLocationChanges\").should == true\n    end\n\n    it \"should use normal update functions\" do\n      BW::Location.get do\n      end\n\n      location_manager.instance_variable_get(\"@startUpdatingLocation\").should == true\n    end\n\n    it \"should use compass update functions\" do\n      BW::Location.get_compass do\n      end\n\n      location_manager.instance_variable_get(\"@startUpdatingHeading\").should == true\n    end\n\n    it \"should have correct location when succeeding\" do\n      to = CLLocation.alloc.initWithLatitude(100, longitude: 50)\n      from = CLLocation.alloc.initWithLatitude(100, longitude: 49)\n\n      BW::Location.get do |result|\n        result[:to].longitude.should == 50\n        result[:from].longitude.should == 49\n      end\n\n      BW::Location.locationManager(location_manager, didUpdateLocations: [from, to])\n    end\n  end\n\n  describe \".get_once\" do\n    it \"should have correct location when succeeding\" do\n      to = CLLocation.alloc.initWithLatitude(100, longitude: 50)\n      from = CLLocation.alloc.initWithLatitude(100, longitude: 49)\n\n      @number_times = 0\n      BW::Location.get_once do |location|\n        location.longitude.should == 50\n        location.latitude.should == 100\n        @number_times += 1\n      end\n\n      BW::Location.locationManager(location_manager, didUpdateLocations: [from, to])\n\n      to = CLLocation.alloc.initWithLatitude(0, longitude: 0)\n      from = CLLocation.alloc.initWithLatitude(0, longitude: 0)\n      BW::Location.locationManager(location_manager, didUpdateLocations: [from, to])\n      @number_times.should == 1\n    end\n  end\n\n  describe \".get_compass\" do\n    before do\n      reset\n    end\n\n    it \"should use compass functions\" do\n      BW::Location.get_compass do |result|\n      end\n\n      location_manager.instance_variable_get(\"@startUpdatingHeading\").should == true\n      BW::Location.locationManagerShouldDisplayHeadingCalibration(location_manager).should == false\n    end\n\n    it \"should have correct heading when succeeding\" do\n      timestamp = Time.now\n      heading = BWCLHeading.new.tap do |h|\n        h.timestamp = timestamp\n        h.headingAccuracy = 4\n        h.trueHeading = 220\n        h.magneticHeading = 270\n      end\n\n      BW::Location.get_compass do |heading|\n        heading[:magnetic_heading].should == 270\n        heading[:true_heading].should == 220\n        heading[:accuracy].should == 4\n        heading[:timestamp].should == timestamp\n      end\n\n      BW::Location.locationManager(location_manager, didUpdateHeading: heading)\n    end\n\n    it \"should show the calibration screen when needed\" do\n      BW::Location.get_compass(calibration: true) do |result|\n      end\n\n      BW::Location.locationManagerShouldDisplayHeadingCalibration(location_manager).should == true\n    end\n  end\n\n  describe \".get_significant\" do\n    before do\n      reset\n    end\n\n    it \"should use significant changes functions\" do\n      BW::Location.get_significant do |result|\n      end\n\n      location_manager.instance_variable_get(\"@startMonitoringSignificantLocationChanges\").should == true\n    end\n\n    it \"should have correct location when succeeding\" do\n      to = CLLocation.alloc.initWithLatitude(100, longitude: 50)\n      from = CLLocation.alloc.initWithLatitude(100, longitude: 49)\n\n      BW::Location.get_significant do |result|\n        result[:to].longitude.should == 50\n        result[:from].longitude.should == 49\n      end\n\n      BW::Location.locationManager(location_manager, didUpdateLocations: [from, to])\n    end\n    \n    it \"should include previous locations\" do\n      to = CLLocation.alloc.initWithLatitude(100, longitude: 50)\n      from = CLLocation.alloc.initWithLatitude(100, longitude: 49)\n      previous = CLLocation.alloc.initWithLatitude(100, longitude: 48)\n\n      BW::Location.get_significant do |result|\n        result[:to].longitude.should == 50\n        result[:from].longitude.should == 49\n        result[:previous].last.longitude.should == 48\n      end\n\n      BW::Location.locationManager(location_manager, didUpdateLocations: [previous, from, to])\n    end\n    \n    it \"should preserve previous location when delegate method only returns current location\" do\n      to = CLLocation.alloc.initWithLatitude(100, longitude: 49)\n\n      @number_times = 0\n      BW::Location.get_significant do |result|\n        if @number_times == 0\n          result[:to].longitude.should == 49\n          result[:from].should == nil\n          result[:previous].last.should == nil\n        else\n          result[:to].longitude.should == 50\n          result[:from].longitude.should == 49\n          result[:previous].last.should == nil\n        end\n        @number_times += 1\n      end\n\n      BW::Location.locationManager(location_manager, didUpdateLocations: [to])\n      \n      to = CLLocation.alloc.initWithLatitude(100, longitude: 50)\n      BW::Location.locationManager(location_manager, didUpdateLocations: [to])\n\n      @number_times.should == 2\n    end\n  end\n\n  describe \".start\" do\n    before do\n      reset\n    end\n    \n    it \"should not throw an error starting when the Location Manager has not been initialized\" do\n      Proc.new { BW::Location.start }.should.not.raise Exception\n    end\n    \n  end\n\n  describe \".stop\" do\n    before do\n      reset\n    end\n\n    it \"should use normal update functions\" do\n      BW::Location.get do |result|\n      end\n\n      BW::Location.stop\n\n      location_manager.instance_variable_get(\"@stopUpdatingLocation\").should == true\n    end\n\n    it \"should use compass update functions\" do\n      BW::Location.get_compass do |result|\n      end\n\n      BW::Location.stop\n\n      location_manager.instance_variable_get(\"@stopUpdatingHeading\").should == true\n    end\n\n    it \"should use significant update functions with get_significant\" do\n      BW::Location.get_significant do\n      end\n\n      BW::Location.stop\n\n      location_manager.instance_variable_get(\"@stopMonitoringSignificantLocationChanges\").should == true\n    end\n\n    it \"should not throw an error stopping before it was started\" do\n      Proc.new { BW::Location.stop }.should.not.raise Exception\n    end\n\n  end\nend\n"
  },
  {
    "path": "spec/motion/mail/mail_spec.rb",
    "content": "# Mocking the presentViewController\nclass MailViewController < UIViewController\n  attr_accessor :expectation\n\n  def presentViewController(modal, animated: animated, completion: completion)\n    expectation.call modal, animated\n  end\nend\n\n# Monkey-patching MFMailComposeViewController\n# So we can access the values that are set.\n# This of course breaks MFMailComposeViewController from actually working,\n# but it's testable.\nclass MFMailComposeViewController\n  attr_accessor :toRecipients, :ccRecipients, :bccRecipients, :subject, :message, :html\n\n  def setToRecipients(r)\n    self.toRecipients = r\n  end\n\n  def setCcRecipients(r)\n    self.ccRecipients = r\n  end\n\n  def setBccRecipients(r)\n    self.bccRecipients = r\n  end\n\n  def setSubject(r)\n    self.subject = r\n  end\n\n  def setMessageBody(message, isHTML: html)\n    self.message = message\n    self.html = html\n  end\nend\n\ndescribe BW::Mail do\n  describe \".compose\" do\n    before do\n      @view_controller = MailViewController.new\n      @standard_mail_options = {\n        delegate: @view_controller,\n        to: [ \"tom@example.com\" ],\n        cc: [ \"itchy@example.com\", \"scratchy@example.com\" ],\n        bcc: [ \"jerry@example.com\" ],\n        html: false,\n        subject: \"My Subject\",\n        message: \"This is my message. It isn't very long.\",\n        animated: false\n      }\n    end\n\n    if BW::Mail.can_send_mail? || Device.ios_version.to_f < 10.0\n      it \"should determine if the device can send mail or not\" do\n        [true, false].include?(BW::Mail.can_send_mail?).should == true\n      end\n\n      it \"should open the mail controller in a modal\" do\n        @view_controller.expectation = lambda { |mail_controller, animated|\n          mail_controller.should.be.kind_of(MFMailComposeViewController)\n        }\n\n        BubbleWrap::Mail.compose @standard_mail_options\n      end\n\n      it \"should create a mail controller with the right to: address set\" do\n        @view_controller.expectation = lambda { |mail_controller, animated|\n          mail_controller.toRecipients.should.be.kind_of(Array)\n          mail_controller.toRecipients.should == @standard_mail_options[:to]\n        }\n\n        BubbleWrap::Mail.compose @standard_mail_options\n      end\n\n      it \"should create a mail controller with the right cc: address set\" do\n        @view_controller.expectation = lambda { |mail_controller, animated|\n          mail_controller.ccRecipients.should.be.kind_of(Array)\n          mail_controller.ccRecipients.should == @standard_mail_options[:cc]\n        }\n\n        BubbleWrap::Mail.compose @standard_mail_options\n      end\n\n      it \"should create a mail controller with the right bcc: address set\" do\n        @view_controller.expectation = lambda { |mail_controller, animated|\n          mail_controller.bccRecipients.should.be.kind_of(Array)\n          mail_controller.bccRecipients.should == @standard_mail_options[:bcc]\n        }\n\n        BubbleWrap::Mail.compose @standard_mail_options\n      end\n\n      it \"should create a mail controller with the right subject: set\" do\n        @view_controller.expectation = lambda { |mail_controller, animated|\n          mail_controller.subject.should.be.kind_of(String)\n          mail_controller.subject.should == @standard_mail_options[:subject]\n        }\n\n        BubbleWrap::Mail.compose @standard_mail_options\n      end\n\n      it \"should create a mail controller with the right message: set\" do\n        @view_controller.expectation = lambda { |mail_controller, animated|\n          mail_controller.message.should.be.kind_of(String)\n          mail_controller.message.should == @standard_mail_options[:message]\n        }\n\n        BubbleWrap::Mail.compose @standard_mail_options\n      end\n\n      it \"should create a mail controller with the right html: set\" do\n        @view_controller.expectation = lambda { |mail_controller, animated|\n          mail_controller.html.should == @standard_mail_options[:html]\n        }\n\n        BubbleWrap::Mail.compose @standard_mail_options\n      end\n\n      it \"should create a mail controller with the right animation\" do\n        @view_controller.expectation = lambda { |mail_controller, animated|\n          animated.should.be.false\n        }\n\n        BubbleWrap::Mail.compose @standard_mail_options\n      end\n    end\n\n  end\nend\n"
  },
  {
    "path": "spec/motion/mail/result_spec.rb",
    "content": "describe BW::Mail::Result do\n\n  before do\n    @subject = BW::Mail::Result.new(MFMailComposeResultCancelled, nil)\n  end\n\n  it \"should set sent? when sent\" do\n    @subject.result = MFMailComposeResultSent\n    @subject.should.be.sent\n  end\n\n  it \"should not set sent? when not sent\" do\n    @subject.result = MFMailComposeResultCancelled\n    @subject.should.not.be.sent\n  end\n\n  it \"should set canceled? when canceled\" do\n    @subject.result = MFMailComposeResultCancelled\n    @subject.should.be.canceled\n  end\n\n  it \"should not set canceled? when not canceled\" do\n    @subject.result = MFMailComposeResultSent\n    @subject.should.not.be.canceled\n  end\n\n  it \"should set saved? when saved\" do\n    @subject.result = MFMailComposeResultSaved\n    @subject.should.be.saved\n  end\n\n  it \"should not set saved? when not saved\" do\n    @subject.result = MFMailComposeResultFailed\n    @subject.should.not.be.saved\n  end\n\n  it \"should set failed? when failed\" do\n    @subject.result = MFMailComposeResultFailed\n    @subject.should.be.failed\n  end\n\n  it \"should not set failed? when not failed\" do\n    @subject.result = MFMailComposeResultSent\n    @subject.should.not.be.failed\n  end\n\n  it \"should set failed? when error\" do\n    @subject.result = MFMailComposeResultCancelled\n    @subject.error = :errored\n    @subject.should.be.failed\n  end\n\nend\n"
  },
  {
    "path": "spec/motion/media/player_spec.rb",
    "content": "describe BubbleWrap::Media::Player do\n  describe \".play\" do\n    before do\n      @player = BW::Media::Player.new\n      @local_file = NSURL.fileURLWithPath(File.join(App.resources_path, 'test.mp3'))\n    end\n\n    it \"should raise error if not modal and no callback given\" do\n      should.raise(BW::Media::Error::NilPlayerCallback) do\n        @player.play(@local_file)\n      end\n    end\n\n    it \"should yield to a block if not modal\" do\n      @did_yield = false\n      @player.play(@local_file) do |_player|\n        @did_yield = true\n      end\n\n      @did_yield.should == true\n      @player.stop\n    end\n\n    it \"should use the MPMoviePlayerController option overrides\" do\n      @player.play(@local_file, allows_air_play: true) do |_player|\n        @did_yield = true\n      end\n      @player.media_player.allowsAirPlay.should == true\n    end\n  end\n\n=begin\n  describe \".play_modal\" do\n    before do\n      @player = BW::Media::Player.new\n      @local_file = NSURL.fileURLWithPath(File.join(App.resources_path, 'test.mp3'))\n    end\n\n    it \"should present a modalViewController on root if no controller given\" do\n      @controller = App.window.rootViewController\n\n      @player.play_modal(@local_file)\n\n\n      EM.add_timer 2.0 do\n        resume\n      end\n      wait_max 5 do\n        @controller.modalViewController.should.not == nil\n        EM.add_timer 4.0 do\n          resume\n        end\n\n        @player.stop\n        wait_max 5 do\n          @controller.modalViewController.should == nil\n          @controller = nil\n          @player = nil\n        end\n      end\n    end\n\n    it \"should present a modalViewController if controller given\" do\n      parent = App.window.rootViewController\n      @controller = UINavigationController.alloc.init\n      parent.addChildViewController @controller\n      @controller.viewWillAppear(false)\n      parent.view.addSubview(@controller.view)\n      @controller.viewDidAppear(false)\n\n      @controller.didMoveToParentViewController(parent)\n\n      EM.add_timer 3.0 do\n        resume\n      end\n      wait_max 5 do\n        @player.play_modal(@local_file, controller: @controller)\n        @controller.modalViewController.should.not == nil\n        EM.add_timer 2.0 do\n          @player.stop\n        end\n        EM.add_timer 4.0 do\n          resume\n        end\n\n        wait_max 5 do\n          @controller.modalViewController.should == nil\n          @controller.willMoveToParentViewController(nil)\n          @controller.viewWillDisappear(false)\n          @controller.removeFromParentViewController\n          @controller.viewDidDisappear(false)\n          @controller = nil\n          @player = nil\n        end\n      end\n    end\n  end\n=end\nend\n"
  },
  {
    "path": "spec/motion/motion/core_motion_spec.rb",
    "content": "describe BubbleWrap::Motion do\n\n  it 'should be able to instantiate all the things' do\n    mgr = CMMotionManager.alloc.init\n    mgr.should.be.kind_of(CMMotionManager)\n\n    accelerometer = BubbleWrap::Motion::Accelerometer.new(mgr)\n    accelerometer.should.be.kind_of(BubbleWrap::Motion::Accelerometer)\n\n    gyroscope = BubbleWrap::Motion::Gyroscope.new(mgr)\n    gyroscope.should.be.kind_of(BubbleWrap::Motion::Gyroscope)\n\n    magnetometer = BubbleWrap::Motion::Magnetometer.new(mgr)\n    magnetometer.should.be.kind_of(BubbleWrap::Motion::Magnetometer)\n\n    devicemotion = BubbleWrap::Motion::DeviceMotion.new(mgr)\n    devicemotion.should.be.kind_of(BubbleWrap::Motion::DeviceMotion)\n  end\n\n  describe 'BubbleWrap::Motion.manager' do\n    it 'should return a CMMotionManager' do\n      BubbleWrap::Motion.manager.should.be.kind_of(CMMotionManager)\n    end\n    it 'should be a shared instance' do\n      mgr1 = BubbleWrap::Motion.manager\n      mgr2 = BubbleWrap::Motion.manager\n      mgr1.should.equal? mgr2\n    end\n  end\n\n  describe 'BubbleWrap::Motion.accelerometer' do\n    it 'should be a BubbleWrap::Motion::Accelerometer' do\n      BubbleWrap::Motion.accelerometer.should.be.kind_of(BubbleWrap::Motion::Accelerometer)\n    end\n    it 'should be shared instanceAccelerometer' do\n      obj1 = BubbleWrap::Motion.accelerometer\n      obj2 = BubbleWrap::Motion.accelerometer\n      obj1.should.equal? obj2\n    end\n    it 'should not be available in the simulator' do\n      BubbleWrap::Motion.accelerometer.available?.should == false\n    end\n    it 'should not be active in the simulator' do\n      BubbleWrap::Motion.accelerometer.active?.should == false\n    end\n    it 'should not have data' do\n      BubbleWrap::Motion.accelerometer.data.should == nil\n    end\n    it 'should be able to start with a block' do\n      -> do\n        BubbleWrap::Motion.accelerometer.start {}\n        BubbleWrap::Motion.accelerometer.stop\n      end.should.not.raise\n    end\n    it 'should be able to start without a block' do\n      -> do\n        BubbleWrap::Motion.accelerometer.start\n        BubbleWrap::Motion.accelerometer.stop\n      end.should.not.raise\n    end\n    it 'should be able to repeat' do\n      -> do\n        BubbleWrap::Motion.accelerometer.repeat {}\n        BubbleWrap::Motion.accelerometer.stop\n      end.should.not.raise\n    end\n    it 'should be able to repeat via every' do\n      -> do\n        BubbleWrap::Motion.accelerometer.every(5) {}\n        BubbleWrap::Motion.accelerometer.stop\n      end.should.not.raise\n    end\n    it 'should be able to run once' do\n      -> do\n        BubbleWrap::Motion.accelerometer.once {}\n        BubbleWrap::Motion.accelerometer.stop\n      end.should.not.raise\n    end\n  end\n\n  describe 'BubbleWrap::Motion.gyroscope' do\n    it 'should be a BubbleWrap::Motion::Gyroscope' do\n      BubbleWrap::Motion.gyroscope.should.be.kind_of(BubbleWrap::Motion::Gyroscope)\n    end\n    it 'should be a shared instance' do\n      obj1 = BubbleWrap::Motion.gyroscope\n      obj2 = BubbleWrap::Motion.gyroscope\n      obj1.should.equal? obj2\n    end\n    it 'should not be available in the simulator' do\n      BubbleWrap::Motion.gyroscope.available?.should == false\n    end\n    it 'should not be active in the simulator' do\n      BubbleWrap::Motion.gyroscope.active?.should == false\n    end\n    it 'should not have data' do\n      BubbleWrap::Motion.gyroscope.data.should == nil\n    end\n    it 'should be able to start with a block' do\n      -> do\n        BubbleWrap::Motion.gyroscope.start {}\n        BubbleWrap::Motion.gyroscope.stop\n      end.should.not.raise\n    end\n    it 'should be able to start without a block' do\n      -> do\n        BubbleWrap::Motion.gyroscope.start\n        BubbleWrap::Motion.gyroscope.stop\n      end.should.not.raise\n    end\n    it 'should be able to repeat' do\n      -> do\n        BubbleWrap::Motion.gyroscope.repeat {}\n        BubbleWrap::Motion.gyroscope.stop\n      end.should.not.raise\n    end\n    it 'should be able to repeat via every' do\n      -> do\n        BubbleWrap::Motion.gyroscope.every(5) {}\n        BubbleWrap::Motion.gyroscope.stop\n      end.should.not.raise\n    end\n    it 'should be able to run once' do\n      -> do\n        BubbleWrap::Motion.gyroscope.once {}\n        BubbleWrap::Motion.gyroscope.stop\n      end.should.not.raise\n    end\n  end\n\n  describe 'BubbleWrap::Motion.magnetometer' do\n    it 'should be a BubbleWrap::Motion::Magnetometer' do\n      BubbleWrap::Motion.magnetometer.should.be.kind_of(BubbleWrap::Motion::Magnetometer)\n    end\n    it 'should be a shared instance' do\n      obj1 = BubbleWrap::Motion.magnetometer\n      obj2 = BubbleWrap::Motion.magnetometer\n      obj1.should.equal? obj2\n    end\n    it 'should not be available in the simulator' do\n      BubbleWrap::Motion.magnetometer.available?.should == false\n    end\n    it 'should not be active in the simulator' do\n      BubbleWrap::Motion.magnetometer.active?.should == false\n    end\n    it 'should not have data' do\n      BubbleWrap::Motion.magnetometer.data.should == nil\n    end\n    it 'should be able to start with a block' do\n      -> do\n        BubbleWrap::Motion.magnetometer.start {}\n        BubbleWrap::Motion.magnetometer.stop\n      end.should.not.raise\n    end\n    it 'should be able to start without a block' do\n      -> do\n        BubbleWrap::Motion.magnetometer.start\n        BubbleWrap::Motion.magnetometer.stop\n      end.should.not.raise\n    end\n    it 'should be able to repeat' do\n      -> do\n        BubbleWrap::Motion.magnetometer.repeat {}\n        BubbleWrap::Motion.magnetometer.stop\n      end.should.not.raise\n    end\n    it 'should be able to repeat via every' do\n      -> do\n        BubbleWrap::Motion.magnetometer.every(5) {}\n        BubbleWrap::Motion.magnetometer.stop\n      end.should.not.raise\n    end\n    it 'should be able to run once' do\n      -> do\n        BubbleWrap::Motion.magnetometer.once {}\n        BubbleWrap::Motion.magnetometer.stop\n      end.should.not.raise\n    end\n  end\n\n  describe 'BubbleWrap::Motion.device' do\n    it 'should be a BubbleWrap::Motion::DeviceMotion' do\n      BubbleWrap::Motion.device.should.be.kind_of(BubbleWrap::Motion::DeviceMotion)\n    end\n    it 'should be a shared instance' do\n      obj1 = BubbleWrap::Motion.device\n      obj2 = BubbleWrap::Motion.device\n      obj1.should.equal? obj2\n    end\n    it 'should not be available in the simulator' do\n      BubbleWrap::Motion.device.available?.should == false\n    end\n    it 'should not be active in the simulator' do\n      BubbleWrap::Motion.device.active?.should == false\n    end\n    it 'should not have data' do\n      BubbleWrap::Motion.device.data.should == nil\n    end\n    it 'should be able to start with a block' do\n      -> do\n        BubbleWrap::Motion.device.start {}\n        BubbleWrap::Motion.device.stop\n      end.should.not.raise\n    end\n    it 'should be able to start without a block' do\n      -> do\n        BubbleWrap::Motion.device.start\n        BubbleWrap::Motion.device.stop\n      end.should.not.raise\n    end\n    it 'should be able to repeat' do\n      -> do\n        BubbleWrap::Motion.device.repeat {}\n        BubbleWrap::Motion.device.stop\n      end.should.not.raise\n    end\n    it 'should be able to repeat via every' do\n      -> do\n        BubbleWrap::Motion.device.every(5) {}\n        BubbleWrap::Motion.device.stop\n      end.should.not.raise\n    end\n    it 'should be able to run once' do\n      -> do\n        BubbleWrap::Motion.device.once {}\n        BubbleWrap::Motion.device.stop\n      end.should.not.raise\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/motion/network-indicator/network_indicator_spec.rb",
    "content": "describe BW::NetworkIndicator do\n\n  before do\n    BW::NetworkIndicator.reset!\n  end\n\n  after do\n    BW::NetworkIndicator.instance_variable_set(:@counter, 0)\n    UIApplication.sharedApplication.networkActivityIndicatorVisible = false\n  end\n\n  it 'should show the indicator immediately' do\n    BW::NetworkIndicator.show\n    UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true\n  end\n\n  it 'should have a counter' do\n    BW::NetworkIndicator.show\n    BW::NetworkIndicator.counter.should == 1\n    BW::NetworkIndicator.hide\n    BW::NetworkIndicator.counter.should == 0\n  end\n\n  it 'should show the indicator from any thread' do\n    Dispatch::Queue.concurrent.async do\n      BW::NetworkIndicator.show\n    end\n    wait BW::NetworkIndicator::DELAY do\n      UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true\n    end\n  end\n\n  it 'should hide the indicator' do\n    BW::NetworkIndicator.show\n    BW::NetworkIndicator.hide\n    wait BW::NetworkIndicator::DELAY do\n      UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == false\n    end\n  end\n\n  it 'should hide the indicator after a delay' do\n    BW::NetworkIndicator.show\n    BW::NetworkIndicator.hide\n    UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true\n    wait BW::NetworkIndicator::DELAY do\n      UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == false\n    end\n  end\n\n  it 'should not hide the indicator if show/hide/show is called quickly' do\n    BW::NetworkIndicator.show\n    BW::NetworkIndicator.hide\n    UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true\n    wait BW::NetworkIndicator::DELAY/2 do\n      UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true\n      BW::NetworkIndicator.show\n      wait BW::NetworkIndicator::DELAY do\n        UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true\n      end\n    end\n  end\n\n  it 'should keep track of how many times `show` was called' do\n    BW::NetworkIndicator.show\n    BW::NetworkIndicator.show\n    UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true\n    BW::NetworkIndicator.hide\n    wait BW::NetworkIndicator::DELAY do\n      UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true\n      BW::NetworkIndicator.hide\n      wait BW::NetworkIndicator::DELAY do\n        UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == false\n      end\n    end\n  end\n\n  it 'should allow `hide` to be called too many times' do\n    BW::NetworkIndicator.show\n    BW::NetworkIndicator.show\n    BW::NetworkIndicator.hide\n    BW::NetworkIndicator.hide\n    wait BW::NetworkIndicator::DELAY do\n      UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == false\n\n      BW::NetworkIndicator.hide\n      BW::NetworkIndicator.hide\n      wait BW::NetworkIndicator::DELAY do\n        UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == false\n\n        BW::NetworkIndicator.show\n        UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == true\n      end\n    end\n  end\n\n  it 'should reset the counter when `reset!` is called' do\n    BW::NetworkIndicator.show\n    BW::NetworkIndicator.show\n    BW::NetworkIndicator.reset!\n    UIApplication.sharedApplication.networkActivityIndicatorVisible?.should == false\n  end\n\n  it 'should have `visible?` method' do\n    BW::NetworkIndicator.show\n    BW::NetworkIndicator.visible?.should == true\n    BW::NetworkIndicator.hide\n    wait BW::NetworkIndicator::DELAY do\n      BW::NetworkIndicator.visible?.should == false\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/motion/reactor/deferrable_spec.rb",
    "content": "describe BubbleWrap::Reactor::Deferrable do\n\n  before do\n    @subject = Class.new do\n      include BubbleWrap::Reactor::Deferrable\n    end\n    @object = @subject.new\n  end\n\n  describe '.callback' do\n\n    it \"calls the callback block with arguments after .succeed\" do\n      args = [1, 2]\n      @object.callback do |*passed_args|\n        passed_args.should.equal args\n      end\n      @object.succeed *args\n    end\n\n    it \"calls the callback if even if the deferrable already suceeded\" do\n      @object.succeed :succeeded\n      @object.callback do |*args|\n        args[0].should.equal :succeeded\n      end\n    end\n\n  end\n\n  describe '.errback' do\n    it \"calls the errback block after the deferrable fails\" do\n      args = [1, 2]\n      @object.errback do |*passed_args|\n        passed_args.should.equal args\n      end\n      @object.fail *args\n    end\n\n    it \"calls the errback block even if the deferrable failed first\" do\n      @object.fail :err\n      @object.errback do |err|\n        err.should.equal :err\n      end\n    end\n  end\n\n  describe '.delegate' do\n    it \"passes the delegate to both .errback_delegate and .callback_delegate\" do\n      @object.define_singleton_method(:callback_delegate) { |d| @callback_delegated = true }\n      @object.define_singleton_method(:errback_delegate) { |d| @errback_delegated = true }\n\n      @object.delegate(@subject.new)\n      @object.instance_variable_get(\"@callback_delegated\").should.equal true\n      @object.instance_variable_get(\"@errback_delegated\").should.equal true\n    end\n  end\n\n  describe '.errback_delegate' do\n    it \"calls the delegate errback method when the deferrable fails\" do\n      delegate = @subject.new\n      @object.errback_delegate delegate\n\n      delegate.errback do |*args|\n        args[0].should.equal :err\n      end\n      @object.fail :err\n    end\n  end\n\n  describe '.callback_delegate' do\n    it \"calls the delegate callback when the deferrable suceeds\" do\n      delegate = @subject.new\n      @object.callback_delegate delegate\n\n      delegate.callback do |*args|\n        args[0].should.equal :passed\n      end\n      @object.succeed :passed\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/motion/reactor/dependent_deferrable_spec.rb",
    "content": "describe BubbleWrap::Reactor::DependentDeferrable do\n\n  before do\n    @subject = Class.new do\n      include BubbleWrap::Reactor::Deferrable\n    end\n    @d1 = @subject.new\n    @d2 = @subject.new\n    @object = BubbleWrap::Reactor::DependentDeferrable.on(@d1, @d2)\n  end\n\n  describe '.callback' do\n\n    it \"calls the callback block with arguments after all children .succeed\" do\n      args1 = [1, 2]\n      args2 = [3, 4]\n      @object.callback do |*passed_args|\n        passed_args.should.equal [args1, args2]\n      end\n      @d1.succeed *args1\n      @d2.succeed *args2\n    end\n\n    it \"calls the callback block with arguments only after all children .succeed\" do\n      execution_order = []\n      @object.callback do |*passed_args|\n        execution_order << 3\n      end\n      execution_order << 1\n      @d1.succeed :succeeds\n      execution_order << 2\n      @d2.succeed :succeeds\n      execution_order.should.equal [1, 2, 3]\n    end\n\n    it \"calls the callback if even if the deferrable already succeeded\" do\n      @d1.succeed :succeeded1\n      @d2.succeed :succeeded2\n      @object.callback do |*args|\n        NSLog \"args: #{args} class: #{args.class}\"\n        args[0].should.equal [:succeeded1]\n        args[1].should.equal [:succeeded2]\n      end\n    end\n\n  end\n\n  describe '.errback' do\n    it \"calls the errback block after a child fails\" do\n      args1 = [1, 2]\n      args2 = [3, 4]\n      @object.errback do |*passed_args|\n        passed_args.should.equal args1\n      end\n      @d1.fail *args1\n    end\n\n    it \"calls the errback block immediately after a child fails\" do\n      execution_order = []\n      @object.errback do |*passed_args|\n        execution_order << 2\n      end\n      execution_order << 1\n      @d1.fail :fail\n      execution_order << 3\n      @d2.fail :fail\n      execution_order.should.equal [1, 2, 3]\n    end\n\n    it \"calls the errback block after a child fails even if others succeeds\" do\n      args1 = [1, 2]\n      args2 = [3, 4]\n      callback_called = false\n      @object.callback do |*passed_args|\n        callback_called = true\n      end\n      @object.errback do |*passed_args|\n        passed_args.should.equal args2\n      end\n      @d1.succeed *args1\n      @d2.fail *args2\n      callback_called.should.equal false\n    end\n\n    it \"calls the errback block even if the deferrable failed first\" do\n      @d1.fail :err\n      @object.errback do |err|\n        err.should.equal :err\n      end\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/motion/reactor/eventable_spec.rb",
    "content": "describe BubbleWrap::Reactor::Eventable do\n  before do\n    @subject = Class.new do\n      include BubbleWrap::Reactor::Eventable\n    end.new\n    @proxy = Class.new do\n      attr_accessor :proof\n    end.new\n  end\n\n  describe '.on' do\n    it 'registers events passing a block' do\n      proof = proc {  }\n      @subject.on(:foo, &proof)\n      events = @subject.instance_variable_get(:@__events__)\n      events[:foo].member?(proof).should == true\n    end\n\n    it 'registers events passing a Method object' do\n      def bar; end\n      proof = method(:bar)\n      @subject.on(:foo, proof)\n      events = @subject.instance_variable_get(:@__events__)\n      events[:foo].member?(proof).should == true\n    end\n\n    it 'returns the array of blocks for the event' do\n      proof = proc {  }\n      @subject.on(:foo, &proof).should == [proof]\n    end\n  end\n\n  describe '.off' do\n    it 'unregisters proc events' do\n      proof = proc { }\n      @subject.on(:foo, &proof)\n      events = @subject.instance_variable_get(:@__events__)\n      @subject.off(:foo, &proof)\n      events[:foo].member?(proof).should == false\n    end\n\n    it 'unregisters method events' do\n      def bar; end\n      proof = method(:bar)\n      @subject.on(:foo, proof)\n      events = @subject.instance_variable_get(:@__events__)\n      @subject.off(:foo, proof)\n      events[:foo].member?(proof).should == false\n    end\n\n    it 'unregisters all events' do\n      def bar; end\n      proof = method(:bar)\n      @subject.on(:foo, proof)\n      proof_2 = proc { }\n      @subject.on(:foo, &proof_2)\n      events = @subject.instance_variable_get(:@__events__)\n      @subject.off(:foo)\n      events[:foo].should == []\n    end\n\n    it 'unregisters method events after kvo' do\n      observing_object = Class.new do\n        include BubbleWrap::KVO\n      end.new\n\n      @subject.on(:foo, @subject.method(:description))\n      block = lambda { |old_value, new_value| }\n      observing_object.observe(@subject, :cool_variable, &block)\n      @subject.off(:foo, @subject.method(:description))\n\n      events = @subject.instance_variable_get(:@__events__)\n      events[:foo].length.should == 0\n      observing_object.unobserve_all\n    end\n\n    it 'calls other event procs when a proc unregisters itself' do\n      @proxy.proof = 0\n      proof1 = proc do |r|\n        @proxy.proof += r\n        @subject.off(:foo, &proof1)\n      end\n      proof2 = proc do |r|\n        @proxy.proof += r\n      end\n      @subject.on(:foo, &proof1)\n      @subject.on(:foo, &proof2)\n      @subject.trigger(:foo, 2)\n      @proxy.proof.should == 4\n    end\n  end\n\n  describe '.trigger' do\n    it 'calls event procs' do\n      @proxy.proof = false\n      @subject.on(:foo) do |r|\n        @proxy.proof = r\n      end\n      @subject.trigger(:foo, true)\n      @proxy.proof.should == true\n    end\n\n    it 'calls event methods' do\n      @proxy.proof = false\n      def bar(r)\n        @proxy.proof = r\n      end\n      @subject.on(:foo, method(:bar))\n      @subject.trigger(:foo, true)\n      @proxy.proof.should == true\n    end\n\n    it 'calls event procs for correct event' do\n      @proxy.proof = false\n      @subject.on(:foo) do |r|\n        @proxy.proof = r\n      end\n      @subject.trigger(:bar, true)\n      @proxy.proof.should == false\n    end\n\n    it 'calls event methods for correct event' do\n      @proxy.proof = false\n      def bar(r)\n        @proxy.proof = r\n      end\n      @subject.on(:foo, method(:bar))\n      @subject.trigger(:bar, true)\n      @proxy.proof.should == false\n    end\n\n    it 'calls all the event procs' do\n      @proxy.proof = 0\n      @subject.on(:foo) { |r| @proxy.proof += r }\n      @subject.on(:foo) { |r| @proxy.proof += r }\n      @subject.on(:foo) { |r| @proxy.proof += r }\n      @subject.trigger(:foo, 2)\n      @proxy.proof.should == 6\n    end\n\n    it 'calls all the event methods' do\n      def bar(r); @proxy.proof += r; end\n      def baz(r); @proxy.proof += r; end\n      @proxy.proof = 0\n      @subject.on(:foo, method(:baz))\n      @subject.on(:foo, method(:bar))\n      @subject.trigger(:foo, 2)\n      @proxy.proof.should == 4\n    end\n\n    class TestUIViewControllerWithEventable\n      include BubbleWrap::Reactor::Eventable\n      def test_on\n        on(:foo) do\n        end\n        method(:bar)\n      end\n\n      def bar\n      end\n\n      def dealloc\n        $test_dealloc = true\n        super\n      end\n    end\n\n    describe 'memory implications' do\n\n      it 'does not cause a retain-cycle prior to loading __events__' do\n        autorelease_pool do\n          $test_dealloc = false\n          TestUIViewControllerWithEventable.alloc.init\n        end\n        wait 0 do\n          $test_dealloc.should == true\n        end\n      end\n\n      it 'does not cause a retain-cycle after calling trigger' do\n        autorelease_pool do\n        autorelease_pool do\n          $test_dealloc = false\n          obj = TestUIViewControllerWithEventable.alloc.init\n          obj.trigger(:something)\n        end\n        end\n        wait 0 do\n          $test_dealloc.should == true\n        end\n      end\n\n      it 'does not cause a retain-cycle after loading __events__' do\n        autorelease_pool do\n          $test_dealloc = false\n          obj = TestUIViewControllerWithEventable.alloc.init\n          obj.send(:__events__)\n        end\n        wait 0 do\n          $test_dealloc.should == true\n        end\n      end\n\n      it 'does not cause a retain-cycle after adding an event' do\n        autorelease_pool do\n          $test_dealloc = false\n          obj = TestUIViewControllerWithEventable.alloc.init\n          obj.test_on\n        end\n        wait 0 do\n          $test_dealloc.should == true\n        end\n      end\n\n    end\n  end\n\nend\n"
  },
  {
    "path": "spec/motion/reactor/thread_aware_deferrable_spec.rb",
    "content": "shared :queue_caching_deferrable_method do\n  it \"stores the current queue that is being used when a callback is added\" do\n    @subject.send(@method, &@blk)\n    cache = @subject.instance_variable_get(\"@queue_cache\")\n    cache.should.not.be.nil\n    cache[@blk.object_id].to_s.should.equal Dispatch::Queue.main.to_s\n  end\n\n  it \"stores the queue, even if its not the main\" do\n    @queue.async do\n      @subject.send(@method, &@blk)\n      Dispatch::Queue.main.async { resume }\n    end\n    wait do\n      cache = @subject.instance_variable_get(\"@queue_cache\")\n      cache.should.not.be.nil\n      cache[@blk.object_id].to_s.should.equal @queue.to_s\n    end\n  end\nend\n\nshared :queue_block_execution do\n  it \"calls the block on the right thread, with deferred argument once the deferrable is finished\" do\n    @queue.async do\n      @subject.send(@block_method) do |*args|\n        Dispatch::Queue.current.to_s.should.equal @queue.to_s\n        args.should.equal [true]\n        Dispatch::Queue.main.async { resume }\n      end\n    end\n    @subject.send(@status_method, true)\n\n    wait {}\n  end\n\n  it \"removes the queue from internal cache once the deferrable is finished\" do\n    @subject.send(@block_method) do |*args|\n      Dispatch::Queue.main.async { resume }\n    end\n    @subject.send(@status_method, true)\n\n    wait do\n      @subject.instance_variable_get(\"@queue_cache\").length.should.equal 0\n    end\n  end\nend\n\ndescribe BubbleWrap::Reactor::ThreadAwareDeferrable do\n\n  before do\n    @subject = BubbleWrap::Reactor::ThreadAwareDeferrable.new\n    @queue = Dispatch::Queue.new(:test_queue.to_s)\n    @blk = Proc.new {|*args| }\n  end\n\n  describe '.callback' do\n    before do\n      @method = :callback\n    end\n    behaves_like :queue_caching_deferrable_method\n  end\n\n  describe '.errback' do\n    before do\n      @method = :errback\n    end\n    behaves_like :queue_caching_deferrable_method\n  end\n\n  describe '.succeed' do\n    before do\n      @block_method = :callback\n      @status_method = :succeed\n    end\n    behaves_like :queue_block_execution\n  end\n\n  describe '.fail' do\n    before do\n      @block_method = :callback\n      @status_method = :succeed\n    end\n    behaves_like :queue_block_execution\n  end\nend\n"
  },
  {
    "path": "spec/motion/reactor_spec.rb",
    "content": "describe BubbleWrap::Reactor do\n  before do\n    @subject = ::BubbleWrap::Reactor\n    @proxy = Class.new do\n      attr_accessor :proof\n    end.new\n  end\n\n  describe 'slavish EventMachine compatibility' do\n    describe '.reactor_running?' do\n      it 'returns true' do\n        @subject.reactor_running?.should == true\n      end\n    end\n\n    describe '.reactor_thread?' do\n      it 'returns true' do\n        @subject.reactor_running?.should == true\n      end\n    end\n  end\n\n  describe '.add_timer' do\n    it 'schedules and executes timers' do\n      @proxy.proof = false\n      @subject.add_timer 0.5 do\n        @proxy.proof = true\n      end\n      wait_for_change @proxy, 'proof' do\n        @proxy.proof.should == true\n      end\n    end\n\n    it 'only runs the callback once' do\n      @proxy.proof = 0\n      @subject.add_timer 0.1 do\n        @proxy.proof = @proxy.proof + 1\n      end\n      wait 1 do\n        @proxy.proof.should == 1\n      end\n    end\n  end\n\n  describe '.add_periodic_timer' do\n    it 'runs callbacks repeatedly' do\n      @proxy.proof = 0\n      @timer = @subject.add_periodic_timer 0.5 do\n        @proxy.proof = @proxy.proof + 1\n        @subject.cancel_timer(@timer) if @proxy.proof > 2\n      end\n      wait 1.1 do\n        @proxy.proof.should >= 2\n      end\n    end\n\n    it 'accepts non-block callbacks' do\n      @proxy.proof = 0\n      callback = lambda {\n        @proxy.proof = @proxy.proof + 1\n        @subject.cancel_timer(@timer) if @proxy.proof > 2\n      }\n      @timer = @subject.add_periodic_timer 0.5, callback\n      wait 1.1 do\n        @proxy.proof.should >= 2\n      end\n    end\n  end\n\n  describe '.cancel_timer' do\n    it 'cancels timers' do\n      @proxy.proof = true\n      timer = @subject.add_timer 10.0 do\n        @proxy.proof = false\n      end\n      @subject.cancel_timer(timer)\n      @proxy.proof.should == true\n    end\n\n    it 'cancels periodic timers' do\n      @proxy.proof = true\n      timer = @subject.add_periodic_timer 10.0 do\n        @proxy.proof = false\n      end\n      @subject.cancel_timer(timer)\n      @proxy.proof.should == true\n    end\n  end\n\n  describe '.defer' do\n    it 'defers the operation' do\n      @proxy.proof = false\n      @subject.defer do\n        @proxy.proof = true\n      end\n      @proxy.proof.should == false\n      wait 0.5 do\n        @proxy.proof.should == true\n      end\n    end\n\n    it 'calls the callback after the operation finishes' do\n      @proxy.proof = false\n      cb = proc do |result|\n        @proxy.proof = result\n      end\n      op = proc do\n        true\n      end\n      @subject.defer(op,cb)\n      wait 0.5 do\n        @proxy.proof.should == true\n      end\n    end\n  end\n\n  describe '.schedule' do\n    it 'defers the operation' do\n      @proxy.proof = false\n      @subject.schedule do\n        @proxy.proof = true\n      end\n      @proxy.proof.should == false\n      wait 0.5 do\n        @proxy.proof.should == true\n      end\n    end\n\n# I wish these specs would run, but they kill RubyMotion. *sad face*\n\n#     it 'runs the operation on the reactor queue' do\n#       @proxy.proof = false\n#       @subject.schedule do\n#         @proxy.proof = ::Reactor::Queue.current.to_s\n#       end\n#       wait 0.75 do\n#         @proxy.proof.should == \"#{NSBundle.mainBundle.bundleIdentifier}.reactor\"\n#       end\n#     end\n\n#     it 'runs the callback on the main queue' do\n#       @proxy.proof = false\n#       @subject.schedule do\n#         @proxy.proof = ::Reactor::Queue.current.to_s\n#       end\n#       wait 0.75 do\n#         @proxy.proof.should == ::Reactor::Queue.main.to_s\n#       end\n#     end\n  end\n\n  describe '.schedule_on_main' do\n    it 'defers the operation' do\n      @proxy.proof = false\n      @subject.schedule do\n        @proxy.proof = true\n      end\n      @proxy.proof.should == false\n      wait 0.5 do\n        @proxy.proof.should == true\n      end\n    end\n\n#     it 'runs the operation on the main queue' do\n#       @proxy.proof = false\n#       @subject.schedule do\n#         @proxy.proof = ::Reactor::Queue.current.to_s\n#       end\n#       wait 0.75 do\n#         @proxy.proof.should == ::Reactor::Queue.main.to_s\n#       end\n#     end\n  end\n\nend\n"
  },
  {
    "path": "spec/motion/rss_parser_spec.rb",
    "content": "describe \"RSSParser\" do\n  extend WebStub::SpecHelpers\n\n  before do\n    @feed_url = 'https://raw.github.com/gist/2952427/9f1522cbe5d77a72c7c96c4fdb4b77bd58d7681e/atom.xml'\n    @ns_url = NSURL.alloc.initWithString(@feed_url)\n    @local_feed = File.join(App.resources_path, 'atom.xml')\n  end\n\n  describe \"initialization\" do\n\n    it \"works with a string representing an url\" do\n      parser = BW::RSSParser.new(@feed_url)\n      parser.source.class.should.equal NSURL\n      parser.source.absoluteString.should.equal @feed_url\n    end\n\n    it \"works with a NSURL instance\" do\n      parser = BW::RSSParser.new(@ns_url)\n      parser.source.class.should.equal NSURL\n      parser.source.absoluteString.should.equal @feed_url\n    end\n  end\n\n  describe \"parsing\" do\n\n    it \"parses local file data\" do\n      parser = BW::RSSParser.new(File.read(@local_feed).to_data, true)\n      episodes = []\n      parser.parse { |episode| episodes << episode }\n      episodes.length.should.equal 108\n      episodes.last.title.should.equal 'Episode 001: Summer of Rails'\n    end\n\n    it \"parses url data\" do\n      string = File.read(File.join(App.resources_path, 'atom.xml'))\n\n      stub_request(:get, @feed_url).\n        to_return(body: string, content_type: \"application/xml\")\n\n      parser = BW::RSSParser.new(@feed_url)\n      episodes = []\n      parser.parse { |episode| episodes << episode }\n      episodes.length.should.equal 108\n      episodes.last.title.should.equal 'Episode 001: Summer of Rails'\n    end\n\n    # FIXME\n    # it \"handles errors\" do\n    #   error_url = 'http://doesnotexist.com'\n\n    #   parser = BW::RSSParser.new(error_url)\n    #   parser.parse\n    #   wait 0.1 do\n    #     parser.state.should.equal :errors\n    #   end\n    # end\n  end\nend\n\n"
  },
  {
    "path": "spec/motion/sms/result_spec.rb",
    "content": "describe BW::SMS::Result do\n\n  before do\n    @subject = BW::SMS::Result.new(MessageComposeResultCancelled)\n  end\n\n  it \"should set sent? when sent\" do\n    @subject.result = MessageComposeResultSent\n    @subject.should.be.sent\n  end\n\n  it \"should not set sent? when not sent\" do\n    @subject.result = MessageComposeResultFailed\n    @subject.should.not.be.sent\n  end\n\n  it \"should set canceled? when canceled\" do\n    @subject.result = MessageComposeResultCancelled\n    @subject.should.be.canceled\n  end\n\n  it \"should not set canceled? when not canceled\" do\n    @subject.result = MessageComposeResultFailed\n    @subject.should.not.be.canceled\n  end\n\n\n  it \"should set failed? when failed\" do\n    @subject.result = MessageComposeResultFailed\n    @subject.should.be.failed\n  end\n\n  it \"should not set failed? when not failed\" do\n    @subject.result = MessageComposeResultSent\n    @subject.should.not.be.failed\n  end\n\nend\n"
  },
  {
    "path": "spec/motion/sms/sms_spec.rb",
    "content": "# Mocking the presentViewController\nclass MessageViewController < UIViewController\n  attr_accessor :expectation\n\n  def presentModalViewController(modal, animated: animated)\n    expectation.call modal, animated\n  end\nend\n\n# Monkey-patching MFMessageComposeViewController\n# So we can access the values that are set.\n# This of course breaks MFMessageComposeViewController from actually working,\n# but it's testable.\nclass MFMessageComposeViewController\n  attr_accessor :recipients, :body\n\n  # for iOS7 compatibility\n  # on similators, MFMessageComposeViewController.alloc.init returns nil\n  def init\n    self\n  end\nend\n\ndescribe BW::SMS do\n  describe \".compose\" do\n    before do\n      @view_controller = MessageViewController.new\n      @standard_message_options = {\n        delegate: @view_controller,\n        to: [ \"1(234)567-8910\" ],\n        message: \"This is my message. It isn't very long.\",\n        animated: false\n      }\n    end\n\n    it \"should determine if the device can send a text message\" do\n      # False in simulator, but true on a device with SMS capability.\n      BW::SMS.can_send_sms?.should == (Device.iphone? && !Device.simulator?)\n    end\n\n    it \"should open the message controller in a modal\" do\n      @view_controller.expectation = lambda { |message_controller, animated|\n        message_controller.should.be.kind_of(MFMessageComposeViewController)\n      }\n\n      BW::SMS.compose @standard_message_options\n    end\n\n    it \"should create a message controller with the right recipient address set\" do\n      @view_controller.expectation = lambda { |message_controller, animated|\n        message_controller.recipients.should.be.kind_of(Array)\n        message_controller.recipients.should == @standard_message_options[:to]\n      }\n\n      BubbleWrap::SMS.compose @standard_message_options\n    end\n\n\n    it \"should create a message controller with the right message: set\" do\n      @view_controller.expectation = lambda { |message_controller, animated|\n        message_controller.body.should.be.kind_of(String)\n        message_controller.body.should == @standard_message_options[:message]\n      }\n\n      BubbleWrap::SMS.compose @standard_message_options\n    end\n\n    it \"should create a mail controller with the right animation\" do\n      @view_controller.expectation = lambda { |message_controller, animated|\n        animated.should.be.false\n      }\n      BubbleWrap::SMS.compose @standard_message_options\n    end\n\n  end\nend\n"
  },
  {
    "path": "spec/motion/ui/pollute_spec.rb",
    "content": "describe \"UIKit pollution\" do\n  it \"pollutes UIControl\" do\n    UIControl.ancestors.should.include BW::UIControlWrapper\n  end\n\n  it \"pollutes UIView\" do\n    UIView.ancestors.should.include BW::UIViewWrapper\n  end\n\n  it \"pollutes UIViewController\" do\n    UIViewController.ancestors.should.include BW::UIViewControllerWrapper\n  end\nend\n"
  },
  {
    "path": "spec/motion/ui/ui_activity_view_controller_wrapper_spec.rb",
    "content": "describe BubbleWrap::UIActivityViewController do\n  before do\n    @controller = UIViewController.alloc.init\n    @controller.instance_eval do\n      def presentViewController(*args)\n        true\n      end\n    end\n\n    @options = options = {\n      item:\"BubbleWrap!!!\",\n      animated:false\n    }\n  end\n\n  after do\n    presenting_controller ||= App.window.rootViewController.presentedViewController\n    presenting_controller ||= App.window.rootViewController\n    presenting_controller.dismissViewControllerAnimated(false, completion:nil)\n  end\n\n  it 'Creates an instance of UIActivityViewController' do\n    activity = BW::UIActivityViewController.new(@options)\n\n    activity.kind_of?(UIActivityViewController).should == true\n    activity.excludedActivityTypes.should == nil\n    activity.activityItems.is_a?(Array).should == true\n    activity.activityItems.count.should == 1\n  end\n\n  it 'Sets a completion block' do\n    activity = BW::UIActivityViewController.new(@options) do |activity_type, completed|\n      test = 2\n    end\n    activity.completionHandler.should.not == nil\n  end\n\n  it 'Sets multiple items' do\n    options = @options.tap { |o| o.delete(:item) }.merge(items: [\"Hello\", \"BubbleWrap!\"])\n\n    activity = BW::UIActivityViewController.new(options)\n    activity.activityItems.is_a?(Array).should == true\n    activity.activityItems.count.should == 2\n  end\n\n  it 'Sets a single excluded activity' do\n    activity = BW::UIActivityViewController.new(@options.merge(excluded: :print))\n    activity.excludedActivityTypes.is_a?(Array).should == true\n    activity.excludedActivityTypes.count.should == 1\n  end\n\n  it 'Sets multiple excluded activities' do\n    activity = BW::UIActivityViewController.new(@options.merge(excluded: [:print, :add_to_reading_list]))\n    activity.excludedActivityTypes.is_a?(Array).should == true\n    activity.excludedActivityTypes.count.should == 2\n  end\n\nend\n"
  },
  {
    "path": "spec/motion/ui/ui_alert_view_spec.rb",
    "content": "shared \"an instance with no options\" do\n  it \"has the correct class\" do\n    @subject.class.should.equal(BW::UIAlertView)\n  end\n\n  it \"has the correct superclass\" do\n    @subject.superclass.should.equal(::UIAlertView)\n  end\n\n  it \"has no title\" do\n    @subject.title.should.be.nil\n  end\n\n  it \"has no message\" do\n    @subject.message.should.be.nil\n  end\n\n  it \"has the correct delegate\" do\n    @subject.delegate.should.equal(@subject)\n  end\n\n  it \"has no clicked button\" do\n    @subject.clicked_button.should.be.nil\n  end\n\n  it \"has no will_present handler\" do\n    @subject.will_present.should.be.nil\n  end\n\n  it \"has no did_present handler\" do\n    @subject.did_present.should.be.nil\n  end\n\n  it \"has no on_system_cancel handler\" do\n    @subject.on_system_cancel.should.be.nil\n  end\n\n  it \"has no will_dismiss handler\" do\n    @subject.will_dismiss.should.be.nil\n  end\n\n  it \"has no did_dismiss handler\" do\n    @subject.did_dismiss.should.be.nil\n  end\n\n  it \"has no enable_first_other_button? handler\" do\n    @subject.enable_first_other_button?.should.be.nil\n  end\nend\n\n###################################################################################################\n\nshared \"an instance with a full set of options\" do\n  it \"has the correct title\" do\n    @subject.title.should.equal(@options[:title])\n  end\n\n  it \"has the correct message\" do\n    @subject.message.should.equal(@options[:message])\n  end\n\n  it \"has the correct delegate\" do\n    @subject.delegate.should.equal(@subject)\n  end\n\n  it \"has the correct cancel button index\" do\n    @subject.cancel_button_index.should.equal(@options[:cancel_button_index])\n  end\n\n  it \"has the correct buttons\" do\n    @subject.numberOfButtons.should.equal(1)\n    @subject.buttonTitleAtIndex(0).should.equal(@options[:buttons])\n  end\n\n  it \"has no clicked button\" do\n    @subject.clicked_button.should.be.nil\n  end\n\n  it \"has no will_present handler\" do\n    @subject.will_present.should.equal(@options[:will_present])\n  end\n\n  it \"has no did_present handler\" do\n    @subject.did_present.should.equal(@options[:did_present])\n  end\n\n  it \"has no on_system_cancel handler\" do\n    @subject.on_system_cancel.should.equal(@options[:on_system_cancel])\n  end\n\n  it \"has the correct on_click handler\" do\n    @subject.on_click.should.equal(@options[:on_click])\n  end\n\n  it \"has no will_dismiss handler\" do\n    @subject.will_dismiss.should.equal(@options[:will_dismiss])\n  end\n\n  it \"has no did_dismiss handler\" do\n    @subject.did_dismiss.should.equal(@options[:did_dismiss])\n  end\n\n  it \"has no enable_first_other_button? handler\" do\n    @subject.enable_first_other_button?.should.equal(@options[:enable_first_other_button?])\n  end\nend\n\n###################################################################################################\n\ndescribe BW::UIAlertView do\n  describe \".new\" do\n    describe \"given no options\" do\n      before do\n        @subject = BW::UIAlertView.new\n      end\n\n      behaves_like \"an instance with no options\"\n\n      it \"has the correct style\" do\n        @subject.style.should.equal(UIAlertViewStyleDefault)\n      end\n\n      it \"has no buttons\" do\n        @subject.numberOfButtons.should.equal(0)\n      end\n\n      it \"has the correct cancel button index\" do\n        @subject.cancel_button_index.should.equal(-1)\n      end\n\n      it \"has no on_click handler\" do\n        @subject.on_click.should.be.nil\n      end\n\n      it \"has no plain text field\" do\n        @subject.plain_text_field.should.be.nil\n      end\n\n      it \"has no secure text field\" do\n        @subject.secure_text_field.should.be.nil\n      end\n\n      it \"has no login text field\" do\n        @subject.login_text_field.should.be.nil\n      end\n\n      it \"has no password text field\" do\n        @subject.password_text_field.should.be.nil\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given no options with a block\" do\n      before do\n        @options = {}\n        @block   = -> { true }\n        @subject = BW::UIAlertView.new(@options, &@block)\n      end\n\n      behaves_like \"an instance with no options\"\n\n      it \"has the correct style\" do\n        @subject.style.should.equal(UIAlertViewStyleDefault)\n      end\n\n      it \"has no buttons\" do\n        @subject.numberOfButtons.should.equal(0)\n      end\n\n      it \"has the correct cancel button index\" do\n        @subject.cancel_button_index.should.equal(-1)\n      end\n\n      it \"has the correct on_click handler\" do\n        @subject.on_click.should.equal(@block)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given a complete set of options\" do\n      before do\n        @options = {\n          :title                      => \"title\",\n          :message                    => \"message\",\n          :style                      => :plain_text_input,\n          :keyboard_type              => UIKeyboardTypeURL,\n          :text                       => \"text\",\n          :placeholder                => \"placeholder\",\n          :buttons                    => \"button title\",\n          :cancel_button_index        => 0,\n          :will_present               => -> { true },\n          :did_present                => -> { true },\n          :on_system_cancel           => -> { true },\n          :on_click                   => -> { true },\n          :will_dismiss               => -> { true },\n          :did_dismiss                => -> { true },\n          :enable_first_other_button? => -> { true }\n        }\n        @subject = BW::UIAlertView.new(@options)\n      end\n\n      behaves_like \"an instance with a full set of options\"\n    end\n\n    ###############################################################################################\n\n    describe \"given options with multiple button titles\" do\n      before do\n        @options = { buttons: [\"first button\", \"second button\"] }\n        @subject = BW::UIAlertView.new(@options)\n      end\n\n      it \"has the correct buttons\" do\n        @subject.numberOfButtons.should.equal(2)\n        @subject.buttonTitleAtIndex(0).should.equal(@options[:buttons][0])\n        @subject.buttonTitleAtIndex(1).should.equal(@options[:buttons][1])\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given options with both an on_click handler and a block\" do\n      before do\n        @options = { on_click: -> { true }}\n        @block   = -> { true }\n        @subject = BW::UIAlertView.new(@options, &@block)\n      end\n\n      it \"has the correct on_click handler\" do\n        @subject.on_click.should.equal(@options[:on_click])\n      end\n    end\n  end\n\n  #################################################################################################\n\n  describe \".default\" do\n    describe \"given no options\" do\n      before do\n        @subject = BW::UIAlertView.default\n      end\n\n      behaves_like \"an instance with no options\"\n\n      it \"has the correct style\" do\n        @subject.style.should.equal(UIAlertViewStyleDefault)\n      end\n\n      it \"has the correct buttons\" do\n        @subject.numberOfButtons.should.equal(1)\n        @subject.buttonTitleAtIndex(0).should.equal(\"OK\")\n      end\n\n      it \"has the correct cancel button index\" do\n        @subject.cancel_button_index.should.equal(-1)\n      end\n\n      it \"has no on_click handler\" do\n        @subject.on_click.should.be.nil\n      end\n\n      it \"has no plain text field\" do\n        @subject.plain_text_field.should.be.nil\n      end\n\n      it \"has no secure text field\" do\n        @subject.secure_text_field.should.be.nil\n      end\n\n      it \"has no login text field\" do\n        @subject.login_text_field.should.be.nil\n      end\n\n      it \"has no password text field\" do\n        @subject.password_text_field.should.be.nil\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given no options with a block\" do\n      before do\n        @options = {}\n        @block   = -> { true }\n        @subject = BW::UIAlertView.default(@options, &@block)\n      end\n\n      behaves_like \"an instance with no options\"\n\n      it \"has the correct style\" do\n        @subject.style.should.equal(UIAlertViewStyleDefault)\n      end\n\n      it \"has the correct buttons\" do\n        @subject.numberOfButtons.should.equal(1)\n        @subject.buttonTitleAtIndex(0).should.equal(\"OK\")\n      end\n\n      it \"has the correct cancel button index\" do\n        @subject.cancel_button_index.should.equal(-1)\n      end\n\n      it \"has the correct on_click handler\" do\n        @subject.on_click.should.equal(@block)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given a complete set of options\" do\n      before do\n        @options = {\n          :title                      => \"title\",\n          :message                    => \"message\",\n          :style                      => :plain_text_input,\n          :text                       => \"text\",\n          :placeholder                => \"placeholder\",\n          :buttons                    => \"button title\",\n          :cancel_button_index        => 0,\n          :will_present               => -> { true },\n          :did_present                => -> { true },\n          :on_system_cancel           => -> { true },\n          :on_click                   => -> { true },\n          :will_dismiss               => -> { true },\n          :did_dismiss                => -> { true },\n          :enable_first_other_button? => -> { true }\n        }\n        @subject = BW::UIAlertView.default(@options)\n      end\n\n      behaves_like \"an instance with a full set of options\"\n    end\n\n    ###############################################################################################\n\n    describe \"given options with multiple button titles\" do\n      before do\n        @options = { buttons: [\"first button\", \"second button\"] }\n        @subject = BW::UIAlertView.new(@options)\n      end\n\n      it \"has the correct buttons\" do\n        @subject.numberOfButtons.should.equal(2)\n        @subject.buttonTitleAtIndex(0).should.equal(@options[:buttons][0])\n        @subject.buttonTitleAtIndex(1).should.equal(@options[:buttons][1])\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given options with a cancel button index\" do\n      before do\n        @options = { cancel_button_index: 0 }\n        @subject = BW::UIAlertView.new(@options)\n      end\n\n      it \"has the correct cancel button index\" do\n        @subject.cancel_button_index.should.equal(0)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given options with both an on_click handler and a block\" do\n      before do\n        @options = { on_click: -> { true }}\n        @block   = -> { true }\n        @subject = BW::UIAlertView.default(@options, &@block)\n      end\n\n      it \"has the correct on_click handler\" do\n        @subject.on_click.should.equal(@options[:on_click])\n      end\n    end\n  end\n\n  #################################################################################################\n\n  describe \".plain_text_input\" do\n    describe \"given no options\" do\n      before do\n        @subject = BW::UIAlertView.plain_text_input\n      end\n\n      behaves_like \"an instance with no options\"\n\n      it \"has the correct style\" do\n        @subject.style.should.equal(UIAlertViewStylePlainTextInput)\n      end\n\n      it \"has the correct buttons\" do\n        @subject.numberOfButtons.should.equal(2)\n        @subject.buttonTitleAtIndex(0).should.equal(\"Cancel\")\n        @subject.buttonTitleAtIndex(1).should.equal(\"OK\")\n      end\n\n      it \"has the correct cancel button index\" do\n        @subject.cancel_button_index.should.equal(0)\n      end\n\n      it \"has no on_click handler\" do\n        @subject.on_click.should.be.nil\n      end\n\n      it \"has a plain text field\" do\n        @subject.plain_text_field.should.be.kind_of(UITextField)\n      end\n\n      it \"has no placeholder\" do\n        @subject.plain_text_field.placeholder.should.be.nil\n      end\n\n      it \"has no text\" do\n        @subject.plain_text_field.text.should == \"\"\n      end\n\n      it \"has the default keyboard type\" do\n        @subject.plain_text_field.keyboardType.should == UIKeyboardTypeDefault\n      end\n\n      it \"has no secure text field\" do\n        @subject.secure_text_field.should.be.nil\n      end\n\n      it \"has no login text field\" do\n        @subject.login_text_field.should.be.nil\n      end\n\n      it \"has no password text field\" do\n        @subject.password_text_field.should.be.nil\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given a text placeholder\" do\n      before do\n        @options = { placeholder: \"placeholder\" }\n        @subject = BW::UIAlertView.plain_text_input(@options)\n      end\n\n      behaves_like \"an instance with no options\"\n\n      it \"has the correct placeholder\" do\n        @subject.plain_text_field.placeholder.should == \"placeholder\"\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given text\" do\n      before do\n        @options = { text: \"text\" }\n        @subject = BW::UIAlertView.plain_text_input(@options)\n      end\n\n      behaves_like \"an instance with no options\"\n\n      it \"has the correct text\" do\n        @subject.plain_text_field.text.should == \"text\"\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given a keyboard type\" do\n      before do\n        @options = { keyboard_type: UIKeyboardTypeURL }\n        @subject = BW::UIAlertView.plain_text_input(@options)\n      end\n\n      behaves_like \"an instance with no options\"\n\n      it \"has the correct keyboard type\" do\n        @subject.plain_text_field.keyboardType.should == UIKeyboardTypeURL\n      end\n    end\n\n    describe \"given a keyboard type as a symbol\" do\n      before do\n        @options = { keyboard_type: :email_address }\n        @subject = BW::UIAlertView.plain_text_input(@options)\n      end\n\n      behaves_like \"an instance with no options\"\n\n      it \"has the correct keyboard type\" do\n        @subject.plain_text_field.keyboardType.should == UIKeyboardTypeEmailAddress\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given no options with a block\" do\n      before do\n        @options = {}\n        @block   = -> { true }\n        @subject = BW::UIAlertView.plain_text_input(@options, &@block)\n      end\n\n      behaves_like \"an instance with no options\"\n\n      it \"has the correct style\" do\n        @subject.style.should.equal(UIAlertViewStylePlainTextInput)\n      end\n\n      it \"has the correct buttons\" do\n        @subject.numberOfButtons.should.equal(2)\n        @subject.buttonTitleAtIndex(0).should.equal(\"Cancel\")\n        @subject.buttonTitleAtIndex(1).should.equal(\"OK\")\n      end\n\n      it \"has the correct cancel button index\" do\n        @subject.cancel_button_index.should.equal(0)\n      end\n\n      it \"has the correct on_click handler\" do\n        @subject.on_click.should.equal(@block)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given a complete set of options\" do\n      before do\n        @options = {\n          :title                      => \"title\",\n          :message                    => \"message\",\n          :style                      => :default,\n          :text                       => \"text\",\n          :placeholder                => \"placeholder\",\n          :buttons                    => \"button title\",\n          :cancel_button_index        => 0,\n          :will_present               => -> { true },\n          :did_present                => -> { true },\n          :on_system_cancel           => -> { true },\n          :on_click                   => -> { true },\n          :will_dismiss               => -> { true },\n          :did_dismiss                => -> { true },\n          :enable_first_other_button? => -> { true }\n        }\n        @subject = BW::UIAlertView.plain_text_input(@options)\n      end\n\n      behaves_like \"an instance with a full set of options\"\n    end\n\n    ###############################################################################################\n\n    describe \"given options with multiple button titles\" do\n      before do\n        @options = { buttons: [\"first button\", \"second button\"] }\n        @subject = BW::UIAlertView.new(@options)\n      end\n\n      it \"has the correct buttons\" do\n        @subject.numberOfButtons.should.equal(2)\n        @subject.buttonTitleAtIndex(0).should.equal(@options[:buttons][0])\n        @subject.buttonTitleAtIndex(1).should.equal(@options[:buttons][1])\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given options with a cancel button index\" do\n      before do\n        @options = { cancel_button_index: -1 }\n        @subject = BW::UIAlertView.plain_text_input(@options)\n      end\n\n      it \"has the correct cancel button index\" do\n        @subject.cancel_button_index.should.equal(-1)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given options with both an on_click handler and a block\" do\n      before do\n        @options = { on_click: -> { true }}\n        @block   = -> { true }\n        @subject = BW::UIAlertView.plain_text_input(@options, &@block)\n      end\n\n      it \"has the correct on_click handler\" do\n        @subject.on_click.should.equal(@options[:on_click])\n      end\n    end\n  end\n\n  #################################################################################################\n\n  describe \".secure_text_input\" do\n    describe \"given no options\" do\n      before do\n        @subject = BW::UIAlertView.secure_text_input\n      end\n\n      behaves_like \"an instance with no options\"\n\n      it \"has the correct style\" do\n        @subject.style.should.equal(UIAlertViewStyleSecureTextInput)\n      end\n\n      it \"has the correct buttons\" do\n        @subject.numberOfButtons.should.equal(2)\n        @subject.buttonTitleAtIndex(0).should.equal(\"Cancel\")\n        @subject.buttonTitleAtIndex(1).should.equal(\"OK\")\n      end\n\n      it \"has the correct cancel button index\" do\n        @subject.cancel_button_index.should.equal(0)\n      end\n\n      it \"has no on_click handler\" do\n        @subject.on_click.should.be.nil\n      end\n\n      it \"has no plain text field\" do\n        @subject.plain_text_field.should.be.nil\n      end\n\n      it \"has a secure text field\" do\n        @subject.secure_text_field.should.be.kind_of(UITextField)\n      end\n\n      it \"has no login text field\" do\n        @subject.login_text_field.should.be.nil\n      end\n\n      it \"has no password text field\" do\n        @subject.password_text_field.should.be.nil\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given no options with a block\" do\n      before do\n        @options = {}\n        @block   = -> { true }\n        @subject = BW::UIAlertView.secure_text_input(@options, &@block)\n      end\n\n      behaves_like \"an instance with no options\"\n\n      it \"has the correct style\" do\n        @subject.style.should.equal(UIAlertViewStyleSecureTextInput)\n      end\n\n      it \"has the correct buttons\" do\n        @subject.numberOfButtons.should.equal(2)\n        @subject.buttonTitleAtIndex(0).should.equal(\"Cancel\")\n        @subject.buttonTitleAtIndex(1).should.equal(\"OK\")\n      end\n\n      it \"has the correct cancel button index\" do\n        @subject.cancel_button_index.should.equal(0)\n      end\n\n      it \"has the correct on_click handler\" do\n        @subject.on_click.should.equal(@block)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given a complete set of options\" do\n      before do\n        @options = {\n          :title                      => \"title\",\n          :message                    => \"message\",\n          :style                      => :default,\n          :text                       => \"text\",\n          :placeholder                => \"placeholder\",\n          :buttons                    => \"button title\",\n          :cancel_button_index        => 0,\n          :will_present               => -> { true },\n          :did_present                => -> { true },\n          :on_system_cancel           => -> { true },\n          :on_click                   => -> { true },\n          :will_dismiss               => -> { true },\n          :did_dismiss                => -> { true },\n          :enable_first_other_button? => -> { true }\n        }\n        @subject = BW::UIAlertView.secure_text_input(@options)\n      end\n\n      behaves_like \"an instance with a full set of options\"\n    end\n\n    ###############################################################################################\n\n    describe \"given options with multiple button titles\" do\n      before do\n        @options = { buttons: [\"first button\", \"second button\"] }\n        @subject = BW::UIAlertView.new(@options)\n      end\n\n      it \"has the correct buttons\" do\n        @subject.numberOfButtons.should.equal(2)\n        @subject.buttonTitleAtIndex(0).should.equal(@options[:buttons][0])\n        @subject.buttonTitleAtIndex(1).should.equal(@options[:buttons][1])\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given options with a cancel button index\" do\n      before do\n        @options = { cancel_button_index: -1 }\n        @subject = BW::UIAlertView.secure_text_input(@options)\n      end\n\n      it \"has the correct cancel button index\" do\n        @subject.cancel_button_index.should.equal(-1)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given options with both an on_click handler and a block\" do\n      before do\n        @options = { on_click: -> { true }}\n        @block   = -> { true }\n        @subject = BW::UIAlertView.secure_text_input(@options, &@block)\n      end\n\n      it \"has the correct on_click handler\" do\n        @subject.on_click.should.equal(@options[:on_click])\n      end\n    end\n  end\n\n  #################################################################################################\n\n  describe \".login_and_password_input\" do\n    describe \"given no options\" do\n      before do\n        @subject = BW::UIAlertView.login_and_password_input\n      end\n\n      behaves_like \"an instance with no options\"\n\n      it \"has the correct style\" do\n        @subject.style.should.equal(UIAlertViewStyleLoginAndPasswordInput)\n      end\n\n      it \"has the correct buttons\" do\n        @subject.numberOfButtons.should.equal(2)\n        @subject.buttonTitleAtIndex(0).should.equal(\"Cancel\")\n        @subject.buttonTitleAtIndex(1).should.equal(\"Log in\")\n      end\n\n      it \"has the correct cancel button index\" do\n        @subject.cancel_button_index.should.equal(0)\n      end\n\n      it \"has no on_click handler\" do\n        @subject.on_click.should.be.nil\n      end\n\n      it \"has no plain text field\" do\n        @subject.plain_text_field.should.be.nil\n      end\n\n      it \"has no secure text field\" do\n        @subject.secure_text_field.should.be.nil\n      end\n\n      it \"has a login text field\" do\n        @subject.login_text_field.should.be.kind_of(UITextField)\n      end\n\n      it \"has a password text field\" do\n        @subject.password_text_field.should.be.kind_of(UITextField)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given no options with a block\" do\n      before do\n        @options = {}\n        @block   = -> { true }\n        @subject = BW::UIAlertView.login_and_password_input(@options, &@block)\n      end\n\n      behaves_like \"an instance with no options\"\n\n      it \"has the correct style\" do\n        @subject.style.should.equal(UIAlertViewStyleLoginAndPasswordInput)\n      end\n\n      it \"has the correct buttons\" do\n        @subject.numberOfButtons.should.equal(2)\n        @subject.buttonTitleAtIndex(0).should.equal(\"Cancel\")\n        @subject.buttonTitleAtIndex(1).should.equal(\"Log in\")\n      end\n\n      it \"has the correct cancel button index\" do\n        @subject.cancel_button_index.should.equal(0)\n      end\n\n      it \"has the correct on_click handler\" do\n        @subject.on_click.should.equal(@block)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given a complete set of options\" do\n      before do\n        @options = {\n          :title                      => \"title\",\n          :message                    => \"message\",\n          :style                      => :default,\n          :text                       => \"text\",\n          :placeholder                => \"placeholder\",\n          :buttons                    => \"button title\",\n          :cancel_button_index        => 0,\n          :will_present               => -> { true },\n          :did_present                => -> { true },\n          :on_system_cancel           => -> { true },\n          :on_click                   => -> { true },\n          :will_dismiss               => -> { true },\n          :did_dismiss                => -> { true },\n          :enable_first_other_button? => -> { true }\n        }\n        @subject = BW::UIAlertView.login_and_password_input(@options)\n      end\n\n      behaves_like \"an instance with a full set of options\"\n    end\n\n    ###############################################################################################\n\n    describe \"given options with multiple button titles\" do\n      before do\n        @options = { buttons: [\"first button\", \"second button\"] }\n        @subject = BW::UIAlertView.new(@options)\n      end\n\n      it \"has the correct buttons\" do\n        @subject.numberOfButtons.should.equal(2)\n        @subject.buttonTitleAtIndex(0).should.equal(@options[:buttons][0])\n        @subject.buttonTitleAtIndex(1).should.equal(@options[:buttons][1])\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given options with a cancel button index\" do\n      before do\n        @options = { cancel_button_index: -1 }\n        @subject = BW::UIAlertView.login_and_password_input(@options)\n      end\n\n      it \"has the correct cancel button index\" do\n        @subject.cancel_button_index.should.equal(-1)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given options with both an on_click handler and a block\" do\n      before do\n        @options = { on_click: -> { true }}\n        @block   = -> { true }\n        @subject = BW::UIAlertView.login_and_password_input(@options, &@block)\n      end\n\n      it \"has the correct on_click handler\" do\n        @subject.on_click.should.equal(@options[:on_click])\n      end\n    end\n  end\n\n  #################################################################################################\n\n  BW::UIAlertView.callbacks.each do |callback|\n    describe \".#{callback}\" do\n      before do\n        @subject = BW::UIAlertView.new\n      end\n\n      describe \"given no block\" do\n        before do\n          @return = @subject.send(callback)\n        end\n\n        it \"returns no handler\" do\n          @return.should.be.nil\n        end\n\n        it \"has no handler\" do\n          @subject.send(callback).should.be.nil\n        end\n      end\n\n      ###############################################################################################\n\n      describe \"given a block\" do\n        before do\n          @block  = -> { true }\n          @return = @subject.send(callback, &@block)\n        end\n\n        it \"returns the subject\" do\n          @return.should.equal(@subject)\n        end\n\n        it \"has the correct handler\" do\n          @subject.send(callback).should.equal(@block)\n        end\n      end\n    end\n  end\n\n  #################################################################################################\n\n  describe \"#style=\" do\n    before do\n      @subject = BW::UIAlertView.new\n    end\n\n    describe \"given no style\" do\n      before do\n        @subject.style = nil\n      end\n\n      it \"has the correct style\" do\n        @subject.style.should.equal(UIAlertViewStyleDefault)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given a style\" do\n      before do\n        @subject.style = :plain_text_input\n      end\n\n      it \"has the correct style\" do\n        @subject.style.should.equal(UIAlertViewStylePlainTextInput)\n      end\n    end\n  end\n\n  #################################################################################################\n\n  describe \"#cancel_button_index=\" do\n    before do\n      @subject = BW::UIAlertView.new\n    end\n\n    describe \"given no cancel button index\" do\n      before do\n        @subject.cancel_button_index = nil\n      end\n\n      it \"has the correct cancel button index\" do\n        @subject.cancel_button_index.should.equal(-1)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given a cancel button index\" do\n      before do\n        @subject.cancel_button_index = 0\n      end\n\n      it \"has the correct cancel button index\" do\n        @subject.cancel_button_index.should.equal(0)\n      end\n    end\n  end\n\n  #################################################################################################\n\n  describe \"-willPresentAlertView:\" do\n    before do\n      @subject = BW::UIAlertView.new\n    end\n\n    describe \"given no will_present handler\" do\n      it \"returns noting\" do\n        @subject.willPresentAlertView(@subject).should.be.nil\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given a will_present handler\" do\n      before do\n        @subject.will_present do |alert|\n          alert.should.equal(@subject)\n          alert.clicked_button.should.be.nil\n          :will_present\n        end\n      end\n\n      it \"returns correctly\" do\n        @subject.willPresentAlertView(@subject).should.equal(:will_present)\n      end\n    end\n  end\n\n  #################################################################################################\n\n  describe \"-didPresentAlertView:\" do\n    before do\n      @subject = BW::UIAlertView.new\n    end\n\n    describe \"given no did_present handler\" do\n      it \"returns noting\" do\n        @subject.didPresentAlertView(@subject).should.be.nil\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given a did_present handler\" do\n      before do\n        @subject.did_present do |alert|\n          alert.should.equal(@subject)\n          alert.clicked_button.should.be.nil\n          :did_present\n        end\n      end\n\n      it \"returns correctly\" do\n        @subject.didPresentAlertView(@subject).should.equal(:did_present)\n      end\n    end\n  end\n\n  #################################################################################################\n\n  describe \"-alertViewCancel:\" do\n    before do\n      @subject = BW::UIAlertView.new\n    end\n\n    describe \"given no on_system_cancel handler\" do\n      it \"returns noting\" do\n        @subject.alertViewCancel(@subject).should.be.nil\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given an on_system_cancel handler\" do\n      before do\n        @subject.on_system_cancel do |alert|\n          alert.should.equal(@subject)\n          alert.clicked_button.should.be.nil\n          :on_system_cancel\n        end\n      end\n\n      it \"returns correctly\" do\n        @subject.alertViewCancel(@subject).should.equal(:on_system_cancel)\n      end\n    end\n  end\n\n  #################################################################################################\n\n  describe \"-alertViewShouldEnableFirstOtherButton:\" do\n    before do\n      @subject = BW::UIAlertView.new\n    end\n\n    describe \"given no enable_first_other_button? handler\" do\n      it \"returns noting\" do\n        @subject.alertViewShouldEnableFirstOtherButton(@subject).should.be.nil\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given an enable_first_other_button? handler\" do\n      before do\n        @subject.enable_first_other_button? do |alert|\n          alert.should.equal(@subject)\n          alert.clicked_button.should.be.nil\n          true\n        end\n      end\n\n      it \"returns correctly\" do\n        @subject.alertViewShouldEnableFirstOtherButton(@subject).should.equal(true)\n      end\n    end\n  end\n\n  #################################################################################################\n\n  describe \"-alertView:clickedButtonAtIndex:\" do\n    before do\n      @index   = 0\n      @button  = \"button title\"\n      @subject = BW::UIAlertView.new(buttons: @button)\n    end\n\n    describe \"given no on_click handler\" do\n      it \"returns noting\" do\n        @subject.alertView(@subject, clickedButtonAtIndex:@index).should.be.nil\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given an on_click handler\" do\n      before do\n        @subject.on_click do |alert|\n          alert.should.equal(@subject)\n          alert.clicked_button.should.not.be.nil\n          alert.clicked_button.index.should.equal(@index)\n          alert.clicked_button.title.should.equal(@button)\n          alert.clicked_button.should.not.be.cancel\n          :on_click\n        end\n      end\n\n      it \"returns correctly\" do\n        @subject.alertView(@subject, clickedButtonAtIndex:@index).should.equal(:on_click)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given an on_click handler with a cancel button index\" do\n      before do\n        @subject.cancel_button_index = @index\n\n        @subject.on_click do |alert|\n          alert.should.equal(@subject)\n          alert.clicked_button.should.not.be.nil\n          alert.clicked_button.index.should.equal(@index)\n          alert.clicked_button.title.should.equal(@button)\n          alert.clicked_button.should.be.cancel\n          :on_click\n        end\n      end\n\n      it \"returns correctly\" do\n        @subject.alertView(@subject, clickedButtonAtIndex:@index).should.equal(:on_click)\n      end\n    end\n  end\n\n  #################################################################################################\n\n  describe \"-alertView:willDismissWithButtonIndex:\" do\n    before do\n      @index   = 0\n      @button  = \"button title\"\n      @subject = BW::UIAlertView.new(buttons: @button)\n    end\n\n    describe \"given no will_dismiss handler\" do\n      it \"returns noting\" do\n        @subject.alertView(@subject, willDismissWithButtonIndex:@index).should.be.nil\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given a will_dismiss handler\" do\n      before do\n        @subject.will_dismiss do |alert|\n          alert.should.equal(@subject)\n          alert.clicked_button.should.not.be.nil\n          alert.clicked_button.index.should.equal(@index)\n          alert.clicked_button.title.should.equal(@button)\n          alert.clicked_button.should.not.be.cancel\n          :will_dismiss\n        end\n      end\n\n      it \"returns correctly\" do\n        @subject.alertView(@subject, willDismissWithButtonIndex:@index).should.equal(:will_dismiss)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given a will_dismiss handler with a cancel button index\" do\n      before do\n        @subject.cancel_button_index = @index\n\n        @subject.will_dismiss do |alert|\n          alert.should.equal(@subject)\n          alert.clicked_button.should.not.be.nil\n          alert.clicked_button.index.should.equal(@index)\n          alert.clicked_button.title.should.equal(@button)\n          alert.clicked_button.should.be.cancel\n          :will_dismiss\n        end\n      end\n\n      it \"returns correctly\" do\n        @subject.alertView(@subject, willDismissWithButtonIndex:@index).should.equal(:will_dismiss)\n      end\n    end\n  end\n\n  #################################################################################################\n\n  describe \"-alertView:didDismissWithButtonIndex:\" do\n    before do\n      @index   = 0\n      @button  = \"button title\"\n      @subject = BW::UIAlertView.new(buttons: @button)\n    end\n\n    describe \"given no did_dismiss handler\" do\n      it \"returns noting\" do\n        @subject.alertView(@subject, didDismissWithButtonIndex:@index).should.be.nil\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given a did_dismiss handler\" do\n      before do\n        @subject.did_dismiss do |alert|\n          alert.should.equal(@subject)\n          alert.clicked_button.should.not.be.nil\n          alert.clicked_button.index.should.equal(@index)\n          alert.clicked_button.title.should.equal(@button)\n          alert.clicked_button.should.not.be.cancel\n          :did_dismiss\n        end\n      end\n\n      it \"returns correctly\" do\n        @subject.alertView(@subject, didDismissWithButtonIndex:@index).should.equal(:did_dismiss)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given a did_dismiss handler with a cancel button index\" do\n      before do\n        @subject.cancel_button_index = @index\n\n        @subject.did_dismiss do |alert|\n          alert.should.equal(@subject)\n          alert.clicked_button.should.not.be.nil\n          alert.clicked_button.index.should.equal(@index)\n          alert.clicked_button.title.should.equal(@button)\n          alert.clicked_button.should.be.cancel\n          :did_dismiss\n        end\n      end\n\n      it \"returns correctly\" do\n        @subject.alertView(@subject, didDismissWithButtonIndex:@index).should.equal(:did_dismiss)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/motion/ui/ui_bar_button_item_spec.rb",
    "content": "describe BW::UIBarButtonItem do\n  class NavigationItemContainingBarButtonItem < UINavigationItem\n    def init_with_styled\n      initWithTitle('dummy')\n      subject = BW::UIBarButtonItem.styled(:plain, 'dummy') do\n        #Can be empty, but we need a block/proc here to potentially create a retain cycle\n      end\n      self.leftBarButtonItem = subject\n      self\n    end\n\n    def tag=(value)\n      @tag = value\n    end\n\n    def init_with_system\n      initWithTitle('dummy')\n      subject = BW::UIBarButtonItem.system(:save) do\n        #Can be empty, but we need a block/proc here to potentially create a retain cycle\n      end\n      self.leftBarButtonItem = subject\n      self\n    end\n\n    def dealloc\n      App.notification_center.post('NavigationItemContainingBarButtonItem dealloc', nil, {'tag'=>@tag})\n      super\n    end\n  end\n\n  describe \".styled\" do\n    describe \"given an unknown style\" do\n      it \"raises an exception\" do\n        exception = should.raise(NameError) { BW::UIBarButtonItem.styled(:unknown, \"object\") }\n        exception.message.should.equal(\"uninitialized constant Kernel::UIBarButtonItemStyleUnknown\")\n      end\n    end\n\n    describe \"given an invalid object\" do\n      it \"raises an exception\" do\n        exception = should.raise(ArgumentError) { BW::UIBarButtonItem.styled(:plain, 2) }\n        exception.message.should.equal(\"invalid object - 2\")\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given a String object\" do\n      before do\n        @object  = \"Friends\"\n        @target  = -> { true }\n        @subject = BW::UIBarButtonItem.styled(:plain, @object, &@target)\n      end\n\n      it \"has the correct class\" do\n        @subject.class.should.equal(BW::UIBarButtonItem)\n      end\n\n      it \"has the correct superclass\" do\n        @subject.superclass.should.equal(::UIBarButtonItem)\n      end\n\n      it \"has the correct style\" do\n        @subject.style.should.equal(UIBarButtonItemStylePlain)\n      end\n\n      it \"has the correct title\" do\n        @subject.title.should.equal(@object)\n      end\n\n      it \"has the correct target\" do\n        @subject.target.should.equal(@target)\n      end\n\n      it \"has the correct action\" do\n        @subject.action.should.equal(:call)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given a String object but no block\" do\n      before do\n        @object  = \"Friends\"\n        @subject = BW::UIBarButtonItem.styled(:plain, @object)\n      end\n\n      it \"has the correct class\" do\n        @subject.class.should.equal(BW::UIBarButtonItem)\n      end\n\n      it \"has the correct superclass\" do\n        @subject.superclass.should.equal(::UIBarButtonItem)\n      end\n\n      it \"has the correct style\" do\n        @subject.style.should.equal(UIBarButtonItemStylePlain)\n      end\n\n      it \"has the correct title\" do\n        @subject.title.should.equal(@object)\n      end\n\n      it \"has the correct target\" do\n        @subject.target.should.equal(nil)\n      end\n\n      it \"has the correct action\" do\n        @subject.target.should.equal(nil)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given an UIImage object\" do\n      before do\n        @object  = UIImage.alloc.init\n        @target  = -> { true }\n        @subject = BW::UIBarButtonItem.styled(:done, @object, &@target)\n      end\n\n      it \"has the correct class\" do\n        @subject.class.should.equal(BW::UIBarButtonItem)\n      end\n\n      it \"has the correct superclass\" do\n        @subject.superclass.should.equal(::UIBarButtonItem)\n      end\n\n      it \"has the correct style\" do\n        @subject.style.should.equal(UIBarButtonItemStyleDone)\n      end\n\n      it \"has the correct image\" do\n        @subject.image.should.equal(@object)\n      end\n\n      it \"has the correct target\" do\n        @subject.target.should.equal(@target)\n      end\n\n      it \"has the correct action\" do\n        @subject.action.should.equal(:call)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given two UIImage objects\" do\n      before do\n        @object1 = UIImage.alloc.init\n        @object2 = UIImage.alloc.init\n        @target  = -> { true }\n        @subject = BW::UIBarButtonItem.styled(:done, @object1, @object2, &@target)\n      end\n\n      it \"has the correct class\" do\n        @subject.class.should.equal(BW::UIBarButtonItem)\n      end\n\n      it \"has the correct superclass\" do\n        @subject.superclass.should.equal(::UIBarButtonItem)\n      end\n\n      it \"has the correct style\" do\n        @subject.style.should.equal(UIBarButtonItemStyleDone)\n      end\n\n      it \"has the correct image\" do\n        @subject.image.should.equal(@object1)\n      end\n\n      it \"has the correct iPhone landscape image\" do\n        @subject.landscapeImagePhone.should.equal(@object2)\n      end\n\n      it \"has the correct target\" do\n        @subject.target.should.equal(@target)\n      end\n\n      it \"has the correct action\" do\n        @subject.action.should.equal(:call)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"with BubbleWrap.use_weak_callbacks=true\" do\n      it \"removes cyclic references\" do\n        observer = App.notification_center.observe('NavigationItemContainingBarButtonItem dealloc') do |obj|\n          if obj.userInfo['tag'] == 1\n            @weak_deallocated = true\n          elsif obj.userInfo['tag'] == 2\n            @strong_deallocated = true\n          end\n        end\n        autorelease_pool {\n          BubbleWrap.use_weak_callbacks = true\n          v1 = NavigationItemContainingBarButtonItem.alloc.init_with_styled\n          v1.tag = 1\n          BubbleWrap.use_weak_callbacks = false\n          v2 = NavigationItemContainingBarButtonItem.alloc.init_with_styled\n          v2.tag = 2\n        }\n        App.notification_center.unobserve(observer)\n        @weak_deallocated.should.equal true\n        @strong_deallocated.should.equal nil\n      end\n    end\n  end\n\n  #################################################################################################\n\n  describe \".system\" do\n    describe \"given an unknown system item\" do\n      it \"raises an exception\" do\n        exception = should.raise(NameError) { BW::UIBarButtonItem.system(:unknown) }\n        exception.message.should.equal(\"uninitialized constant Kernel::UIBarButtonSystemItemUnknown\")\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given a system item\" do\n      before do\n        @target  = -> { true }\n        @subject = BW::UIBarButtonItem.system(:save, &@target)\n      end\n\n      it \"has the correct class\" do\n        @subject.class.should.equal(BW::UIBarButtonItem)\n      end\n\n      it \"has the correct superclass\" do\n        @subject.superclass.should.equal(::UIBarButtonItem)\n      end\n\n      it \"has the correct system item\" do\n        # TIP: systemItem is an undocumented property\n        @subject.systemItem.should.equal(UIBarButtonSystemItemSave)\n      end\n\n      it \"has the correct target\" do\n        @subject.target.should.equal(@target)\n      end\n\n      it \"has the correct action\" do\n        @subject.action.should.equal(:call)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given a system item but no block\" do\n      before do\n        @subject = BW::UIBarButtonItem.system(:save)\n      end\n\n      it \"has the correct class\" do\n        @subject.class.should.equal(BW::UIBarButtonItem)\n      end\n\n      it \"has the correct superclass\" do\n        @subject.superclass.should.equal(::UIBarButtonItem)\n      end\n\n      it \"has the correct system item\" do\n        @subject.systemItem.should.equal(UIBarButtonSystemItemSave)\n      end\n\n      it \"has the correct target\" do\n        @subject.target.should.equal(nil)\n      end\n\n      it \"has the correct action\" do\n        @subject.action.should.equal(nil)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"with BubbleWrap.use_weak_callbacks=true\" do\n      it \"removes cyclic references\" do\n        observer = App.notification_center.observe('NavigationItemContainingBarButtonItem dealloc') do |obj|\n          if obj.userInfo['tag'] == 1\n            @weak_deallocated = true\n          elsif obj.userInfo['tag'] == 2\n            @strong_deallocated = true\n          end\n        end\n        autorelease_pool {\n          BubbleWrap.use_weak_callbacks = true\n          v1 = NavigationItemContainingBarButtonItem.alloc.init_with_system\n          v1.tag = 1\n          BubbleWrap.use_weak_callbacks = false\n          v2 = NavigationItemContainingBarButtonItem.alloc.init_with_system\n          v2.tag = 2\n        }\n        App.notification_center.unobserve(observer)\n        @weak_deallocated.should.equal true\n        @strong_deallocated.should.equal nil\n      end\n    end\n  end\n\n  #################################################################################################\n\n  describe \".custom\" do\n    describe \"given a custom view\" do\n      before do\n        @view    = UIView.alloc.init\n        @target  = -> { true }\n        @subject = BW::UIBarButtonItem.custom(@view, &@target)\n      end\n\n      it \"has the correct class\" do\n        @subject.class.should.equal(BW::UIBarButtonItem)\n      end\n\n      it \"has the correct superclass\" do\n        @subject.superclass.should.equal(::UIBarButtonItem)\n      end\n\n      it \"has a custom view\" do\n        @subject.customView.should.equal(@view)\n      end\n\n      it \"adds one, single tap gesture recognizer to the custom view\" do\n        @view.gestureRecognizers.size.should.equal(1)\n        @view.gestureRecognizers.first.class.should.equal(UITapGestureRecognizer)\n        @view.gestureRecognizers.first.numberOfTapsRequired.should.equal(1)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given a custom view but no block\" do\n      before do\n        @view    = UIView.alloc.init\n        @subject = BW::UIBarButtonItem.custom(@view)\n      end\n\n      it \"has the correct class\" do\n        @subject.class.should.equal(BW::UIBarButtonItem)\n      end\n\n      it \"has the correct superclass\" do\n        @subject.superclass.should.equal(::UIBarButtonItem)\n      end\n\n      it \"has a custom view\" do\n        @subject.customView.should.equal(@view)\n      end\n\n      it \"adds no gesture recognizers to the custom view\" do\n        @view.gestureRecognizers.should.equal(nil)\n      end\n    end\n  end\n\n  #################################################################################################\n\n  describe \".new\" do\n    describe \"not given options\" do\n      it \"raises an exception\" do\n        exception = should.raise(ArgumentError) { BW::UIBarButtonItem.new }\n        exception.message.should.equal(\"invalid options - {}\")\n      end\n    end\n\n    describe \"given unknown options\" do\n      it \"raises an exception\" do\n        exception = should.raise(ArgumentError) { BW::UIBarButtonItem.new(:unknown => true) }\n        exception.message.should.equal(\"invalid options - {:unknown=>true}\")\n      end\n    end\n\n    describe \"given incompatible options for a styled item\" do\n      before do\n        @options = {\n          :styled => :bordered,\n          :title  => \"Friends\",\n          :image  => UIImage.alloc.init\n        }\n      end\n\n      it \"raises an exception\" do\n        exception = should.raise(ArgumentError) { BW::UIBarButtonItem.new(@options) }\n        exception.message.should.equal(\"invalid object - #{@options.values_at(:title, :image)}\")\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given options for a styled item with a title\" do\n      before do\n        @options = { :styled => :plain, :title => \"Friends\" }\n        @target  = -> { true }\n        @subject = BW::UIBarButtonItem.new(@options, &@target)\n      end\n\n      it \"has the correct class\" do\n        @subject.class.should.equal(BW::UIBarButtonItem)\n      end\n\n      it \"has the correct superclass\" do\n        @subject.superclass.should.equal(::UIBarButtonItem)\n      end\n\n      it \"has the correct style\" do\n        @subject.style.should.equal(UIBarButtonItemStylePlain)\n      end\n\n      it \"has the correct title\" do\n        @subject.title.should.equal(@options[:title])\n      end\n\n      it \"has the correct target\" do\n        @subject.target.should.equal(@target)\n      end\n\n      it \"has the correct action\" do\n        @subject.action.should.equal(:call)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given options for a styled item with an image\" do\n      before do\n        @options = { :styled => :done, :image => UIImage.alloc.init }\n        @target  = -> { true }\n        @subject = BW::UIBarButtonItem.new(@options, &@target)\n      end\n\n      it \"has the correct class\" do\n        @subject.class.should.equal(BW::UIBarButtonItem)\n      end\n\n      it \"has the correct superclass\" do\n        @subject.superclass.should.equal(::UIBarButtonItem)\n      end\n\n      it \"has the correct style\" do\n        @subject.style.should.equal(UIBarButtonItemStyleDone)\n      end\n\n      it \"has the correct image\" do\n        @subject.image.should.equal(@options[:image])\n      end\n\n      it \"has the correct target\" do\n        @subject.target.should.equal(@target)\n      end\n\n      it \"has the correct action\" do\n        @subject.action.should.equal(:call)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given options for a styled item with two images\" do\n      before do\n        @options = {\n          :styled    => :done,\n          :image     => UIImage.alloc.init,\n          :landscape => UIImage.alloc.init\n        }\n        @target  = -> { true }\n        @subject = BW::UIBarButtonItem.new(@options, &@target)\n      end\n\n      it \"has the correct class\" do\n        @subject.class.should.equal(BW::UIBarButtonItem)\n      end\n\n      it \"has the correct superclass\" do\n        @subject.superclass.should.equal(::UIBarButtonItem)\n      end\n\n      it \"has the correct style\" do\n        @subject.style.should.equal(UIBarButtonItemStyleDone)\n      end\n\n      it \"has the correct image\" do\n        @subject.image.should.equal(@options[:image])\n      end\n\n      it \"has the correct iPhone landscape image\" do\n        @subject.landscapeImagePhone.should.equal(@options[:landscape])\n      end\n\n      it \"has the correct target\" do\n        @subject.target.should.equal(@target)\n      end\n\n      it \"has the correct action\" do\n        @subject.action.should.equal(:call)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given options for a system item\" do\n      before do\n        @options = { :system => :save }\n        @target  = -> { true }\n        @subject = BW::UIBarButtonItem.new(@options, &@target)\n      end\n\n      it \"has the correct class\" do\n        @subject.class.should.equal(BW::UIBarButtonItem)\n      end\n\n      it \"has the correct superclass\" do\n        @subject.superclass.should.equal(::UIBarButtonItem)\n      end\n\n      it \"has the correct system item\" do\n        @subject.systemItem.should.equal(UIBarButtonSystemItemSave)\n      end\n\n      it \"has the correct target\" do\n        @subject.target.should.equal(@target)\n      end\n\n      it \"has the correct action\" do\n        @subject.action.should.equal(:call)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given options for a custom view\" do\n      before do\n        @options = { :custom => UIView.alloc.init }\n        @target  = -> { true }\n        @subject = BW::UIBarButtonItem.new(@options, &@target)\n      end\n\n      it \"has the correct class\" do\n        @subject.class.should.equal(BW::UIBarButtonItem)\n      end\n\n      it \"has the correct superclass\" do\n        @subject.superclass.should.equal(::UIBarButtonItem)\n      end\n\n      it \"has a custom view\" do\n        @subject.customView.should.equal(@options[:custom])\n      end\n\n      it \"adds one, single tap gesture recognizer to the custom view\" do\n        @options[:custom].gestureRecognizers.size.should.equal(1)\n        @options[:custom].gestureRecognizers.first.class.should.equal(UITapGestureRecognizer)\n        @options[:custom].gestureRecognizers.first.numberOfTapsRequired.should.equal(1)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given options for a view\" do\n      before do\n        @options = { :view => UIView.alloc.init }\n        @target  = -> { true }\n        @subject = BW::UIBarButtonItem.new(@options, &@target)\n      end\n\n      it \"has the correct class\" do\n        @subject.class.should.equal(BW::UIBarButtonItem)\n      end\n\n      it \"has the correct superclass\" do\n        @subject.superclass.should.equal(::UIBarButtonItem)\n      end\n\n      it \"has a custom view\" do\n        @subject.customView.should.equal(@options[:view])\n      end\n\n      it \"adds one, single tap gesture recognizer to the view\" do\n        @options[:view].gestureRecognizers.size.should.equal(1)\n        @options[:view].gestureRecognizers.first.class.should.equal(UITapGestureRecognizer)\n        @options[:view].gestureRecognizers.first.numberOfTapsRequired.should.equal(1)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/motion/ui/ui_control_wrapper_spec.rb",
    "content": "describe BW::UIControlWrapper do\n  describe \"#when\" do\n    before do\n      @subject = UIControl.alloc.init\n      @touched = []\n\n      @subject.when(UIControlEventTouchUpInside) do\n        @touched << 'for the very first time'\n      end\n    end\n\n    it \"supports the 'when' event handler\" do\n      @subject.sendActionsForControlEvents(UIControlEventTouchUpInside)\n      @touched.should.equal ['for the very first time']\n    end\n\n    it \"replaces the target for a given control event by default\" do\n      @subject.when(UIControlEventTouchUpInside) do\n        @touched << 'touched'\n      end\n\n      @subject.sendActionsForControlEvents(UIControlEventTouchUpInside)\n      @touched.should.equal ['touched']\n    end\n\n    it \"allows bitwise operators\" do\n      @subject.when(UIControlEventTouchUpInside | UIControlEventTouchUpOutside) do\n        @touched << 'touched'\n      end\n\n      @subject.sendActionsForControlEvents(UIControlEventTouchUpInside)\n      @touched.should.equal ['touched']\n\n      @touched = []\n      @touched.should.equal []\n\n      @subject.sendActionsForControlEvents(UIControlEventTouchUpOutside)\n      @touched.should.equal ['touched']\n    end\n\n    it \"BubbleWrap.use_weak_callbacks=true removes cyclic references\" do\n      class ControlSuperView < UIView\n        def initWithFrame(frame)\n          super\n          subject = UIControl.alloc.init\n          subject.when(UIControlEventTouchUpInside) do\n            #Can be empty, but we need a block/proc here to potentially create a retain cycle\n          end\n          addSubview(subject)\n          self\n        end\n\n        def dealloc\n          App.notification_center.post('ControlSuperView dealloc', nil, {'tag'=>tag})\n          super\n        end\n      end\n\n      observer = App.notification_center.observe('ControlSuperView dealloc') do |obj|\n        if obj.userInfo['tag'] == 1\n          @weak_deallocated = true\n        elsif obj.userInfo['tag'] == 2\n          @strong_deallocated = true\n        end\n      end\n      autorelease_pool {\n        BubbleWrap.use_weak_callbacks = true\n        v1 = ControlSuperView.new\n        v1.tag = 1\n        BubbleWrap.use_weak_callbacks = false\n        v2 = ControlSuperView.new\n        v2.tag = 2\n      }\n      App.notification_center.unobserve(observer)\n      @weak_deallocated.should.equal true\n      @strong_deallocated.should.equal nil\n    end\n\n    it \"allows multiple targets for a given control event if specified\" do\n      @subject.when(UIControlEventTouchUpInside, append: true) do\n        @touched << 'touched'\n      end\n\n      @subject.sendActionsForControlEvents(UIControlEventTouchUpInside)\n      @touched.should.equal ['for the very first time', 'touched']\n    end\n\n    it \"allows symbols for actions\" do\n      @subject.when(:touch_up_inside) do\n        @touched << 'touched'\n      end\n\n      @subject.sendActionsForControlEvents(UIControlEventTouchUpInside)\n      @touched.should.equal ['touched']\n    end\n  end\nend\n"
  },
  {
    "path": "spec/motion/ui/ui_view_controller_wrapper_spec.rb",
    "content": "describe BW::UIViewControllerWrapper do\n  describe \"#content_frame\" do\n    describe \"given a UIViewController\" do\n      before do\n        @subject = UIViewController.alloc.init\n      end\n\n      it \"has the correct content frame\" do\n        height   = App.frame.size.height\n        expected = CGRectMake(0, 0, App.frame.size.width, height)\n\n        @subject.content_frame.should.equal(expected)\n      end\n    end\n\n    ###############################################################################################\n\n    describe \"given a UIViewController inside a UINavigationController\" do\n      before do\n        @subject = UIViewController.alloc.init\n\n        UINavigationController.alloc.initWithRootViewController(@subject)\n      end\n\n      it \"has the correct content frame\" do\n        height   = App.frame.size.height\n        height   -= @subject.navigationController.navigationBar.frame.size.height\n        expected = CGRectMake(0, 0, App.frame.size.width, height)\n\n        @subject.content_frame.should.equal(expected)\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/motion/ui/ui_view_wrapper_spec.rb",
    "content": "describe BW::UIViewWrapper do\n  describe \"gestures\" do\n    before do\n      @view = App.delegate.window.rootViewController.view\n      @orig = @view.isUserInteractionEnabled\n      @view.setUserInteractionEnabled false\n    end\n\n    after do\n      @view.setUserInteractionEnabled @orig\n    end\n\n    testMethod = proc do |method|\n      it \"returns a gesture recognizer\" do\n        recognizer = @view.send(method, false, &:nil)\n        recognizer.is_a?(UIGestureRecognizer).should == true\n      end\n\n      it 'enables interaction when called' do\n        @view.send(method, &:nil)\n        @view.isUserInteractionEnabled.should == true\n      end\n\n      it \"doesn't enable interaction if asked not to\" do\n        @view.send(method, false, &:nil)\n        @view.isUserInteractionEnabled.should == false\n      end\n\n      # it 'responds to interaction'\n    end\n\n    describe '#when_tapped' do\n      testMethod.call :when_tapped\n      testMethod.call :whenTapped\n    end\n\n    describe '#when_pinched' do\n      testMethod.call :when_pinched\n      testMethod.call :whenPinched\n    end\n\n    describe '#when_rotated' do\n      testMethod.call :when_rotated\n      testMethod.call :whenRotated\n    end\n\n    describe '#when_swiped' do\n      testMethod.call :when_swiped\n      testMethod.call :whenSwiped\n    end\n\n    describe '#when_panned' do\n      testMethod.call :when_panned\n      testMethod.call :whenPanned\n    end\n\n    describe '#when_screen_edge_panned' do\n      testMethod.call :when_screen_edge_panned\n    end\n\n    describe '#when_pressed' do\n      testMethod.call :when_pressed\n      testMethod.call :whenPressed\n    end\n\n    it \"BubbleWrap.use_weak_callbacks=true removes cyclic references\" do\n      class ViewSuperView < UIView\n        def initWithFrame(frame)\n          super\n          subject = UIView.alloc.init\n          subject.when_tapped do\n            #Can be empty, but we need a block/proc here to potentially create a retain cycle\n          end\n          addSubview(subject)\n          self\n        end\n\n        def dealloc\n          App.notification_center.post('ViewSuperView dealloc', nil, {'tag'=>tag})\n          super\n        end\n      end\n\n      observer = App.notification_center.observe('ViewSuperView dealloc') do |obj|\n        if obj.userInfo['tag'] == 1\n          @weak_deallocated = true\n        elsif obj.userInfo['tag'] == 2\n          @strong_deallocated = true\n        end\n      end\n      autorelease_pool {\n        BubbleWrap.use_weak_callbacks = true\n        v1 = ViewSuperView.new\n        v1.tag = 1\n        BubbleWrap.use_weak_callbacks = false\n        v2 = ViewSuperView.new\n        v2.tag = 2\n      }\n      App.notification_center.unobserve(observer)\n      @weak_deallocated.should.equal true\n      @strong_deallocated.should.equal nil\n    end\n  end\nend\n"
  },
  {
    "path": "spec/motion/util/constants_spec.rb",
    "content": "describe BubbleWrap::Constants do\n  describe \".get\" do\n    BubbleWrap::Constants.register NSStringEncodingConversionAllowLossy, NSStringEncodingConversionExternalRepresentation\n\n\n    it \"should return integer passed\" do\n      BW::Constants.get(\"anything\", 5).should == 5\n    end\n\n    it \"should return integer for decimal passed\" do\n      BW::Constants.get(\"anything\", 5.0).should == 5\n    end\n\n    it \"should return the correct integer for a string\" do\n      BW::Constants.get(\"NSStringEncodingConversion\", \"allow_lossy\").should == NSStringEncodingConversionAllowLossy\n    end\n\n    it \"should return the correct integer for a symbol\" do\n      BW::Constants.get(\"NSStringEncodingConversion\", :allow_lossy).should == NSStringEncodingConversionAllowLossy\n    end\n\n    it \"should bitmask array values\" do\n      BW::Constants.get(\"NSStringEncodingConversion\", :allow_lossy, :external_representation).should == (NSStringEncodingConversionAllowLossy | NSStringEncodingConversionExternalRepresentation)\n    end\n\n    if App.ios?\n      it \"should return an array of string constant values\" do\n        BW::Constants.get(\"UIActivityType\", [:air_drop, :print]).should == [\"com.apple.UIKit.activity.AirDrop\", \"com.apple.UIKit.activity.Print\"]\n      end\n    end\n  end\nend\n"
  },
  {
    "path": "spec/motion/util/deprecated_spec.rb",
    "content": "module ModuleExample\n  include BubbleWrap::Deprecated\n\n  module_function\n\n  def a_method\n    @called = true\n  end\n\n  deprecated :a_method, \"100.0.0\"\nend\n\nclass ClassExample\n  include BubbleWrap::Deprecated\n\n  def a_method\n    @called = true\n  end\n  deprecated :a_method, \"100.0.0\"\nend\n\nmodule BubbleWrap\n  def self.set_version(version)\n    define_singleton_method(\"version\") do\n      version\n    end\n  end\nend\n\ndescribe BubbleWrap::Deprecated do\n  describe \".deprecated\" do\n    describe \"on a module method\" do\n      describe \"with valid version\" do\n        it \"should not raise an exception\" do\n          should.not.raise(BubbleWrap::Deprecated::DeprecatedError) {\n            ModuleExample.a_method\n          }\n        end\n      end\n\n      describe \"with invalid version\" do\n        before do\n          @old_version = BubbleWrap.version\n          BubbleWrap.set_version(\"100.0.0\")\n        end\n        after do\n          BubbleWrap.set_version(@old_version)\n        end\n\n        it \"should raise an exception\" do\n          should.raise(BubbleWrap::Deprecated::DeprecatedError) {\n            ModuleExample.a_method\n          }\n        end\n      end\n    end\n\n    describe \"on an instance method\" do\n      describe \"with valid version\" do\n        it \"should not raise an exception\" do\n          should.not.raise(BubbleWrap::Deprecated::DeprecatedError) {\n            ClassExample.new.a_method\n          }\n        end\n      end\n\n      describe \"with invalid version\" do\n        before do\n          @old_version = BubbleWrap.version\n          BubbleWrap.set_version(\"100.0.0\")\n        end\n        after do\n          BubbleWrap.set_version(@old_version)\n        end\n\n        it \"should raise an exception\" do\n          should.raise(BubbleWrap::Deprecated::DeprecatedError) {\n            ClassExample.new.a_method\n          }\n        end\n      end\n    end\n  end\nend\n"
  }
]