motion_record 0.0.2 → 0.0.3
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.
- checksums.yaml +13 -5
- data/CHANGELOG.md +4 -0
- data/README.md +40 -14
- data/lib/motion_record/persistence.rb +38 -9
- data/lib/motion_record/schema/migration.rb +4 -0
- data/lib/motion_record/schema/migrator.rb +1 -1
- data/lib/motion_record/schema/table_definition.rb +6 -0
- data/lib/motion_record/serialization.rb +3 -1
- data/lib/motion_record/serialization/date_serializer.rb +63 -0
- data/lib/motion_record/version.rb +1 -1
- metadata +8 -6
checksums.yaml
CHANGED
@@ -1,7 +1,15 @@
|
|
1
1
|
---
|
2
|
-
|
3
|
-
metadata.gz:
|
4
|
-
|
2
|
+
!binary "U0hBMQ==":
|
3
|
+
metadata.gz: !binary |-
|
4
|
+
NzZjNjNiN2I4MWRlMmI3YjU0ZGY1YTY3M2Q0OTg5MGIwZmFjMjQyZQ==
|
5
|
+
data.tar.gz: !binary |-
|
6
|
+
NmRiNTEyMDRhMDQ5MDk0ODg4M2QwZTEyOWY4ZWIwMmVlZWYyN2U4MQ==
|
5
7
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
|
8
|
+
metadata.gz: !binary |-
|
9
|
+
NzhjMjI1OTZlZWNkMjRjYzIxOGI3ZTUwOTg4NTc4NWVhM2RmNmU3NmQ1YTAz
|
10
|
+
NmMzMjhjMDUwZDZmYmJhZWMxMjYyMzI0YjdjMmQzZmQ2ZjM5Yjk3MGIyZWNk
|
11
|
+
ZjUxNDBmMjZlODVkMjhkODZhNDIyZWNjNjEzNzFkMmI5ZmI2ODM=
|
12
|
+
data.tar.gz: !binary |-
|
13
|
+
MDUwNjRkYTMwMGQ1MjJmOGViYjU3ODllOTY5Mjc0NWRhOTg4MWMzZGIyMTA0
|
14
|
+
NWZjZWFiODQ5NjYxMGIzY2YzODU1NjlkNzg1NWE0MzBlYmE3YmZlMDI4M2Zh
|
15
|
+
ZGFlMDY3MDE2ODg4NzkwMjg5OGI5Mjg4MjU2NGUxN2VjOWI5ZjU=
|
data/CHANGELOG.md
ADDED
data/README.md
CHANGED
@@ -6,6 +6,13 @@ MotionRecord
|
|
6
6
|
Everything you need to start using SQLite as the datastore for your RubyMotion
|
7
7
|
app.
|
8
8
|
|
9
|
+
* [Installation](#installation)
|
10
|
+
* [MotionRecord::Base](#motionrecordbase)
|
11
|
+
* [MotionRecord::Schema](#motionrecordschema)
|
12
|
+
* [MotionRecord::Scope](#motionrecordscope)
|
13
|
+
* [MotionRecord::Serialization](#motionrecordserialization)
|
14
|
+
* [MotionRecord::Association](#motionrecordassociation)
|
15
|
+
|
9
16
|
:turtle: Android support should be [coming soon](https://github.com/magoosh/motion_record/issues/3)
|
10
17
|
|
11
18
|
[](http://badge.fury.io/rb/motion_record) [](https://codeclimate.com/github/magoosh/motion_record) [](https://codeclimate.com/github/magoosh/motion_record)
|
@@ -50,24 +57,29 @@ end
|
|
50
57
|
Attribute methods are inferred from the associated SQLite table definition.
|
51
58
|
|
52
59
|
```ruby
|
53
|
-
message = Message.new(subject: "Welcome!"
|
54
|
-
# => #<Message: @id=nil @subject="Welcome!" @body=
|
55
|
-
message.satisfaction
|
56
|
-
# => 0.0
|
60
|
+
message = Message.new(subject: "Welcome!")
|
61
|
+
# => #<Message: @id=nil @subject="Welcome!" @body=nil, @created_at=nil ...>
|
57
62
|
```
|
58
63
|
|
59
|
-
Manage persistence with `save
|
64
|
+
Manage persistence with `create`, `save`, `destroy`, and `persisted?`
|
60
65
|
|
61
66
|
```ruby
|
62
|
-
message = Message.
|
63
|
-
message.
|
64
|
-
message.
|
65
|
-
#
|
66
|
-
|
67
|
+
message = Message.create(subject: "Welcome!")
|
68
|
+
message.body = "If you have any questions, just ask us :)"
|
69
|
+
message.save
|
70
|
+
# SQL: UPDATE messages SET subject = ?, body = ?, ... WHERE id = ?
|
71
|
+
# Params: ["Welcome!", "If you have any questions, just ask :)", ..., 1]
|
72
|
+
message.destroy
|
67
73
|
message.persisted?
|
68
74
|
# => false
|
69
75
|
```
|
70
76
|
|
77
|
+
### Timestamp Columns
|
78
|
+
|
79
|
+
If any of the columns are named `created_at` or `updated_at` then they are
|
80
|
+
automatically [serialized as Time objects](#motionrecordserialization) and set
|
81
|
+
to `Time.now` when the record is created or updated.
|
82
|
+
|
71
83
|
MotionRecord::Schema
|
72
84
|
--------------------
|
73
85
|
|
@@ -83,6 +95,7 @@ def application(application, didFinishLaunchingWithOptions:launchOptions)
|
|
83
95
|
t.integer :read_at
|
84
96
|
t.integer :remote_id
|
85
97
|
t.float :satisfaction, default: 0.0
|
98
|
+
t.timestamps
|
86
99
|
end
|
87
100
|
end
|
88
101
|
|
@@ -163,7 +176,7 @@ class Message < MotionRecord::Base
|
|
163
176
|
serialize :read_at, :time
|
164
177
|
end
|
165
178
|
|
166
|
-
Message.create
|
179
|
+
Message.create(subject: "Hello!", read_at: Time.now)
|
167
180
|
# SQL: INSERT INTO messages (subject, body, read_at, ...) VALUES (?, ?, ?...)
|
168
181
|
# Params: ["Hello!", nil, 1420099200, ...]
|
169
182
|
Message.first.read_at
|
@@ -186,14 +199,27 @@ class Survey < MotionRecord::Base
|
|
186
199
|
serialize :response, :json
|
187
200
|
end
|
188
201
|
|
189
|
-
survey = Survey.
|
190
|
-
survey.save!
|
202
|
+
survey = Survey.create(response: {nps: 10, what_can_we_improve: "Nothing :)"})
|
191
203
|
# SQL: INSERT INTO surveys (response) VALUES (?)
|
192
204
|
# Params: ['{"nps":10, "what_can_we_improve":"Nothing :)"}']
|
193
|
-
|
205
|
+
survey
|
194
206
|
# => #<Survey: @id=1 @response={"nps"=>10, "what_can_we_improve"=>"Nothing :)"}>
|
195
207
|
```
|
196
208
|
|
209
|
+
RubyMotion doesn't have a Date class, but as long as you're okay with using Time
|
210
|
+
objects with only the date attributes, you can serialize them to TEXT columns:
|
211
|
+
|
212
|
+
```ruby
|
213
|
+
class User < MotionRecord::Base
|
214
|
+
serialize :birthday, :date
|
215
|
+
end
|
216
|
+
|
217
|
+
drake = User.create(birthday: Time.new(1986, 10, 24))
|
218
|
+
# SQL: INSERT INTO users (birthday) VALUES (?)
|
219
|
+
# Params: ["1986-10-24"]
|
220
|
+
# => #<User: @id=1, @birthday=1986-10-24 00:00:00 UTC>
|
221
|
+
```
|
222
|
+
|
197
223
|
#### Custom Serializers
|
198
224
|
|
199
225
|
To write a custom serializer, extend MotionRecord::Serialization::BaseSerializer
|
@@ -1,16 +1,14 @@
|
|
1
1
|
module MotionRecord
|
2
2
|
module Persistence
|
3
3
|
|
4
|
-
|
4
|
+
TIMESTAMP_COLUMNS = [:created_at, :updated_at]
|
5
|
+
|
6
|
+
def save
|
5
7
|
persist!
|
6
8
|
end
|
7
9
|
|
8
|
-
def
|
9
|
-
|
10
|
-
self.class.where(primary_key_condition).delete_all
|
11
|
-
else
|
12
|
-
raise "Can't delete unpersisted records"
|
13
|
-
end
|
10
|
+
def destroy
|
11
|
+
delete!
|
14
12
|
end
|
15
13
|
|
16
14
|
def persisted?
|
@@ -31,6 +29,7 @@ module MotionRecord
|
|
31
29
|
# HACK: Must ensure that attribute definitions are loaded from the table
|
32
30
|
self.class.table_columns
|
33
31
|
|
32
|
+
self.apply_persistence_timestamps
|
34
33
|
params = self.to_attribute_hash.reject { |k, _v| k == self.class.primary_key }
|
35
34
|
table_params = self.class.serialize_table_params(params)
|
36
35
|
|
@@ -38,18 +37,44 @@ module MotionRecord
|
|
38
37
|
self.class.where(primary_key_condition).update_all(table_params)
|
39
38
|
else
|
40
39
|
connection.insert self.class.table_name, table_params
|
40
|
+
if self.class.primary_key
|
41
|
+
# HACK: This assumes that primary keys are monotonically increasing
|
42
|
+
newest_primary_key = self.class.maximum(self.class.primary_key)
|
43
|
+
self.self.instance_variable_set "@#{self.class.primary_key}", newest_primary_key
|
44
|
+
end
|
41
45
|
end
|
42
46
|
|
43
47
|
self.mark_persisted!
|
44
48
|
end
|
45
49
|
|
50
|
+
def delete!
|
51
|
+
if persisted?
|
52
|
+
self.class.where(primary_key_condition).delete_all
|
53
|
+
else
|
54
|
+
raise "Can't delete unpersisted records"
|
55
|
+
end
|
56
|
+
end
|
57
|
+
|
58
|
+
# Update persistence auto-timestamp attributes
|
59
|
+
def apply_persistence_timestamps
|
60
|
+
self.updated_at = Time.now if self.class.attribute_names.include?(:updated_at)
|
61
|
+
self.created_at ||= Time.now if self.class.attribute_names.include?(:created_at)
|
62
|
+
end
|
63
|
+
|
46
64
|
def primary_key_condition
|
47
65
|
{self.class.primary_key => self.instance_variable_get("@#{self.class.primary_key}")}
|
48
66
|
end
|
49
67
|
|
50
68
|
module ClassMethods
|
51
|
-
|
52
|
-
|
69
|
+
# Create a new record
|
70
|
+
#
|
71
|
+
# attributes - a Hash of attribute values
|
72
|
+
#
|
73
|
+
# Returns the created record
|
74
|
+
def create(attributes={})
|
75
|
+
record = self.new(attributes)
|
76
|
+
record.save
|
77
|
+
record
|
53
78
|
end
|
54
79
|
|
55
80
|
# Sybmol name of the primary key column
|
@@ -86,6 +111,10 @@ module MotionRecord
|
|
86
111
|
def define_attribute_from_column(column)
|
87
112
|
# TODO: handle options
|
88
113
|
define_attribute column.name, default: column.default
|
114
|
+
|
115
|
+
if TIMESTAMP_COLUMNS.include?(column.name)
|
116
|
+
serialize column.name, :time
|
117
|
+
end
|
89
118
|
end
|
90
119
|
end
|
91
120
|
|
@@ -36,6 +36,12 @@ module MotionRecord
|
|
36
36
|
@index_definitions << IndexDefinition.new(@name, columns, options)
|
37
37
|
end
|
38
38
|
|
39
|
+
# Add :created_at and :updated_at columns to the table
|
40
|
+
def timestamps
|
41
|
+
self.integer(:created_at)
|
42
|
+
self.integer(:updated_at)
|
43
|
+
end
|
44
|
+
|
39
45
|
protected
|
40
46
|
|
41
47
|
def add_default_primary_column
|
@@ -4,13 +4,15 @@ module MotionRecord
|
|
4
4
|
# Register a new attribute serializer
|
5
5
|
#
|
6
6
|
# attribute - Symbol name of the attribute
|
7
|
-
# serializer_class_or_sym - One of :time, :boolean, :json or a custom
|
7
|
+
# serializer_class_or_sym - One of :time, :boolean, :json, :date or a custom
|
8
8
|
# subclass of Serialization::BaseSerializer
|
9
9
|
def serialize(attribute, serializer_class_or_sym)
|
10
10
|
if serializer_class_or_sym.is_a?(Symbol)
|
11
11
|
self.serializer_classes[attribute] = case serializer_class_or_sym
|
12
12
|
when :time
|
13
13
|
Serialization::TimeSerializer
|
14
|
+
when :date
|
15
|
+
Serialization::DateSerializer
|
14
16
|
when :boolean
|
15
17
|
Serialization::BooleanSerializer
|
16
18
|
when :json
|
@@ -0,0 +1,63 @@
|
|
1
|
+
# This serializer stores Time objects to TEXT columns, but discards all
|
2
|
+
# information except for year, month, and day.
|
3
|
+
#
|
4
|
+
# (Time is used because RubyMotion doesn't currently support Date objects)
|
5
|
+
|
6
|
+
module MotionRecord
|
7
|
+
module Serialization
|
8
|
+
class DateSerializer < BaseSerializer
|
9
|
+
|
10
|
+
# ISO8601 pattern that only matches date strings
|
11
|
+
ISO8601_PATTERN = /\A\s*
|
12
|
+
(-?\d+)-(\d\d)-(\d\d)
|
13
|
+
\s*\z/ix
|
14
|
+
|
15
|
+
def serialize(value)
|
16
|
+
case @column.type
|
17
|
+
when :text
|
18
|
+
self.class.date_to_iso8601(value)
|
19
|
+
else
|
20
|
+
raise "Can't serialize #{value.inspect} to #{@column.type.inspect}"
|
21
|
+
end
|
22
|
+
end
|
23
|
+
|
24
|
+
def deserialize(value)
|
25
|
+
case @column.type
|
26
|
+
when :text
|
27
|
+
self.class.date_from_iso8601(value)
|
28
|
+
else
|
29
|
+
raise "Can't deserialize #{value.inspect} from #{@column.type.inspect}"
|
30
|
+
end
|
31
|
+
end
|
32
|
+
|
33
|
+
# Convert a Time object to an ISO8601 format date string.
|
34
|
+
#
|
35
|
+
# time - the Time to convert
|
36
|
+
#
|
37
|
+
# Returns the String representation
|
38
|
+
def self.date_to_iso8601(time)
|
39
|
+
return nil unless time
|
40
|
+
|
41
|
+
"%04d-%02d-%02d" % [time.year, time.month, time.day]
|
42
|
+
end
|
43
|
+
|
44
|
+
# Parse an ISO8601 format date string.
|
45
|
+
#
|
46
|
+
# date_str - the String date representation in ISO8601 format
|
47
|
+
#
|
48
|
+
# Returns a Time object
|
49
|
+
def self.date_from_iso8601(date_str)
|
50
|
+
return nil unless date_str
|
51
|
+
|
52
|
+
if (match = ISO8601_PATTERN.match(date_str))
|
53
|
+
year = match[1].to_i
|
54
|
+
mon = match[2].to_i
|
55
|
+
day = match[3].to_i
|
56
|
+
Time.utc(year, mon, day)
|
57
|
+
else
|
58
|
+
raise ArgumentError.new("invalid date: #{date_str.inspect}")
|
59
|
+
end
|
60
|
+
end
|
61
|
+
end
|
62
|
+
end
|
63
|
+
end
|
metadata
CHANGED
@@ -1,14 +1,14 @@
|
|
1
1
|
--- !ruby/object:Gem::Specification
|
2
2
|
name: motion_record
|
3
3
|
version: !ruby/object:Gem::Version
|
4
|
-
version: 0.0.
|
4
|
+
version: 0.0.3
|
5
5
|
platform: ruby
|
6
6
|
authors:
|
7
7
|
- Zach Millman
|
8
8
|
autorequire:
|
9
9
|
bindir: bin
|
10
10
|
cert_chain: []
|
11
|
-
date: 2015-03-
|
11
|
+
date: 2015-03-19 00:00:00.000000000 Z
|
12
12
|
dependencies:
|
13
13
|
- !ruby/object:Gem::Dependency
|
14
14
|
name: bundler
|
@@ -28,14 +28,14 @@ dependencies:
|
|
28
28
|
name: rake
|
29
29
|
requirement: !ruby/object:Gem::Requirement
|
30
30
|
requirements:
|
31
|
-
- - '>='
|
31
|
+
- - ! '>='
|
32
32
|
- !ruby/object:Gem::Version
|
33
33
|
version: '0'
|
34
34
|
type: :development
|
35
35
|
prerelease: false
|
36
36
|
version_requirements: !ruby/object:Gem::Requirement
|
37
37
|
requirements:
|
38
|
-
- - '>='
|
38
|
+
- - ! '>='
|
39
39
|
- !ruby/object:Gem::Version
|
40
40
|
version: '0'
|
41
41
|
description: Mini ActiveRecord for RubyMotion
|
@@ -46,6 +46,7 @@ extensions: []
|
|
46
46
|
extra_rdoc_files: []
|
47
47
|
files:
|
48
48
|
- .gitignore
|
49
|
+
- CHANGELOG.md
|
49
50
|
- Gemfile
|
50
51
|
- LICENSE.txt
|
51
52
|
- README.md
|
@@ -67,6 +68,7 @@ files:
|
|
67
68
|
- lib/motion_record/serialization.rb
|
68
69
|
- lib/motion_record/serialization/base_serializer.rb
|
69
70
|
- lib/motion_record/serialization/boolean_serializer.rb
|
71
|
+
- lib/motion_record/serialization/date_serializer.rb
|
70
72
|
- lib/motion_record/serialization/default_serializer.rb
|
71
73
|
- lib/motion_record/serialization/json_serializer.rb
|
72
74
|
- lib/motion_record/serialization/time_serializer.rb
|
@@ -82,12 +84,12 @@ require_paths:
|
|
82
84
|
- lib
|
83
85
|
required_ruby_version: !ruby/object:Gem::Requirement
|
84
86
|
requirements:
|
85
|
-
- - '>='
|
87
|
+
- - ! '>='
|
86
88
|
- !ruby/object:Gem::Version
|
87
89
|
version: '0'
|
88
90
|
required_rubygems_version: !ruby/object:Gem::Requirement
|
89
91
|
requirements:
|
90
|
-
- - '>='
|
92
|
+
- - ! '>='
|
91
93
|
- !ruby/object:Gem::Version
|
92
94
|
version: '0'
|
93
95
|
requirements: []
|