motion-prime 0.6.0 → 0.7.0
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.
- checksums.yaml +8 -8
- data/CHANGELOG.md +4 -0
- data/Gemfile +1 -0
- data/Gemfile.lock +6 -1
- data/ROADMAP.md +7 -6
- data/bin/prime +31 -16
- data/doc/code/models.rb +5 -1
- data/doc/code/tables.rb +54 -0
- data/doc/docs/getting_started.html +7 -0
- data/doc/docs/index.html +205 -0
- data/doc/docs/models.html +13 -0
- data/doc/docs/screens.html +7 -0
- data/doc/docs/sections.html +7 -0
- data/doc/docs/tables.html +128 -0
- data/files/Gemfile +3 -3
- data/lib/motion-prime.rb +2 -0
- data/motion-prime.gemspec +3 -2
- data/motion-prime/api_client.rb +35 -36
- data/motion-prime/config/base.rb +0 -2
- data/motion-prime/core_ext/kernel.rb +2 -1
- data/motion-prime/elements/base_element.rb +7 -2
- data/motion-prime/elements/spinner.rb +7 -0
- data/motion-prime/elements/table_view.rb +7 -0
- data/motion-prime/helpers/has_search_bar.rb +11 -5
- data/motion-prime/models/_finder_mixin.rb +5 -12
- data/motion-prime/models/_nano_bag_mixin.rb +1 -1
- data/motion-prime/models/_sync_mixin.rb +52 -37
- data/motion-prime/screens/_base_mixin.rb +4 -0
- data/motion-prime/screens/_navigation_mixin.rb +3 -3
- data/motion-prime/screens/screen.rb +1 -1
- data/motion-prime/sections/base_section.rb +12 -12
- data/motion-prime/sections/form/base_field_section.rb +1 -1
- data/motion-prime/sections/form/date_field_section.rb +5 -0
- data/motion-prime/sections/form/password_field_section.rb +1 -1
- data/motion-prime/sections/form/string_field_section.rb +1 -1
- data/motion-prime/sections/form/text_field_section.rb +1 -1
- data/motion-prime/sections/table.rb +7 -0
- data/motion-prime/sections/table/table_delegate.rb +14 -4
- data/motion-prime/support/mp_spinner.rb +16 -0
- data/motion-prime/support/mp_table_view.rb +6 -0
- data/motion-prime/support/tab_bar_controller.rb +3 -3
- data/motion-prime/support/temp_fixes.rb +6 -0
- data/motion-prime/version.rb +1 -1
- data/motion-prime/views/layout.rb +2 -1
- data/motion-prime/views/view_builder.rb +1 -1
- metadata +24 -2
@@ -9,8 +9,8 @@ module MotionPrime
|
|
9
9
|
end
|
10
10
|
|
11
11
|
# Get normalized sync url of this Prime::Model
|
12
|
-
#
|
13
|
-
# @param method [Symbol] http method
|
12
|
+
#
|
13
|
+
# @param method [Symbol] http method
|
14
14
|
# @return url [String] url to use in model sync
|
15
15
|
def sync_url(method = :get, options = {})
|
16
16
|
url = self.class.sync_url
|
@@ -19,7 +19,7 @@ module MotionPrime
|
|
19
19
|
end
|
20
20
|
|
21
21
|
# Get normalized sync url of associated Prime::Model
|
22
|
-
#
|
22
|
+
#
|
23
23
|
# @param key [Symbol] association name
|
24
24
|
# @return url [String] url to use in model association sync
|
25
25
|
def association_sync_url(key, options)
|
@@ -29,7 +29,7 @@ module MotionPrime
|
|
29
29
|
end
|
30
30
|
|
31
31
|
# Destroy model on server and delete on local
|
32
|
-
#
|
32
|
+
#
|
33
33
|
# @param block [Proc] block to be executed after destroy
|
34
34
|
# @return self[Prime::Model] deleted model.
|
35
35
|
def destroy(&block)
|
@@ -41,13 +41,13 @@ module MotionPrime
|
|
41
41
|
end
|
42
42
|
|
43
43
|
# Fetch model from server and save on local
|
44
|
-
#
|
44
|
+
#
|
45
45
|
def fetch!(options = {}, &block)
|
46
46
|
fetch(options.merge(save: true), &block)
|
47
47
|
end
|
48
48
|
|
49
49
|
# Fetch model from server
|
50
|
-
#
|
50
|
+
#
|
51
51
|
# @param options [Hash] fetch options
|
52
52
|
# @option options [Symbol] :method Http method to calculate url, `:get` by default
|
53
53
|
# @option options [Boolean] :associations Also fetch associations
|
@@ -60,8 +60,9 @@ module MotionPrime
|
|
60
60
|
|
61
61
|
will_fetch_model = !url.blank?
|
62
62
|
will_fetch_associations = !options.has_key?(:associations) || options[:associations]
|
63
|
+
will_fetch_associations = false unless has_associations_to_fetch?
|
63
64
|
|
64
|
-
fetch_with_url url do |data, status_code|
|
65
|
+
fetch_with_url url, options do |data, status_code|
|
65
66
|
save if options[:save]
|
66
67
|
block.call(data, status_code, data) if use_callback && !will_fetch_associations
|
67
68
|
end if will_fetch_model
|
@@ -98,10 +99,12 @@ module MotionPrime
|
|
98
99
|
#
|
99
100
|
# @param url [String] url to fetch
|
100
101
|
# @param block [Proc] block to be executed after fetch
|
101
|
-
def fetch_with_url(url, &block)
|
102
|
+
def fetch_with_url(url, options = {}, &block)
|
102
103
|
use_callback = block_given?
|
103
104
|
api_client.get(url) do |data, status_code|
|
104
|
-
|
105
|
+
if data.present?
|
106
|
+
fetch_with_attributes(data, save_associations: options[:save], &block)
|
107
|
+
end
|
105
108
|
block.call(data, status_code, data) if use_callback
|
106
109
|
end
|
107
110
|
end
|
@@ -114,12 +117,10 @@ module MotionPrime
|
|
114
117
|
use_callback = block_given?
|
115
118
|
filtered_attributes = filtered_updatable_attributes(options)
|
116
119
|
|
120
|
+
attributes = attributes_to_post_data(model_name, filtered_attributes)
|
121
|
+
|
117
122
|
post_data = options[:params_root] || {}
|
118
|
-
post_data
|
119
|
-
filtered_attributes.delete(:files).each do |file_name, file|
|
120
|
-
post_data[:files][[model_name, file_name].join] = file
|
121
|
-
end
|
122
|
-
post_data[model_name] = filtered_attributes
|
123
|
+
post_data.merge!(attributes)
|
123
124
|
|
124
125
|
method = options[:method] || (persisted? ? :put : :post)
|
125
126
|
api_client.send(method, url, post_data, options) do |data, status_code|
|
@@ -171,12 +172,17 @@ module MotionPrime
|
|
171
172
|
self
|
172
173
|
end
|
173
174
|
|
175
|
+
def associations
|
176
|
+
@associations ||= (self.class._associations || {}).clone
|
177
|
+
end
|
178
|
+
|
179
|
+
def associations_to_fetch
|
180
|
+
@associations_to_fetch ||= associations.select { |key, v| fetch_association?(key) }
|
181
|
+
end
|
182
|
+
|
174
183
|
def fetch_associations(sync_options = {}, &block)
|
175
184
|
use_callback = block_given?
|
176
|
-
|
177
|
-
association_keys = associations.keys.select { |key| fetch_association?(key) }
|
178
|
-
|
179
|
-
association_keys.each_with_index do |key, index|
|
185
|
+
associations_to_fetch.keys.each_with_index do |key, index|
|
180
186
|
if use_callback && associations.count - 1 == index
|
181
187
|
fetch_association(key, sync_options, &block)
|
182
188
|
else
|
@@ -185,19 +191,23 @@ module MotionPrime
|
|
185
191
|
end
|
186
192
|
end
|
187
193
|
|
194
|
+
def has_associations_to_fetch?
|
195
|
+
associations_to_fetch.present?
|
196
|
+
end
|
197
|
+
|
188
198
|
def has_association?(key)
|
189
|
-
!
|
199
|
+
!associations[key.to_sym].nil?
|
190
200
|
end
|
191
201
|
|
192
202
|
def fetch_association?(key)
|
193
|
-
options =
|
203
|
+
options = associations[key.to_sym]
|
194
204
|
return false if options[:if] && !options[:if].to_proc.call(self)
|
195
205
|
association_sync_url(key, options).present?
|
196
206
|
end
|
197
207
|
|
198
208
|
def fetch_association(key, sync_options = {}, &block)
|
199
209
|
return unless fetch_association?(key)
|
200
|
-
options =
|
210
|
+
options = associations[key.to_sym]
|
201
211
|
if options[:type] == :many
|
202
212
|
fetch_has_many(key, options, sync_options, &block)
|
203
213
|
else
|
@@ -206,12 +216,12 @@ module MotionPrime
|
|
206
216
|
end
|
207
217
|
|
208
218
|
def fetch_association_with_attributes(key, data, sync_options = {})
|
209
|
-
options =
|
219
|
+
options = associations[key.to_sym]
|
210
220
|
return unless options
|
211
221
|
if options[:type] == :many
|
212
|
-
fetch_has_many_with_attributes(key, data, sync_options)
|
222
|
+
fetch_has_many_with_attributes(key, data || [], sync_options)
|
213
223
|
else
|
214
|
-
fetch_has_one_with_attributes(key, data, sync_options)
|
224
|
+
fetch_has_one_with_attributes(key, data || {}, sync_options)
|
215
225
|
end
|
216
226
|
end
|
217
227
|
|
@@ -290,13 +300,11 @@ module MotionPrime
|
|
290
300
|
updatable_attributes = self.class.updatable_attributes
|
291
301
|
|
292
302
|
if updatable_attributes.blank?
|
293
|
-
|
294
|
-
return attrs.merge(files: {})
|
303
|
+
return slice_attributes ? attributes_hash.slice(*slice_attributes) : attributes_hash
|
295
304
|
end
|
296
305
|
|
297
306
|
updatable_attributes = updatable_attributes.slice(*slice_attributes) if slice_attributes
|
298
|
-
updatable_attributes.
|
299
|
-
key, options = *attribute
|
307
|
+
updatable_attributes.inject({}) do |hash, (key, options)|
|
300
308
|
next hash if options[:if] && !send(options[:if])
|
301
309
|
value = if block = options[:block]
|
302
310
|
block.call(self, hash)
|
@@ -304,14 +312,7 @@ module MotionPrime
|
|
304
312
|
info[key]
|
305
313
|
end
|
306
314
|
|
307
|
-
|
308
|
-
value.to_a.each do |file_data|
|
309
|
-
file_name, file = file_data.to_a
|
310
|
-
hash[:files]["[#{key.partition('_').last}]#{file_name}"] = file
|
311
|
-
end
|
312
|
-
else
|
313
|
-
hash.merge!(key => value)
|
314
|
-
end
|
315
|
+
hash[key] = value
|
315
316
|
hash
|
316
317
|
end
|
317
318
|
end
|
@@ -320,6 +321,20 @@ module MotionPrime
|
|
320
321
|
normalize_object(url).to_s.gsub(':id', id.to_s)
|
321
322
|
end
|
322
323
|
|
324
|
+
def attributes_to_post_data(root_name, attributes)
|
325
|
+
result = {:_files => [], root_name => attributes}
|
326
|
+
|
327
|
+
result[root_name].each do |name, field_attrs|
|
328
|
+
next unless field_attrs.is_a?(Hash)
|
329
|
+
files = Array.wrap(field_attrs.delete(:_files)).map do |file|
|
330
|
+
file[:name].insert(0, "#{root_name}[#{name}]")
|
331
|
+
file
|
332
|
+
end
|
333
|
+
result[:_files] += files
|
334
|
+
end
|
335
|
+
result
|
336
|
+
end
|
337
|
+
|
323
338
|
module ClassMethods
|
324
339
|
def fetch_all_with_attributes(data)
|
325
340
|
data.map do |attrs|
|
@@ -348,7 +363,7 @@ module MotionPrime
|
|
348
363
|
def updatable_attributes(*attrs)
|
349
364
|
return self._updatable_attributes if attrs.blank?
|
350
365
|
attrs.each do |attribute|
|
351
|
-
updatable_attribute
|
366
|
+
updatable_attribute(attribute)
|
352
367
|
end
|
353
368
|
end
|
354
369
|
|
@@ -17,6 +17,10 @@ module MotionPrime
|
|
17
17
|
UIApplication.sharedApplication.delegate
|
18
18
|
end
|
19
19
|
|
20
|
+
def parent_screen=(value)
|
21
|
+
@parent_screen = value.try(:weak_ref)
|
22
|
+
end
|
23
|
+
|
20
24
|
# Setup the screen, this method will be called when you run MPViewController.new
|
21
25
|
# @param options [hash] Options passed to setup
|
22
26
|
# @return [MotionPrime::Screen] Ready to use screen
|
@@ -49,7 +49,7 @@ module MotionPrime
|
|
49
49
|
end
|
50
50
|
|
51
51
|
def wrap_in_navigation?
|
52
|
-
options
|
52
|
+
options.fetch(:navigation, true)
|
53
53
|
end
|
54
54
|
|
55
55
|
def wrap_in_navigation
|
@@ -63,11 +63,11 @@ module MotionPrime
|
|
63
63
|
end
|
64
64
|
|
65
65
|
def navigation_controller
|
66
|
-
@navigation_controller ||= self.navigationController
|
66
|
+
@navigation_controller ||= self.navigationController.try(:weak_ref)
|
67
67
|
end
|
68
68
|
|
69
69
|
def navigation_controller=(val)
|
70
|
-
@navigation_controller = val
|
70
|
+
@navigation_controller = val.try(:weak_ref)
|
71
71
|
end
|
72
72
|
|
73
73
|
private
|
@@ -47,7 +47,7 @@ module MotionPrime
|
|
47
47
|
def dealloc
|
48
48
|
pp 'Deallocating Screen', self.object_id, self.to_s
|
49
49
|
# FIXME: calling instance_eval in title method (_base_screen_mixin) instance variables need to be cleared manually
|
50
|
-
clear_instance_variables
|
50
|
+
clear_instance_variables(except: [:_search_bar])
|
51
51
|
super
|
52
52
|
end
|
53
53
|
|
@@ -37,7 +37,7 @@ module MotionPrime
|
|
37
37
|
end
|
38
38
|
|
39
39
|
def dealloc
|
40
|
-
# pp '
|
40
|
+
# pp 'Deallocating section', self.name, self.to_s, self.object_id
|
41
41
|
NSNotificationCenter.defaultCenter.removeObserver self # unbinding events created in bind_keyboard_events
|
42
42
|
super
|
43
43
|
end
|
@@ -55,7 +55,7 @@ module MotionPrime
|
|
55
55
|
end
|
56
56
|
|
57
57
|
# Get computed container height
|
58
|
-
#
|
58
|
+
#
|
59
59
|
# @example
|
60
60
|
# class MySection < Prime::Section
|
61
61
|
# container height: proc { element(:title).content_outer_height }
|
@@ -63,42 +63,42 @@ module MotionPrime
|
|
63
63
|
# end
|
64
64
|
# section = MySection.new
|
65
65
|
# section.container_height # => 46
|
66
|
-
#
|
66
|
+
#
|
67
67
|
# @return height [Float, Integer] computed height
|
68
68
|
def container_height
|
69
69
|
container_options[:height] || DEFAULT_CONTENT_HEIGHT
|
70
70
|
end
|
71
71
|
|
72
72
|
# Get section default name, based on class name
|
73
|
-
#
|
73
|
+
#
|
74
74
|
# @example
|
75
75
|
# class ProfileSection < Prime::Section
|
76
76
|
# end
|
77
|
-
#
|
77
|
+
#
|
78
78
|
# section = ProfileSection.new
|
79
79
|
# section.default_name # => 'profile'
|
80
80
|
# section.name # => 'profile'
|
81
|
-
#
|
81
|
+
#
|
82
82
|
# another_section = ProfileSection.new(name: 'another')
|
83
83
|
# another_section.default_name # => 'profile'
|
84
84
|
# another_section.name # => 'another'
|
85
|
-
#
|
85
|
+
#
|
86
86
|
# @return name [String] section default name
|
87
87
|
def default_name
|
88
88
|
self.class_name_without_kvo.demodulize.underscore.gsub(/\_section$/, '')
|
89
89
|
end
|
90
90
|
|
91
91
|
# Get section elements options, where the key is element name.
|
92
|
-
#
|
92
|
+
#
|
93
93
|
# @return options [Hash] elements options
|
94
94
|
def elements_options
|
95
95
|
self.class.elements_options || {}
|
96
96
|
end
|
97
97
|
|
98
98
|
# Create elements if they are not created yet.
|
99
|
-
# This will not cause rendering elements,
|
99
|
+
# This will not cause rendering elements,
|
100
100
|
# they will be rendered immediately after that or rendered async later, based on type of section.
|
101
|
-
#
|
101
|
+
#
|
102
102
|
# @return result [Boolean] true if has been loaded by this thread.
|
103
103
|
def load_section
|
104
104
|
return if @section_loaded
|
@@ -113,7 +113,7 @@ module MotionPrime
|
|
113
113
|
end
|
114
114
|
|
115
115
|
# Force load section
|
116
|
-
#
|
116
|
+
#
|
117
117
|
# @return result [Boolean] true if has been loaded by this thread.
|
118
118
|
def load_section!
|
119
119
|
@section_loaded = false
|
@@ -267,7 +267,7 @@ module MotionPrime
|
|
267
267
|
elements = Array.wrap(keyboard_close_bindings_options[:elements])
|
268
268
|
views = Array.wrap(keyboard_close_bindings_options[:views])
|
269
269
|
|
270
|
-
elements.each do |el|
|
270
|
+
elements.each do |el|
|
271
271
|
views << el.view if %w[text_field text_view].include?(el.view_name) && el.view
|
272
272
|
end
|
273
273
|
views.compact.each(&:resignFirstResponder)
|
@@ -108,7 +108,6 @@ module MotionPrime
|
|
108
108
|
focus
|
109
109
|
form.on_input_change(view(:input))
|
110
110
|
end
|
111
|
-
view(:input).delegate = self.form.table_delegate
|
112
111
|
end
|
113
112
|
|
114
113
|
def observing_errors?
|
@@ -154,6 +153,7 @@ module MotionPrime
|
|
154
153
|
unobserve observing_errors_for.errors, observing_errors_for.errors.unique_key(field)
|
155
154
|
}.weak!
|
156
155
|
errors_observer_fields.each(&block)
|
156
|
+
# TODO: clear 'on' events
|
157
157
|
end
|
158
158
|
|
159
159
|
def container_height
|
@@ -3,7 +3,7 @@ module MotionPrime
|
|
3
3
|
element :label, type: :label do
|
4
4
|
options[:label] || {}
|
5
5
|
end
|
6
|
-
element :input, type: :text_field do
|
6
|
+
element :input, type: :text_field, delegate: proc { form.table_delegate } do
|
7
7
|
{secure_text_entry: true}.merge(options[:input] || {})
|
8
8
|
end
|
9
9
|
element :error_message, type: :error_message, text: proc { all_errors.join("\n") if observing_errors? }
|
@@ -19,6 +19,13 @@ module MotionPrime
|
|
19
19
|
[]
|
20
20
|
end
|
21
21
|
|
22
|
+
def dealloc
|
23
|
+
pp 'Deallocating table', self.to_s, self.table_view.to_s
|
24
|
+
table_delegate.clear_delegated
|
25
|
+
table_view.setDataSource nil
|
26
|
+
super
|
27
|
+
end
|
28
|
+
|
22
29
|
def async_data?
|
23
30
|
self.class.async_data_options
|
24
31
|
end
|
@@ -3,12 +3,22 @@ module MotionPrime
|
|
3
3
|
attr_accessor :table_section
|
4
4
|
def initialize(options)
|
5
5
|
self.table_section = options[:section].try(:weak_ref)
|
6
|
+
@section_instance = table_section.to_s
|
6
7
|
end
|
7
8
|
|
8
|
-
|
9
|
-
|
10
|
-
|
11
|
-
|
9
|
+
def delegated_by(view)
|
10
|
+
@delegated_views ||= []
|
11
|
+
@delegated_views << view
|
12
|
+
end
|
13
|
+
|
14
|
+
def clear_delegated
|
15
|
+
Array.wrap(@delegated_views).each { |view| view.setDelegate(nil) }
|
16
|
+
end
|
17
|
+
|
18
|
+
def dealloc
|
19
|
+
pp 'Deallocating table_delegate for ', @section_instance
|
20
|
+
super
|
21
|
+
end
|
12
22
|
|
13
23
|
def init_pull_to_refresh
|
14
24
|
return unless block = table_section.class.pull_to_refresh_block
|