fire-model - REST wrapper for Firebase.

Gem Version

Install gem

gem install fire-model

Click here to create a free Firebase account if you don't have one.

Setup Firebase. (If you are using Rails create a file Rails.root/config/initializers/fire_model.rb and put next line there).

require 'fire-model'
Fire.setup(firebase_path: 'https://<your subdomain here>.firebaseio.com')

Declare your Model

class LibraryBook < Fire::Model
  has_path_keys(:library, :floor, :row_number, :shelf)
end

Use query syntax

LibraryBook.create(library: 'Shevchenko', floor: 1, row_number: 1, shelf: 10, 
  name: 'Kobzar', author: 'T.G. Shevchenko')
LibraryBook.create(library: 'Shevchenko', floor: 1, row_number: 1, shelf: 15, 
  name: 'Eneida', author: 'I. Kotlyrevskiy')
LibraryBook.create(library: 'Shevchenko', floor: 2, row_number: 15, shelf: 115, 
  name: 'Lord Of The Rings', author: ' J.R.R. Tolkien')
LibraryBook.create(library: 'Skovoroda', floor: 1, row_number: 25, shelf: 34, 
  name: 'Harry Potter', author: 'J.K. Rowling')
LibraryBook.create(library: 'Skovoroda', floor: 2, row_number: 12, shelf: 15, 
  name: 'Hobbit', author: ' J.R.R. Tolkien')

LibraryBook.all.map(&:name)
=> [ 'Kobzar', 'Eneida', 'Lord Of The Rings', 'Harry Potter', 'Hobbit' ]

# Query by library
LibraryBook.query(library: 'Shevchenko').map(&:name)
=> [ 'Kobzar', 'Eneida', 'Lord Of The Rings' ]
LibraryBook.query(library: 'Skovoroda').map(&:name)
=> [ 'Harry Potter', 'Hobbit' ]

# Query by library, floor
LibraryBook.query(library: 'Shevchenko', floor: 1).map(&:name)
=> [ 'Kobzar', 'Eneida' ]

# Query by library, floor, row
LibraryBook.query(library: 'Shevchenko', floor: 1, row_number: 1).map(&:name)
=> [ 'Kobzar', 'Eneida' ]

# Query by shelf
LibraryBook.query(shelf: 15).map(&:name)
=> [ 'Eneida', 'Hobbit' ]

# Query by author
LibraryBook.query(author: ' J.R.R. Tolkien').map(&:name) 
=> [ 'Lord Of The Rings', 'Hobbit' ]

# Query by math condition
LibraryBook.query{|m| m.row_number % 5 == 0  }.map(&:name)
=> [ 'Lord Of The Rings', 'Harry Potter' ]

Play with CRUD

class Point < Fire::Model
  has_path_keys(:x, :y)
end

p1 = Point.create(x: 1, y: 1, value: 1)
p2 = Point.create(x: 1, y: 2, value: 2)
p3 = Point.create(x: 2, y: 1, value: 3)
p4 = Point.create(x: 1, y: 1, value: 4)

Point.all.map(&:value)
=> [ 1, 2, 3, 4 ]

p1.value = 5
p1.path_changed?
=> false

p1.save

reloaded_point = Point.take(x: p2.x, y: p2.y, id: p2.id)
reloaded_point.value = 6

reloaded_point.path_changed?
=> false

reloaded_point.save

p1.delete

Point.all.map(&:value)
=> [ 6, 3, 4]

p3.x = 4
p3.path_changed?
=> true

p3.save

Point.all.map(&:value)
=> [ 6, 3, 4]

Create Nested Models

class Organization < Fire::Model
  set_id_key(:name)
  has_path_keys :country, :state
end

class Employee < Fire::NestedModel
  nested_in Organization, folder: 'employees'
  has_path_keys :department
end

google = Organization.create(name: 'Google', country: 'USA', state: 'CA')
apple = Organization.create(name: 'Apple', country: 'USA', state: 'CA')

larry = Employee.create(name: 'Google', country: 'USA', state: 'CA',
  department: 'HQ', full_name: 'Larry Page', position: 'CEO')

employee = Organization.query(name: 'Google').first.nested_employees.first
larry == employee
=> true

employee.department = 'Research'
employee.save
=> true

google.reload

tim = apple.add_to_employees(
  full_name: 'Tim Cook',
  position: 'CEO',
  department: 'HQ'
)

Fire.tree
=> {'Organization'=>
     {'usa'=>
          {'ca'=>
               {'apple'=>
                    {'country'=>'USA',
                     'employees'=>
                         {'hq'=>
                              {'h543ka'=>
                                   {'department'=>'HQ',
                                    'full_name'=>'Tim Cook',
                                    'id'=>'h543ka',
                                    'position'=>'CEO'}}},
                     'name'=>'Apple',
                     'state'=>'CA'},
                'google'=>
                    {'country'=>'USA',
                     'employees'=>
                         {'research'=>
                              {'d23h1a'=>
                                   {'department'=>'Research',
                                    'full_name'=>'Larry Page',
                                    'id'=>'d23h1a',
                                    'position'=>'CEO'}}},
                     'name'=>'Google',
                     'state'=>'CA'}}}}}

Single Nested Models

class Car < Fire::Model
  has_path_keys :manufacturer, :model, :car_class
end

class Engine < Fire::SingleNestedModel
  nested_in Car
end

scirocco = Car.create(manufacturer: 'Volkswagen', model: 'Scirocco', car_class: 'Sport compact')
scirocco.add_to_engine(code: 'I4 turbo', power: '122 PS')

car = Car.create(manufacturer: 'Zaporozhets', model: 'ZAZ-965', car_class: 'Mini', 
  engine: { code: 'MeMZ-966' })

zaporozhets = Car.take(manufacturer: 'Zaporozhets', model: 'ZAZ-965', car_class: 'Mini', id: car.id)
zaporozhets.nested_engine.code
=> 'MeMZ-966'

Fire.tree
=> {
  'Car'=>
    {'volkswagen'=>
         {'scirocco'=>
              {'sport-compact'=>
                   {'adqa21'=>
                        {'car_class'=>'Sport compact',
                         'engine'=>{'code'=>'I4 turbo', 'power'=>'122 PS'},
                         'id'=>'adqa21',
                         'manufacturer'=>'Volkswagen',
                         'model'=>'Scirocco'}}}},
     'zaporozhets'=>
         {'zaz-965'=>
              {'mini'=>
                   {'23rtfw'=>
                        {'car_class'=>'Mini',
                         'engine'=>{'code'=>'MeMZ-966'},
                         'id'=>'23rtfw',
                         'manufacturer'=>'Zaporozhets',
                         'model'=>'ZAZ-965'}}}}}})

zap2 = Car.take(manufacturer: 'Zaporozhets', model: 'ZAZ-965', car_class: 'Mini', id: car.id)
zap2.nested_engine.update(code: 'MeMZ-555')
zaporozhets.nested_engine.reload.code
=> 'MeMZ-555'

Nested Models with Parent`s values

class House < Fire::Model
  set_id_key(:house_number)
  has_path_keys :country, :city, :street
end

class Room < Fire::NestedModel
  set_id_key(:number)
  nested_in House, parent_values: true
  has_path_keys :floor
end

house = House.create(country: 'Ukraine', city: 'Kyiv', 
  street: 'Shevchenko Ave.', house_number: '53101')

house.add_to_rooms(floor: 200, number: '1A')
house.add_to_rooms(floor: 150, number: '2A')


rooms = house.reload.nested_rooms
rooms.map(&:number)
=> [ '1A', '2A' ]

Fire.tree
=>  {'House'=>
      {'ukraine'=>
           {'kyiv'=>
                {'shevchenko-ave'=>
                     {'53101'=>
                          {'city'=>'Kyiv',
                           'country'=>'Ukraine',
                           'house_number'=>'53101',
                           'rooms'=>
                               {'150_'=>
                                    {'2a'=>
                                         {'city'=>'Kyiv',
                                          'country'=>'Ukraine',
                                          'floor'=>150,
                                          'number'=>'2A',
                                          'house_number'=>'53101',
                                          'street'=>'Shevchenko Ave.'}},
                                '200_'=>
                                    {'1a'=>
                                         {'city'=>'Kyiv',
                                          'country'=>'Ukraine',
                                          'floor'=>200,
                                          'number'=>'1A',
                                          'house_number'=>'53101',
                                          'street'=>'Shevchenko Ave.'}}},
                           'street'=>'Shevchenko Ave.'}}}}}})

Examples

Check this example to see how fire-model integrates in a Rails app.

Contributing to fire-model

  • Check out the latest master to make sure the feature hasn't been implemented or the bug hasn't been fixed yet.
  • Check out the issue tracker to make sure someone already hasn't requested it and/or contributed it.
  • Fork the project.
  • Start a feature/bugfix branch.
  • Commit and push until you are happy with your contribution.
  • Make sure to add tests for it. This is important so I don't break it in a future version unintentionally.
  • Please try not to mess with the Rakefile, version, or history. If you want to have your own version, or is otherwise necessary, that is fine, but please isolate to its own commit so I can cherry-pick around it.

Copyright

Copyright (c) 2015 Vitaly Tarasenko. See LICENSE.txt for further details.