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.
- checksums.yaml +8 -8
- data/CHANGELOG.md +6 -0
- data/Gemfile.lock +1 -1
- data/ROADMAP.md +3 -0
- data/files/Gemfile +1 -1
- data/motion-prime/api_client.rb +5 -2
- data/motion-prime/core_ext/kernel.rb +36 -0
- data/motion-prime/elements/_content_text_mixin.rb +32 -11
- data/motion-prime/elements/base_element.rb +33 -16
- data/motion-prime/elements/draw.rb +10 -4
- data/motion-prime/elements/draw/image.rb +26 -15
- data/motion-prime/elements/map.rb +5 -0
- data/motion-prime/models/_association_mixin.rb +0 -3
- data/motion-prime/models/_base_mixin.rb +24 -3
- data/motion-prime/models/_filter_mixin.rb +28 -0
- data/motion-prime/models/_sync_mixin.rb +13 -10
- data/motion-prime/models/association_collection.rb +41 -16
- data/motion-prime/models/errors.rb +46 -24
- data/motion-prime/models/model.rb +8 -0
- data/motion-prime/screens/_sections_mixin.rb +1 -1
- data/motion-prime/screens/extensions/_indicators_mixin.rb +4 -2
- data/motion-prime/screens/extensions/_navigation_bar_mixin.rb +1 -1
- data/motion-prime/screens/screen.rb +4 -0
- data/motion-prime/sections/_async_form_mixin.rb +12 -0
- data/motion-prime/sections/_async_table_mixin.rb +193 -0
- data/motion-prime/sections/_cell_section_mixin.rb +6 -6
- data/motion-prime/sections/_draw_section_mixin.rb +13 -5
- data/motion-prime/sections/base_section.rb +62 -36
- data/motion-prime/sections/form.rb +19 -35
- data/motion-prime/sections/form/base_field_section.rb +42 -34
- data/motion-prime/sections/form/static_field_section.rb +9 -0
- data/motion-prime/sections/table.rb +143 -201
- data/motion-prime/sections/table/table_delegate.rb +15 -15
- data/motion-prime/services/table_data_indexes.rb +12 -2
- data/motion-prime/styles/base.rb +1 -1
- data/motion-prime/styles/form.rb +6 -6
- data/motion-prime/version.rb +1 -1
- data/motion-prime/views/layout.rb +5 -2
- data/motion-prime/views/view_styler.rb +2 -0
- data/spec/models/association_collection_spec.rb +28 -6
- metadata +6 -2
@@ -78,9 +78,7 @@ module MotionPrime
|
|
78
78
|
|
79
79
|
association = association_name.classify.constantize.new
|
80
80
|
association.fetch_with_attributes(value)
|
81
|
-
association.save
|
82
81
|
self.send(:"#{bag_name}") << association
|
83
|
-
self.send(:"#{bag_name}").save
|
84
82
|
association
|
85
83
|
end
|
86
84
|
define_method("#{association_name}") do
|
@@ -111,7 +109,6 @@ module MotionPrime
|
|
111
109
|
end
|
112
110
|
association_data = collection.values
|
113
111
|
self.send(:"#{bag_name}=", association_data)
|
114
|
-
self.send(:"#{bag_name}").save
|
115
112
|
association_data
|
116
113
|
end
|
117
114
|
define_method("#{association_name}=") do |value|
|
@@ -33,7 +33,7 @@ module MotionPrime
|
|
33
33
|
super || self.class.store
|
34
34
|
end
|
35
35
|
|
36
|
-
# Assigns attributes to model
|
36
|
+
# Assigns attributes to model
|
37
37
|
#
|
38
38
|
# @params attributes [Hash] attributes to be assigned
|
39
39
|
# @params options [Hash] options
|
@@ -53,7 +53,7 @@ module MotionPrime
|
|
53
53
|
end
|
54
54
|
end
|
55
55
|
|
56
|
-
# Assigns attribute to model
|
56
|
+
# Assigns attribute to model
|
57
57
|
#
|
58
58
|
# @params name [String, Symbol] attribute name
|
59
59
|
# @params value [Object] attribute value
|
@@ -105,6 +105,13 @@ module MotionPrime
|
|
105
105
|
"#<#{self.class}:0x#{self.object_id.to_s(16)}> " + MotionPrime::JSON.generate(info)
|
106
106
|
end
|
107
107
|
|
108
|
+
# Returns a clone of the record with empty bags
|
109
|
+
#
|
110
|
+
# @return new [Prime::Model] model
|
111
|
+
def clone
|
112
|
+
self.class.new(self.info.select { |key, value| !key.to_s.ends_with?('_bag') })
|
113
|
+
end
|
114
|
+
|
108
115
|
module ClassMethods
|
109
116
|
# Initialize a new object
|
110
117
|
#
|
@@ -167,7 +174,7 @@ module MotionPrime
|
|
167
174
|
define_method((name + "=").to_sym) do |*args, &block|
|
168
175
|
value = args[0]
|
169
176
|
case options[:type].to_s
|
170
|
-
when 'integer'
|
177
|
+
when 'integer'
|
171
178
|
value = value.to_i
|
172
179
|
when 'float'
|
173
180
|
value = value.to_f
|
@@ -178,6 +185,20 @@ module MotionPrime
|
|
178
185
|
self.info[name] = value
|
179
186
|
end
|
180
187
|
|
188
|
+
define_method(name.to_sym) do
|
189
|
+
value = self.info[name]
|
190
|
+
case options[:type].to_s
|
191
|
+
when 'integer'
|
192
|
+
value.to_i
|
193
|
+
when 'float'
|
194
|
+
value.to_f
|
195
|
+
when 'time' && !value.is_a?(String)
|
196
|
+
value.to_short_iso8601
|
197
|
+
else
|
198
|
+
value
|
199
|
+
end
|
200
|
+
end if options[:convert]
|
201
|
+
|
181
202
|
if options[:type].to_s == 'boolean'
|
182
203
|
define_method("#{name}?") do
|
183
204
|
!!self.info[name]
|
@@ -0,0 +1,28 @@
|
|
1
|
+
module MotionPrime
|
2
|
+
module FilterMixin
|
3
|
+
def filter_array(data, find_options = {}, sort_options = nil)
|
4
|
+
data = data.select do |entity|
|
5
|
+
find_options.all? { |field, value| entity.info[field] == value }
|
6
|
+
end if find_options.present?
|
7
|
+
|
8
|
+
data.sort! do |a, b|
|
9
|
+
left_part = []
|
10
|
+
right_part = []
|
11
|
+
|
12
|
+
sort_options[:sort].each do |(k,v)|
|
13
|
+
left = a.send(k)
|
14
|
+
right = b.send(k)
|
15
|
+
if left.class != right.class
|
16
|
+
left = left.to_s
|
17
|
+
right = right.to_s
|
18
|
+
end
|
19
|
+
left, right = right, left if v.to_s == 'desc'
|
20
|
+
left_part << left
|
21
|
+
right_part << right
|
22
|
+
end
|
23
|
+
left_part <=> right_part
|
24
|
+
end if sort_options.try(:[], :sort).present?
|
25
|
+
data
|
26
|
+
end
|
27
|
+
end
|
28
|
+
end
|
@@ -50,7 +50,7 @@ module MotionPrime
|
|
50
50
|
#
|
51
51
|
# @param options [Hash] fetch options
|
52
52
|
# @option options [Symbol] :method Http method to calculate url, `:get` by default
|
53
|
-
# @option options [Boolean] :associations Also fetch associations
|
53
|
+
# @option options [Boolean or Array] :associations Also fetch associations
|
54
54
|
# @option options [Boolean] :save Save model after fetch
|
55
55
|
# @param block [Proc] block to be executed after fetch
|
56
56
|
def fetch(options = {}, &block)
|
@@ -59,8 +59,8 @@ module MotionPrime
|
|
59
59
|
url = sync_url(method, options)
|
60
60
|
|
61
61
|
will_fetch_model = !url.blank?
|
62
|
-
will_fetch_associations =
|
63
|
-
will_fetch_associations = false unless has_associations_to_fetch?
|
62
|
+
will_fetch_associations = options.fetch(:associations, true)
|
63
|
+
will_fetch_associations = false unless has_associations_to_fetch?(options)
|
64
64
|
|
65
65
|
fetch_with_url url, options do |data, status_code|
|
66
66
|
save if options[:save]
|
@@ -176,13 +176,13 @@ module MotionPrime
|
|
176
176
|
@associations ||= (self.class._associations || {}).clone
|
177
177
|
end
|
178
178
|
|
179
|
-
def associations_to_fetch
|
180
|
-
|
179
|
+
def associations_to_fetch(options = {})
|
180
|
+
associations.select { |key, v| fetch_association?(key, options) }
|
181
181
|
end
|
182
182
|
|
183
183
|
def fetch_associations(sync_options = {}, &block)
|
184
184
|
use_callback = block_given?
|
185
|
-
associations_to_fetch.keys.each_with_index do |key, index|
|
185
|
+
associations_to_fetch(sync_options).keys.each_with_index do |key, index|
|
186
186
|
if use_callback && associations.count - 1 == index
|
187
187
|
fetch_association(key, sync_options, &block)
|
188
188
|
else
|
@@ -191,22 +191,25 @@ module MotionPrime
|
|
191
191
|
end
|
192
192
|
end
|
193
193
|
|
194
|
-
def has_associations_to_fetch?
|
195
|
-
associations_to_fetch.present?
|
194
|
+
def has_associations_to_fetch?(options = {})
|
195
|
+
associations_to_fetch(options).present?
|
196
196
|
end
|
197
197
|
|
198
198
|
def has_association?(key)
|
199
199
|
!associations[key.to_sym].nil?
|
200
200
|
end
|
201
201
|
|
202
|
-
def fetch_association?(key)
|
202
|
+
def fetch_association?(key, options = {})
|
203
|
+
allowed_associations = options[:associations].map(&:to_sym) if options[:associations].is_a?(Array)
|
204
|
+
return false if allowed_associations.try(:exclude?, key.to_sym)
|
205
|
+
|
203
206
|
options = associations[key.to_sym]
|
204
207
|
return false if options[:if] && !options[:if].to_proc.call(self)
|
205
208
|
association_sync_url(key, options).present?
|
206
209
|
end
|
207
210
|
|
208
211
|
def fetch_association(key, sync_options = {}, &block)
|
209
|
-
return unless fetch_association?(key)
|
212
|
+
return unless fetch_association?(key, sync_options)
|
210
213
|
options = associations[key.to_sym]
|
211
214
|
if options[:type] == :many
|
212
215
|
fetch_has_many(key, options, sync_options, &block)
|
@@ -1,10 +1,10 @@
|
|
1
1
|
module MotionPrime
|
2
2
|
class AssociationCollection < ::Array
|
3
|
+
include FilterMixin
|
4
|
+
|
3
5
|
attr_reader :bag, :association_name
|
4
6
|
attr_reader :inverse_relation_name, :inverse_relation_key, :model_inverse_relation_name
|
5
7
|
|
6
|
-
delegate :<<, to: :bag
|
7
|
-
|
8
8
|
def initialize(bag, options, *args)
|
9
9
|
@bag = bag
|
10
10
|
@association_name = options[:association_name]
|
@@ -17,7 +17,8 @@ module MotionPrime
|
|
17
17
|
options[:class_name] == inverse_relation.class_name_without_kvo
|
18
18
|
end.try(:first)
|
19
19
|
|
20
|
-
|
20
|
+
data = bag.store.present? ? find(*args) : filter(*args)
|
21
|
+
super data
|
21
22
|
end
|
22
23
|
|
23
24
|
# Initialize a new object and add to collection.
|
@@ -28,9 +29,7 @@ module MotionPrime
|
|
28
29
|
# @params attributes [Hash] attributes beeing assigned to model
|
29
30
|
# @return MotionPrime::Model unsaved model
|
30
31
|
def new(attributes = {})
|
31
|
-
record = model_class.new(attributes)
|
32
|
-
set_inverse_relation_for(model)
|
33
|
-
end
|
32
|
+
record = model_class.new(attributes)
|
34
33
|
add(record)
|
35
34
|
end
|
36
35
|
|
@@ -42,30 +41,50 @@ module MotionPrime
|
|
42
41
|
# @params record [Prime::Model] model which will be added to collection.
|
43
42
|
# @return MotionPrime::Model model
|
44
43
|
def add(record)
|
44
|
+
set_inverse_relation_for(record)
|
45
45
|
self.bag << record
|
46
46
|
record
|
47
47
|
end
|
48
|
+
alias_method :<<, :add
|
48
49
|
|
49
50
|
# Return all association records.
|
50
51
|
#
|
51
52
|
# @example:
|
52
53
|
# project.users.all
|
53
|
-
#
|
54
|
+
#
|
55
|
+
# @return Array<MotionPrime::Model> association records
|
56
|
+
def all
|
57
|
+
data = bag.to_a
|
58
|
+
set_inverse_relation_for(data)
|
59
|
+
data
|
60
|
+
end
|
61
|
+
alias_method :to_a, :all
|
62
|
+
|
63
|
+
# Find association records.
|
64
|
+
#
|
65
|
+
# @example:
|
66
|
+
# project.users.find(age: 10)
|
54
67
|
#
|
55
68
|
# @params find_options [Hash] finder options.
|
56
69
|
# @params sort_options [Hash] sorting options.
|
57
70
|
# @return Array<MotionPrime::Model> association records
|
58
|
-
def
|
71
|
+
def find(find_options = {}, sort_options = nil)
|
72
|
+
raise "Use `filter` method when bag has not been saved yet" unless bag.store.present?
|
73
|
+
|
59
74
|
find_options = build_find_options(find_options)
|
60
75
|
sort_options = build_sort_options(sort_options)
|
61
76
|
|
62
|
-
data =
|
63
|
-
|
64
|
-
|
65
|
-
|
66
|
-
|
67
|
-
|
68
|
-
|
77
|
+
data = bag.find(find_options, sort_options)
|
78
|
+
set_inverse_relation_for(data)
|
79
|
+
data
|
80
|
+
end
|
81
|
+
|
82
|
+
def filter(find_options = {}, sort_options = nil)
|
83
|
+
find_options = build_find_options(find_options)
|
84
|
+
sort_options = build_sort_options(sort_options)
|
85
|
+
|
86
|
+
data = filter_array(bag.to_a, find_options, sort_options)
|
87
|
+
|
69
88
|
set_inverse_relation_for(data)
|
70
89
|
data
|
71
90
|
end
|
@@ -96,7 +115,13 @@ module MotionPrime
|
|
96
115
|
end
|
97
116
|
|
98
117
|
def build_sort_options(options)
|
99
|
-
options ||
|
118
|
+
options || begin
|
119
|
+
{sort: model_class.default_sort_options} if has_default_sort?
|
120
|
+
end
|
121
|
+
end
|
122
|
+
|
123
|
+
def has_default_sort?
|
124
|
+
model_class.default_sort_options.present?
|
100
125
|
end
|
101
126
|
|
102
127
|
def set_inverse_relation_for(models)
|
@@ -1,41 +1,39 @@
|
|
1
1
|
module MotionPrime
|
2
2
|
class Errors
|
3
|
-
attr_accessor :
|
3
|
+
attr_accessor :info
|
4
|
+
attr_reader :changes
|
4
5
|
|
5
6
|
def initialize(model)
|
6
|
-
@
|
7
|
+
@info = MotionSupport::HashWithIndifferentAccess.new
|
8
|
+
@changes = MotionSupport::HashWithIndifferentAccess.new
|
7
9
|
@model = model
|
8
10
|
model.class.attributes.map(&:to_sym).each do |key|
|
9
11
|
initialize_for_key key
|
10
12
|
end
|
11
13
|
end
|
12
14
|
|
13
|
-
def
|
14
|
-
|
15
|
-
end
|
16
|
-
|
17
|
-
def initialize_for_key(key)
|
18
|
-
unique_key = unique_key(key)
|
19
|
-
|
20
|
-
return if @_unique_keys.include?(unique_key)
|
21
|
-
@_unique_keys << unique_key
|
22
|
-
instance_variable_set("@#{unique_key}", [])
|
23
|
-
self.class.send :attr_accessor, unique_key
|
15
|
+
def to_hash
|
16
|
+
@info
|
24
17
|
end
|
25
18
|
|
26
19
|
def get(key)
|
27
20
|
initialize_for_key(key)
|
28
|
-
|
21
|
+
to_hash[key]
|
29
22
|
end
|
30
23
|
|
31
|
-
def set(key, errors)
|
24
|
+
def set(key, errors, options = {})
|
32
25
|
initialize_for_key(key)
|
33
|
-
|
26
|
+
|
27
|
+
track_changed options do
|
28
|
+
to_hash[key] = Array.wrap(errors)
|
29
|
+
end
|
34
30
|
end
|
35
31
|
|
36
|
-
def add(key, error)
|
32
|
+
def add(key, error, options = {})
|
37
33
|
initialize_for_key(key)
|
38
|
-
|
34
|
+
track_changed do
|
35
|
+
to_hash[key] << error
|
36
|
+
end
|
39
37
|
end
|
40
38
|
|
41
39
|
def [](key)
|
@@ -46,22 +44,26 @@ module MotionPrime
|
|
46
44
|
set(key, errors)
|
47
45
|
end
|
48
46
|
|
49
|
-
def reset_for(key)
|
50
|
-
|
47
|
+
def reset_for(key, options = {})
|
48
|
+
track_changed options do
|
49
|
+
to_hash[key] = []
|
50
|
+
end
|
51
51
|
end
|
52
52
|
|
53
53
|
def reset
|
54
|
-
|
55
|
-
|
54
|
+
track_changed do
|
55
|
+
to_hash.keys.each do |key|
|
56
|
+
reset_for(key, silent: true)
|
57
|
+
end
|
56
58
|
end
|
57
59
|
end
|
58
60
|
|
59
61
|
def messages
|
60
|
-
|
62
|
+
to_hash.values.flatten
|
61
63
|
end
|
62
64
|
|
63
65
|
def blank?
|
64
|
-
messages.
|
66
|
+
messages.none?
|
65
67
|
end
|
66
68
|
|
67
69
|
def present?
|
@@ -71,5 +73,25 @@ module MotionPrime
|
|
71
73
|
def to_s
|
72
74
|
messages.join(';')
|
73
75
|
end
|
76
|
+
|
77
|
+
def track_changed(options = {})
|
78
|
+
return yield if options[:silent]
|
79
|
+
@changes = MotionSupport::HashWithIndifferentAccess.new
|
80
|
+
saved_info = to_hash.clone
|
81
|
+
willChangeValueForKey(:info)
|
82
|
+
yield
|
83
|
+
to_hash.each do |key, value|
|
84
|
+
@changes[key] = [value, saved_info[key]] unless value == saved_info[key]
|
85
|
+
end
|
86
|
+
didChangeValueForKey(:info)
|
87
|
+
end
|
88
|
+
|
89
|
+
private
|
90
|
+
def initialize_for_key(key)
|
91
|
+
key = key.to_sym
|
92
|
+
return if @info.has_key?(key)
|
93
|
+
|
94
|
+
to_hash[key] ||= []
|
95
|
+
end
|
74
96
|
end
|
75
97
|
end
|
@@ -24,6 +24,14 @@ module MotionPrime
|
|
24
24
|
@errors ||= Errors.new(self.weak_ref)
|
25
25
|
end
|
26
26
|
|
27
|
+
def set_errors(data)
|
28
|
+
errors.track_changed do
|
29
|
+
data.symbolize_keys.each do |key, error_messages|
|
30
|
+
errors.set(key, error_messages, silent: true) if error_messages.present?
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
27
35
|
def dealloc
|
28
36
|
Prime.logger.dealloc_message :model, self
|
29
37
|
super
|
@@ -38,8 +38,10 @@ module MotionPrime
|
|
38
38
|
when 'alert' then MBAlertViewHUDTypeExclamationMark
|
39
39
|
else MBAlertViewHUDTypeCheckmark
|
40
40
|
end
|
41
|
-
|
42
|
-
|
41
|
+
|
42
|
+
unless time === false
|
43
|
+
MBHUDView.hudWithBody(message, type: hud_type, hidesAfter: time, show: true)
|
44
|
+
end
|
43
45
|
end
|
44
46
|
|
45
47
|
def show_spinner(message = nil)
|
@@ -58,8 +58,8 @@ module MotionPrime
|
|
58
58
|
face = UIButton.buttonWithType UIButtonTypeCustom
|
59
59
|
face.setImage(image, forState: UIControlStateNormal)
|
60
60
|
face.setTitle(title, forState: UIControlStateNormal)
|
61
|
-
face.bounds = CGRectMake(0, 0, 100, 60)
|
62
61
|
face.setContentHorizontalAlignment UIControlContentHorizontalAlignmentLeft
|
62
|
+
face.sizeToFit
|
63
63
|
face.on :touch do
|
64
64
|
args[:action].to_proc.call(self)
|
65
65
|
end
|