motion-prime 0.8.1 → 0.8.2

Sign up to get free protection for your applications and to get access to all the features.
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