rm-extensions 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
data/.gitignore ADDED
@@ -0,0 +1,17 @@
1
+ *.gem
2
+ *.rbc
3
+ .bundle
4
+ .config
5
+ .yardoc
6
+ Gemfile.lock
7
+ InstalledFiles
8
+ _yardoc
9
+ coverage
10
+ doc/
11
+ lib/bundler/man
12
+ pkg
13
+ rdoc
14
+ spec/reports
15
+ test/tmp
16
+ test/version_tmp
17
+ tmp
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source 'https://rubygems.org'
2
+
3
+ # Specify your gem's dependencies in rm-extensions.gemspec
4
+ gemspec
data/LICENSE.txt ADDED
@@ -0,0 +1,22 @@
1
+ Copyright (c) 2013 Joe Noon
2
+
3
+ MIT License
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining
6
+ a copy of this software and associated documentation files (the
7
+ "Software"), to deal in the Software without restriction, including
8
+ without limitation the rights to use, copy, modify, merge, publish,
9
+ distribute, sublicense, and/or sell copies of the Software, and to
10
+ permit persons to whom the Software is furnished to do so, subject to
11
+ the following conditions:
12
+
13
+ The above copyright notice and this permission notice shall be
14
+ included in all copies or substantial portions of the Software.
15
+
16
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
17
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
18
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
19
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
20
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
21
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
22
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,32 @@
1
+ # RMExtensions
2
+
3
+ Extensions and helpers for dealing with various areas of rubymotion:
4
+
5
+ - memory management
6
+ - block/scope/local variable issues
7
+ - GCD blocks
8
+ - retaining objects through async procedures
9
+
10
+ ## Installation
11
+
12
+ Add this line to your application's Gemfile:
13
+
14
+ gem 'rm-extensions'
15
+
16
+ And then execute:
17
+
18
+ $ bundle
19
+
20
+ ## Usage
21
+
22
+ Read through the commented code.
23
+
24
+ ## Contributing
25
+
26
+ If you have a better way to accomplish anything this library is doing, please share!
27
+
28
+ 1. Fork it
29
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
30
+ 3. Commit your changes (`git commit -am 'Add some feature'`)
31
+ 4. Push to the branch (`git push origin my-new-feature`)
32
+ 5. Create new Pull Request
data/Rakefile ADDED
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,177 @@
1
+ module RMExtensions
2
+
3
+ # this module is included on Object, so these methods are available from anywhere in your code.
4
+ module ObjectExtensions
5
+
6
+ def rmext_assert_main_thread!
7
+ raise "This method must be called on the main thread." unless NSThread.currentThread.isMainThread
8
+ end
9
+
10
+ # https://gist.github.com/mattetti/2951773
11
+ # https://github.com/MacRuby/MacRuby/issues/152
12
+ # blocks within blocks can be a problem with GCD (and maybe RM/MacRuby in general?).
13
+ # these helpers make it easy to use nested blocks with GCD, and also ensures those
14
+ # blocks will not be garbage collected until at least after they have been called.
15
+
16
+ def rmext_on_main_q(&block)
17
+ rmext_retained_context do |x|
18
+ x.block = -> do
19
+ block.call
20
+ x.detach!
21
+ end
22
+ Dispatch::Queue.main.async(&x.block)
23
+ end
24
+ end
25
+
26
+ def rmext_on_serial_q(q, &block)
27
+ Dispatch.once { $serial_qs = {} }
28
+ rmext_retained_context do |x|
29
+ x.block = -> do
30
+ block.call
31
+ x.detach!
32
+ end
33
+ x.key = "#{NSBundle.mainBundle.bundleIdentifier}.serial.#{q}"
34
+ $serial_qs[x.key] ||= Dispatch::Queue.new(x.key)
35
+ $serial_qs[x.key].async(&x.block)
36
+ end
37
+ end
38
+
39
+ def rmext_on_concurrent_q(q, &block)
40
+ rmext_retained_context do |x|
41
+ x.block = -> do
42
+ block.call
43
+ x.detach!
44
+ end
45
+ x.key = "#{NSBundle.mainBundle.bundleIdentifier}.concurrent.#{q}"
46
+ Dispatch::Queue.concurrent(x.key).async(&x.block)
47
+ end
48
+ end
49
+
50
+ # #rmext_retain! is different than a normal retain. it adds the object(self) to a retained
51
+ # array, utilizing RM's underlying GC logic
52
+ #
53
+ # you most likely want to use #rmext_retained_context and not call this directly
54
+ #
55
+ def rmext_retain!
56
+ ::RMExtensions::RetainedContext.rmext_retains_queue.sync do
57
+ ::RMExtensions::RetainedContext.rmext_retains.push(self)
58
+ end
59
+ end
60
+
61
+ # #rmext_detach! is slightly similar to the concept of "release". it removes the object(self)
62
+ # from a retained array (only one hit, in case the same object is #rmext_retain!'d multiple times),
63
+ # utilizing RM's underlying GC logic. if nothing else has a strong reference to the object after
64
+ # it is detached, it will eventually be handled by RM's GC.
65
+ #
66
+ # you most likely want to use #rmext_retained_context and not call this directly
67
+ #
68
+ def rmext_detach!
69
+ ::RMExtensions::RetainedContext.rmext_retains_queue.async do
70
+ ::RMExtensions::RetainedContext.rmext_retains.delete_at(::RMExtensions::RetainedContext.rmext_retains.index(self) || ::RMExtensions::RetainedContext.rmext_retains.length)
71
+ end
72
+ end
73
+
74
+ # #rmext_retained_context yields an object you can treat like an openstruct. you can get/set any
75
+ # property on it. the context is globally retained, until #detach! is called on the context.
76
+ # this convention should fill the gap where local variables and scope bugs currently occur in RM,
77
+ # and it also solves the re-entrant problem of using instance variables for retaining purposes.
78
+ #
79
+ # always be sure to #detach! the context at the correct place in time.
80
+ #
81
+ # example:
82
+ #
83
+ # rmext_retained_context do |x|
84
+ # rmext_on_serial_q("my_serial_q") do
85
+ # some_async_http_request do |results1|
86
+ # x.results1 = results1
87
+ # rmext_on_serial_q("my_serial_q") do
88
+ # some_other_async_http_request do |results2|
89
+ # x.results2 = results2
90
+ # rmext_on_main_q do
91
+ # p "results1", x.results1
92
+ # p "results2", x.results2
93
+ # x.detach!
94
+ # end
95
+ # end
96
+ # end
97
+ # end
98
+ # end
99
+ # end
100
+ #
101
+ # experimental feature:
102
+ #
103
+ # you can call #beginBackground! on the context, and it will check-out a background task identifier,
104
+ # and automatically end the background task when you call #detach! as normal.
105
+ def rmext_retained_context(&block)
106
+ ::RMExtensions::RetainedContext.retained(self, &block)
107
+ end
108
+
109
+ end
110
+
111
+ # You don't use this class directly.
112
+ class RetainedContext
113
+
114
+ class << self
115
+ def rmext_retains
116
+ Dispatch.once { @rmext_retains = [] }
117
+ @rmext_retains
118
+ end
119
+
120
+ def rmext_retains_queue
121
+ Dispatch.once { @rmext_retains_queue = Dispatch::Queue.new("#{NSBundle.mainBundle.bundleIdentifier}.rmext_retains_queue") }
122
+ @rmext_retains_queue
123
+ end
124
+
125
+ def retained(origin, &block)
126
+ x = new
127
+ x.hash["retained_origin"] = origin
128
+ x.hash["retained_block"] = block
129
+ x.rmext_retain!
130
+ block.call(x)
131
+ x
132
+ end
133
+ end
134
+
135
+ attr_accessor :hash
136
+
137
+ def initialize
138
+ self.hash = {}
139
+ end
140
+
141
+ # if you provide a block, you are responsible for calling #detach!,
142
+ # otherwise, the expiration handler will just call #detach!
143
+ def beginBackground!(&block)
144
+ hash["bgTaskExpirationHandler"] = block
145
+ hash["bgTask"] = UIApplication.sharedApplication.beginBackgroundTaskWithExpirationHandler(-> do
146
+ if hash["bgTaskExpirationHandler"]
147
+ hash["bgTaskExpirationHandler"].call
148
+ else
149
+ x.detach!
150
+ end
151
+ end)
152
+ end
153
+
154
+ def detach!
155
+ if hash["bgTask"] && hash["bgTask"] != UIBackgroundTaskInvalid
156
+ UIApplication.sharedApplication.endBackgroundTask(hash["bgTask"])
157
+ end
158
+ self.hash = nil
159
+ rmext_detach!
160
+ end
161
+
162
+ def method_missing(method, *args)
163
+ unless hash
164
+ raise "You detached this rmext_retained_context and then called: #{method}"
165
+ end
166
+ m = method.to_s
167
+ if m =~ /(.+)?=$/
168
+ hash[$1] = args.first
169
+ else
170
+ hash[m]
171
+ end
172
+ end
173
+
174
+ end
175
+
176
+ end
177
+ Object.send(:include, ::RMExtensions::ObjectExtensions)
@@ -0,0 +1,3 @@
1
+ module RMExtensions
2
+ VERSION = "0.0.1"
3
+ end
@@ -0,0 +1,11 @@
1
+ require "rm-extensions/version"
2
+
3
+ unless defined?(Motion::Project::Config)
4
+ raise "This file must be required within a RubyMotion project Rakefile."
5
+ end
6
+
7
+ Motion::Project::App.setup do |app|
8
+ Dir.glob(File.join(File.dirname(__FILE__), 'motion/**/*.rb')).each do |file|
9
+ app.files.unshift(file)
10
+ end
11
+ end
@@ -0,0 +1,19 @@
1
+ # -*- encoding: utf-8 -*-
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'rm-extensions/version'
5
+
6
+ Gem::Specification.new do |gem|
7
+ gem.name = "rm-extensions"
8
+ gem.version = RMExtensions::VERSION
9
+ gem.authors = ["Joe Noon"]
10
+ gem.email = ["joenoon@gmail.com"]
11
+ gem.description = %q{Extensions and helpers for dealing with various areas of rubymotion}
12
+ gem.summary = %q{Extensions and helpers for dealing with various areas of rubymotion}
13
+ gem.homepage = "https://github.com/joenoon/rm-extensions"
14
+
15
+ gem.files = `git ls-files`.split($/)
16
+ gem.executables = gem.files.grep(%r{^bin/}).map{ |f| File.basename(f) }
17
+ gem.test_files = gem.files.grep(%r{^(test|spec|features)/})
18
+ gem.require_paths = ["lib"]
19
+ end
metadata ADDED
@@ -0,0 +1,54 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: rm-extensions
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.1
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Joe Noon
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2013-04-19 00:00:00.000000000 Z
13
+ dependencies: []
14
+ description: Extensions and helpers for dealing with various areas of rubymotion
15
+ email:
16
+ - joenoon@gmail.com
17
+ executables: []
18
+ extensions: []
19
+ extra_rdoc_files: []
20
+ files:
21
+ - .gitignore
22
+ - Gemfile
23
+ - LICENSE.txt
24
+ - README.md
25
+ - Rakefile
26
+ - lib/motion/rm-extensions.rb
27
+ - lib/rm-extensions.rb
28
+ - lib/rm-extensions/version.rb
29
+ - rm-extensions.gemspec
30
+ homepage: https://github.com/joenoon/rm-extensions
31
+ licenses: []
32
+ post_install_message:
33
+ rdoc_options: []
34
+ require_paths:
35
+ - lib
36
+ required_ruby_version: !ruby/object:Gem::Requirement
37
+ none: false
38
+ requirements:
39
+ - - ! '>='
40
+ - !ruby/object:Gem::Version
41
+ version: '0'
42
+ required_rubygems_version: !ruby/object:Gem::Requirement
43
+ none: false
44
+ requirements:
45
+ - - ! '>='
46
+ - !ruby/object:Gem::Version
47
+ version: '0'
48
+ requirements: []
49
+ rubyforge_project:
50
+ rubygems_version: 1.8.23
51
+ signing_key:
52
+ specification_version: 3
53
+ summary: Extensions and helpers for dealing with various areas of rubymotion
54
+ test_files: []