motion-prime 0.1.0 → 0.1.1

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 (54) hide show
  1. data/.gitignore +2 -1
  2. data/.travis.yml +6 -0
  3. data/Gemfile +5 -10
  4. data/Gemfile.lock +10 -7
  5. data/README.md +15 -7
  6. data/Rakefile +3 -2
  7. data/files/Gemfile +8 -1
  8. data/files/Rakefile +2 -0
  9. data/files/app/app_delegate.rb +10 -0
  10. data/files/app/config/base.rb +6 -0
  11. data/files/app/models/.gitkeep +0 -0
  12. data/files/app/screens/application_screen.rb +4 -0
  13. data/files/app/screens/help_screen.rb +5 -0
  14. data/files/app/screens/home_screen.rb +5 -0
  15. data/files/app/screens/sidebar_screen.rb +14 -0
  16. data/files/app/sections/sidebar/action.rb +5 -0
  17. data/files/app/sections/sidebar/table.rb +20 -0
  18. data/files/app/styles/sidebar.rb +38 -0
  19. data/files/resources/images/navigation/bg.png +0 -0
  20. data/files/resources/images/navigation/bg@2x.png +0 -0
  21. data/files/resources/images/navigation/button.png +0 -0
  22. data/files/resources/images/navigation/button@2x.png +0 -0
  23. data/lib/motion-prime.rb +1 -1
  24. data/motion-prime.gemspec +3 -0
  25. data/motion-prime/config/base.rb +8 -0
  26. data/motion-prime/config/config.rb +49 -0
  27. data/motion-prime/elements/draw/label.rb +1 -1
  28. data/motion-prime/models/association.rb +115 -0
  29. data/motion-prime/models/bag.rb +129 -0
  30. data/motion-prime/models/base.rb +15 -65
  31. data/motion-prime/models/errors.rb +3 -0
  32. data/motion-prime/models/finder.rb +189 -0
  33. data/motion-prime/models/json.rb +45 -0
  34. data/motion-prime/models/model.rb +118 -0
  35. data/motion-prime/models/store.rb +53 -0
  36. data/motion-prime/models/store_extension.rb +159 -0
  37. data/motion-prime/styles/{forms.rb → base.rb} +4 -0
  38. data/motion-prime/support/dm_view_controller.rb +1 -1
  39. data/motion-prime/version.rb +1 -1
  40. data/spec/config/store_spec.rb +73 -0
  41. data/spec/delegate/base_delegate_spec.rb +12 -0
  42. data/spec/helpers/base_delegate.rb +5 -0
  43. data/spec/helpers/base_screen.rb +9 -0
  44. data/spec/helpers/models.rb +53 -0
  45. data/spec/models/association_spec.rb +49 -0
  46. data/spec/models/bag_spec.rb +92 -0
  47. data/spec/models/base_model_spec.rb +158 -0
  48. data/spec/models/finder_spec.rb +183 -0
  49. data/spec/models/store_extension_spec.rb +120 -0
  50. data/spec/models/store_spec.rb +51 -0
  51. data/spec/screens/base_screen_spec.rb +77 -0
  52. metadata +84 -8
  53. data/lib/view_styler.rb +0 -141
  54. data/spec/main_spec.rb +0 -9
@@ -0,0 +1,129 @@
1
+ module MotionPrime
2
+ module BagInstanceMethods
3
+ def self.included(base)
4
+ base.class_eval do
5
+ alias_method :saved, :savedObjects
6
+ alias_method :unsaved, :unsavedObjects
7
+ alias_method :removed, :removedObjects
8
+ alias_method :size, :count
9
+ alias_method :inflate, :inflateBag
10
+ alias_method :deflate, :deflateBag
11
+ end
12
+ end
13
+
14
+ def originalClassString
15
+ 'NSFNanoBag'
16
+ end
17
+
18
+ def changed?
19
+ self.hasUnsavedChanges
20
+ end
21
+
22
+ # Return all objects in bag
23
+ #
24
+ # @return Array
25
+ def to_a
26
+ self.savedObjects.values + self.unsavedObjects.values
27
+ end
28
+
29
+ # Add an object to bag
30
+ #
31
+ # @return self
32
+ def <<(object)
33
+ error_ptr = Pointer.new(:id)
34
+ self.addObject(object, error:error_ptr)
35
+ raise StoreError, error_ptr[0].description if error_ptr[0]
36
+ self
37
+ end
38
+
39
+ # Add an object or array of objects to bag
40
+ #
41
+ # @return self
42
+ def add(object_or_array)
43
+ error_ptr = Pointer.new(:id)
44
+ if object_or_array.is_a?(Array)
45
+ self.addObjectsFromArray(object_or_array, error:error_ptr)
46
+ else
47
+ self.addObject(object_or_array, error:error_ptr)
48
+ end
49
+ raise StoreError, error_ptr[0].description if error_ptr[0]
50
+ self
51
+ end
52
+ alias_method :+, :add
53
+
54
+ # Remove object from bag with key
55
+ #
56
+ # @param [String] key - a key or array of keys
57
+ # @return self
58
+ def delete_key(key)
59
+ if key.is_a?(Array)
60
+ self.removeObjectsWithKeysInArray(key)
61
+ else
62
+ self.removeObjectWithKey(key)
63
+ end
64
+ self
65
+ end
66
+
67
+ # Remove an object or array of objects from bag
68
+ #
69
+ # @param [String] object_or_array
70
+ # @return self
71
+ def delete(object_or_array)
72
+ error_ptr = Pointer.new(:id)
73
+ if object_or_array.is_a?(Array)
74
+ self.removeObjectsInArray(object_or_array, error: error_ptr)
75
+ else
76
+ self.removeObject(object_or_array, error_ptr)
77
+ end
78
+ raise StoreError, error_ptr[0].description if error_ptr[0]
79
+ self
80
+ end
81
+ alias_method :-, :delete
82
+
83
+ # Clear content of the bag
84
+ #
85
+ # @return self
86
+ def delete_all
87
+ bag_copy = to_a.clone
88
+ # this removes childrens from model
89
+ delete(self.to_a)
90
+ # this removes collection from db
91
+ bag_copy.each(&:delete)
92
+ self
93
+ end
94
+ alias_method :clear, :delete_all
95
+
96
+
97
+ def store=(store)
98
+ store.addObject(self, error:nil)
99
+ end
100
+
101
+ def save
102
+ self.store ||= MotionPrime::Store.shared_store
103
+ error_ptr = Pointer.new(:id)
104
+ result = self.saveAndReturnError(error_ptr)
105
+ raise StoreError, error_ptr[0].description if error_ptr[0]
106
+ result
107
+ end
108
+
109
+ def reload
110
+ error_ptr = Pointer.new(:id)
111
+ result = self.reloadBagWithError(error_ptr)
112
+ raise StoreError, error_ptr[0].description if error_ptr[0]
113
+ result
114
+ end
115
+
116
+ def undo
117
+ error_ptr = Pointer.new(:id)
118
+ result = self.undoChangesWithError(error_ptr)
119
+ raise StoreError, error_ptr[0].description if error_ptr[0]
120
+ result
121
+ end
122
+ end
123
+
124
+ Bag = ::NSFNanoBag
125
+ end
126
+
127
+ class NSFNanoBag
128
+ include MotionPrime::BagInstanceMethods
129
+ end
@@ -1,12 +1,25 @@
1
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 './store_extension.rb'
2
7
  module MotionPrime
3
- class BaseModel < NanoStore::Model
8
+ class BaseModel < NSFNanoObject
4
9
  class_attribute :sync_url
5
10
  class_attribute :sync_attributes
6
11
  class_attribute :_associations
7
12
  alias_method :attributes, :info
8
13
  include MotionPrime::HasAuthorization
9
14
 
15
+ include MotionPrime::ModelMethods
16
+ extend MotionPrime::ModelClassMethods
17
+
18
+ extend MotionPrime::ModelFinderMethods
19
+ include MotionPrime::ModelAssociationMethods
20
+
21
+ extend MotionPrime::ModelAssociationClassMethods
22
+
10
23
  def sync_url
11
24
  self.class.sync_url.to_s.gsub(':id', id.to_s)
12
25
  end
@@ -134,15 +147,7 @@ module MotionPrime
134
147
  end
135
148
 
136
149
  def inspect
137
- "#<#{self.class}:0x#{self.object_id.to_s(16)}> " + BW::JSON.generate(attributes)
138
- end
139
-
140
- # NOTE: .clear method doesn't work, using removeArray hack for now
141
- def _clear_bag(bag_name)
142
- bag = self.send(bag_name.to_sym)
143
- bag_copy = bag.to_a.clone
144
- bag - bag.to_a # this removes association from model
145
- bag_copy.each(&:delete) # this removes collection from db
150
+ "#<#{self.class}:0x#{self.object_id.to_s(16)}> " + MotionPrime::JSON.generate(attributes)
146
151
  end
147
152
 
148
153
  def filtered_sync_attributes
@@ -160,61 +165,6 @@ module MotionPrime
160
165
  def sync_attributes(*attrs)
161
166
  attrs ? self.sync_attributes = attrs : super
162
167
  end
163
-
164
- def has_one(association_name, options = {})
165
- bag_name = "#{association_name.pluralize}_bag"
166
- self.bag bag_name.to_sym
167
-
168
- self._associations ||= {}
169
- self._associations[association_name] = options.merge(type: :one)
170
-
171
- define_method("#{association_name}=") do |value|
172
- self._clear_bag(bag_name)
173
-
174
- self.send(:"#{bag_name}") << value
175
- value
176
- end
177
- define_method("#{association_name}_attributes=") do |value|
178
- self._clear_bag(bag_name)
179
-
180
- association = association_name.classify.constantize.new
181
- association.sync_with_attributes(value)
182
- association.save
183
- self.send(:"#{bag_name}") << association
184
- association
185
- end
186
- define_method("#{association_name}") do
187
- self.send(:"#{bag_name}").to_a.first
188
- end
189
- end
190
-
191
- def has_many(association_name, options = {})
192
- bag_name = "#{association_name}_bag"
193
- self.bag bag_name.to_sym
194
-
195
- self._associations ||= {}
196
- self._associations[association_name] = options.merge(type: :many)
197
-
198
- define_method("#{association_name}_attributes=") do |value|
199
- self._clear_bag(bag_name)
200
-
201
- association = []
202
- value.each do |attrs|
203
- model = association_name.classify.constantize.new
204
- model.sync_with_attributes(attrs)
205
- association << model
206
- end
207
- self.send(:"#{bag_name}=", association)
208
- association
209
- end
210
- define_method("#{association_name}=") do |value|
211
- self._clear_bag(bag_name)
212
- self.send(:"#{bag_name}=", value)
213
- end
214
- define_method("#{association_name}") do
215
- self.send(:"#{bag_name}").to_a
216
- end
217
- end
218
168
  end
219
169
  end
220
170
  end
@@ -0,0 +1,3 @@
1
+ module MotionPrime
2
+ class StoreError < StandardError; end
3
+ end
@@ -0,0 +1,189 @@
1
+ module MotionPrime
2
+ module ModelFinderMethods
3
+ # Find all models
4
+ #
5
+ # @return [Array] array of models
6
+ def all(*args)
7
+ if args[0].is_a?(Hash)
8
+ sort_options = args[0][:sort] || {}
9
+ else
10
+ sort_options = {}
11
+ end
12
+
13
+ if sort_options.empty?
14
+ self.store.objectsOfClassNamed(self.bare_class_name)
15
+ else
16
+ sort_descriptors = sort_descriptor_with_options(sort_options)
17
+ self.store.objectsOfClassNamed(self.bare_class_name, usingSortDescriptors:sort_descriptors)
18
+ end
19
+ end
20
+
21
+ # Find model by criteria
22
+ #
23
+ # Examples:
24
+ # User.find(:name, NSFEqualTo, "Bob") # => [<User#1>]
25
+ # User.find(:name => "Bob") # => [<User#1>]
26
+ # User.find(:name => {NSFEqualTo => "Bob"}) # => [<User#1>]
27
+ #
28
+ # @return [Array] array of models
29
+ def find(*arg)
30
+ if arg[0].is_a?(Hash)
31
+ # hash style
32
+ options = arg[0]
33
+ if arg[1] && arg[1].is_a?(Hash)
34
+ sort_options = arg[1][:sort] || {}
35
+ else
36
+ sort_options = {}
37
+ end
38
+ elsif arg[0] && arg[1] && arg[2]
39
+ # standard way to find
40
+ options = {arg[0] => {arg[1] => arg[2]}}
41
+ if arg[4] && arg[4].is_a?(Hash)
42
+ sort_options = arg[4][:sort] || {}
43
+ else
44
+ sort_options = {}
45
+ end
46
+ elsif arg.empty?
47
+ options = {}
48
+ sort_options = {}
49
+ else
50
+ raise "unexpected parameters #{arg}"
51
+ end
52
+ search = NSFNanoSearch.searchWithStore(self.store)
53
+
54
+ expressions = expressions_with_options(options)
55
+ search.expressions = expressions
56
+
57
+ sort_descriptors = sort_descriptor_with_options(sort_options)
58
+ search.sort = sort_descriptors
59
+ search.filterClass = self.bare_class_name
60
+
61
+ error_ptr = Pointer.new(:id)
62
+ searchResults = search.searchObjectsWithReturnType(NSFReturnObjects, error:error_ptr)
63
+ raise StoreError, error_ptr[0].description if error_ptr[0]
64
+
65
+ if searchResults.is_a?(NSDictionary)
66
+ searchResults.values
67
+ else
68
+ searchResults
69
+ end
70
+ end
71
+
72
+ # Find model keys by criteria
73
+ #
74
+ # Examples:
75
+ # User.find_keys(:name, NSFEqualTo, "Bob") # => ["1"]
76
+ # User.find_keys(:name => "Bob") # => ["1"]
77
+ # User.find_keys(:name => {NSFEqualTo => "Bob"}) # => ["1"]
78
+ #
79
+ # @return [Array] array of models
80
+ def find_keys(*arg)
81
+ if arg[0].is_a?(Hash)
82
+ # hash style
83
+ options = arg[0]
84
+ if arg[1] && arg[1].is_a?(Hash)
85
+ sort_options = arg[1][:sort] || {}
86
+ else
87
+ sort_options = {}
88
+ end
89
+ elsif arg[0] && arg[1] && arg[2]
90
+ # standard way to find
91
+ options = {arg[0] => {arg[1] => arg[2]}}
92
+ if arg[4] && arg[4].is_a?(Hash)
93
+ sort_options = arg[4][:sort] || {}
94
+ else
95
+ sort_options = {}
96
+ end
97
+ elsif arg.empty?
98
+ options = {}
99
+ sort_options = {}
100
+ else
101
+ raise "unexpected parameters #{arg}"
102
+ end
103
+
104
+ search = NSFNanoSearch.searchWithStore(self.store)
105
+
106
+ expressions = expressions_with_options(options)
107
+ search.expressions = expressions
108
+
109
+ sort_descriptors = sort_descriptor_with_options(sort_options)
110
+ search.sort = sort_descriptors
111
+ search.filterClass = self.bare_class_name
112
+
113
+ error_ptr = Pointer.new(:id)
114
+ searchResults = search.searchObjectsWithReturnType(NSFReturnKeys, error:error_ptr)
115
+ raise StoreError, error_ptr[0].description if error_ptr[0]
116
+
117
+ if searchResults.is_a?(NSDictionary)
118
+ searchResults.values
119
+ else
120
+ searchResults
121
+ end
122
+ end
123
+
124
+ # Find a model by key
125
+ #
126
+ # Examples:
127
+ # User.find_by_key(my_key)
128
+ #
129
+ # @return [Object, Nil] an object or nil (if not found)
130
+ def find_by_key(key)
131
+ search = NSFNanoSearch.searchWithStore(self.store)
132
+ search.key = key
133
+
134
+ error_ptr = Pointer.new(:id)
135
+ searchResult = search.searchObjectsWithReturnType(NSFReturnObjects, error:error_ptr).first
136
+ raise StoreError, error_ptr[0].description if error_ptr[0]
137
+
138
+ searchResult.last if searchResult
139
+ end
140
+
141
+ def bare_class_name
142
+ self.to_s.split("::").last
143
+ end
144
+
145
+ private
146
+ def expressions_with_options(options)
147
+ expressions = []
148
+
149
+ options.each do |key, val|
150
+ attribute = NSFNanoPredicate.predicateWithColumn(NSFAttributeColumn, matching:NSFEqualTo, value:key.to_s)
151
+ expression = NSFNanoExpression.expressionWithPredicate(attribute)
152
+ if val.is_a?(Hash)
153
+ val.each do |operator, sub_val|
154
+ value = NSFNanoPredicate.predicateWithColumn(NSFValueColumn, matching:operator, value:sub_val.to_s)
155
+ expression.addPredicate(value, withOperator:NSFAnd)
156
+ end
157
+ elsif val.is_a?(Array)
158
+ value = NSFNanoPredicate.predicateWithColumn(NSFValueColumn, matching:NSFEqualTo, value:val.pop)
159
+ expression.addPredicate(value, withOperator:NSFAnd)
160
+
161
+ val.each do |sub_val|
162
+ value = NSFNanoPredicate.predicateWithColumn(NSFValueColumn, matching:NSFEqualTo, value:sub_val.to_s)
163
+ expression.addPredicate(value, withOperator:NSFOr)
164
+ end
165
+ else
166
+ value = NSFNanoPredicate.predicateWithColumn(NSFValueColumn, matching:NSFEqualTo, value:val.to_s)
167
+ expression.addPredicate(value, withOperator:NSFAnd)
168
+ end
169
+ expressions << expression
170
+ end
171
+ return expressions
172
+ end
173
+
174
+ SORT_MAPPING = {
175
+ 'asc' => true,
176
+ 'desc' => false,
177
+ }
178
+
179
+ def sort_descriptor_with_options(options)
180
+ sorter = options.collect do |opt_key, opt_val|
181
+ if SORT_MAPPING.keys.include?(opt_val.to_s.downcase)
182
+ NSFNanoSortDescriptor.alloc.initWithAttribute(opt_key.to_s, ascending:SORT_MAPPING[opt_val.to_s.downcase])
183
+ else
184
+ raise "unsupported sort parameters: #{opt_val}"
185
+ end
186
+ end
187
+ end
188
+ end
189
+ end
@@ -0,0 +1,45 @@
1
+ module MotionPrime
2
+ module JSON
3
+ PARAMETRIZE_CLASSES = [Time, Date]
4
+ class ParserError < StandardError; end
5
+
6
+ # Parses a string or data object and converts it in data structure.
7
+ #
8
+ # @param [String, NSData] str_data the string or data to convert.
9
+ # @raise [ParserError] If the parsing of the passed string/data isn't valid.
10
+ # @return [Hash, Array, NilClass] the converted data structure, nil if the incoming string isn't valid.
11
+ def self.parse(str_data, &block)
12
+ return nil unless str_data
13
+ data = str_data.respond_to?(:to_data) ? str_data.to_data : str_data
14
+ opts = NSJSONReadingMutableContainers | NSJSONReadingMutableLeaves | NSJSONReadingAllowFragments
15
+ error = Pointer.new(:id)
16
+ obj = NSJSONSerialization.JSONObjectWithData(data, options: opts, error: error)
17
+ raise ParserError, error[0].description if error[0]
18
+ if block_given?
19
+ yield obj
20
+ else
21
+ obj
22
+ end
23
+ end
24
+
25
+ # Generates a string from data structure.
26
+ #
27
+ # @param [String, Fixnum, Array, Hash, Nil] obj the object to serialize.
28
+ # @param [Boolean] parametrize option to parametrize data before serialization.
29
+ # @return [String] the serialized data json.
30
+ def self.generate(obj, parametrize = true)
31
+ if parametrize && obj.is_a?(Hash)
32
+ obj.each do |key, value|
33
+ obj[key] = value.to_s if PARAMETRIZE_CLASSES.include?(value.class)
34
+ end
35
+ end
36
+ if parametrize && obj.is_a?(Array)
37
+ obj.map! do |value|
38
+ PARAMETRIZE_CLASSES.include?(value.class) ? value.to_s : value
39
+ end
40
+ end
41
+ data = NSJSONSerialization.dataWithJSONObject(obj, options: 0, error: nil)
42
+ data.to_str
43
+ end
44
+ end
45
+ end