motion-prime 0.4.5 → 0.5.0

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