motion-prime 0.9.6 → 0.9.7

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.
Files changed (90) hide show
  1. checksums.yaml +8 -8
  2. data/CHANGELOG.md +6 -0
  3. data/Gemfile.lock +6 -6
  4. data/README.md +1 -10
  5. data/Rakefile +0 -1
  6. data/app/app_delegate.rb +1 -1
  7. data/bin/prime +1 -1
  8. data/files/Gemfile +1 -1
  9. data/motion-prime/api_client.rb +87 -31
  10. data/motion-prime/config/base.rb +20 -11
  11. data/motion-prime/config/config.rb +9 -3
  12. data/motion-prime/core_ext/kernel.rb +2 -1
  13. data/motion-prime/core_ext/nil_class.rb +5 -0
  14. data/motion-prime/delegate/app_delegate.rb +5 -0
  15. data/motion-prime/elements/base_element.rb +21 -2
  16. data/motion-prime/elements/draw.rb +6 -16
  17. data/motion-prime/helpers/has_authorization.rb +1 -1
  18. data/motion-prime/models/_base_mixin.rb +1 -1
  19. data/motion-prime/prime.rb +4 -0
  20. data/motion-prime/screens/extensions/_indicators_mixin.rb +16 -10
  21. data/motion-prime/screens/screen.rb +1 -1
  22. data/motion-prime/sections/_async_table_mixin.rb +1 -2
  23. data/motion-prime/sections/_draw_section_mixin.rb +3 -1
  24. data/motion-prime/sections/base_section.rb +37 -26
  25. data/motion-prime/sections/form.rb +23 -8
  26. data/motion-prime/sections/form/base_field_section.rb +1 -15
  27. data/motion-prime/sections/form/date_field_section.rb +1 -1
  28. data/motion-prime/sections/form/password_field_section.rb +1 -1
  29. data/motion-prime/sections/form/select_field_section.rb +1 -1
  30. data/motion-prime/sections/form/string_field_section.rb +1 -1
  31. data/motion-prime/sections/form/submit_field_section.rb +1 -1
  32. data/motion-prime/sections/form/text_field_section.rb +1 -1
  33. data/motion-prime/sections/table.rb +58 -27
  34. data/motion-prime/sections/table/refresh_mixin.rb +1 -1
  35. data/motion-prime/services/logger.rb +60 -21
  36. data/motion-prime/support/consts.rb +11 -0
  37. data/motion-prime/version.rb +1 -1
  38. data/motion-prime/views/layout.rb +5 -4
  39. data/motion-prime/views/view_styler.rb +3 -0
  40. data/spec/{helpers → factories}/delegates.rb +0 -3
  41. data/spec/{helpers → factories}/init.rb +0 -0
  42. data/spec/{helpers → factories}/models.rb +0 -0
  43. data/spec/factories/scaffold/models/task.rb +4 -0
  44. data/spec/factories/scaffold/screens/tasks.rb +45 -0
  45. data/spec/factories/scaffold/sections/tasks/form.rb +31 -0
  46. data/spec/factories/scaffold/sections/tasks/index_cell.rb +4 -0
  47. data/spec/factories/scaffold/sections/tasks/index_table.rb +12 -0
  48. data/spec/factories/scaffold/sections/tasks/show.rb +3 -0
  49. data/spec/factories/scaffold/styles/tasks.rb +18 -0
  50. data/spec/{helpers → factories}/screens.rb +0 -0
  51. data/spec/factories/sections.rb +9 -0
  52. data/spec/features/scaffold/index.rb +14 -0
  53. data/spec/features/screens/open_screen.rb +14 -2
  54. data/spec/helpers/has_content.rb +25 -0
  55. data/spec/unit/models/dirty_spec.rb +0 -1
  56. data/spec/unit/models/model_spec.rb +2 -0
  57. data/spec/unit/prime/logger.rb +49 -0
  58. data/spec/unit/sections/section_spec.rb +59 -0
  59. data/travis.sh +1 -1
  60. metadata +36 -41
  61. data/doc/FAQ.md +0 -2
  62. data/doc/SECTION.md +0 -7
  63. data/doc/STYLE.md +0 -39
  64. data/doc/code/getting_started.rb +0 -106
  65. data/doc/code/models.rb +0 -36
  66. data/doc/code/screens.rb +0 -93
  67. data/doc/code/sections.rb +0 -65
  68. data/doc/code/tables.rb +0 -54
  69. data/doc/docs/docco.css +0 -337
  70. data/doc/docs/getting_started.html +0 -195
  71. data/doc/docs/index.html +0 -205
  72. data/doc/docs/models.html +0 -115
  73. data/doc/docs/public/fonts/aller-bold.eot +0 -0
  74. data/doc/docs/public/fonts/aller-bold.ttf +0 -0
  75. data/doc/docs/public/fonts/aller-bold.woff +0 -0
  76. data/doc/docs/public/fonts/aller-light.eot +0 -0
  77. data/doc/docs/public/fonts/aller-light.ttf +0 -0
  78. data/doc/docs/public/fonts/aller-light.woff +0 -0
  79. data/doc/docs/public/fonts/fleurons.eot +0 -0
  80. data/doc/docs/public/fonts/fleurons.ttf +0 -0
  81. data/doc/docs/public/fonts/fleurons.woff +0 -0
  82. data/doc/docs/public/fonts/novecento-bold.eot +0 -0
  83. data/doc/docs/public/fonts/novecento-bold.ttf +0 -0
  84. data/doc/docs/public/fonts/novecento-bold.woff +0 -0
  85. data/doc/docs/public/images/gray.png +0 -0
  86. data/doc/docs/public/stylesheets/normalize.css +0 -375
  87. data/doc/docs/screens.html +0 -187
  88. data/doc/docs/sections.html +0 -138
  89. data/doc/docs/tables.html +0 -128
  90. data/spec/helpers/sections.rb +0 -3
@@ -36,36 +36,27 @@ module MotionPrime
36
36
 
37
37
  def bind_gesture(action, receiver = nil, target = nil)
38
38
  target ||= section
39
- target.bind_gesture_on_container_for(self, action, receiver)
39
+ target.bind_gesture_on_container_for(self, action, receiver.weak_ref)
40
40
  end
41
41
 
42
42
  def hide
43
43
  return if computed_options[:hidden]
44
44
  computed_options[:hidden] = true
45
- section.cached_draw_image = nil
46
- view.setNeedsDisplay
45
+ rerender!
47
46
  end
48
47
 
49
48
  def show
50
49
  return if !computed_options[:hidden]
51
50
  computed_options[:hidden] = false
52
- section.cached_draw_image = nil
53
- view.setNeedsDisplay
51
+ rerender!
54
52
  end
55
53
 
56
- def reload!
57
- reset_computed_values
58
- compute_options!
54
+ def rerender!
59
55
  section.cached_draw_image = nil
60
- end
61
-
62
- def update_with_options(new_options = {})
63
- options.merge!(new_options)
64
- reload!
65
56
  view.setNeedsDisplay
66
57
  end
67
58
 
68
- private
59
+ protected
69
60
  def frame_outer_width; computed_frame.size.width end
70
61
  def frame_width; frame_outer_width - content_padding_width end
71
62
 
@@ -85,8 +76,7 @@ module MotionPrime
85
76
  def frame_inner_right; frame_right - content_padding_right end
86
77
 
87
78
  def reset_computed_values
88
- @content_height = nil
89
- @content_width = nil
79
+ super
90
80
  @computed_frame = nil
91
81
  end
92
82
 
@@ -10,7 +10,7 @@ module MotionPrime
10
10
  current_user.present?
11
11
  end
12
12
  def api_client
13
- @api_client ||= ApiClient.new(access_token: current_user.try(:access_token))
13
+ App.delegate.api_client
14
14
  end
15
15
  end
16
16
  end
@@ -49,7 +49,7 @@ module MotionPrime
49
49
  elsif options[:validate_attribute_presence]
50
50
  raise(StoreError, "unknown attribute: '#{k}'")
51
51
  else
52
- NSLog("unknown attribute: #{k}")
52
+ Prime.logger.info("unknown attribute: #{k}")
53
53
  end
54
54
  end
55
55
  end
@@ -18,5 +18,9 @@ module MotionPrime
18
18
  def self.logger
19
19
  @logger ||= MotionPrime::Logger.new
20
20
  end
21
+
22
+ def self.logger=(value)
23
+ @logger = value
24
+ end
21
25
  end
22
26
  ::Prime = MotionPrime unless defined?(::Prime)
@@ -1,17 +1,23 @@
1
1
  module MotionPrime
2
2
  module ScreenIndicatorsMixin
3
- def show_activity_indicator
4
- if @activity_indicator_view.nil?
5
- @activity_indicator_view = UIActivityIndicatorView.gray
6
- @activity_indicator_view.center = CGPointMake(view.center.x, view.center.y)
7
- view.addSubview @activity_indicator_view
3
+ def show_activity_indicator(render_target = nil, options = {})
4
+ render_target ||= view
5
+ @activity_indicator_view ||= {}
6
+ indicator = @activity_indicator_view[render_target.object_id] ||= begin
7
+ indicator = UIActivityIndicatorView.gray
8
+ render_target.addSubview(indicator)
9
+ indicator
8
10
  end
9
- @activity_indicator_view.startAnimating
11
+
12
+ center = options[:center] || {}
13
+ indicator.center = CGPointMake(center.fetch(:x, render_target.center.x), center.fetch(:y, render_target.center.y))
14
+ indicator.startAnimating
10
15
  end
11
16
 
12
- def hide_activity_indicator
13
- return unless @activity_indicator_view
14
- @activity_indicator_view.stopAnimating
17
+ def hide_activity_indicator(render_target = nil)
18
+ @activity_indicator_view ||= {}
19
+ render_target ||= view
20
+ @activity_indicator_view[render_target.object_id].try(:stopAnimating)
15
21
  end
16
22
 
17
23
  def show_progress_indicator(text = nil, options = {})
@@ -24,7 +30,7 @@ module MotionPrime
24
30
  options[:add_to_view] ||= self.view
25
31
  @progress_indicator_view = self.progress_hud(options).view
26
32
  else
27
- self.set_options(@progress_indicator_view, options.except(:add_to_view))
33
+ self.update_options_for(@progress_indicator_view, options.except(:add_to_view))
28
34
  @progress_indicator_view.show options.has_key?(:animated) ? options[:animatetd] : true
29
35
  end
30
36
  end
@@ -35,7 +35,7 @@ module MotionPrime
35
35
  @visible = true
36
36
  @on_appear_happened ||= {}
37
37
  unless @on_appear_happened[view.object_id]
38
- set_options view, styles: default_styles do
38
+ set_options_for view, styles: default_styles do
39
39
  run_callbacks :render do
40
40
  send((action).to_sym)
41
41
  end
@@ -24,7 +24,7 @@ module Prime
24
24
  # Reset async loaded table data and preloader queue.
25
25
  #
26
26
  # @return [Boolean] true
27
- def reset_data
27
+ def reset_table_data
28
28
  super # must be before to update fixed_table_data
29
29
  @async_loaded_data = async_data? ? fixed_table_data : nil
30
30
  Array.wrap(@preloader_queue).each { |queue| queue[:state] = :cancelled }
@@ -175,7 +175,6 @@ module Prime
175
175
 
176
176
  def preload_section_by_index(index)
177
177
  section = cell_section_by_index(index)
178
-
179
178
  if section.create_elements && !section.container_element && async_data? # perform only if just loaded
180
179
  section.load_container_with_elements(container: container_element_options_for(index))
181
180
  section
@@ -37,7 +37,9 @@ module MotionPrime
37
37
 
38
38
  def clear_gesture_for_receiver(receiver)
39
39
  return unless self.container_gesture_recognizers
40
- self.container_gesture_recognizers.delete_if { |recognizer| recognizer[:receiver] == receiver }
40
+ self.container_gesture_recognizers.delete_if { |recognizer|
41
+ !recognizer[:receiver].weakref_alive? || recognizer[:receiver] == receiver
42
+ }
41
43
  end
42
44
 
43
45
  def prerender_elements_for_state(state = :normal)
@@ -22,7 +22,7 @@ module MotionPrime
22
22
  include DelegateMixin
23
23
 
24
24
  attr_accessor :screen, :model, :name, :options, :elements, :section_styles
25
- class_attribute :elements_options, :container_options, :keyboard_close_bindings
25
+ class_attribute :elements_options, :container_options, :keyboard_close_bindings, :elements_callbacks
26
26
  define_callbacks :render, :initialize
27
27
 
28
28
  def initialize(options = {})
@@ -139,22 +139,17 @@ module MotionPrime
139
139
 
140
140
  # Force reload section, will also re-render elements.
141
141
  # For table view cells will also reload it's table data.
142
+ # Useful on some cases, but in common case please use #reload.
142
143
  #
143
144
  # @return [Boolean] true
144
- def reload_section
145
+ def hard_reload_section
145
146
  # reload Base Elements
146
- # TODO: probably should use update_with_options for Base elements too?
147
147
  self.elements_to_render.values.map(&:view).flatten.each do |view|
148
148
  view.removeFromSuperview if view
149
149
  end
150
- create_elements!
151
- run_callbacks :render do
152
- render!
153
- end
150
+ render({}, true)
154
151
  # reload Draw Elements
155
- elements_to_draw.each do |key, element|
156
- element.update_with_options
157
- end
152
+ elements_to_draw.values.each(&:update)
158
153
 
159
154
  if @table && !self.is_a?(BaseFieldSection)
160
155
  cell.setNeedsDisplay
@@ -163,11 +158,23 @@ module MotionPrime
163
158
  true
164
159
  end
165
160
 
166
- # Alias for reload_section
161
+ # Reload section, will re-render elements.
167
162
  #
168
163
  # @return [Boolean] true
169
164
  def reload
170
- reload_section
165
+ elements.values.each(&:update)
166
+ true
167
+ end
168
+
169
+ def render_container(options = {}, &block)
170
+ if should_render_container? && !self.container_element.try(:view)
171
+ element = self.init_container_element(options)
172
+ element.render do
173
+ block.call
174
+ end
175
+ else
176
+ block.call
177
+ end
171
178
  end
172
179
 
173
180
  def add_element(key, options = {})
@@ -190,33 +197,30 @@ module MotionPrime
190
197
  true
191
198
  end
192
199
 
193
- def render(container_options = {})
194
- create_elements
200
+ def render(container_options = {}, force = false)
201
+ force ? create_elements! : create_elements
195
202
  self.container_options.merge!(container_options)
196
203
  run_callbacks :render do
197
204
  render!
198
205
  end
199
206
  end
200
207
 
201
- def render_container(options = {}, &block)
202
- if should_render_container? && !self.container_element.try(:view)
203
- element = self.init_container_element(options)
204
- element.render do
205
- block.call
206
- end
207
- else
208
- block.call
209
- end
210
- end
211
-
212
208
  def render!
213
209
  render_container(container_options) do
214
210
  elements_to_render.each do |key, element|
215
211
  element.render
212
+ on_element_render(element)
216
213
  end
217
214
  end
218
215
  end
219
216
 
217
+ def on_element_render(element)
218
+ return unless callbacks = elements_callbacks.try(:[], element.name)
219
+ callbacks.each do |options|
220
+ options[:method].to_proc.call(options[:target] || self)
221
+ end
222
+ end
223
+
220
224
  def element(name)
221
225
  self.elements ||= {}
222
226
  self.elements[name.to_sym]
@@ -305,7 +309,7 @@ module MotionPrime
305
309
  end
306
310
 
307
311
  def screen?
308
- screen && screen.weakref_alive?
312
+ screen.weakref_alive?
309
313
  end
310
314
 
311
315
  protected
@@ -387,6 +391,7 @@ module MotionPrime
387
391
 
388
392
  class << self
389
393
  def inherited(subclass)
394
+ subclass.elements_callbacks = self.elements_callbacks.try(:clone)
390
395
  subclass.elements_options = self.elements_options.try(:clone)
391
396
  subclass.container_options = self.container_options.try(:clone)
392
397
  subclass.keyboard_close_bindings = self.keyboard_close_bindings.try(:clone)
@@ -415,6 +420,12 @@ module MotionPrime
415
420
  def after_initialize(*method_names, &block)
416
421
  set_callback :initialize, :after, *method_names, &block
417
422
  end
423
+ def after_element_render(element_name, method, options = {})
424
+ options.merge!(method: method)
425
+ self.elements_callbacks ||= {}
426
+ self.elements_callbacks[element_name] ||= []
427
+ self.elements_callbacks[element_name] << options
428
+ end
418
429
  def bind_keyboard_close(options)
419
430
  self.keyboard_close_bindings = options
420
431
  end
@@ -18,18 +18,18 @@ module MotionPrime
18
18
  # end
19
19
  #
20
20
 
21
- class_attribute :fields_options, :text_field_limits, :text_view_limits
21
+ class_attribute :fields_options, :text_field_limits, :text_view_limits, :fields_callbacks
22
22
  attr_accessor :fields, :field_indexes, :keyboard_visible, :rendered_views, :grouped_data
23
23
 
24
24
  def table_data
25
25
  if has_many_sections?
26
- grouped_data.compact
26
+ grouped_data.reject(&:nil?)
27
27
  else
28
28
  fields.values
29
29
  end
30
30
  end
31
31
 
32
- def reload_cell_section(section)
32
+ def hard_reload_cell_section(section)
33
33
  field = section.name.to_sym
34
34
  path = field_indexes[field]
35
35
  section.cell.try(:removeFromSuperview)
@@ -43,10 +43,7 @@ module MotionPrime
43
43
  end
44
44
 
45
45
  set_data_stamp(fields[field].object_id)
46
-
47
- # table_view.beginUpdates
48
46
  table_view.reloadRowsAtIndexPaths([path], withRowAnimation: UITableViewRowAnimationNone)
49
- # table_view.endUpdates
50
47
  end
51
48
 
52
49
  # Returns element based on field name and element name
@@ -173,7 +170,7 @@ module MotionPrime
173
170
  def reload_data
174
171
  @groups_count = nil
175
172
  init_form_fields # must be before resetting to reflect changes on @data
176
- reset_data
173
+ reset_table_data
177
174
  reload_table_data
178
175
  end
179
176
 
@@ -191,6 +188,13 @@ module MotionPrime
191
188
  if options && options[:after_render]
192
189
  self.send(options[:after_render])
193
190
  end
191
+
192
+ section = cell_section_by_index(index)
193
+ if callbacks = fields_callbacks.try(:[], section.name)
194
+ callbacks.each do |options|
195
+ options[:method].to_proc.call(options[:target] || self)
196
+ end
197
+ end
194
198
  super
195
199
  end
196
200
 
@@ -198,13 +202,14 @@ module MotionPrime
198
202
  # ---------------------
199
203
 
200
204
  def number_of_groups(table = nil)
201
- has_many_sections? ? grouped_data.compact.count : 1
205
+ has_many_sections? ? grouped_data.reject(&:nil?).count : 1
202
206
  end
203
207
 
204
208
  class << self
205
209
  def inherited(subclass)
206
210
  super
207
211
  subclass.fields_options = self.fields_options.try(:clone)
212
+ subclass.fields_callbacks = self.fields_callbacks.try(:clone)
208
213
  subclass.text_field_limits = self.text_field_limits.try(:clone)
209
214
  subclass.text_view_limits = self.text_view_limits.try(:clone)
210
215
  end
@@ -223,6 +228,13 @@ module MotionPrime
223
228
  self.fields_options[name]
224
229
  end
225
230
 
231
+ def after_field_render(field_name, method, options = {})
232
+ options.merge!(method: method)
233
+ self.fields_callbacks ||= {}
234
+ self.fields_callbacks[field_name] ||= []
235
+ self.fields_callbacks[field_name] << options
236
+ end
237
+
226
238
  def limit_text_field_length(name, limit)
227
239
  self.text_field_limits ||= {}
228
240
  self.text_field_limits[name] = limit
@@ -252,6 +264,9 @@ module MotionPrime
252
264
  section_indexes[section_id] ||= 0
253
265
 
254
266
  section = load_field(field)
267
+ if section.options[:after_render].present?
268
+ puts "DEPRECATION: form field's option :after_render is deprecated, please use Prime::Section#after_field_render instead"
269
+ end
255
270
  self.fields[key] = section
256
271
  self.field_indexes[key] = NSIndexPath.indexPathForRow(section_indexes[section_id], inSection: section_id)
257
272
  grouped_data[section_id][section_indexes[section_id]] = section
@@ -30,19 +30,6 @@ module MotionPrime
30
30
  end
31
31
  end
32
32
 
33
- # Changes height of the field (the cell in table) with animation.
34
- #
35
- # @param height [Integet] new height of field
36
- # @return [MotionPrime::BaseFieldSection]
37
- def update_height(height)
38
- return if container_options[:height] == height
39
- container_options[:height] = height
40
- index = form.field_indexes[name]
41
- form.send :set_data_stamp, self.object_id
42
- form.table_view.reloadRowsAtIndexPaths([index], withRowAnimation: UITableViewRowAnimationFade)
43
- self
44
- end
45
-
46
33
  def on_section_render
47
34
  @status_for_updated = :rendered
48
35
  form.register_elements_from_section(self)
@@ -171,14 +158,13 @@ module MotionPrime
171
158
 
172
159
  def reload_section
173
160
  clear_observers
174
- form.reload_cell_section(self)
161
+ form.hard_reload_cell_section(self)
175
162
  end
176
163
 
177
164
  def clear_observers
178
165
  return unless observing_errors?
179
166
  # unobserve_all cause double dealloc, following code is a replacement
180
167
  unobserve observing_errors_for.errors, :info
181
- # TODO: clear 'on' events
182
168
  end
183
169
 
184
170
  def container_height
@@ -8,7 +8,7 @@ module MotionPrime
8
8
  options[:input] || {}
9
9
  end
10
10
 
11
- after_render :bind_input
11
+ after_element_render :input, :bind_input
12
12
 
13
13
  def bind_input
14
14
  picker = view(:input)
@@ -7,7 +7,7 @@ module MotionPrime
7
7
  {secure_text_entry: true}.merge(options[:input] || {})
8
8
  end
9
9
  element :error_message, type: :error_message, text: proc { |field| field.all_errors.join("\n") if field.observing_errors? }
10
- after_render :bind_text_input
10
+ after_element_render :input, :bind_text_input
11
11
 
12
12
  def value
13
13
  view(:input).text