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 CHANGED
@@ -1,7 +1,15 @@
1
1
  ---
2
- SHA1:
3
- metadata.gz: 806d2c421426bad6f8c077bcd5a6d12e74015367
4
- data.tar.gz: 2a906afefcc90eecd49b3f03badeda1bee7f3171
2
+ !binary "U0hBMQ==":
3
+ metadata.gz: !binary |-
4
+ NzZjNjNiN2I4MWRlMmI3YjU0ZGY1YTY3M2Q0OTg5MGIwZmFjMjQyZQ==
5
+ data.tar.gz: !binary |-
6
+ NmRiNTEyMDRhMDQ5MDk0ODg4M2QwZTEyOWY4ZWIwMmVlZWYyN2U4MQ==
5
7
  SHA512:
6
- metadata.gz: e46095019c1d27c71be32543d3b8896f909a0f64446e4fdd6a76d8e39e2d59fbcb00bdc806206a17fa5554e9b0f73264b7769deb79d5b25dbdc6281d554be082
7
- data.tar.gz: f314d3c7defa369ab86623572789dcea5f9c0127e46a45325100a4ccd64b4846779c336a3c31525dd39199403e9525e078cb0eb58c7f454b7159409b38cddf4f
8
+ metadata.gz: !binary |-
9
+ NzhjMjI1OTZlZWNkMjRjYzIxOGI3ZTUwOTg4NTc4NWVhM2RmNmU3NmQ1YTAz
10
+ NmMzMjhjMDUwZDZmYmJhZWMxMjYyMzI0YjdjMmQzZmQ2ZjM5Yjk3MGIyZWNk
11
+ ZjUxNDBmMjZlODVkMjhkODZhNDIyZWNjNjEzNzFkMmI5ZmI2ODM=
12
+ data.tar.gz: !binary |-
13
+ MDUwNjRkYTMwMGQ1MjJmOGViYjU3ODllOTY5Mjc0NWRhOTg4MWMzZGIyMTA0
14
+ NWZjZWFiODQ5NjYxMGIzY2YzODU1NjlkNzg1NWE0MzBlYmE3YmZlMDI4M2Zh
15
+ ZGFlMDY3MDE2ODg4NzkwMjg5OGI5Mjg4MjU2NGUxN2VjOWI5ZjU=
@@ -0,0 +1,4 @@
1
+ CHANGELOG
2
+ =========
3
+
4
+ Version changes are recorded with [GitHub releases](https://github.com/magoosh/motion_record/releases)
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
  [![Gem Version](https://badge.fury.io/rb/motion_record.svg)](http://badge.fury.io/rb/motion_record) [![Code Climate](https://codeclimate.com/github/magoosh/motion_record/badges/gpa.svg)](https://codeclimate.com/github/magoosh/motion_record) [![Test Coverage](https://codeclimate.com/github/magoosh/motion_record/badges/coverage.svg)](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!", body: "If you have any questions...")
54
- # => #<Message: @id=nil @subject="Welcome!" @body="If you have any..." ...>
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!`, `delete!`, and `persisted?`
64
+ Manage persistence with `create`, `save`, `destroy`, and `persisted?`
60
65
 
61
66
  ```ruby
62
- message = Message.new(subject: "Welcome!", body: "If you have any questions...")
63
- message.save!
64
- message.id
65
- # => 1
66
- message.delete!
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!(subject: "Hello!", read_at: Time.now)
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.new(response: {nps: 10, what_can_we_improve: "Nothing :)"})
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
- Survey.first
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
- def save!
4
+ TIMESTAMP_COLUMNS = [:created_at, :updated_at]
5
+
6
+ def save
5
7
  persist!
6
8
  end
7
9
 
8
- def delete!
9
- if persisted?
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
- def create!(attributes={})
52
- self.new(attributes).save!
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
 
@@ -13,6 +13,10 @@ module MotionRecord
13
13
  connection.table_exists?(table_name)
14
14
  end
15
15
 
16
+ def primary_key
17
+ nil
18
+ end
19
+
16
20
  def create_table
17
21
  table = Schema::TableDefinition.new(table_name, id: false)
18
22
  table.integer :version, :null => false
@@ -15,7 +15,7 @@ module MotionRecord
15
15
  pending_migrations.each do |migration|
16
16
  migration.execute
17
17
  @migrated_versions << migration.version
18
- Schema::Migration.create!(version: migration.version)
18
+ Schema::Migration.create(version: migration.version)
19
19
  end
20
20
  end
21
21
 
@@ -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
@@ -1,3 +1,3 @@
1
1
  module MotionRecord
2
- VERSION = "0.0.2"
2
+ VERSION = "0.0.3"
3
3
  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.2
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-14 00:00:00.000000000 Z
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: []