bubble-wrap 0.4.0 → 1.0.0.pre

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
data/CHANGELOG.md CHANGED
@@ -1,3 +1,12 @@
1
+ ## 1.0.0
2
+
3
+ * Improved the integration with RubyMotion build system.
4
+ * Improved test suite.
5
+ * Added better documentation, including on how to work on the internals.
6
+ * Added a KVO DSL to observe objects.
7
+ * Renamed `Device.screen.widthForOrientation` to Device.screen.width_for_orientation` and `Device.screen.heightForOrientation` to `Device.screen.height_for_orientation`.
8
+ * The `HTTP` wrapper now encodes arrays in params in a way that's compatible with Rails.
9
+
1
10
  ## 0.4.0
2
11
 
3
12
  * Refactored the code and test suite to be more modular and to handle
data/GEM.md ADDED
@@ -0,0 +1,104 @@
1
+ # Creating a RubyMotion gem with BubbleWrap
2
+
3
+ Let's say we want to develop a simple library gem that lists the
4
+ people in a user's addressbook.
5
+
6
+ Let's start by initializing an empty gem directory:
7
+
8
+ ```
9
+ $ gem install bundler
10
+ $ bundle gem bw-addressbook
11
+ ```
12
+
13
+ Add BubbleWrap and Rake to your gem's dependencies in `bw-addressbook.gemspec`:
14
+
15
+ ```ruby
16
+ Gem::Specification.new do |gem|
17
+ gem.add_dependency 'bubble-wrap'
18
+ gem.add_development_dependency 'rake'
19
+ end
20
+ ```
21
+
22
+ Then run `bundler`:
23
+ ```
24
+ $ bundle
25
+ Fetching gem metadata from https://rubygems.org/..
26
+ Using rake (0.9.2.2)
27
+ Installing bubble-wrap (0.4.0)
28
+ Using bw-addressbook (0.0.1) from source at /Users/jnh/Dev/tmp/bw-addressbook
29
+ Using bundler (1.1.4)
30
+ Your bundle is complete! Use `bundle show [gemname]` to see where a bundled gem is installed.
31
+ ```
32
+
33
+ Modify your `lib/bw-addressbook.rb` to include:
34
+
35
+ ```ruby
36
+ require 'bw-addressbook/version'
37
+ BW.require 'motion/address_book.rb'
38
+ ```
39
+
40
+ Edit your project's `Rakefile` to include:
41
+
42
+ ```ruby
43
+ #!/usr/bin/env rake
44
+ $:.unshift("/Library/RubyMotion/lib")
45
+ require 'motion/project'
46
+ require "bundler/gem_tasks"
47
+ Bundler.setup
48
+ Bundler.require
49
+ require 'bubble-wrap/test'
50
+ ```
51
+
52
+ At this point we should have a working RubyMotion environment able to
53
+ compile our code as we write it.
54
+
55
+ Let's start by creating a spec for our address book gem in `spec/address_book_spec.rb`:
56
+
57
+ ```ruby
58
+ describe AddressBook do
59
+ describe '.list' do
60
+ it 'returns an Enumerable' do
61
+ AddressBook.list.is_a?(Enumerable).should == true
62
+ end
63
+ end
64
+ end
65
+ ```
66
+
67
+ Now if you run `rake spec` you can watch the spec fail:
68
+
69
+ ```
70
+ 2012-06-07 11:19:35.506 Untitled[14987:f803] *** Terminating app due to uncaught exception 'NameError', reason: 'uninitialized constant AddressBook (NameError)'
71
+ *** First throw call stack:
72
+ (0x8f6022 0x286cd6 0x140054 0x291f 0x2645 0x1)
73
+ terminate called throwing an exception
74
+ ```
75
+
76
+ Let's go and define ourselves an `AddressBook` class in `motion/address_book.rb`:
77
+
78
+ ```ruby
79
+ class AddressBook
80
+ end
81
+ ```
82
+
83
+ You'll now get a spec failure:
84
+
85
+ ```
86
+ NoMethodError: undefined method `list' for AddressBook:Class
87
+ spec.rb:156:in `block in run_spec_block': .list - returns an Enumerable
88
+ 4:in `execute_block'
89
+ spec.rb:156:in `run_spec_block'
90
+ spec.rb:171:in `run'
91
+ ```
92
+
93
+ Well, we'd better go and define it then, eh?
94
+
95
+ ```
96
+ class AddressBook
97
+ def self.list
98
+ []
99
+ end
100
+ end
101
+ ```
102
+
103
+ I'm going to leave it here for now, but you're welcome to take a look at the
104
+ fully working demonstration project on [Github](http://github.com/jamesotron/bw-addressbook-demo).
data/HACKING.md ADDED
@@ -0,0 +1,101 @@
1
+ # Hacking on BubbleWrap
2
+
3
+ ## A library in two parts
4
+
5
+ RubyMotion forces a certain background-radiation of schitzophrenia
6
+ due to the fact that it's build tools run using the system ruby
7
+ via Rake. BubbleWrap manipulates the build environment in order
8
+ to make it possible to include itself (and other code) into the
9
+ build process from outsite the project heirarchy.
10
+
11
+ ### Part the first: `lib/`
12
+
13
+ This is where [RubyGems](http://rubygems.org) goes looking for
14
+ code when you call
15
+
16
+ ```ruby
17
+ require 'bubble-wrap'
18
+ ```
19
+
20
+ When `bubble-wrap` is required it immediately requires `bubble-wrap/loader` which sets up the infrastructure needed to manipulate the `Rakefile` build process. Once that is done we can freely call
21
+
22
+ ```ruby
23
+ BubbleWrap.require 'motion/core**/*.rb'
24
+ ```
25
+
26
+ `BubbleWrap.require` (or simply `BW.require`) is used to include
27
+ library code into the Rake build process used by RubyMotion.
28
+ `BW.require` is similar to ruby's standard `require` method with
29
+ two major changes:
30
+
31
+ - it can take a file pattern as used by [`Dir.glob`](http://ruby-doc.org/core-1.9.3/Dir.html#method-c-glob).
32
+ - it can be passed a block to manipulate dependencies.
33
+
34
+ If a block is passed to `BW.require` it is evaluated in the context
35
+ of `BW::Requirement` and thus has access to all it's class methods.
36
+ The most common use cases are setting file dependencies:
37
+
38
+ ```ruby
39
+ BW.require('motion/core**/*.rb') do
40
+ file('motion/core/device/screen.rb').depends_on 'motion/core/device.rb'
41
+ end
42
+ ```
43
+
44
+ and specifying frameworks that need to be included at build time:
45
+
46
+ ```ruby
47
+ BW.require('motion/**/*.rb') do
48
+ file('motion/address_book.rb').uses_framework 'Addressbook'
49
+ end
50
+ ```
51
+
52
+ ### Part the second: `motion/`
53
+
54
+ Inside the `motion` directory you'll see the actual implementation code
55
+ which is compiled into RubyMotion projects that are using BubbleWrap.
56
+
57
+ - `motion/core` contains "core" extension, things that the developers
58
+ reasonably think should be included in every BubbleWrap using project.
59
+ Careful consideration should be taken when making changes to the
60
+ contents and test coverage (in `spec/core`) must be updated to match.
61
+ This can be included in your project by requiring `bubble-wrap` or
62
+ `bubble-wrap/core` in your project `Rakefile`.
63
+ - `motion/http` contains the "http" extension. This can be included
64
+ by requiring `bubble-wrap/http` in your project `Rakefile`.
65
+ - `motion/test_suite_delegate` contains a simple `AppDelegate` which
66
+ can be used to enable the `rake spec` to run when developing a
67
+ BubbleWrap gem. Using `require 'bubble-wrap/test'` will include
68
+ it in the build process and also configure the app delegate to point
69
+ to `TestSuiteDelegate`. See the [BubbleWrap gem guide](gem.html) for
70
+ more information.
71
+
72
+ #### Your project here
73
+
74
+ If you think that your project would be of interest to the large number
75
+ of RubyMotion users that use BubbleWrap in their daily development then
76
+ feel free to fork [the repository on GitHub](https://github.com/mattetti/BubbleWrap)
77
+ and send us a pull request.
78
+
79
+ You should place your implemenation files in a subdirectory of `motion`
80
+ (eg `motion/my_awesome_project`), your tests in a subdirectory of `spec`
81
+ (eg `spec/my_awesome_project`) and you can create a require file in
82
+ `lib/bubble-wrap` for example `lib/bubble-wrap/my_awesome_project.rb`:
83
+
84
+ ```ruby
85
+ require 'bubble-wrap/loader'
86
+ BW.require 'motion/my_awesome_project.rb'
87
+ ```
88
+
89
+ People will then be able to use it by adding:
90
+
91
+ ```ruby
92
+ require 'bubble-wrap/my_awesome_project'
93
+ ```
94
+
95
+ to their project's `Rakefile`
96
+
97
+ ## Go forth and conquer!
98
+
99
+ The developers wish to thank you so much for taking the time
100
+ to improve BubbleWrap and by extension the RubyMotion
101
+ ecosystem. You're awesome!
data/README.md CHANGED
@@ -3,6 +3,7 @@
3
3
  A collection of (tested) helpers and wrappers used to wrap CocoaTouch code and provide more Ruby like APIs.
4
4
 
5
5
  [BubbleWrap website](http://bubblewrap.io)
6
+ [BubbleWrap mailing list](https://groups.google.com/forum/#!forum/bubblewrap)
6
7
 
7
8
  ## Installation
8
9
 
@@ -43,7 +44,7 @@ Make sure to append onto the array or use `+=`.
43
44
  ```ruby
44
45
  class AppDelegate
45
46
  def application(application, didFinishLaunchingWithOptions:launchOptions)
46
- puts "#{App.name} (#{documents_path})"
47
+ puts "#{App.name} (#{App.documents_path})"
47
48
  true
48
49
  end
49
50
  end
@@ -112,9 +113,9 @@ Examples:
112
113
  # 320
113
114
  > Device.screen.height
114
115
  # 480
115
- > Device.screen.widthForOrientation(:landscape_left)
116
+ > Device.screen.width_for_orientation(:landscape_left)
116
117
  # 480
117
- > Device.screen.heightForOrientation(:landscape_left)
118
+ > Device.screen.height_for_orientation(:landscape_left)
118
119
  # 320
119
120
  ```
120
121
 
@@ -163,7 +164,7 @@ def viewWillAppear(animated)
163
164
  loadAndRefresh
164
165
  end
165
166
 
166
- @reload_observer notification_center.observe ReloadNotification do |notification|
167
+ @reload_observer = notification_center.observe ReloadNotification do |notification|
167
168
  loadAndRefresh
168
169
  end
169
170
  end
@@ -196,6 +197,33 @@ simple interface:
196
197
  # ['TF1', 'France 2', 'France 3']
197
198
  ```
198
199
 
200
+ ### Observers
201
+ **Since: > version 0.4**
202
+
203
+ You can observe for object's changes and trigger blocks:
204
+
205
+ ``` ruby
206
+ class ExampleViewController < UIViewController
207
+ include BW::KVO
208
+
209
+ def viewDidLoad
210
+ @label = "Initial state"
211
+
212
+ observe(@label, "text") do |old_value, new_value|
213
+ puts "Hello from viewDidLoad!"
214
+ end
215
+ end
216
+
217
+ def viewDidAppear(animated)
218
+ observe(@label, "text") do |old_value, new_value|
219
+ puts "Hello from viewDidAppear!"
220
+ end
221
+ end
222
+
223
+ end
224
+ ```
225
+
226
+
199
227
  ### String
200
228
 
201
229
  The Ruby `String` class was extended to add `#camelize` and
data/Rakefile CHANGED
@@ -1,19 +1,18 @@
1
1
  require "bundler/gem_tasks"
2
2
  $:.unshift("/Library/RubyMotion/lib")
3
3
  require 'motion/project'
4
- require File.expand_path '../lib/bubble-wrap', __FILE__
5
- require File.expand_path '../lib/bubble-wrap/http', __FILE__
4
+ Bundler.setup
5
+ Bundler.require
6
6
 
7
- task :rspec do
8
- sh "rspec lib_spec/"
7
+ require 'bubble-wrap/test'
8
+
9
+ task :lib_spec do
10
+ sh "bacon #{Dir.glob("lib_spec/**/*_spec.rb").join(' ')}"
9
11
  end
10
12
 
13
+ task :test => [ :lib_spec, :spec ]
14
+
11
15
  Motion::Project::App.setup do |app|
12
16
  app.name = 'testSuite'
13
17
  app.identifier = 'io.bubblewrap.testSuite'
14
-
15
- app.development do
16
- app.files << './lib/tests/test_suite_delegate.rb'
17
- app.delegate_class = 'TestSuiteDelegate'
18
- end
19
18
  end
data/bubble-wrap.gemspec CHANGED
@@ -16,6 +16,7 @@ Gem::Specification.new do |gem|
16
16
 
17
17
  gem.extra_rdoc_files = gem.files.grep(%r{motion})
18
18
 
19
- gem.add_development_dependency 'rspec'
19
+ gem.add_development_dependency 'bacon'
20
+ gem.add_development_dependency 'mocha-on-bacon'
20
21
  gem.add_development_dependency 'rake'
21
22
  end
@@ -1,4 +1,4 @@
1
- require File.expand_path('../loader.rb', __FILE__)
1
+ require 'bubble-wrap/loader'
2
2
  BubbleWrap.require('motion/core.rb')
3
3
  BubbleWrap.require('motion/core/**/*.rb') do
4
4
  file('motion/core/device/screen.rb').depends_on 'motion/core/device.rb'
@@ -1,2 +1,2 @@
1
- require File.expand_path('../ext/motion_project_config', __FILE__)
2
- require File.expand_path('../ext/motion_project_app', __FILE__)
1
+ require 'bubble-wrap/ext/motion_project_config'
2
+ require 'bubble-wrap/ext/motion_project_app'
@@ -6,10 +6,10 @@ module BubbleWrap
6
6
  base.instance_eval do
7
7
  def setup_with_bubblewrap(&block)
8
8
  bw_config = proc do |app|
9
- app.files = ::BubbleWrap::Requirement.files + Dir.glob('./app/**/*.rb')
9
+ app.files = (::BubbleWrap::Requirement.files + (app.files||[])).uniq
10
10
  app.files_dependencies ::BubbleWrap::Requirement.files_dependencies
11
- app.frameworks = ::BubbleWrap::Requirement.frameworks
12
- block.call(app)
11
+ app.frameworks = (::BubbleWrap::Requirement.frameworks + (app.frameworks||[])).uniq
12
+ block.call(app) unless block.nil?
13
13
  end
14
14
  configs.each_value &bw_config
15
15
  config.validate
@@ -1,6 +1,7 @@
1
1
  module Motion
2
2
  module Project
3
3
  class Config
4
+
4
5
  # HACK NEEDED since RubyMotion doesn't support full path
5
6
  # dependencies.
6
7
  def files_dependencies(deps_hash)
@@ -1,2 +1,2 @@
1
- require File.expand_path('../loader.rb', __FILE__)
1
+ require 'bubble-wrap/loader'
2
2
  BubbleWrap.require('motion/http.rb')
@@ -2,9 +2,9 @@ unless defined?(Motion::Project::Config)
2
2
  raise "This file must be required within a RubyMotion project Rakefile."
3
3
  end
4
4
 
5
- require File.expand_path('../version', __FILE__) unless defined?(BubbleWrap::VERSION)
6
- require File.expand_path('../ext', __FILE__)
7
- require File.expand_path('../requirement', __FILE__)
5
+ require 'bubble-wrap/version'
6
+ require 'bubble-wrap/ext'
7
+ require 'bubble-wrap/requirement'
8
8
 
9
9
  module BubbleWrap
10
10
 
@@ -1,4 +1,4 @@
1
- require File.expand_path "../requirement/path_manipulation", __FILE__
1
+ require 'bubble-wrap/requirement/path_manipulation'
2
2
 
3
3
  module BubbleWrap
4
4
  class Requirement
@@ -0,0 +1,8 @@
1
+ require 'bubble-wrap/loader'
2
+ BW.require 'motion/test_suite_delegate.rb'
3
+
4
+ Motion::Project::App.setup do |app|
5
+ app.development do
6
+ app.delegate_class = 'TestSuiteDelegate'
7
+ end
8
+ end
@@ -1,3 +1,3 @@
1
1
  module BubbleWrap
2
- VERSION = '0.4.0'
2
+ VERSION = '1.0.0.pre'
3
3
  end
@@ -0,0 +1,138 @@
1
+ require 'mocha-on-bacon'
2
+ require File.expand_path('../../../motion_stub', __FILE__)
3
+ require 'bubble-wrap'
4
+
5
+ describe BubbleWrap::Ext::BuildTask do
6
+
7
+ before do
8
+ @subject = Class.new do
9
+ def self.setup; end
10
+ def self.configs
11
+ @configs ||= { :development => Object.new }
12
+ end
13
+ end
14
+ @subject.extend BubbleWrap::Ext::BuildTask
15
+ end
16
+
17
+ describe '.extended' do
18
+ it 'responds to :setup_with_bubblewrap' do
19
+ @subject.respond_to?(:setup_with_bubblewrap).should == true
20
+ end
21
+
22
+ it 'responds to :setup_without_bubblewrap' do
23
+ @subject.respond_to?(:setup_without_bubblewrap).should == true
24
+ end
25
+
26
+ it 'replaces :setup with :setup_with_bubblewrap' do
27
+ @subject.method(:setup).should == @subject.method(:setup_with_bubblewrap)
28
+ end
29
+ end
30
+
31
+ describe '.setup_with_bubblewrap' do
32
+ before do
33
+ @config = @subject.configs[:development]
34
+ @config.stubs(:files=)
35
+ @config.stubs(:files)
36
+ @config.stubs(:files_dependencies)
37
+ @config.stubs(:frameworks)
38
+ @config.stubs(:frameworks=)
39
+ @subject.stubs(:config).returns(mock())
40
+ @subject.config.stubs(:validate)
41
+ end
42
+
43
+ it 'calls the passed-in block' do
44
+ block = proc { }
45
+ block.expects(:call).with(@config)
46
+ @subject.setup &block
47
+ end
48
+
49
+ describe 'when app.files is nil' do
50
+ it 'sets app.files' do
51
+ @config.stubs(:files).returns(nil)
52
+ files = BubbleWrap::Requirement.files
53
+ @config.expects(:files=).with(files)
54
+ @subject.setup
55
+ end
56
+ end
57
+
58
+ describe 'when app.files is empty' do
59
+ it 'sets app.files' do
60
+ @config.stubs(:files).returns([])
61
+ files = BubbleWrap::Requirement.files
62
+ @config.expects(:files=).with(files)
63
+ @subject.setup
64
+ end
65
+ end
66
+
67
+ describe 'when app.files has contents' do
68
+ it 'sets app.files' do
69
+ mock_files = ['a', 'b', 'c']
70
+ @config.stubs(:files).returns(mock_files)
71
+ files = BubbleWrap::Requirement.files + mock_files
72
+ @config.expects(:files=).with(files)
73
+ @subject.setup
74
+ end
75
+ end
76
+
77
+ it 'removes duplicates from app.files' do
78
+ files = ['a', 'a', 'b', 'b', 'c', 'c']
79
+ @config.stubs(:files).returns(files)
80
+ @config.expects(:files=).with(BubbleWrap::Requirement.files + files.uniq)
81
+ @subject.setup
82
+ end
83
+
84
+ it 'adds BW dependencies' do
85
+ @config.expects(:files_dependencies).with(BubbleWrap::Requirement.files_dependencies)
86
+ @subject.setup
87
+ end
88
+
89
+ describe 'when app.frameworks is empty' do
90
+ it 'sets the default frameworks' do
91
+ defaults = ['CoreGraphics', 'Foundation', 'UIKit']
92
+ @config.stubs(:frameworks).returns(nil)
93
+ @config.expects(:frameworks=).with(defaults)
94
+ @subject.setup
95
+ end
96
+ end
97
+
98
+ describe 'when app.frameworks is empty' do
99
+ it 'sets the default frameworks' do
100
+ defaults = ['CoreGraphics', 'Foundation', 'UIKit']
101
+ @config.stubs(:frameworks).returns([])
102
+ @config.expects(:frameworks=).with(defaults)
103
+ @subject.setup
104
+ end
105
+ end
106
+
107
+ describe 'when app.frameworks contains defaults' do
108
+ it 'sets the default frameworks' do
109
+ defaults = ['CoreGraphics', 'Foundation', 'UIKit']
110
+ @config.stubs(:frameworks).returns(defaults)
111
+ @config.expects(:frameworks=).with(defaults)
112
+ @subject.setup
113
+ end
114
+ end
115
+
116
+ describe 'when app.frameworks contains non-defaults' do
117
+ it 'sets the default frameworks and the contents' do
118
+ defaults = ['CoreGraphics', 'Foundation', 'UIKit']
119
+ @config.stubs(:frameworks).returns(['Addressbook'])
120
+ @config.expects(:frameworks=).with(defaults + ['Addressbook'])
121
+ @subject.setup
122
+ end
123
+ end
124
+
125
+ describe 'when BW::Requirement.frameworks has contents' do
126
+ it 'sets the default frameworks and the contents' do
127
+ defaults = ['CoreGraphics', 'Foundation', 'UIKit']
128
+ BW.require('motion/core.rb') do
129
+ file('motion/core.rb').uses_framework('Addressbook')
130
+ end
131
+ @config.stubs(:frameworks).returns(nil)
132
+ @config.expects(:frameworks=).with(['Addressbook'] + defaults)
133
+ @subject.setup
134
+ end
135
+ end
136
+ end
137
+
138
+ end