motion-prime 0.4.5 → 0.5.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.
Files changed (88) hide show
  1. checksums.yaml +14 -6
  2. data/.gitignore +1 -0
  3. data/CHANGELOG.md +14 -0
  4. data/Gemfile.lock +1 -1
  5. data/README.md +10 -8
  6. data/ROADMAP.md +9 -10
  7. data/Rakefile +1 -1
  8. data/doc/code/getting_started.rb +3 -3
  9. data/doc/code/screens.rb +25 -10
  10. data/doc/code/sections.rb +41 -0
  11. data/doc/docs/getting_started.html +8 -3
  12. data/doc/docs/screens.html +58 -19
  13. data/doc/docs/sections.html +134 -0
  14. data/files/Gemfile +7 -1
  15. data/files/Gemfile.lock +18 -9
  16. data/files/Rakefile +2 -11
  17. data/files/app/app_delegate.rb +2 -2
  18. data/files/app/config/base.rb +0 -4
  19. data/files/app/screens/application_screen.rb +2 -2
  20. data/files/app/screens/home_screen.rb +2 -0
  21. data/files/app/screens/sidebar_screen.rb +3 -3
  22. data/files/app/sections/home/section.rb +3 -0
  23. data/files/app/sections/sidebar/action.rb +1 -1
  24. data/files/app/styles/home.rb +9 -0
  25. data/files/app/styles/sidebar.rb +3 -3
  26. data/lib/motion-prime.rb +9 -0
  27. data/motion-prime/api_client.rb +5 -1
  28. data/motion-prime/app_delegate.rb +40 -45
  29. data/motion-prime/core_ext/kernel.rb +8 -0
  30. data/motion-prime/elements/_content_text_mixin.rb +3 -1
  31. data/motion-prime/elements/_text_mixin.rb +1 -1
  32. data/motion-prime/elements/base_element.rb +6 -5
  33. data/motion-prime/elements/draw/image.rb +6 -1
  34. data/motion-prime/elements/view_with_section.rb +7 -0
  35. data/motion-prime/helpers/has_style_chain_builder.rb +4 -1
  36. data/motion-prime/models/_association_mixin.rb +139 -0
  37. data/motion-prime/models/_base_mixin.rb +184 -0
  38. data/motion-prime/models/_dirty_mixin.rb +31 -0
  39. data/motion-prime/models/_finder_mixin.rb +208 -0
  40. data/motion-prime/models/{bag.rb → _nano_bag_mixin.rb} +4 -4
  41. data/motion-prime/models/{sync.rb → _sync_mixin.rb} +75 -76
  42. data/motion-prime/models/json.rb +6 -9
  43. data/motion-prime/models/model.rb +24 -172
  44. data/motion-prime/{mp.rb → prime.rb} +0 -1
  45. data/motion-prime/screens/_base_mixin.rb +9 -10
  46. data/motion-prime/screens/_navigation_mixin.rb +12 -17
  47. data/motion-prime/screens/extensions/_indicators_mixin.rb +1 -1
  48. data/motion-prime/screens/extensions/_navigation_bar_mixin.rb +3 -3
  49. data/motion-prime/screens/{base_screen.rb → screen.rb} +11 -5
  50. data/motion-prime/sections/_cell_section_mixin.rb +13 -0
  51. data/motion-prime/sections/_draw_section_mixin.rb +17 -0
  52. data/motion-prime/sections/base_section.rb +51 -16
  53. data/motion-prime/sections/form/base_field_section.rb +27 -16
  54. data/motion-prime/sections/form/base_header_section.rb +4 -3
  55. data/motion-prime/sections/form/password_field_section.rb +4 -0
  56. data/motion-prime/sections/form/select_field_section.rb +4 -0
  57. data/motion-prime/sections/form/string_field_section.rb +4 -0
  58. data/motion-prime/sections/form/submit_field_section.rb +4 -0
  59. data/motion-prime/sections/form/text_field_section.rb +4 -0
  60. data/motion-prime/sections/form.rb +7 -1
  61. data/motion-prime/sections/tabbed.rb +1 -1
  62. data/motion-prime/sections/table/table_delegate.rb +5 -0
  63. data/motion-prime/sections/table.rb +71 -51
  64. data/motion-prime/services/logger.rb +37 -0
  65. data/motion-prime/support/mp_cell_with_section.rb +9 -4
  66. data/motion-prime/support/tab_bar_controller.rb +2 -1
  67. data/motion-prime/version.rb +1 -1
  68. data/motion-prime/views/layout.rb +1 -1
  69. data/motion-prime/views/view_builder.rb +7 -0
  70. data/motion-prime.gemspec +1 -1
  71. data/spec/delegate/{base_delegate_spec.rb → delegate_spec.rb} +1 -1
  72. data/spec/helpers/{base_delegate.rb → delegates.rb} +0 -0
  73. data/spec/helpers/models.rb +11 -18
  74. data/spec/helpers/{base_screen.rb → screens.rb} +1 -1
  75. data/spec/models/{association_spec.rb → associations_spec.rb} +1 -1
  76. data/spec/models/bag_spec.rb +1 -1
  77. data/spec/models/errors_spec.rb +1 -1
  78. data/spec/models/finder_spec.rb +1 -1
  79. data/spec/models/json.rb +87 -0
  80. data/spec/models/model_spec.rb +23 -3
  81. data/spec/models/store_extension_spec.rb +1 -1
  82. data/spec/models/store_spec.rb +1 -1
  83. data/spec/screens/{base_screen_spec.rb → screen_spec.rb} +5 -1
  84. metadata +52 -43
  85. data/motion-prime/models/association.rb +0 -134
  86. data/motion-prime/models/base.rb +0 -27
  87. data/motion-prime/models/finder.rb +0 -202
  88. data/motion-prime/screens/sidebar_container_screen.rb +0 -80
@@ -1,134 +0,0 @@
1
- module MotionPrime
2
- module ModelAssociationMethods
3
- def _bags
4
- @_bags ||= {}
5
- end
6
-
7
- def save
8
- _bags.values.each do |bag|
9
- bag.store = self.store
10
- bag.save
11
- end
12
- super
13
- end
14
- end
15
- module ModelAssociationClassMethods
16
- # Defines bag associated with model, creates accessor for bag
17
- #
18
- # @param [String] name - the name of bag
19
- # @return [Nil]
20
- def bag(name)
21
- define_method(name) do |*args, &block|
22
- return _bags[name] if _bags[name]
23
- bag_key = self.info[name]
24
- if bag_key.present?
25
- bag = self.class.store.bagsWithKeysInArray([bag_key]).first
26
- end
27
- unless bag
28
- bag = Bag.bag
29
- self.info[name] = bag.key
30
- end
31
-
32
- _bags[name] = bag
33
- end
34
-
35
- define_method((name + "=").to_sym) do |*args, &block|
36
- bag = self.send(name)
37
- case args[0]
38
- when Bag
39
- bag.clear
40
- bag += args[0].saved.values
41
- when Array
42
- bag.clear
43
- bag += args[0]
44
- else
45
- raise StoreError, "Unexpected type assigned to bags, must be an Array or MotionPrime::Bag, now: #{args[0].class}"
46
- end
47
- bag
48
- end
49
- end
50
-
51
- # Defines has one association for model, creates accessor for association
52
- #
53
- # @param [String] name - the name of association
54
- # @return [Nil]
55
- def has_one(association_name, options = {})
56
- bag_name = "#{association_name.pluralize}_bag"
57
- self.bag bag_name.to_sym
58
-
59
- self._associations ||= {}
60
- self._associations[association_name] = options.merge(type: :one)
61
-
62
- define_method("#{association_name}=") do |value|
63
- self.send(bag_name).clear
64
- self.send(:"#{bag_name}") << value
65
- value
66
- end
67
- define_method("#{association_name}_attributes=") do |value|
68
- self.send(bag_name).clear
69
-
70
- association = association_name.classify.constantize.new
71
- association.fetch_with_attributes(value)
72
- association.save
73
- self.send(:"#{bag_name}") << association
74
- association
75
- end
76
- define_method("#{association_name}") do
77
- self.send(:"#{bag_name}").to_a.first
78
- end
79
- end
80
-
81
- # Defines has many association for model, creates accessor for association
82
- #
83
- # @param [String] name - the name of association
84
- # @return [Nil]
85
- def has_many(association_name, options = {})
86
- bag_name = "#{association_name}_bag"
87
- self.bag bag_name.to_sym
88
-
89
- self._associations ||= {}
90
- self._associations[association_name] = options.merge(type: :many)
91
-
92
- define_method("#{association_name}_attributes=") do |value|
93
- self.send(bag_name).clear
94
-
95
- pending_save_counter = 0
96
- collection = value.inject({}) do |result, attrs|
97
- model = association_name.classify.constantize.new
98
- model.fetch_with_attributes(attrs)
99
- unique_key = model.id || "pending_#{pending_save_counter+=1}"
100
- result.merge(unique_key => model)
101
- end
102
- association_data = collection.values
103
- self.send(:"#{bag_name}=", association_data)
104
- association_data
105
- end
106
- define_method("#{association_name}=") do |value|
107
- self.send(bag_name).clear
108
- self.send(:"#{bag_name}=", value)
109
- end
110
- define_method("#{association_name}") do |*args|
111
- bag = self.send(:"#{bag_name}")
112
- collection_options = {
113
- association_name: association_name,
114
- inverse_relation: {
115
- type: :has_one,
116
- name: self.class_name_without_kvo.demodulize.underscore,
117
- instance: self
118
- }
119
- }
120
- AssociationCollection.new(bag, collection_options, *args)
121
- end
122
- end
123
-
124
- def belongs_to(association_name, options = {})
125
- self._associations ||= {}
126
- self._associations[association_name] = {
127
- type: :belongs_to_one,
128
- class_name: association_name.classify
129
- }.merge(options)
130
-
131
- self.send(:attr_accessor, association_name)
132
- end
133
- end
134
- end
@@ -1,27 +0,0 @@
1
- motion_require '../helpers/has_authorization'
2
- motion_require './bag.rb'
3
- motion_require './finder.rb'
4
- motion_require './model.rb'
5
- motion_require './store.rb'
6
- motion_require './sync.rb'
7
- motion_require './association.rb'
8
- motion_require './store_extension.rb'
9
- module MotionPrime
10
- class BaseModel < NSFNanoObject
11
- include MotionPrime::HasAuthorization
12
- include MotionPrime::ModelMethods
13
- include MotionPrime::ModelAssociationMethods
14
- include MotionPrime::ModelSyncMethods
15
-
16
- extend MotionPrime::ModelClassMethods
17
- extend MotionPrime::ModelFinderMethods
18
- extend MotionPrime::ModelAssociationClassMethods
19
- extend MotionPrime::ModelSyncClassMethods
20
-
21
- attribute :bag_key # need this as we use shared store; each nested resource must belong to parent bag
22
-
23
- def errors
24
- @errors ||= Errors.new(self)
25
- end
26
- end
27
- end
@@ -1,202 +0,0 @@
1
- module MotionPrime
2
- module ModelFinderMethods
3
- attr_accessor :bare_class
4
-
5
- # Find all models
6
- #
7
- # @return [Array] array of models
8
- def all(*args)
9
- return [] unless self.store
10
-
11
- if args[0].is_a?(Hash)
12
- sort_options = args[0][:sort] || {}
13
- else
14
- sort_options = {}
15
- end
16
-
17
- if sort_options.empty?
18
- self.store.objectsOfClassNamed(self.bare_class_name)
19
- else
20
- sort_descriptors = sort_descriptor_with_options(sort_options)
21
- self.store.objectsOfClassNamed(self.bare_class_name, usingSortDescriptors:sort_descriptors)
22
- end
23
- end
24
-
25
- def last
26
- all.last
27
- end
28
-
29
- def first
30
- all.first
31
- end
32
-
33
- # Find model by criteria
34
- #
35
- # Examples:
36
- # User.find(:name, NSFEqualTo, "Bob") # => [<User#1>]
37
- # User.find(:name => "Bob") # => [<User#1>]
38
- # User.find(:name => {NSFEqualTo => "Bob"}) # => [<User#1>]
39
- #
40
- # @return [Array] array of models
41
- def find(*arg)
42
- if arg[0].is_a?(Hash)
43
- # hash style
44
- options = arg[0]
45
- if arg[1] && arg[1].is_a?(Hash)
46
- sort_options = arg[1][:sort] || {}
47
- else
48
- sort_options = {}
49
- end
50
- elsif arg[0] && arg[1] && arg[2]
51
- # standard way to find
52
- options = {arg[0] => {arg[1] => arg[2]}}
53
- if arg[4] && arg[4].is_a?(Hash)
54
- sort_options = arg[4][:sort] || {}
55
- else
56
- sort_options = {}
57
- end
58
- elsif arg.empty?
59
- options = {}
60
- sort_options = {}
61
- else
62
- raise "unexpected parameters #{arg}"
63
- end
64
- search = NSFNanoSearch.searchWithStore(self.store)
65
-
66
- expressions = expressions_with_options(options)
67
- search.expressions = expressions
68
-
69
- sort_descriptors = sort_descriptor_with_options(sort_options)
70
- search.sort = sort_descriptors
71
- search.filterClass = self.bare_class_name
72
-
73
- error_ptr = Pointer.new(:id)
74
- searchResults = search.searchObjectsWithReturnType(NSFReturnObjects, error:error_ptr)
75
- raise StoreError, error_ptr[0].description if error_ptr[0]
76
-
77
- if searchResults.is_a?(NSDictionary)
78
- searchResults.values
79
- else
80
- searchResults
81
- end
82
- end
83
-
84
- # Find model keys by criteria
85
- #
86
- # Examples:
87
- # User.find_keys(:name, NSFEqualTo, "Bob") # => ["1"]
88
- # User.find_keys(:name => "Bob") # => ["1"]
89
- # User.find_keys(:name => {NSFEqualTo => "Bob"}) # => ["1"]
90
- #
91
- # @return [Array] array of models
92
- def find_keys(*arg)
93
- if arg[0].is_a?(Hash)
94
- # hash style
95
- options = arg[0]
96
- if arg[1] && arg[1].is_a?(Hash)
97
- sort_options = arg[1][:sort] || {}
98
- else
99
- sort_options = {}
100
- end
101
- elsif arg[0] && arg[1] && arg[2]
102
- # standard way to find
103
- options = {arg[0] => {arg[1] => arg[2]}}
104
- if arg[4] && arg[4].is_a?(Hash)
105
- sort_options = arg[4][:sort] || {}
106
- else
107
- sort_options = {}
108
- end
109
- elsif arg.empty?
110
- options = {}
111
- sort_options = {}
112
- else
113
- raise "unexpected parameters #{arg}"
114
- end
115
-
116
- search = NSFNanoSearch.searchWithStore(self.store)
117
-
118
- expressions = expressions_with_options(options)
119
- search.expressions = expressions
120
-
121
- sort_descriptors = sort_descriptor_with_options(sort_options)
122
- search.sort = sort_descriptors
123
- search.filterClass = self.bare_class_name
124
-
125
- error_ptr = Pointer.new(:id)
126
- searchResults = search.searchObjectsWithReturnType(NSFReturnKeys, error:error_ptr)
127
- raise StoreError, error_ptr[0].description if error_ptr[0]
128
-
129
- if searchResults.is_a?(NSDictionary)
130
- searchResults.values
131
- else
132
- searchResults
133
- end
134
- end
135
-
136
- # Find a model by key
137
- #
138
- # Examples:
139
- # User.find_by_key(my_key)
140
- #
141
- # @return [Object, Nil] an object or nil (if not found)
142
- def find_by_key(key)
143
- search = NSFNanoSearch.searchWithStore(self.store)
144
- search.key = key
145
-
146
- error_ptr = Pointer.new(:id)
147
- searchResult = search.searchObjectsWithReturnType(NSFReturnObjects, error:error_ptr).first
148
- raise StoreError, error_ptr[0].description if error_ptr[0]
149
-
150
- searchResult.last if searchResult
151
- end
152
-
153
- def bare_class_name
154
- subject = @bare_class || self
155
- subject.to_s.split("::").last
156
- end
157
-
158
- private
159
- def expressions_with_options(options)
160
- expressions = []
161
-
162
- options.each do |key, val|
163
- attribute = NSFNanoPredicate.predicateWithColumn(NSFAttributeColumn, matching:NSFEqualTo, value:key.to_s)
164
- expression = NSFNanoExpression.expressionWithPredicate(attribute)
165
- if val.is_a?(Hash)
166
- val.each do |operator, sub_val|
167
- value = NSFNanoPredicate.predicateWithColumn(NSFValueColumn, matching:operator, value:sub_val.to_s)
168
- expression.addPredicate(value, withOperator:NSFAnd)
169
- end
170
- elsif val.is_a?(Array)
171
- value = NSFNanoPredicate.predicateWithColumn(NSFValueColumn, matching:NSFEqualTo, value:val.pop)
172
- expression.addPredicate(value, withOperator:NSFAnd)
173
-
174
- val.each do |sub_val|
175
- value = NSFNanoPredicate.predicateWithColumn(NSFValueColumn, matching:NSFEqualTo, value:sub_val.to_s)
176
- expression.addPredicate(value, withOperator:NSFOr)
177
- end
178
- else
179
- value = NSFNanoPredicate.predicateWithColumn(NSFValueColumn, matching:NSFEqualTo, value:val.to_s)
180
- expression.addPredicate(value, withOperator:NSFAnd)
181
- end
182
- expressions << expression
183
- end
184
- return expressions
185
- end
186
-
187
- SORT_MAPPING = {
188
- 'asc' => true,
189
- 'desc' => false,
190
- }
191
-
192
- def sort_descriptor_with_options(options)
193
- sorter = options.collect do |opt_key, opt_val|
194
- if SORT_MAPPING.keys.include?(opt_val.to_s.downcase)
195
- NSFNanoSortDescriptor.alloc.initWithAttribute(opt_key.to_s, ascending:SORT_MAPPING[opt_val.to_s.downcase])
196
- else
197
- raise "unsupported sort parameters: #{opt_val}"
198
- end
199
- end
200
- end
201
- end
202
- end
@@ -1,80 +0,0 @@
1
- module MotionPrime
2
- class SidebarContainerScreen < RESideMenu
3
- include ::MotionPrime::ScreenBaseMixin
4
-
5
- def self.new(menu, content, options={})
6
- screen = self.alloc.initWithContentViewController(nil, menuViewController: nil)
7
- screen.backgroundImage = Prime::Config.sidebar.background_image
8
- screen.parallaxEnabled = false
9
-
10
- full_width = UIScreen.mainScreen.bounds.size.width
11
-
12
- if scale = options[:content_scale_value]
13
- screen.contentViewScaleValue = scale
14
- end
15
- x_offset = options[:x_offset] || 45
16
- screen.contentViewInPortraitOffsetCenterX = full_width*(1 + screen.contentViewScaleValue/2) - x_offset
17
-
18
- if y_offset = options[:y_offset]
19
- screen.contentViewInPortraitOffsetCenterY = UIScreen.mainScreen.bounds.size.height/2 + y_offset
20
- end
21
-
22
- screen.on_create(options) if screen.respond_to?(:on_create)
23
- screen.menu_controller = menu unless menu.nil?
24
- screen.content_controller = content unless content.nil?
25
-
26
- screen
27
- end
28
-
29
- def show_sidebar
30
- self.presentMenuViewController
31
- end
32
-
33
- def hide_sidebar
34
- self.hideMenuViewController
35
- end
36
-
37
- def menu_controller=(c)
38
- self.setMenuViewController prepare_controller(c)
39
- end
40
-
41
- def content_controller=(c)
42
- controller = prepare_controller(c)
43
- if should_reinit_content?(controller)
44
- self.setContentViewController controller
45
- else
46
- content_controller.viewControllers = [controller]
47
- end
48
- hide_sidebar
49
- end
50
-
51
- def menu_controller
52
- self.menuViewController
53
- end
54
-
55
- def content_controller
56
- self.contentViewController
57
- end
58
-
59
- private
60
-
61
- def should_reinit_content?(new_controller)
62
- content_controller.nil? ||
63
- content_controller.is_a?(TabBarController) ||
64
- new_controller.is_a?(TabBarController)
65
- end
66
-
67
- def prepare_controller(controller)
68
- controller = setup_screen_for_open(controller, {})
69
- if should_reinit_content?(controller)
70
- controller.wrap_in_navigation if controller.respond_to?(:wrap_in_navigation)
71
- controller.send(:on_screen_load) if controller.respond_to?(:on_screen_load)
72
- controller = controller.main_controller if controller.respond_to?(:main_controller)
73
- else
74
- controller.navigation_controller = content_controller if controller.respond_to?(:navigation_controller)
75
- controller.send(:on_screen_load) if controller.respond_to?(:on_screen_load)
76
- end
77
- controller
78
- end
79
- end
80
- end