motion-async 0.5

Sign up to get free protection for your applications and to get access to all the features.
checksums.yaml ADDED
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA1:
3
+ metadata.gz: ebdc675fdcad19bacb5e6b61b767abb402480031
4
+ data.tar.gz: d4880f32b2067e5b3b89a3ba319997cac462de6a
5
+ SHA512:
6
+ metadata.gz: 7f95af187c39d1878188ace11f37ab15f64563c09729ff486ca95bd1dd66543f45caf5eb8ec1f804c1c1d27cae55bffc7667bc5c9cf8b6d89bb27bbcd6eee175
7
+ data.tar.gz: e442a6f8e3467d4886838d98aadc8b2165a27c5fa7f077a4e149919f9b995110bfad3394b57a9b308123f3953801d46b9d64489a3709b3131d7d431c3cf2b690
data/LICENSE ADDED
@@ -0,0 +1,22 @@
1
+ The MIT License (MIT)
2
+
3
+ Copyright (c) 2015 Silvertop Studio, LLC
4
+
5
+ Permission is hereby granted, free of charge, to any person obtaining a copy
6
+ of this software and associated documentation files (the "Software"), to deal
7
+ in the Software without restriction, including without limitation the rights
8
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
9
+ copies of the Software, and to permit persons to whom the Software is
10
+ furnished to do so, subject to the following conditions:
11
+
12
+ The above copyright notice and this permission notice shall be included in
13
+ all copies or substantial portions of the Software.
14
+
15
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
16
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
17
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
18
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
19
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
20
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
21
+ THE SOFTWARE.
22
+
data/README.md ADDED
@@ -0,0 +1,199 @@
1
+ # motion-async
2
+
3
+ motion-async is a gem for RubyMotion Android that provides a friendly Ruby wrapper around Android's [AsyncTask:](https://developer.android.com/reference/android/os/AsyncTask.html)
4
+
5
+ > AsyncTask enables proper and easy use of the UI thread. This class allows [you] to perform background operations and publish results on the UI thread without having to manipulate threads and/or handlers.
6
+ >
7
+ > AsyncTask is designed to be a helper class around Thread and Handler and does not constitute a generic threading framework. AsyncTasks should ideally be used for short operations (a few seconds at the most.)
8
+
9
+ AsyncTask must be loaded on the UI thread, _and must only be executed once._ See [the documentation](https://developer.android.com/reference/android/os/AsyncTask.html) for more details.
10
+
11
+ ## Setup
12
+
13
+ Gemfile:
14
+
15
+ ```ruby
16
+ gem "motion-async"
17
+ ```
18
+
19
+ then run `bundle` on the command line.
20
+
21
+ ## Usage
22
+
23
+ The main entry point is the `MotionAsync.async` function, which creates, and then executes the async code with the options you provide (see below for details).
24
+
25
+ ```ruby
26
+ MotionAsync.async do
27
+ # some long operation
28
+ end
29
+ ```
30
+
31
+ You can also `include MotionAsync` to have access to the `async` function without the module prefix:
32
+
33
+ ```ruby
34
+ include MotionAsync
35
+ ...
36
+ async do
37
+ # some long operation
38
+ end
39
+ ```
40
+
41
+ `async` takes a block, which is the code that should be executed in the background. You can optionally specify callback blocks that are run at various points during the tasks's lifecycle:
42
+
43
+ * `:pre_execute` : before the background task is executed
44
+ * `:completion` : when the task finishes
45
+ * `:progress` : whenever `progress` is called on the task object
46
+ * `:cancelled` : if the task is cancelled
47
+
48
+ These callbacks can be added with the `on` method, or passed in as options to `async`.
49
+
50
+ This:
51
+
52
+ ```ruby
53
+ async do
54
+ # some long operation
55
+ end.on(:completion) do |result|
56
+ # process result
57
+ end
58
+ ```
59
+
60
+ is the same as this:
61
+
62
+ ```ruby
63
+ async(
64
+ completion: -> (result) {
65
+ # process result
66
+ }
67
+ ) do
68
+ # some long operation
69
+ end
70
+ ```
71
+
72
+ To avoid the awkward syntax of the latter example, you can use the `:background` option to specify the async code:
73
+
74
+ ```ruby
75
+ async(
76
+ background: -> {
77
+ # some long operation
78
+ },
79
+ completion: -> (result) {
80
+ # process result
81
+ }
82
+ )
83
+ ```
84
+
85
+ ## Examples
86
+
87
+ Run a block of code in the background:
88
+
89
+ ```ruby
90
+ async do
91
+ # some long operation
92
+ end
93
+ ```
94
+
95
+ Specify a block to execute when the operation completes. The return value of the async block is passed
96
+ in as a parameter:
97
+
98
+ ```ruby
99
+ task = async do
100
+ some_expensive_calculation()
101
+ end
102
+ task.on :completion do |result|
103
+ p "The result was #{result}"
104
+ end
105
+ ```
106
+
107
+ Alternate syntax for the same example:
108
+
109
+ ```ruby
110
+ async do
111
+ some_expensive_calculation()
112
+ end.on(:completion) do |result|
113
+ p "The result was #{result}"
114
+ end
115
+ ```
116
+
117
+ ### Progress Indicators
118
+
119
+ For progress updates, provide a `:progress block`, and periodically call `#progress` on the task object in the background block. The `:progress` block is executed on the main thread.
120
+
121
+ ```ruby
122
+ async do |task|
123
+ 100.times do |i|
124
+ # do some work
125
+ task.progress i
126
+ end
127
+ end.on(:progress) do |progress_value|
128
+ p "Progress: #{progress_value + 1}% complete"
129
+ end
130
+ ```
131
+
132
+ ### Chaining
133
+
134
+ Calls to `on` are chainable:
135
+
136
+ ```ruby
137
+ async do |task|
138
+ 100.times do |i|
139
+ # do some work
140
+ task.progress i
141
+ end
142
+ end.on(:progress) do |progress_value|
143
+ p "Progress: #{progress_value + 1}% complete"
144
+ end.on(:completion) do |result|
145
+ p "The result was #{result}"
146
+ end
147
+ ```
148
+
149
+ ### Other Callbacks
150
+
151
+ `:pre_execute` is invoked before the async operation begins and `:cancelled` is called if the task is cancelled.
152
+
153
+ ```ruby
154
+ async do
155
+ # long operation
156
+ end.on(:pre_execute) do
157
+ p "About to run a long operation"
158
+ end.on(:cancelled) do
159
+ p "Operation cancelled."
160
+ end
161
+ ```
162
+
163
+ ### Canceling a Task
164
+
165
+ `async` returns a reference to the task object (a subclass of `AsyncTask`); you can hold on to this
166
+ in case you want to cancel it later. You can see if a task has been cancelled by calling
167
+ `cancelled?` The Android docs recommend checking this value periodically during task execution
168
+ so you can exit gracefully.
169
+
170
+ ```ruby
171
+ @async_task = async do |task|
172
+ image_urls.each do |image_url|
173
+ images << load_image(image_url)
174
+ break if task.cancelled?
175
+ end
176
+ end
177
+ ...
178
+ # e.g. in an Activity or Fragment
179
+ def onStop
180
+ @async_task.cancel(true) # passing in true indicates that the task should be interrupted
181
+ end
182
+ ```
183
+
184
+ ### Other Task States
185
+
186
+ ```ruby
187
+ task.pending?
188
+ task.running?
189
+ task.finished?
190
+ ```
191
+
192
+ ## Development
193
+
194
+ ### Tests
195
+
196
+ It's a little tricky to test background threads in a unit test context. I went through a number of blog posts and SO questions, but never could manage to get it to work.
197
+
198
+ So, we've got a few tests in `main_spec.rb` and then a bunch in `main_activity.rb` which are run simply by running the app in this codebase via `rake`. I'm not especially proud of this, but figured it was better than nothing. If anyone can show me a better way, I'd love to see it.
199
+
@@ -0,0 +1,103 @@
1
+ # MotionAsync provides a friendly Ruby wrapper around Android's AsyncTask.
2
+ #
3
+ # You can call it directly:
4
+ #
5
+ # @example
6
+ # MotionAsync.async do
7
+ # # some long task
8
+ # end
9
+ #
10
+ # Or include the module to make the async command available wherever you need it
11
+ #
12
+ # @example
13
+ # include MotionAsync
14
+ # ...
15
+ # async do
16
+ # # some long task
17
+ # end
18
+ #
19
+ # Usage:
20
+ #
21
+ # Run a block of code in the background:
22
+ #
23
+ # @example
24
+ # async do
25
+ # # some long operation
26
+ # end
27
+ #
28
+ # Specify a block to execute when the operation completes. The return value of the async block is passed
29
+ # in as a parameter:
30
+ #
31
+ # @example
32
+ # task = async do
33
+ # some_expensive_calculation()
34
+ # end
35
+ # task.on :completion do |result|
36
+ # p "The result was #{result}"
37
+ # end
38
+ #
39
+ # Alternate syntax for the same example:
40
+ #
41
+ # @example
42
+ # async.on(:background) do |task|
43
+ # some_expensive_calculation()
44
+ # end.on(:completion) do |result|
45
+ # p "The result was #{result}"
46
+ # end
47
+ #
48
+ # For progress updates, provide a :progress block, and periodically call #progress
49
+ # on the task object in the :background block. The :progress block is executed on the
50
+ # main thread.
51
+ #
52
+ # @example
53
+ # async.on(:background) do |task|
54
+ # 100.times do |i|
55
+ # # do some work
56
+ # task.progress i
57
+ # end
58
+ # end.on(:progress) do |result|
59
+ # p "Progress: #{progress + 1}% complete"
60
+ # end
61
+ #
62
+ # :pre_execute is invoked before the async operation begins and :cancelled is called
63
+ # if the task is cancelled.
64
+ #
65
+ # @example
66
+ # async.on(:background) do |task|
67
+ # # long operation
68
+ # end.on(:pre_execute) do
69
+ # p "About to run a long operation"
70
+ # end.on(:cancelled) do
71
+ # p "Operation cancelled."
72
+ # end
73
+ #
74
+ # async returns a reference to the task object (a subclass of AsyncTask); you can hold on to this
75
+ # in case you want to cancel it later. You can see if a task has been cancelled by calling
76
+ # cancelled?
77
+ #
78
+ # @example
79
+ # @async_task = async do |task|
80
+ # image_urls.each do |image_url|
81
+ # images << load_image(image_url)
82
+ # break if task.cancelled?
83
+ # end
84
+ # end
85
+ # ...
86
+ # def on_stop
87
+ # @async_task.cancel
88
+ # end
89
+ #
90
+ module MotionAsync
91
+
92
+ def self.async(options={}, &block)
93
+ MotionAsyncTask.create(options, &block).tap do |task|
94
+ task.execute []
95
+ end
96
+ end
97
+
98
+ def async(options={}, &block)
99
+ MotionAsync.async(options, &block)
100
+ end
101
+
102
+ end
103
+
@@ -0,0 +1,78 @@
1
+ class MotionAsyncTask < Android::Os::AsyncTask
2
+ attr_reader :result
3
+
4
+ # Java is super picky about constructors, so we'll use a factory method
5
+ def self.create(options={}, &block)
6
+ MotionAsyncTask.new.tap do |task|
7
+ options[:background] = block if block
8
+ task.callbacks.merge!(options)
9
+ end
10
+ end
11
+
12
+ def on(callback, &block)
13
+ callbacks[callback] = block
14
+ if callback == :completion && finished?
15
+ # task already ran, but we'll call the completion block anyway
16
+ block.call @result
17
+ end
18
+ self
19
+ end
20
+
21
+ # publishProgress must be passed an Array - we can make that easier
22
+ def progress(progress)
23
+ progress = [progress] unless progress.respond_to?(:[])
24
+ publishProgress(progress)
25
+ end
26
+
27
+ def pending?
28
+ status == Android::Os::AsyncTask::Status::PENDING
29
+ end
30
+
31
+ def running?
32
+ status == Android::Os::AsyncTask::Status::RUNNING
33
+ end
34
+
35
+ def finished?
36
+ status == Android::Os::AsyncTask::Status::FINISHED
37
+ end
38
+
39
+ def cancelled?
40
+ isCancelled
41
+ end
42
+
43
+ ##########################
44
+ ## AsyncTask event methods
45
+
46
+ def onPreExecute
47
+ call_if_defined :pre_execute, self
48
+ end
49
+
50
+ def onPostExecute(result)
51
+ call_if_defined :completion, result
52
+ end
53
+
54
+ def doInBackground(params)
55
+ @result = call_if_defined :background, self
56
+ end
57
+
58
+ def onProgressUpdate(progress)
59
+ progress = progress.first if progress.size == 1
60
+ call_if_defined :progress, progress
61
+ end
62
+
63
+ def onCancelled(result)
64
+ call_if_defined :cancelled, result
65
+ end
66
+
67
+ private
68
+
69
+ def callbacks
70
+ @callbacks ||= {}
71
+ end
72
+
73
+ def call_if_defined(callback, param=nil)
74
+ callbacks[callback].call(param) if callbacks[callback]
75
+ end
76
+
77
+ end
78
+
@@ -0,0 +1,4 @@
1
+ module MotionAsync
2
+ VERSION = "0.5"
3
+ end
4
+
@@ -0,0 +1,8 @@
1
+ unless defined?(Motion::Project::App)
2
+ raise "This must be required from within a RubyMotion Rakefile"
3
+ end
4
+
5
+ lib_dir_path = File.dirname(File.expand_path(__FILE__))
6
+ Motion::Project::App.setup do |app|
7
+ app.files.unshift(Dir.glob(File.join(lib_dir_path, "motion-async/**/*.rb")))
8
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: motion-async
3
+ version: !ruby/object:Gem::Version
4
+ version: '0.5'
5
+ platform: ruby
6
+ authors:
7
+ - Darin Wilson
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2015-06-18 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: bacon
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: '0'
20
+ type: :development
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: '0'
27
+ - !ruby/object:Gem::Dependency
28
+ name: rake
29
+ requirement: !ruby/object:Gem::Requirement
30
+ requirements:
31
+ - - ">="
32
+ - !ruby/object:Gem::Version
33
+ version: '0'
34
+ type: :development
35
+ prerelease: false
36
+ version_requirements: !ruby/object:Gem::Requirement
37
+ requirements:
38
+ - - ">="
39
+ - !ruby/object:Gem::Version
40
+ version: '0'
41
+ description: MotionAsync was written for use with RubyMotion Android, and makes it
42
+ easy to run code off the main UI thread by providing a friendly wrapper around Android's
43
+ AsyncTask class.
44
+ email: darinwilson@gmail.com
45
+ executables: []
46
+ extensions: []
47
+ extra_rdoc_files: []
48
+ files:
49
+ - LICENSE
50
+ - README.md
51
+ - lib/motion-async.rb
52
+ - lib/motion-async/motion_async.rb
53
+ - lib/motion-async/motion_async_task.rb
54
+ - lib/motion-async/version.rb
55
+ homepage: http://github.com/darinwilson/motion-async
56
+ licenses:
57
+ - MIT
58
+ metadata: {}
59
+ post_install_message:
60
+ rdoc_options: []
61
+ require_paths:
62
+ - lib
63
+ required_ruby_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: '0'
73
+ requirements: []
74
+ rubyforge_project:
75
+ rubygems_version: 2.2.2
76
+ signing_key:
77
+ specification_version: 4
78
+ summary: A Ruby wrapper around Android's AsyncTask
79
+ test_files: []