motion-prime 0.8.1 → 0.8.2

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 (41) hide show
  1. checksums.yaml +8 -8
  2. data/CHANGELOG.md +6 -0
  3. data/Gemfile.lock +1 -1
  4. data/ROADMAP.md +3 -0
  5. data/files/Gemfile +1 -1
  6. data/motion-prime/api_client.rb +5 -2
  7. data/motion-prime/core_ext/kernel.rb +36 -0
  8. data/motion-prime/elements/_content_text_mixin.rb +32 -11
  9. data/motion-prime/elements/base_element.rb +33 -16
  10. data/motion-prime/elements/draw.rb +10 -4
  11. data/motion-prime/elements/draw/image.rb +26 -15
  12. data/motion-prime/elements/map.rb +5 -0
  13. data/motion-prime/models/_association_mixin.rb +0 -3
  14. data/motion-prime/models/_base_mixin.rb +24 -3
  15. data/motion-prime/models/_filter_mixin.rb +28 -0
  16. data/motion-prime/models/_sync_mixin.rb +13 -10
  17. data/motion-prime/models/association_collection.rb +41 -16
  18. data/motion-prime/models/errors.rb +46 -24
  19. data/motion-prime/models/model.rb +8 -0
  20. data/motion-prime/screens/_sections_mixin.rb +1 -1
  21. data/motion-prime/screens/extensions/_indicators_mixin.rb +4 -2
  22. data/motion-prime/screens/extensions/_navigation_bar_mixin.rb +1 -1
  23. data/motion-prime/screens/screen.rb +4 -0
  24. data/motion-prime/sections/_async_form_mixin.rb +12 -0
  25. data/motion-prime/sections/_async_table_mixin.rb +193 -0
  26. data/motion-prime/sections/_cell_section_mixin.rb +6 -6
  27. data/motion-prime/sections/_draw_section_mixin.rb +13 -5
  28. data/motion-prime/sections/base_section.rb +62 -36
  29. data/motion-prime/sections/form.rb +19 -35
  30. data/motion-prime/sections/form/base_field_section.rb +42 -34
  31. data/motion-prime/sections/form/static_field_section.rb +9 -0
  32. data/motion-prime/sections/table.rb +143 -201
  33. data/motion-prime/sections/table/table_delegate.rb +15 -15
  34. data/motion-prime/services/table_data_indexes.rb +12 -2
  35. data/motion-prime/styles/base.rb +1 -1
  36. data/motion-prime/styles/form.rb +6 -6
  37. data/motion-prime/version.rb +1 -1
  38. data/motion-prime/views/layout.rb +5 -2
  39. data/motion-prime/views/view_styler.rb +2 -0
  40. data/spec/models/association_collection_spec.rb +28 -6
  41. metadata +6 -2
checksums.yaml CHANGED
@@ -1,15 +1,15 @@
1
1
  ---
2
2
  !binary "U0hBMQ==":
3
3
  metadata.gz: !binary |-
4
- NmUyZDc0MTg4NTY2NDE5MzMwNjI1NDgxMjI2ZDIzYzIwNWEzMWI5Nw==
4
+ YzY4ZDViYzJiOGMwNWM5YTdhN2UwZjNlYTNlOWQ0OTc0MDM4NDM4Yw==
5
5
  data.tar.gz: !binary |-
6
- ZGJkMWQ4NWM0NjVhMzVhMWI1NzBjMGM2M2EyNTE3ZDllZjhjNjBjMg==
6
+ ODRiYzRiZDAxNTMzYzdiNzVmNTg3N2FhNjY0M2E1ZGQ2NDU5YjdlZQ==
7
7
  !binary "U0hBNTEy":
8
8
  metadata.gz: !binary |-
9
- MjNjNjNiMGEzMzVlOTAwM2EyNTkxZDlhZGY2YmMwMGE2ZDk2ZTIyNWUwNjYw
10
- YmYxNDRjOThiZGJlM2IxNTNkZWNiMzI5ZjcyNDZmY2I3YWEwNjQ0MjYzNmMx
11
- MzlmMjk1YjI0MjZjMGE1NTg4NDFkMzlkNTgwZTczZWUwMDZmZDE=
9
+ MDAyNTU2NzE0ODhhZWIxNzg5M2Y3ZWRlOWVlYWE0MjJmMDEzM2ZlMzE2NDI1
10
+ ZmMyODZjZGEyYjBmYmJlODZhZWRmYzZmZjg3OWE5ZmMzMDkxMjBkMGE2YjY1
11
+ NDNiMjMzN2I1NWRhNjVjNjJkYTc3NmZhYTI0ZmRlYTcxYjA4ZWM=
12
12
  data.tar.gz: !binary |-
13
- NTE1MzZiZDA2NWMwOWQ4YjdjNThmNjMzMmMwOTY2ZGY4Y2IwZGU0OWFmMjc0
14
- ZjMxZjg1Yjc4MDkwZGZlYzgwZTlkNDM2YTZiYzE0ODQ5M2FlNGVmZGFhN2Rl
15
- MTRjZjMwNzVkMmYyNmM1NzUzZTg3Y2ZkNzczZTI5YjRiNDFiYTM=
13
+ MWJiZDQ3OGJlN2QzNWRlNjVhNTg2MGQyN2U2NThlNTUwNmUyMmFhMGZiN2Y1
14
+ MmQ2YzY0MjFmYzA4YjU4NzVkOThkY2Q4ZmNiM2Q4YTFiMjBlMWE1OGI2MWFm
15
+ NmZjOGQ2YWU5NGQ4NDJjM2Y2ZTc2MGUxN2IxMzExNGQ0YzViZTU=
data/CHANGELOG.md CHANGED
@@ -1,3 +1,9 @@
1
+ === 0.8.2
2
+ * bug fixes.
3
+ * added #clone method for Model.
4
+ * added #filter method for association collection.
5
+ * memory leak fixes.
6
+
1
7
  === 0.8.1
2
8
  * renamed submit element in submit field to button element.
3
9
  * renamed date_picker element in date_picker field to input element.
data/Gemfile.lock CHANGED
@@ -1,7 +1,7 @@
1
1
  PATH
2
2
  remote: .
3
3
  specs:
4
- motion-prime (0.8.1)
4
+ motion-prime (0.8.2)
5
5
  afmotion (~> 2.0.0)
6
6
  bubble-wrap (~> 1.4.0)
7
7
  cocoapods
data/ROADMAP.md CHANGED
@@ -1,10 +1,13 @@
1
1
  === 0.8.2
2
2
  * Move api_client and model sync mixin to prime_api gem.
3
+ * Move bind keyboard events to forms.
3
4
 
4
5
  === 0.9.0
5
6
  * bug: if mp label do not have text and was set as hidden, it should unhide after setting text
6
7
  * bug: size_to_fit works incorrect with relative width.
7
8
  * bug: bind_keyboard_close breaks bind_guesture
9
+ * bug: dealloc of Prime::Section will not be called for cell created in table_data using #map.
10
+ * bug: images does not render after reload table if using draw_with_layer (prerender not enabled).
8
11
  * add clone to models to prevent problems when bag_key is overrided
9
12
  * add dsl for push notifications
10
13
  * add some extensions/middleware system, at least for networking.
data/files/Gemfile CHANGED
@@ -3,7 +3,7 @@ source 'http://rubygems.org'
3
3
  gem 'motion-cocoapods', '~> 1.4.0'
4
4
  gem 'motion-support', '~> 0.2.4'
5
5
 
6
- gem 'motion-prime', '0.8.1'
6
+ gem 'motion-prime', '0.8.2'
7
7
 
8
8
  # add reside menu for sidebar support
9
9
  gem 'prime_reside_menu', '~> 0.1.4'
@@ -17,7 +17,7 @@ class ApiClient
17
17
  end
18
18
  if config.sign_request?
19
19
  signature = RmDigest::MD5.hexdigest(
20
- config.signature_secret + params.keys.map(&:to_s).sort.join
20
+ config.signature_secret + filter_sign_params(params).keys.map(&:to_s).sort.join
21
21
  )
22
22
  params.merge!(sign: signature)
23
23
  end
@@ -37,7 +37,7 @@ class ApiClient
37
37
  auth_data = response.object
38
38
 
39
39
  self.access_token = auth_data[:access_token] if auth_data
40
- block.call(auth_data, response.operation.response.statusCode) if use_callback
40
+ block.call(auth_data, response.operation.response.try(:statusCode)) if use_callback
41
41
  end
42
42
  true
43
43
  end
@@ -111,6 +111,9 @@ class ApiClient
111
111
  end
112
112
 
113
113
  private
114
+ def filter_sign_params(params)
115
+ params.except(:_files)
116
+ end
114
117
 
115
118
  def config
116
119
  MotionPrime::Config.api_client
@@ -15,6 +15,42 @@ class Kernel
15
15
  self
16
16
  end
17
17
 
18
+ def allocate_strong_references(key = nil)
19
+ unless self.respond_to?(:strong_references)
20
+ Prime.logger.debug "User must define `strong_references` in `#{self.class.name}`"
21
+ return false
22
+ end
23
+
24
+ refs = Array.wrap(self.strong_references).compact
25
+ unless refs.present?
26
+ Prime.logger.debug "`strong_references` are empty for `#{self.class.name}`"
27
+ return false
28
+ end
29
+
30
+ @_strong_references ||= {}
31
+ key ||= [@_strong_references.count, Time.now.to_i].join('_')
32
+ @_strong_references[key] = refs.map(&:strong_ref)
33
+ key
34
+ end
35
+
36
+ def release_strong_references(key = nil)
37
+ unless self.respond_to?(:strong_references)
38
+ Prime.logger.debug "User must define `strong_references` in `#{self.class.name}`"
39
+ return false
40
+ end
41
+ key ||= @_strong_references.keys.last
42
+ @_strong_references.delete(key)
43
+ key
44
+ end
45
+
46
+ def allocated_references_released?
47
+ unless self.respond_to?(:strong_references)
48
+ Prime.logger.debug "User must define `strong_references` in `#{self.class.name}`"
49
+ return false
50
+ end
51
+ @_strong_references.all? { |key, ref| @_strong_references.count; ref.retainCount == @_strong_references.count }
52
+ end
53
+
18
54
  def clear_instance_variables(options = {})
19
55
  ivars = self.instance_variables
20
56
  excluded_ivars = Array.wrap(options[:except]).map(&:to_s)
@@ -11,15 +11,19 @@ module MotionPrime
11
11
  (is_a?(ButtonElement) ? button_content_font : input_content_font) || :system.uifont
12
12
  end
13
13
 
14
- def content_attributed_text
14
+ def current_attributed_text
15
15
  if view.try(:is_a?, UITextView) && view.text.present?
16
16
  text = view.attributedText
17
17
  text += ' ' if text.to_s.end_with?("\n") # does not respect \n at the end by default
18
- return text
18
+ text
19
+ else
20
+ attributed_text_for_text(content_text)
19
21
  end
22
+ end
20
23
 
24
+ def attributed_text_for_text(text)
21
25
  options = {
22
- text: content_text,
26
+ text: text,
23
27
  font: content_font,
24
28
  line_spacing: computed_options[:line_spacing],
25
29
  line_height: computed_options[:line_height]
@@ -28,11 +32,19 @@ module MotionPrime
28
32
  end
29
33
 
30
34
  def content_width
35
+ @content_width = width_for_attributed_text(current_attributed_text)
36
+ end
37
+
38
+ def width_for_text(text)
39
+ width_for_attributed_text(attributed_text_for_text(text))
40
+ end
41
+
42
+ def width_for_attributed_text(attributed_text)
31
43
  min, max = computed_options[:min_width].to_f, computed_options[:max_width]
32
- return min if content_text.blank?
44
+ return min if attributed_text.blank?
33
45
 
34
- rect = get_content_rect(Float::MAX)
35
- @content_width = [[rect.size.width.ceil, max].compact.min, min].max.ceil
46
+ rect = get_content_rect(attributed_text, Float::MAX)
47
+ [[rect.size.width.ceil, max].compact.min, min].max.ceil
36
48
  end
37
49
 
38
50
  def cached_content_width
@@ -40,10 +52,19 @@ module MotionPrime
40
52
  end
41
53
 
42
54
  def content_height
55
+ @content_height = height_for_attributed_text(current_attributed_text)
56
+ end
57
+
58
+ def height_for_text(text)
59
+ height_for_attributed_text(attributed_text_for_text(text))
60
+ end
61
+
62
+ def height_for_attributed_text(attributed_text)
43
63
  min, max = computed_options[:min_height].to_f, computed_options[:max_height]
44
- return min if content_text.blank?
45
- rect = get_content_rect(computed_options[:width] - content_padding_width)
46
- @content_height = [[rect.size.height.ceil, max].compact.min, min].max.ceil
64
+ return min if attributed_text.blank?
65
+
66
+ rect = get_content_rect(attributed_text, computed_options[:width] - content_padding_width)
67
+ [[rect.size.height.ceil, max].compact.min, min].max.ceil
47
68
  end
48
69
 
49
70
  def cached_content_height
@@ -55,14 +76,14 @@ module MotionPrime
55
76
  end
56
77
 
57
78
  private
58
- def get_content_rect(width)
79
+ def get_content_rect(text, width)
59
80
  raise "Please set element width for content size calculation" unless width
60
81
 
61
82
  options = NSStringDrawingUsesLineFragmentOrigin
62
83
  if is_a?(TextViewElement)
63
84
  options |= NSStringDrawingUsesFontLeading | NSStringDrawingTruncatesLastVisibleLine
64
85
  end
65
- content_attributed_text.boundingRectWithSize([width, Float::MAX], options: options, context:nil)
86
+ text.boundingRectWithSize([width, Float::MAX], options: options, context:nil)
66
87
  end
67
88
 
68
89
  def button_content_text
@@ -27,9 +27,19 @@ module MotionPrime
27
27
  @name = options[:name]
28
28
  @block = options[:block]
29
29
  @view_name = self.class_name_without_kvo.demodulize.underscore.gsub(/(_draw)?_element/, '')
30
+
31
+ if Prime.env.development?
32
+ @_element_info = "#{@name} #{view_name} #{section.try(:name)} #{screen.class}"
33
+ @@_allocated_elements ||= []
34
+ @@_allocated_elements << @_element_info
35
+ end
30
36
  end
31
37
 
32
38
  def dealloc
39
+ if Prime.env.development?
40
+ index = @@_allocated_elements.index(@_element_info)
41
+ @@_allocated_elements.delete_at(index) if index
42
+ end
33
43
  Prime.logger.dealloc_message :element, self, self.name
34
44
  super
35
45
  end
@@ -41,12 +51,12 @@ module MotionPrime
41
51
 
42
52
  def render(options = {}, &block)
43
53
  run_callbacks :render do
44
- render!(&block)
54
+ render!(options, &block)
45
55
  end
46
56
  end
47
57
 
48
- def render!(&block)
49
- view = screen.add_view class_factory(view_class), computed_options do |view|
58
+ def render!(options = {}, &block)
59
+ view = screen.add_view class_factory(view_class), computed_options.merge(options) do |view|
50
60
  @view = view
51
61
  block.try(:call, view, self)
52
62
  end
@@ -72,11 +82,14 @@ module MotionPrime
72
82
  normalize_options(@computed_options, section, %w[text placeholder font title_label padding padding_left padding_right min_width min_outer_width max_width max_outer_width width left right])
73
83
  end
74
84
 
85
+ def reload!
86
+ compute_options!
87
+ end
88
+
75
89
  def update_with_options(new_options = {})
76
90
  options.merge!(new_options)
77
- compute_options!
91
+ reload!
78
92
  view.try(:removeFromSuperview)
79
- @view = nil
80
93
  render
81
94
  end
82
95
 
@@ -107,29 +120,33 @@ module MotionPrime
107
120
 
108
121
  def compute_style_options(*style_sources)
109
122
  has_errors = section.respond_to?(:observing_errors?) && observing_errors? && has_errors?
110
- is_cell_section = section.respond_to?(:cell_name)
123
+ is_cell_section = section.respond_to?(:cell_section_name)
111
124
 
112
125
  @styles = []
113
126
  if is_cell_section
114
127
  @styles += compute_cell_style_options(style_sources, has_errors)
115
128
  end
116
129
 
117
- # styles got from mixins option
118
- mixin_styles = style_sources.map do |source|
119
- normalize_object(source.delete(:mixins), section)
120
- end.flatten.map{ |m| :"_mixin_#{m}" }
121
- @styles += mixin_styles
130
+ mixins = []
131
+ custom_styles = []
132
+ style_sources.each do |source|
133
+ if source_mixins = source.delete(:mixins)
134
+ mixins += Array.wrap(normalize_object(source_mixins, section))
135
+ end
136
+ if source_styles = source.delete(:styles)
137
+ custom_styles += Array.wrap(normalize_object(source_styles, section))
138
+ end
139
+ end
140
+ # styles got from mixins option
141
+ @styles += mixins.map{ |m| :"_mixin_#{m}" }
122
142
 
123
143
  # don't use present? here, it's slower, while this method should be very fast
124
144
  if section && section.name && section.name != '' && name && name != ''
125
145
  # using for base sections
126
146
  @styles << [section.name, name].join('_').to_sym
127
147
  end
128
- # custom style (from options or block options), using for TableViews as well
129
- custom_styles = style_sources.map do |source|
130
- normalize_object(source.delete(:styles), section)
131
- end.flatten
132
148
 
149
+ # custom style (from options or block options), using for TableViews as well
133
150
  @styles += custom_styles
134
151
  # puts @view_class.to_s + @styles.inspect, ''
135
152
  @styles
@@ -140,7 +157,7 @@ module MotionPrime
140
157
  suffixes = {common: [], specific: []}
141
158
  all_styles = []
142
159
 
143
- # following example in Prime::TableSection#cell_styles
160
+ # following example in Prime::TableSection#cell_section_styles
144
161
  # form element/cell: <base|user>_form_field, <base|user>_form_string_field, user_form_field_email
145
162
  # table element/cell: <base|categories>_table_cell, categories_table_title
146
163
  if section.section_styles
@@ -60,8 +60,9 @@ module MotionPrime
60
60
  def computed_right; computed_left + computed_width end
61
61
  def computed_inner_right; computed_right - content_padding_right end
62
62
 
63
- def bind_gesture(action, receiver = nil)
64
- section.bind_gesture_on_container_for(self, action, receiver)
63
+ def bind_gesture(action, receiver = nil, target = nil)
64
+ target ||= section
65
+ target.bind_gesture_on_container_for(self, action, receiver)
65
66
  end
66
67
 
67
68
  def hide
@@ -78,10 +79,15 @@ module MotionPrime
78
79
  view.setNeedsDisplay
79
80
  end
80
81
 
81
- def update_with_options(new_options = {})
82
- options.merge!(new_options)
82
+ def reload!
83
+ reset_computed_values
83
84
  compute_options!
84
85
  section.cached_draw_image = nil
86
+ end
87
+
88
+ def update_with_options(new_options = {})
89
+ options.merge!(new_options)
90
+ reload!
85
91
  view.setNeedsDisplay
86
92
  end
87
93
 
@@ -14,7 +14,6 @@ module MotionPrime
14
14
 
15
15
  def draw_in(rect)
16
16
  return if computed_options[:hidden]
17
-
18
17
  draw_background_in_context(UIGraphicsGetCurrentContext())
19
18
  computed_options[:draw_in_rect] ? draw_in_context(UIGraphicsGetCurrentContext()) : draw_with_layer
20
19
  load_image
@@ -49,48 +48,60 @@ module MotionPrime
49
48
 
50
49
  def draw_with_layer
51
50
  options = draw_options
51
+ @layer.removeFromSuperlayer if @layer
52
52
  return unless image = options[:image]
53
53
  rect = options[:rect]
54
54
  radius = options[:corner_radius].to_f if options[:corner_radius] && options[:masks_to_bounds]
55
55
 
56
- layer = CALayer.layer
57
- layer.contents = image.CGImage
58
- layer.frame = rect
59
- layer.bounds = rect
56
+ @layer = CALayer.layer
57
+ @layer.contents = image.CGImage
58
+ @layer.frame = rect
59
+ @layer.bounds = rect
60
60
 
61
- layer.masksToBounds = options[:masks_to_bounds]
62
- layer.cornerRadius = radius if radius
61
+ @layer.masksToBounds = options[:masks_to_bounds]
62
+ @layer.cornerRadius = radius if radius
63
+ view.layer.addSublayer(@layer)
64
+ end
63
65
 
64
- view.layer.addSublayer(layer)
66
+ def strong_references
67
+ [section, (section.table if section.respond_to?(:cell_section_name))].compact
65
68
  end
66
69
 
67
70
  def load_image
68
- return if image_data || !computed_options[:url]
69
- # TODO: why so many references?
70
- @strong_refs = section.strong_references + [screen.main_controller.strong_ref]
71
+ return if @loading || image_data || !computed_options[:url]
72
+ @loading = true
73
+
74
+ ref_key = allocate_strong_references
71
75
  BW::Reactor.schedule do
72
76
  manager = SDWebImageManager.sharedManager
73
77
  manager.downloadWithURL(computed_options[:url],
74
78
  options: 0,
75
79
  progress: lambda{ |r_size, e_size| },
76
80
  completed: lambda{ |image, error, type, finished|
77
- if !image || screen.main_controller.retainCount == 1 || section.retainCount == 1
78
- @strong_refs = nil
81
+ if !image || allocated_references_released?
82
+ @loading = false
83
+ release_strong_references(ref_key)
79
84
  return
80
85
  end
81
86
 
82
87
  self.image_data = image
83
88
 
84
89
  section.cached_draw_image = nil
85
- if section.respond_to?(:cell_name)
90
+ if section.respond_to?(:cell_section_name)
86
91
  section.pending_display!
87
92
  else
88
93
  self.view.performSelectorOnMainThread :setNeedsDisplay, withObject: nil, waitUntilDone: false
89
94
  end
90
- @strong_refs = nil
95
+ @loading = false
96
+ release_strong_references(ref_key)
91
97
  }
92
98
  )
93
99
  end
94
100
  end
101
+
102
+ def update_with_options(new_options = {})
103
+ super
104
+ self.image_data = nil if new_options.has_key?(:url)
105
+ end
95
106
  end
96
107
  end
@@ -1,7 +1,12 @@
1
1
  module MotionPrime
2
2
  class MapElement < BaseElement
3
+ after_render :remove_watermark
3
4
  def view_class
4
5
  "MKMapView"
5
6
  end
7
+
8
+ def remove_watermark
9
+ self.view.subviews.last.removeFromSuperview
10
+ end
6
11
  end
7
12
  end