Miniature ActiveRecord for RubyMotion
Everything you need to start using SQLite as the datastore for your RubyMotion app.
- Installation
- MotionRecord::Base
- MotionRecord::Schema
- MotionRecord::Scope
- MotionRecord::Serialization
- MotionRecord::Association
🐢 Android support should be coming soon
Add this line to your Gemfile:
gem"motion_record"On iOS, MotionRecord uses motion-sqlite3 as a wrapper for connecting to SQLite, so add these too:
gem"motion-sqlite3"# Requires the most recent unpublished version of motion.h# https://github.com/kastiglione/motion.h/issues/11gem"motion.h",:git=>"https://github.com/kastiglione/motion.h"And then execute:
$ bundle
MotionRecord::Base provides a superclass for defining objects which are stored in the database.
classMessage < MotionRecord::Base# That's all!endAttribute methods are inferred from the associated SQLite table definition.
message=Message.new(subject: "Welcome!")# => #<Message: @id=nil @subject="Welcome!" @body=nil, @created_at=nil ...>Manage persistence with create, save, destroy, and persisted?
message=Message.create(subject: "Welcome!")message.body="If you have any questions, just ask us :)"message.save# SQL: UPDATE messages SET subject = ?, body = ?, ... WHERE id = ?# Params: ["Welcome!", "If you have any questions, just ask :)", ..., 1]message.destroymessage.persisted?# => falseIf any of the columns are named created_at or updated_at then they are
automatically serialized as Time objects and set
to Time.now when the record is created or updated.
Define and run all pending SQLite migrations with the up! DSL.
defapplication(application,didFinishLaunchingWithOptions:launchOptions)MotionRecord::Schema.up!domigration1,"Create messages table"docreate_table:messagesdo |t|
t.text:subject,null: falset.text:bodyt.integer:read_att.integer:remote_idt.float:satisfaction,default: 0.0t.timestampsendendmigration2,"Index messages table"doadd_index:messages,:remote_id,:unique=>trueadd_index:messages,[:subject,:read_at]endend# ...endBy default, MotionRecord will print all SQL statements and use a file named
"app.sqlite3" in the application's Application Support folder. To disable
logging (for release) or change the filename, pass configuration options to up!
resource_file=File.join(NSBundle.mainBundle.resourcePath,"data.sqlite3")MotionRecord::Schema.up!(file: resource_file,debug: false)# ...You can also specify that MotionRecord should use an in-memory SQLite database which will be cleared every time the app process is killed.
MotionRecord::Schema.up!(file: :memory)# ...Build scopes on MotionRecord::Base classes with where, order and limit.
Message.where(body: nil).order("read_at DESC").limit(3).find_allRun queries on scopes with exists?, first, find, find_all, pluck,
update_all, and delete_all.
Message.where(remote_id: 2).exists?# => falseMessage.find(21)# => #<Message @id=21 @subject="What's updog?" ...>Message.where(read_at: nil).pluck(:subject)# => ["What's updog?", "What's updog?", "What's updog?"]Message.where(read_at: nil).find_all# => [#<Message @id=20 ...>, #<Message @id=21 ...>, #<Message @id=22 ...>]Message.where(read_at: nil).update_all(read_at: Time.now.to_i)Run calculations on scopes with count, sum, maximum, minimum, and
average.
Message.where(subject: "Welcome!").count# => 1Message.where(subject: "How do you like the app?").maximum(:satisfaction)# => 10.0SQLite has a very limited set of datatypes (TEXT, INTEGER, and REAL), but you can easily store other objects as attributes in the database with serializers.
MotionRecord provides a built-in serializer for Time objects to any column datatype.
classMessage < MotionRecord::Baseserialize:read_at,:timeendMessage.create(subject: "Hello!",read_at: Time.now)# SQL: INSERT INTO messages (subject, body, read_at, ...) VALUES (?, ?, ?...)# Params: ["Hello!", nil, 1420099200, ...]Message.first.read_at# => 2015-01-01 00:00:00 -0800Boolean attributes can be serialized to INTEGER columns where 0 and NULL are
false and any other value is true.
classMessage < MotionRecord::Baseserialize:satisfaction_submitted,:booleanendObjects can also be stored to TEXT columns as JSON.
classSurvey < MotionRecord::Baseserialize:response,:jsonendsurvey=Survey.create(response: {nps: 10,what_can_we_improve: "Nothing :)"})# SQL: INSERT INTO surveys (response) VALUES (?)# Params: ['{"nps":10, "what_can_we_improve":"Nothing :)"}']survey# => #<Survey: @id=1 @response={"nps"=>10, "what_can_we_improve"=>"Nothing :)"}>RubyMotion doesn't have a Date class, but as long as you're okay with using Time objects with only the date attributes, you can serialize them to TEXT columns:
classUser < MotionRecord::Baseserialize:birthday,:dateenddrake=User.create(birthday: Time.new(1986,10,24))# SQL: INSERT INTO users (birthday) VALUES (?)# Params: ["1986-10-24"]# => #<User: @id=1, @birthday=1986-10-24 00:00:00 UTC>To write a custom serializer, extend MotionRecord::Serialization::BaseSerializer
and provide your class to serialize instead of a symbol.
classMoneySerializer < MotionRecord::Serialization::BaseSerializerdefserialize(value)raise"Wrong column type!"unless@column.type == :integervalue.centsenddefdeserialize(value)raise"Wrong column type!"unless@column.type == :integerMoney.new(value)endendclassPurchase < MotionRecord::Baseserialize:amount_paid_cents,MoneySerializerendPlease do!
- Fork it
- Create your feature branch (
git checkout -b my-new-feature) - Commit your changes (
git commit -am 'Add some feature') - Push to the branch (
git push origin my-new-feature) - Create new Pull Request