motion_data_wrapper 0.0.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.
- data/lib/motion_data_wrapper.rb +18 -0
- data/lib/motion_data_wrapper/delegate.rb +52 -0
- data/lib/motion_data_wrapper/errors/record_invalid.rb +11 -0
- data/lib/motion_data_wrapper/errors/record_not_found.rb +11 -0
- data/lib/motion_data_wrapper/errors/record_not_saved.rb +11 -0
- data/lib/motion_data_wrapper/model.rb +18 -0
- data/lib/motion_data_wrapper/model/core_data.rb +19 -0
- data/lib/motion_data_wrapper/model/finder_methods.rb +107 -0
- data/lib/motion_data_wrapper/model/persistence.rb +85 -0
- data/lib/motion_data_wrapper/model/validations.rb +65 -0
- data/lib/motion_data_wrapper/relation.rb +12 -0
- data/lib/motion_data_wrapper/relation/core_data.rb +21 -0
- data/lib/motion_data_wrapper/relation/finder_methods.rb +97 -0
- data/lib/motion_data_wrapper/version.rb +3 -0
- metadata +62 -0
@@ -0,0 +1,18 @@
|
|
1
|
+
require 'motion_data_wrapper/version'
|
2
|
+
|
3
|
+
unless defined?(Motion::Project::Config)
|
4
|
+
raise "This file must be required within a RubyMotion project Rakefile."
|
5
|
+
end
|
6
|
+
|
7
|
+
Motion::Project::App.setup do |app|
|
8
|
+
|
9
|
+
# Load class definitions that require the following modules, these will get compiled after b/c we're using "unshift"
|
10
|
+
Dir[File.join(File.dirname(__FILE__), "motion_data_wrapper/*.rb")].each { |f| app.files.unshift(f) }
|
11
|
+
|
12
|
+
# Load in the modules
|
13
|
+
%w(errors model relation).each do |mod|
|
14
|
+
Dir[File.join(File.dirname(__FILE__), "motion_data_wrapper/#{mod}/**/*.rb")].each { |f| app.files.unshift(f) }
|
15
|
+
end
|
16
|
+
|
17
|
+
app.frameworks << "CoreData" unless app.frameworks.include?("CoreData")
|
18
|
+
end
|
@@ -0,0 +1,52 @@
|
|
1
|
+
module MotionDataWrapper
|
2
|
+
module Delegate
|
3
|
+
|
4
|
+
def managedObjectContext
|
5
|
+
@managedObjectContext ||= begin
|
6
|
+
documentsDirectory = NSFileManager.defaultManager.URLsForDirectory(NSDocumentDirectory, inDomains:NSUserDomainMask).lastObject;
|
7
|
+
storeURL = documentsDirectory.URLByAppendingPathComponent("#{sqlite_store_name}.sqlite")
|
8
|
+
|
9
|
+
error_ptr = Pointer.new(:object)
|
10
|
+
unless persistentStoreCoordinator.addPersistentStoreWithType(NSSQLiteStoreType, configuration:nil, URL:storeURL, options:persistent_store_options, error:error_ptr)
|
11
|
+
raise "Can't add persistent SQLite store: #{error_ptr[0].description}"
|
12
|
+
end
|
13
|
+
|
14
|
+
context = NSManagedObjectContext.alloc.init
|
15
|
+
context.persistentStoreCoordinator = persistentStoreCoordinator
|
16
|
+
|
17
|
+
context
|
18
|
+
end
|
19
|
+
end
|
20
|
+
|
21
|
+
def managedObjectModel
|
22
|
+
@managedObjectModel ||= begin
|
23
|
+
model = NSManagedObjectModel.mergedModelFromBundles([NSBundle.mainBundle]).mutableCopy
|
24
|
+
|
25
|
+
model.entities.each do |entity|
|
26
|
+
begin
|
27
|
+
Kernel.const_get(entity.name)
|
28
|
+
entity.setManagedObjectClassName(entity.name)
|
29
|
+
|
30
|
+
rescue NameError
|
31
|
+
entity.setManagedObjectClassName("Model")
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
model
|
36
|
+
end
|
37
|
+
end
|
38
|
+
|
39
|
+
def persistentStoreCoordinator
|
40
|
+
@coordinator ||= NSPersistentStoreCoordinator.alloc.initWithManagedObjectModel(managedObjectModel)
|
41
|
+
end
|
42
|
+
|
43
|
+
def sqlite_store_name
|
44
|
+
NSBundle.mainBundle.infoDictionary.objectForKey("CFBundleName")
|
45
|
+
end
|
46
|
+
|
47
|
+
def persistent_store_options
|
48
|
+
nil
|
49
|
+
end
|
50
|
+
|
51
|
+
end
|
52
|
+
end
|
@@ -0,0 +1,18 @@
|
|
1
|
+
module MotionDataWrapper
|
2
|
+
class Model < NSManagedObject
|
3
|
+
include CoreData
|
4
|
+
include FinderMethods
|
5
|
+
include Persistence
|
6
|
+
include Validations
|
7
|
+
|
8
|
+
def inspect
|
9
|
+
properties = []
|
10
|
+
entity.properties.select { |p| p.is_a?(NSAttributeDescription) }.each do |property|
|
11
|
+
properties << "#{property.name}: #{valueForKey(property.name).inspect}"
|
12
|
+
end
|
13
|
+
|
14
|
+
"#<#{entity.name} #{properties.join(", ")}>"
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
@@ -0,0 +1,19 @@
|
|
1
|
+
module MotionDataWrapper
|
2
|
+
class Model < NSManagedObject
|
3
|
+
module CoreData
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
|
11
|
+
def entity_description
|
12
|
+
@_metadata ||= UIApplication.sharedApplication.delegate.managedObjectModel.entitiesByName[name]
|
13
|
+
end
|
14
|
+
|
15
|
+
end
|
16
|
+
|
17
|
+
end
|
18
|
+
end
|
19
|
+
end
|
@@ -0,0 +1,107 @@
|
|
1
|
+
module MotionDataWrapper
|
2
|
+
class Model < NSManagedObject
|
3
|
+
module FinderMethods
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
|
11
|
+
def all
|
12
|
+
relation.to_a
|
13
|
+
end
|
14
|
+
|
15
|
+
def count
|
16
|
+
relation.count
|
17
|
+
end
|
18
|
+
|
19
|
+
def destroy_all
|
20
|
+
all.map &:destroy
|
21
|
+
end
|
22
|
+
|
23
|
+
def except(query_part)
|
24
|
+
relation.except(query_part)
|
25
|
+
end
|
26
|
+
|
27
|
+
def find(object_id)
|
28
|
+
raise MotionDataWrapper::RecordNotFound.new(self, object_id) unless entity = find_by_id(object_id)
|
29
|
+
entity
|
30
|
+
end
|
31
|
+
|
32
|
+
def first
|
33
|
+
relation.first
|
34
|
+
end
|
35
|
+
|
36
|
+
def first!
|
37
|
+
first or raise MotionDataWrapper::RecordNotFound
|
38
|
+
end
|
39
|
+
|
40
|
+
def last
|
41
|
+
relation.last
|
42
|
+
end
|
43
|
+
|
44
|
+
def last!
|
45
|
+
last or raise MotionDataWrapper::RecordNotFound
|
46
|
+
end
|
47
|
+
|
48
|
+
def limit(l)
|
49
|
+
relation.limit(l)
|
50
|
+
end
|
51
|
+
|
52
|
+
def method_missing(method, *args, &block)
|
53
|
+
if method.start_with?("find_by_")
|
54
|
+
attribute = method.gsub("find_by_", "")
|
55
|
+
relation.where("#{attribute} = ?", *args).first
|
56
|
+
elsif method.start_with?("find_all_by_")
|
57
|
+
attribute = method.gsub("find_all_by_", "")
|
58
|
+
relation.where("#{attribute} = ?", *args).to_a
|
59
|
+
else
|
60
|
+
super
|
61
|
+
end
|
62
|
+
end
|
63
|
+
|
64
|
+
def offset(o)
|
65
|
+
relation.offset(o)
|
66
|
+
end
|
67
|
+
|
68
|
+
def order(*args)
|
69
|
+
relation.order(*args)
|
70
|
+
end
|
71
|
+
|
72
|
+
def pluck(column)
|
73
|
+
relation.pluck(column)
|
74
|
+
end
|
75
|
+
|
76
|
+
def reorder(*args)
|
77
|
+
relation.except(:order).order(*args)
|
78
|
+
end
|
79
|
+
|
80
|
+
def respond_to?(method)
|
81
|
+
if method.start_with?("find_by_") || method.start_with?("find_all_by_")
|
82
|
+
true
|
83
|
+
else
|
84
|
+
super
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def uniq
|
89
|
+
relation.uniq
|
90
|
+
end
|
91
|
+
|
92
|
+
def where(*args)
|
93
|
+
relation.where(*args)
|
94
|
+
end
|
95
|
+
|
96
|
+
private
|
97
|
+
|
98
|
+
def relation
|
99
|
+
Relation.alloc.initWithClass(self)
|
100
|
+
end
|
101
|
+
|
102
|
+
end
|
103
|
+
|
104
|
+
end
|
105
|
+
|
106
|
+
end
|
107
|
+
end
|
@@ -0,0 +1,85 @@
|
|
1
|
+
module MotionDataWrapper
|
2
|
+
class Model < NSManagedObject
|
3
|
+
module Persistence
|
4
|
+
|
5
|
+
def self.included(base)
|
6
|
+
base.extend(ClassMethods)
|
7
|
+
end
|
8
|
+
|
9
|
+
module ClassMethods
|
10
|
+
|
11
|
+
def create(attributes={})
|
12
|
+
begin
|
13
|
+
model = create!(attributes)
|
14
|
+
rescue MotionDataWrapper::RecordNotSaved
|
15
|
+
end
|
16
|
+
model
|
17
|
+
end
|
18
|
+
|
19
|
+
def create!(attributes={})
|
20
|
+
model = new(attributes)
|
21
|
+
model.save!
|
22
|
+
model
|
23
|
+
end
|
24
|
+
|
25
|
+
def new(attributes={})
|
26
|
+
alloc.initWithEntity(entity_description, insertIntoManagedObjectContext:nil).tap do |model|
|
27
|
+
model.instance_variable_set('@new_record', true)
|
28
|
+
attributes.each do |keyPath, value|
|
29
|
+
model.setValue(value, forKey:keyPath)
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
|
34
|
+
end
|
35
|
+
|
36
|
+
def destroy
|
37
|
+
|
38
|
+
if context = managedObjectContext
|
39
|
+
context.deleteObject(self)
|
40
|
+
error = Pointer.new(:object)
|
41
|
+
context.save(error)
|
42
|
+
end
|
43
|
+
|
44
|
+
@destroyed = true
|
45
|
+
freeze
|
46
|
+
end
|
47
|
+
|
48
|
+
def destroyed?
|
49
|
+
@destroyed || false
|
50
|
+
end
|
51
|
+
|
52
|
+
def new_record?
|
53
|
+
@new_record || false
|
54
|
+
end
|
55
|
+
|
56
|
+
def persisted?
|
57
|
+
!(new_record? || destroyed?)
|
58
|
+
end
|
59
|
+
|
60
|
+
def save
|
61
|
+
begin
|
62
|
+
save!
|
63
|
+
rescue MotionDataWrapper::RecordNotSaved
|
64
|
+
return false
|
65
|
+
end
|
66
|
+
true
|
67
|
+
end
|
68
|
+
|
69
|
+
def save!
|
70
|
+
unless context = managedObjectContext
|
71
|
+
context = UIApplication.sharedApplication.delegate.managedObjectContext
|
72
|
+
context.insertObject(self)
|
73
|
+
end
|
74
|
+
|
75
|
+
error = Pointer.new(:object)
|
76
|
+
unless context.save(error)
|
77
|
+
managedObjectContext.deleteObject(self)
|
78
|
+
raise MotionDataWrapper::RecordNotSaved, self and return false
|
79
|
+
end
|
80
|
+
true
|
81
|
+
end
|
82
|
+
|
83
|
+
end
|
84
|
+
end
|
85
|
+
end
|
@@ -0,0 +1,65 @@
|
|
1
|
+
module MotionDataWrapper
|
2
|
+
class Model < NSManagedObject
|
3
|
+
module Validations
|
4
|
+
|
5
|
+
def valid?
|
6
|
+
error_ptr = Pointer.new(:object)
|
7
|
+
v = new_record? ? validateForInsert(error_ptr) : validateForUpdate(error_ptr)
|
8
|
+
yield(v, error_ptr[0]) if block_given?
|
9
|
+
v
|
10
|
+
end
|
11
|
+
|
12
|
+
def errors
|
13
|
+
errors = {}
|
14
|
+
valid? do |valid, error|
|
15
|
+
next if error.nil?
|
16
|
+
if error.code == NSValidationMultipleErrorsError
|
17
|
+
errs = error.userInfo[NSDetailedErrorsKey]
|
18
|
+
errs.each do |nserr|
|
19
|
+
property = nserr.userInfo['NSValidationErrorKey']
|
20
|
+
errors[property] = message_for_error_code(nserr.code, property)
|
21
|
+
end
|
22
|
+
else
|
23
|
+
property = error.userInfo['NSValidationErrorKey']
|
24
|
+
errors[property] = message_for_error_code(error.code, property)
|
25
|
+
end
|
26
|
+
end
|
27
|
+
errors
|
28
|
+
end
|
29
|
+
|
30
|
+
private
|
31
|
+
def message_for_error_code(c, prop)
|
32
|
+
message = case c
|
33
|
+
when NSValidationMissingMandatoryPropertyError
|
34
|
+
"can't be blank"
|
35
|
+
when NSValidationNumberTooLargeError
|
36
|
+
"too large"
|
37
|
+
when NSValidationNumberTooSmallError
|
38
|
+
"too small"
|
39
|
+
when NSValidationDateTooLateError
|
40
|
+
"too late"
|
41
|
+
when NSValidationDateTooSoonError
|
42
|
+
"too soon"
|
43
|
+
when NSValidationInvalidDateError
|
44
|
+
"invalid date"
|
45
|
+
when NSValidationStringTooLongError
|
46
|
+
"too long"
|
47
|
+
when NSValidationStringTooShortError
|
48
|
+
"too short"
|
49
|
+
when NSValidationStringPatternMatchingError
|
50
|
+
"incorrect pattern"
|
51
|
+
when NSValidationRelationshipExceedsMaximumCountError
|
52
|
+
"too many"
|
53
|
+
when NSValidationRelationshipLacksMinimumCountError
|
54
|
+
"too few"
|
55
|
+
when NSValidationRelationshipDeniedDeleteError
|
56
|
+
"can't delete"
|
57
|
+
when NSManagedObjectValidationError
|
58
|
+
warnings = entity.propertiesByName[prop].validationWarnings rescue []
|
59
|
+
warnings.empty? ? "invalid" : warnings.join(', ')
|
60
|
+
end
|
61
|
+
end
|
62
|
+
|
63
|
+
end
|
64
|
+
end
|
65
|
+
end
|
@@ -0,0 +1,21 @@
|
|
1
|
+
module MotionDataWrapper
|
2
|
+
class Relation < NSFetchRequest
|
3
|
+
module CoreData
|
4
|
+
|
5
|
+
def inspect
|
6
|
+
to_a
|
7
|
+
end
|
8
|
+
|
9
|
+
def to_a
|
10
|
+
error_ptr = Pointer.new(:object)
|
11
|
+
context.executeFetchRequest(self, error:error_ptr)
|
12
|
+
end
|
13
|
+
|
14
|
+
private
|
15
|
+
def context
|
16
|
+
UIApplication.sharedApplication.delegate.managedObjectContext
|
17
|
+
end
|
18
|
+
|
19
|
+
end
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,97 @@
|
|
1
|
+
module MotionDataWrapper
|
2
|
+
class Relation < NSFetchRequest
|
3
|
+
module FinderMethods
|
4
|
+
|
5
|
+
def all
|
6
|
+
to_a
|
7
|
+
end
|
8
|
+
|
9
|
+
def count
|
10
|
+
return to_a.count if fetchOffset > 0
|
11
|
+
old_result_type = self.resultType
|
12
|
+
self.resultType = NSCountResultType
|
13
|
+
count = to_a[0]
|
14
|
+
self.resultType = old_result_type
|
15
|
+
return count
|
16
|
+
end
|
17
|
+
|
18
|
+
def destroy_all
|
19
|
+
all.map &:destroy
|
20
|
+
end
|
21
|
+
|
22
|
+
def except(query_part)
|
23
|
+
case query_part.to_sym
|
24
|
+
when :where
|
25
|
+
self.predicate = nil
|
26
|
+
when :order
|
27
|
+
self.sortDescriptors = nil
|
28
|
+
when :limit
|
29
|
+
self.fetchLimit = 0
|
30
|
+
else
|
31
|
+
raise ArgumentError, "unsupport query part '#{query_part}'"
|
32
|
+
end
|
33
|
+
self
|
34
|
+
end
|
35
|
+
|
36
|
+
def first
|
37
|
+
self.fetchLimit = 1
|
38
|
+
to_a[0]
|
39
|
+
end
|
40
|
+
|
41
|
+
def last
|
42
|
+
self.fetchOffset = self.count - 1 unless self.count < 1
|
43
|
+
self.fetchLimit = 1
|
44
|
+
to_a[0]
|
45
|
+
end
|
46
|
+
|
47
|
+
def limit(l)
|
48
|
+
l = l.to_i
|
49
|
+
raise ArgumentError, "limit '#{l}' cannot be less than zero. Use zero for no limit." if l < 0
|
50
|
+
self.fetchLimit = l
|
51
|
+
self
|
52
|
+
end
|
53
|
+
|
54
|
+
def offset(o)
|
55
|
+
o = o.to_i
|
56
|
+
raise ArgumentError, "offset '#{o}' cannot be less than zero." if o < 0
|
57
|
+
self.fetchOffset = o
|
58
|
+
self
|
59
|
+
end
|
60
|
+
|
61
|
+
def order(column, opts={})
|
62
|
+
descriptors = sortDescriptors.nil? ? [] : sortDescriptors.mutableCopy
|
63
|
+
descriptors << NSSortDescriptor.sortDescriptorWithKey(column.to_s, ascending:opts.fetch(:ascending, true))
|
64
|
+
self.sortDescriptors = descriptors
|
65
|
+
self
|
66
|
+
end
|
67
|
+
|
68
|
+
def pluck(column)
|
69
|
+
self.resultType = NSDictionaryResultType
|
70
|
+
|
71
|
+
attribute_description = entity.attributesByName[column]
|
72
|
+
raise ArgumentError, "#{column} not a valid column name" if attribute_description.nil?
|
73
|
+
|
74
|
+
self.propertiesToFetch = [attribute_description]
|
75
|
+
to_a.collect { |r| r[column] }
|
76
|
+
end
|
77
|
+
|
78
|
+
def uniq
|
79
|
+
self.returnsDistinctResults = true
|
80
|
+
self
|
81
|
+
end
|
82
|
+
|
83
|
+
def where(format, *args)
|
84
|
+
new_predicate = NSPredicate.predicateWithFormat(format.gsub("?", "%@"), argumentArray:args)
|
85
|
+
|
86
|
+
if self.predicate
|
87
|
+
self.predicate = NSCompoundPredicate.andPredicateWithSubpredicates([predicate, new_predicate])
|
88
|
+
else
|
89
|
+
self.predicate = new_predicate
|
90
|
+
end
|
91
|
+
|
92
|
+
self
|
93
|
+
end
|
94
|
+
|
95
|
+
end
|
96
|
+
end
|
97
|
+
end
|
metadata
ADDED
@@ -0,0 +1,62 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: motion_data_wrapper
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
prerelease:
|
6
|
+
platform: ruby
|
7
|
+
authors:
|
8
|
+
- Matt Brewer
|
9
|
+
autorequire:
|
10
|
+
bindir: bin
|
11
|
+
cert_chain: []
|
12
|
+
date: 2013-05-23 00:00:00.000000000 Z
|
13
|
+
dependencies: []
|
14
|
+
description: Forked from the mattgreen/nitron gem, this provides an intuitive way
|
15
|
+
to query and persist data in CoreData, while letting you use the powerful Xcode
|
16
|
+
data modeler and versioning tools for schema definitions.
|
17
|
+
email:
|
18
|
+
- matt.brewer@me.com
|
19
|
+
executables: []
|
20
|
+
extensions: []
|
21
|
+
extra_rdoc_files: []
|
22
|
+
files:
|
23
|
+
- lib/motion_data_wrapper.rb
|
24
|
+
- lib/motion_data_wrapper/delegate.rb
|
25
|
+
- lib/motion_data_wrapper/errors/record_invalid.rb
|
26
|
+
- lib/motion_data_wrapper/errors/record_not_found.rb
|
27
|
+
- lib/motion_data_wrapper/errors/record_not_saved.rb
|
28
|
+
- lib/motion_data_wrapper/model.rb
|
29
|
+
- lib/motion_data_wrapper/model/core_data.rb
|
30
|
+
- lib/motion_data_wrapper/model/finder_methods.rb
|
31
|
+
- lib/motion_data_wrapper/model/persistence.rb
|
32
|
+
- lib/motion_data_wrapper/model/validations.rb
|
33
|
+
- lib/motion_data_wrapper/relation.rb
|
34
|
+
- lib/motion_data_wrapper/relation/core_data.rb
|
35
|
+
- lib/motion_data_wrapper/relation/finder_methods.rb
|
36
|
+
- lib/motion_data_wrapper/version.rb
|
37
|
+
homepage: https://github.com/macfanatic/motion_data_wrapper
|
38
|
+
licenses: []
|
39
|
+
post_install_message:
|
40
|
+
rdoc_options: []
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
none: false
|
45
|
+
requirements:
|
46
|
+
- - ! '>='
|
47
|
+
- !ruby/object:Gem::Version
|
48
|
+
version: '0'
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
none: false
|
51
|
+
requirements:
|
52
|
+
- - ! '>='
|
53
|
+
- !ruby/object:Gem::Version
|
54
|
+
version: '0'
|
55
|
+
requirements: []
|
56
|
+
rubyforge_project:
|
57
|
+
rubygems_version: 1.8.24
|
58
|
+
signing_key:
|
59
|
+
specification_version: 3
|
60
|
+
summary: Provides an easy ActiveRecord-like interface to CoreData
|
61
|
+
test_files: []
|
62
|
+
has_rdoc:
|