rm-extensions 0.0.1
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/.gitignore +17 -0
- data/Gemfile +4 -0
- data/LICENSE.txt +22 -0
- data/README.md +32 -0
- data/Rakefile +1 -0
- data/lib/motion/rm-extensions.rb +177 -0
- data/lib/rm-extensions/version.rb +3 -0
- data/lib/rm-extensions.rb +11 -0
- data/rm-extensions.gemspec +19 -0
- metadata +54 -0
data/.gitignore
ADDED
data/Gemfile
ADDED
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,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: []
|