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.
@@ -1,50 +1,49 @@
1
1
  require File.expand_path('../../../../lib/bubble-wrap/requirement/path_manipulation', __FILE__)
2
2
 
3
3
  describe BubbleWrap::Requirement::PathManipulation do
4
-
5
- subject do
6
- o = Object.new
7
- o.extend BubbleWrap::Requirement::PathManipulation
8
- o
4
+
5
+ before do
6
+ @subject = Object.new
7
+ @subject.extend BubbleWrap::Requirement::PathManipulation
9
8
  end
10
9
 
11
10
  describe '#convert_caller_to_path' do
12
11
  it 'strips off from the second-to-last colon' do
13
- subject.convert_caller_to_path("/fake/:path:91:in `fake_method'").
12
+ @subject.convert_caller_to_path("/fake/:path:91:in `fake_method'").
14
13
  should == '/fake/:path'
15
14
  end
16
15
 
17
16
  it 'leaves plain old paths unmolested' do
18
- subject.convert_caller_to_path("/fake/path").
17
+ @subject.convert_caller_to_path("/fake/path").
19
18
  should == '/fake/path'
20
19
  end
21
20
  end
22
21
 
23
22
  describe '#convert_to_absolute_path' do
24
23
  it 'converts relative paths to absolute paths' do
25
- subject.convert_to_absolute_path('foo')[0].should == '/'
24
+ @subject.convert_to_absolute_path('foo')[0].should == '/'
26
25
  end
27
26
 
28
27
  it "doesn't modify absolute paths" do
29
- subject.convert_to_absolute_path('/foo').should == '/foo'
28
+ @subject.convert_to_absolute_path('/foo').should == '/foo'
30
29
  end
31
30
  end
32
31
 
33
32
  describe '#strip_up_to_last_lib' do
34
33
  it 'strips off from the last lib' do
35
- subject.strip_up_to_last_lib('/fake/lib/dir/lib/foo').
34
+ @subject.strip_up_to_last_lib('/fake/lib/dir/lib/foo').
36
35
  should == '/fake/lib/dir'
37
36
  end
38
37
 
39
38
  it "doesn't modify the path otherwise" do
40
- subject.strip_up_to_last_lib('/fake/path').
39
+ @subject.strip_up_to_last_lib('/fake/path').
41
40
  should == '/fake/path'
42
41
  end
43
42
  end
44
43
 
45
44
  describe "#convert_to_relative" do
46
45
  it 'strips off the root portion' do
47
- subject.convert_to_relative('/foo/bar/baz', '/foo').
46
+ @subject.convert_to_relative('/foo/bar/baz', '/foo').
48
47
  should == 'bar/baz'
49
48
  end
50
49
  end
@@ -3,68 +3,69 @@ require 'bubble-wrap'
3
3
 
4
4
  describe BubbleWrap::Requirement do
5
5
 
6
- subject{ BubbleWrap::Requirement }
6
+ before do
7
+ @subject = BubbleWrap::Requirement
8
+ end
7
9
 
8
10
  describe '.scan' do
9
11
  before do
10
- subject.paths = {}
12
+ @subject.paths = {}
13
+ @root_path = File.expand_path('../../../', __FILE__)
11
14
  end
12
15
 
13
- let(:root_path) { File.expand_path('../../../', __FILE__) }
14
-
15
16
  it 'asking for a not-yet-found file raises an exception' do
16
- proc do
17
- subject.find('foo')
18
- end.should raise_error
17
+ should.raise(Exception) do
18
+ @subject.find('foo')
19
+ end
19
20
  end
20
21
 
21
22
  it 'finds the specified file' do
22
- subject.scan(root_path, 'motion/core.rb')
23
- subject.paths.keys.first.should == 'motion/core.rb'
23
+ @subject.scan(@root_path, 'motion/core.rb')
24
+ @subject.paths.keys.first.should == 'motion/core.rb'
24
25
  end
25
26
 
26
27
  it 'finds multiple files according to spec' do
27
- subject.scan(root_path, 'motion/**/*.rb')
28
- subject.files.size.should > 1
28
+ @subject.scan(@root_path, 'motion/**/*.rb')
29
+ @subject.files.size.should > 1
29
30
  end
30
31
 
31
32
  it 'never depends on itself' do
32
- subject.scan(root_path, 'motion/core.rb') do
33
+ @subject.scan(@root_path, 'motion/core.rb') do
33
34
  file('motion/core.rb').depends_on 'motion/core.rb'
34
35
  end
35
- subject.file('motion/core.rb').file_dependencies.should == []
36
+ @subject.file('motion/core.rb').file_dependencies.should == []
36
37
  end
37
38
 
38
39
  it 'can depend on another file' do
39
- subject.scan(root_path, 'motion/*.rb') do
40
+ @subject.scan(@root_path, 'motion/*.rb') do
40
41
  file('motion/http.rb').depends_on('motion/core.rb')
41
42
  end
42
- subject.file('motion/http.rb').file_dependencies.should be_one
43
+ @subject.file('motion/http.rb').file_dependencies.size.should == 1
43
44
  end
44
45
 
45
46
  it 'can use a framework' do
46
- subject.scan(root_path, 'motion/core.rb') do
47
+ @subject.scan(@root_path, 'motion/core.rb') do
47
48
  file('motion/core.rb').uses_framework('FakeFramework')
48
49
  end
49
- subject.file('motion/core.rb').frameworks.should include('FakeFramework')
50
+ @subject.file('motion/core.rb').frameworks.member?('FakeFramework').should == true
50
51
  end
51
52
 
52
53
  it "figures out the root of the project" do
53
- subject.scan(File.join(root_path, 'lib/bubble-wrap.rb'), 'motion/core.rb')
54
- subject.paths.values.first.root.should == root_path
54
+ @subject.scan(File.join(@root_path, 'lib/bubble-wrap.rb'), 'motion/core.rb')
55
+ @subject.paths.values.first.root.should == @root_path
55
56
  end
56
57
 
57
58
  describe '.frameworks' do
58
59
  it 'includes UIKit by default' do
59
- subject.frameworks.should include('UIKit')
60
+ @subject.frameworks.member?('UIKit').should == true
60
61
  end
61
62
 
62
63
  it 'includes Foundation by default' do
63
- subject.frameworks.should include('Foundation')
64
+ @subject.frameworks.member?('Foundation').should == true
64
65
  end
65
66
 
66
67
  it 'includes CoreGraphics by default' do
67
- subject.frameworks.should include('CoreGraphics')
68
+ @subject.frameworks.member?('CoreGraphics').should == true
68
69
  end
69
70
  end
70
71
  end
@@ -1,3 +1,4 @@
1
+ require 'mocha-on-bacon'
1
2
  require File.expand_path('../motion_stub.rb', __FILE__)
2
3
  require 'bubble-wrap'
3
4
 
@@ -10,7 +11,7 @@ describe BubbleWrap do
10
11
 
11
12
  describe '.require' do
12
13
  it 'delegates to Requirement.scan' do
13
- BW::Requirement.should_receive(:scan)
14
+ BW::Requirement.expects(:scan)
14
15
  BW.require('foo')
15
16
  end
16
17
  end
data/motion/core/app.rb CHANGED
@@ -67,6 +67,11 @@ module BubbleWrap
67
67
  UIScreen.mainScreen.applicationFrame
68
68
  end
69
69
 
70
+ # Main Screen bounds. Useful when starting the app
71
+ def bounds
72
+ UIScreen.mainScreen.bounds
73
+ end
74
+
70
75
  # Application Delegate
71
76
  def delegate
72
77
  UIApplication.sharedApplication.delegate
@@ -52,7 +52,7 @@ module BubbleWrap
52
52
  # The same as `.width` and `.height` but
53
53
  # compensating for screen rotation (which
54
54
  # can do your head in).
55
- def widthForOrientation(o=orientation)
55
+ def width_for_orientation(o=orientation)
56
56
  return height if (o == :landscape_left) || (o == :landscape_right)
57
57
  width
58
58
  end
@@ -60,7 +60,7 @@ module BubbleWrap
60
60
  # The same as `.width` and `.height` but
61
61
  # compensating for screen rotation (which
62
62
  # can do your head in).
63
- def heightForOrientation(o=orientation)
63
+ def height_for_orientation(o=orientation)
64
64
  return width if (o == :landscape_left) || (o == :landscape_right)
65
65
  height
66
66
  end
@@ -0,0 +1,91 @@
1
+ # Usage example:
2
+ #
3
+ # class ExampleViewController < UIViewController
4
+ # include BW::KVO
5
+ #
6
+ # def viewDidLoad
7
+ # @label = create_ui_label()
8
+ #
9
+ # observe(@label, "text") do |old_value, new_value|
10
+ # puts "Changed #{old_value} to #{new_value}"
11
+ # end
12
+ # end
13
+ #
14
+ # end
15
+ #
16
+ # @see https://developer.apple.com/library/mac/#documentation/Cocoa/Conceptual/KeyValueObserving/KeyValueObserving.html#//apple_ref/doc/uid/10000177i
17
+ module BubbleWrap
18
+ module KVO
19
+ COLLECTION_OPERATIONS = [ NSKeyValueChangeInsertion, NSKeyValueChangeRemoval, NSKeyValueChangeReplacement ]
20
+ DEFAULT_OPTIONS = NSKeyValueObservingOptionNew | NSKeyValueObservingOptionOld
21
+
22
+ def observe(target, key_path, &block)
23
+ target.addObserver(self, forKeyPath:key_path, options:DEFAULT_OPTIONS, context:nil) unless registered?(target, key_path)
24
+ add_observer_block(target, key_path, &block)
25
+ end
26
+
27
+ def unobserve(target, key_path)
28
+ return unless registered?(target, key_path)
29
+
30
+ target.removeObserver(self, forKeyPath:key_path)
31
+ remove_observer_block(target, key_path)
32
+ end
33
+
34
+ def unobserve_all
35
+ return if @targets.nil?
36
+
37
+ @targets.each do |target, key_paths|
38
+ key_paths.each_key do |key_path|
39
+ target.removeObserver(self, forKeyPath:key_path)
40
+ end
41
+ end
42
+ remove_all_observer_blocks
43
+ end
44
+
45
+ # Observer blocks
46
+
47
+ private
48
+ def registered?(target, key_path)
49
+ !@targets.nil? && !@targets[target].nil? && @targets[target].has_key?(key_path)
50
+ end
51
+
52
+ def add_observer_block(target, key_path, &block)
53
+ return if target.nil? || key_path.nil? || block.nil?
54
+
55
+ @targets ||= {}
56
+ @targets[target] ||= {}
57
+ @targets[target][key_path] ||= []
58
+ @targets[target][key_path] << block
59
+ end
60
+
61
+ def remove_observer_block(target, key_path)
62
+ return if @targets.nil? || target.nil? || key_path.nil?
63
+
64
+ key_paths = @targets[target]
65
+ if !key_paths.nil? && key_paths.has_key?(key_path)
66
+ key_paths.delete(key_path)
67
+ end
68
+ end
69
+
70
+ def remove_all_observer_blocks
71
+ @targets.clear unless @targets.nil?
72
+ end
73
+
74
+ # NSKeyValueObserving Protocol
75
+
76
+ def observeValueForKeyPath(key_path, ofObject:target, change:change, context:context)
77
+ key_paths = @targets[target] || {}
78
+ blocks = key_paths[key_path] || []
79
+ blocks.each do |block|
80
+ args = [ change[NSKeyValueChangeOldKey], change[NSKeyValueChangeNewKey] ]
81
+ args << change[NSKeyValueChangeIndexesKey] if collection?(change)
82
+ block.call(*args)
83
+ end
84
+ end
85
+
86
+ def collection?(change)
87
+ COLLECTION_OPERATIONS.include?(change[NSKeyValueChangeKindKey])
88
+ end
89
+
90
+ end
91
+ end
@@ -8,14 +8,16 @@ module BubbleWrap
8
8
  end
9
9
 
10
10
  def []=(key, value)
11
- defaults = NSUserDefaults.standardUserDefaults
12
- defaults.setObject(value, forKey: storage_key(key.to_s))
13
- defaults.synchronize
11
+ storage.setObject(value, forKey: storage_key(key.to_s))
12
+ storage.synchronize
14
13
  end
15
14
 
16
15
  def [](key)
17
- defaults = NSUserDefaults.standardUserDefaults
18
- defaults.objectForKey storage_key(key.to_s)
16
+ storage.objectForKey storage_key(key.to_s)
17
+ end
18
+
19
+ def storage
20
+ NSUserDefaults.standardUserDefaults
19
21
  end
20
22
 
21
23
  def storage_key(key)
data/motion/core/time.rb CHANGED
@@ -1,8 +1,19 @@
1
1
  class Time
2
+
2
3
  def self.iso8601(time)
3
- formatter = NSDateFormatter.alloc.init
4
- formatter.dateFormat = "yyyy-MM-dd'T'HH:mm:ss'Z'"
5
- formatter.timeZone = NSTimeZone.timeZoneWithAbbreviation "UTC"
6
- formatter.dateFromString time
4
+ cached_date_formatter("yyyy-MM-dd'T'HH:mm:ss'Z'").
5
+ dateFromString(time)
7
6
  end
7
+
8
+ private
9
+
10
+ def self.cached_date_formatter(dateFormat)
11
+ Thread.current[:date_formatters] ||= {}
12
+ Thread.current[:date_formatters][dateFormat] ||=
13
+ NSDateFormatter.alloc.init.tap do |formatter|
14
+ formatter.dateFormat = dateFormat
15
+ formatter.timeZone = NSTimeZone.timeZoneWithAbbreviation "UTC"
16
+ end
17
+ end
18
+
8
19
  end
data/motion/http.rb CHANGED
@@ -21,38 +21,38 @@ module BubbleWrap
21
21
  # end
22
22
  #
23
23
  def self.get(url, options={}, &block)
24
- delegator = block_given? ? block : options.delete(:action)
25
- HTTP::Query.new( url, :get, options.merge({:action => delegator}) )
24
+ create_query url, :get, options, block
26
25
  end
27
26
 
28
27
  # Make a POST request
29
28
  def self.post(url, options={}, &block)
30
- delegator = block_given? ? block : options.delete(:action)
31
- HTTP::Query.new( url, :post, options.merge({:action => delegator}) )
29
+ create_query url, :post, options, block
32
30
  end
33
31
 
34
32
  # Make a PUT request
35
33
  def self.put(url, options={}, &block)
36
- delegator = block_given? ? block : options.delete(:action)
37
- HTTP::Query.new( url, :put, options.merge({:action => delegator}) )
34
+ create_query url, :put, options, block
38
35
  end
39
36
 
40
37
  # Make a DELETE request
41
38
  def self.delete(url, options={}, &block)
42
- delegator = block_given? ? block : options.delete(:action)
43
- HTTP::Query.new( url, :delete, options.merge({:action => delegator}) )
39
+ create_query url, :delete, options, block
44
40
  end
45
41
 
46
42
  # Make a HEAD request
47
43
  def self.head(url, options={}, &block)
48
- delegator = block_given? ? block : options.delete(:action)
49
- HTTP::Query.new( url, :head, options.merge({:action => delegator}) )
44
+ create_query url, :head, options, block
50
45
  end
51
46
 
52
47
  # Make a PATCH request
53
48
  def self.patch(url, options={}, &block)
54
- delegator = block_given? ? block : options.delete(:action)
55
- HTTP::Query.new( url, :patch, options.merge({:action => delegator}) )
49
+ create_query url, :patch, options, block
50
+ end
51
+
52
+ private
53
+ def self.create_query(url, method, options, passed_block)
54
+ options[:action] = passed_block if passed_block
55
+ HTTP::Query.new( url, method, options )
56
56
  end
57
57
 
58
58
  # Response class wrapping the results of a Query's response
@@ -113,12 +113,8 @@ module BubbleWrap
113
113
  @credentials = options.delete(:credentials) || {}
114
114
  @credentials = {:username => '', :password => ''}.merge(@credentials)
115
115
  @timeout = options.delete(:timeout) || 30.0
116
- headers = options.delete(:headers)
117
- if headers
118
- @headers = {}
119
- headers.each{|k,v| @headers[k] = v.gsub("\n", '\\n') } # escaping LFs
120
- end
121
- @cachePolicy = options.delete(:cache_policy) || NSURLRequestUseProtocolCachePolicy
116
+ @headers = escape_line_feeds(options.delete :headers)
117
+ @cache_policy = options.delete(:cache_policy) || NSURLRequestUseProtocolCachePolicy
122
118
  @options = options
123
119
  @response = HTTP::Response.new
124
120
  initiate_request(url)
@@ -127,16 +123,22 @@ module BubbleWrap
127
123
  connection
128
124
  end
129
125
 
130
- def generate_get_params(payload, prefix=nil)
126
+ def generate_params(payload, prefix=nil)
131
127
  list = []
132
128
  payload.each do |k,v|
133
129
  if v.is_a?(Hash)
134
130
  new_prefix = prefix ? "#{prefix}[#{k.to_s}]" : k.to_s
135
- param = generate_get_params(v, new_prefix)
131
+ param = generate_params(v, new_prefix)
132
+ list << param
133
+ elsif v.is_a?(Array)
134
+ v.each do |val|
135
+ param = prefix ? "#{prefix}[#{k}][]=#{val}" : "#{k}[]=#{val}"
136
+ list << param
137
+ end
136
138
  else
137
139
  param = prefix ? "#{prefix}[#{k}]=#{v}" : "#{k}=#{v}"
140
+ list << param
138
141
  end
139
- list << param
140
142
  end
141
143
  return list.flatten
142
144
  end
@@ -147,16 +149,16 @@ module BubbleWrap
147
149
 
148
150
  unless @payload.nil?
149
151
  if @payload.is_a?(Hash)
150
- params = generate_get_params(@payload)
152
+ params = generate_params(@payload)
151
153
  @payload = params.join("&")
152
154
  end
153
155
  url_string = "#{url_string}?#{@payload}" if @method == "GET"
154
156
  end
155
-
156
- p "BubbleWrap::HTTP building a NSRequest for #{url_string}" if SETTINGS[:debug]
157
+ #this method needs a refactor when the specs are done. (especially this utf8 escaping part)
158
+ log "BubbleWrap::HTTP building a NSRequest for #{url_string}"
157
159
  @url = NSURL.URLWithString(url_string.stringByAddingPercentEscapesUsingEncoding NSUTF8StringEncoding)
158
160
  @request = NSMutableURLRequest.requestWithURL(@url,
159
- cachePolicy:@cachePolicy,
161
+ cachePolicy:@cache_policy,
160
162
  timeoutInterval:@timeout)
161
163
  @request.setHTTPMethod @method
162
164
  @request.setAllHTTPHeaderFields(@headers) if @headers
@@ -168,11 +170,8 @@ module BubbleWrap
168
170
  end
169
171
 
170
172
  # NSHTTPCookieStorage.sharedHTTPCookieStorage
171
-
172
173
  @connection = create_connection(request, self)
173
- @request.instance_variable_set("@done_loading", false)
174
- def @request.done_loading; @done_loading; end
175
- def @request.done_loading!; @done_loading = true; end
174
+ patch_nsurl_request
176
175
  end
177
176
 
178
177
  def connection(connection, didReceiveResponse:response)
@@ -188,7 +187,7 @@ module BubbleWrap
188
187
  end
189
188
 
190
189
  def connection(connection, willSendRequest:request, redirectResponse:redirect_response)
191
- p "HTTP redirected #{request.description}" if SETTINGS[:debug]
190
+ log "HTTP redirected #{request.description}"
192
191
  new_request = request.mutableCopy
193
192
  # new_request.setValue(@credentials.inspect, forHTTPHeaderField:'Authorization') # disabled while we figure this one out
194
193
  new_request.setAllHTTPHeaderFields(@headers) if @headers
@@ -200,7 +199,7 @@ module BubbleWrap
200
199
  def connection(connection, didFailWithError: error)
201
200
  UIApplication.sharedApplication.networkActivityIndicatorVisible = false
202
201
  @request.done_loading!
203
- NSLog"HTTP Connection failed #{error.localizedDescription}" if SETTINGS[:debug]
202
+ log "HTTP Connection failed #{error.localizedDescription}"
204
203
  @response.error_message = error.localizedDescription
205
204
  call_delegator_with_response
206
205
  end
@@ -225,15 +224,37 @@ module BubbleWrap
225
224
  # NSURLCredentialPersistenceNone,
226
225
  # NSURLCredentialPersistenceForSession,
227
226
  # NSURLCredentialPersistencePermanent
228
- p "auth challenged, answered with credentials: #{credentials.inspect}" if SETTINGS[:debug]
227
+ log "auth challenged, answered with credentials: #{credentials.inspect}"
229
228
  new_credential = NSURLCredential.credentialWithUser(credentials[:username], password:credentials[:password], persistence:NSURLCredentialPersistenceForSession)
230
229
  challenge.sender.useCredential(new_credential, forAuthenticationChallenge:challenge)
231
230
  else
232
231
  challenge.sender.cancelAuthenticationChallenge(challenge)
233
- p 'Auth Failed :('
232
+ log 'Auth Failed :('
234
233
  end
235
234
  end
236
235
 
236
+
237
+ private
238
+
239
+ def log(message)
240
+ NSLog message if SETTINGS[:debug]
241
+ end
242
+
243
+ def escape_line_feeds(hash)
244
+ return nil if hash.nil?
245
+ escaped_hash = {}
246
+
247
+ hash.each{|k,v| escaped_hash[k] = v.gsub("\n", '\\n') }
248
+ escaped_hash
249
+ end
250
+
251
+ def patch_nsurl_request
252
+ @request.instance_variable_set("@done_loading", false)
253
+
254
+ def @request.done_loading; @done_loading; end
255
+ def @request.done_loading!; @done_loading = true; end
256
+ end
257
+
237
258
  def call_delegator_with_response
238
259
  if @delegator.respond_to?(:call)
239
260
  @delegator.call( @response, self )