motion_record 0.0.2 → 0.0.3

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