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 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: []