bubble-wrap 0.4.0 → 1.0.0.pre

Sign up to get free protection for your applications and to get access to all the features.
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