motion-prime 0.1.0 → 0.1.1

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