Code Monkey home page Code Monkey logo

daylight's Introduction

Daylight

Gem Version Build Status

Daylight extends Rails and ActiveResource to allow your client API to perform akin to ActiveRecord

Features include those like ActiveRecord such as scopes, find_by lookups, calling associations on server-side models, using through associations, and the ability to chain queries. Eases requesting complex queries on your Rails models using remoted methods.

Typical ActiveResource functionality:

API::Post.all                                      # index request
API::Post.published                                # client-based association
API::Post.find(1)                                  # show request

Daylight adds to ActiveResource with chained queries:

API::Post.where(author_id: 1)                      # simple query
API::Post.where(author_id: 1).first                # chained query
API::Post.where(author_id: 1).published            # chained query with scope
API::Post.where(author_id: 1).published.recent     # chained query with multiple scopes
API::Post.where(author_id: 1).limit(10).offset(20) # chained query with limit and offset
API::Post.where(author_id: 1).order(:date)         # chained query with ordering

API::Post.find_by(slug: '100-best-albums-2014')    # find_by lookup gets first match

Daylight can also chain queries on an ActiveResource's association. All of the chain queries above can be used to refine searches on associations:

API::Post.first.comments                           # lookup and association
API::Post.first.comments.where(user_id: 2)         # query on lookup's association
API::Post.first.comments.where(user_id: 2).first   # chained query on
API::Post.first.comments.where(user_id: 2).edited  # chained query with scope
                                                   # etc.

Daylight allows you to return collections from complex queries on your model:

API::Post.first.top_comments

Daylight packages API query details in one request when it can to lower the network overhead.

Daylight allows you to query for a record before initializing or creating it using ActiveRecord's familiar first_or_create and first_or_initialize methods.

post = API::Post.new(slug: '100-best-albums-2014')
post.author = API::User.find_or_create(username: 'reidmix')
post.save

The last query to the database uses Rails' accepts_nested_attributes_for and the User could have easily been setup with find_or_initialize to reduce the number of server-side queries.

More information can be found in the Daylight Users Guide.

Getting Started

  1. Install Daylight both on your server and your client.

     gem install daylight
    
  2. On your server, add a rails initializer:

    require 'daylight/server'
  3. On your client, setup your API:

    Daylight::API.setup!(endpoint: 'http://localhost/')
  4. Use your client models to query your API!

Development

Ah, but we have to develop our API and client models:

  1. Develop your Rails models, controllers, and routes like you do today. Add a serializer for each model in the API. Daylight provides additions to simplify adding features to your controllers and routes.

  2. Develop your client models using Daylight's extensions to ActiveResource. Daylight provides Mocks to aid in full stack testing of your client models.

  3. Consider versioning your client models and distribute them using a gem. Daylight supports versioned APIs and has facilities available for your development.

  4. More information can be found on:

License

Daylight is released under the Apache License. First released on June 30th, 2014.

daylight's People

Contributors

dmcinnes avatar marktran avatar reidmix avatar

Stargazers

 avatar  avatar  avatar  avatar

Watchers

 avatar  avatar  avatar  avatar  avatar  avatar

daylight's Issues

read_only attributes not being added to metadata when not using default serializer

I'm seeing an issue where read_only attributes aren't in the read_only metadata if e.g. I have an EventsController that uses a serializer named BasicEventSerializer (or anything that's not the default EventSerializer).

Looks like it's because RenderJsonMeta::MetadataReadOnly only looks for the default serializer:

serializer = model.try(:active_model_serializer)

class EventsController < APIController
  handles :show

  def show
    render(
      json: self.record = model.find(params[primary_key]),
      serializer: BasicEventSerializer
    )
  end
end
class BasicEventSerializer < ActiveModel::Serializer
  embed :ids

  attributes :id
  read_only :created_at
end
{
  "event": {
    "id": 24522,
    "created_at": "2014-11-24T16:02:08.383Z"
  },
  "meta": {
    "event": {
      "read_only": [],
      "nested_resources": []
    }
  }
}

Attribute check for belongs_to resolution

class Resource < Daylight::API
  belongs_to :owner
end

ActiveResource supports (rails/activeresource#50) resolving a belongs_to association via a foreign key:

r = Resource.first 
=> {
  ...
  "owner_id": 42
}

or through the attribute name:

r = Resource.first 
=> {
  ...
  "owner": {
    ...
  }
}

It looks like Daylight is failing on the latter since it expects the foreign key to be present.

Relevant line:

reflection.klass.find(send(reflection.foreign_key))

Allow versioned client models to be used concurrently

For example:

API::V1::Post.find(1)
API::V2::Comment.find(2)

Since this is an edge case for development or migration purposes.
Associated classes will adopt the version of the parent (unless we offer an option to fall back).

Will need to set_prefix on a per class bases through inherited.

Apply best practices for API Design

Need to look at this and create issues for each:
https://github.com/interagent/http-api-design

  • Foundations
    • Require TLS
    • Version with Accepts header
    • Support caching with Etags
    • Trace requests with Request-Ids
    • Paginate with ranges
  • Requests
    • Return appropriate status codes
    • Provide full resources where available
    • Accept serialized JSON in request bodies
    • Use consistent path formats
    • Downcase paths and attributes
    • Support non-id dereferencing for convenience
    • Minimize path nesting
  • Responses
    • Provide resource (UU)IDs
    • Provide standard timestamps
    • Use UTC times formatted in ISO8601
    • Nest foreign key relations
    • Generate structured errors
    • Show rate limit status
    • Keep JSON minified in all responses
  • Artifacts
    • Provide machine-readable JSON schema
    • Provide human-readable docs
    • Provide executable examples
    • Describe stability

From this list here are the identified todos:

  • I know ~~~request ID and~~~ pagination (ranges) definitely for 1.0.0

  • Examine how to implement: Etags, Caching (plugin), Rate Limiting (plugin) mechanisms

  • For "Use consistent path formats" -- we could add a mechanism for "actions"

  • Patch Rails to allow dashes in routes, converting them to underscores in controller lookup.

  • Return full resource in update and delete actions; ensure ActiveResource doesn't complain

  • Consider adding created_at, updated_at as default read_only attributes

  • Support non-id dereferencing for convenience, for example /zones/ewr1.json:

    class ZoneController < APIController
      find_by :code, :uuid
    end
  • Nest foreign key relations should be investigated.

  • Consider adding id, url to structured errors; url points back to auto-generated doc in the API

Allow model-based associations for `belongs_to` and `has_one`

Does it make sense to have model-based association on belongs_to and has_one like they are available on has_many

class API::V1::Post < Daylight::API
  has_one :blog, through: :association
  has_one :company, through: association
  has_many :comments, through: association # only one currently supported
end

Allow versioned client models to be used concurrently

For example:

API::V1::Post.find(1)
API::V2::Comment.find(2)

Since this is an edge case for development or migration purposes.
Associated classes will adopt the version of the parent (unless we offer an option to fall back).

Will need to set_prefix on a per class bases through inherited.

Add collection version of remote methods

From a client POV is there a difference between remoted and associated? Can they be consolidated.

For example

GET /v1/posts/1/comments.json # associated
GET /v1/posts/1/top_comments.json # remoted (collection)
GET /v1/posts/1/statistics.json # remoted (single record)

We can detect if it's a collection vs. a single record. (Maybe ActiveResource already does?)
In fact, we could use has_many and has_one/belongs_to to implement remoted instance methods from the client-side

Add collection version of remote methods

Maybe remoted is only for class level methods? (how are they different from scopes?)

GET /v1/posts/by_popularity

We do have scopes and instance-level remote methods but there could be a case where a collection (class-level) based remote method would be useful.

class Post < ActiveRecord::Base
  def self.by_popularity
     order_by(:vists)
  end
end

This by_popularity method is a trivial example (tht could be a scope -- uh, from the client POV, why wouldn't it just be a scope?) but imagine a query that is much more computationally expensive across all Posts

Refinements on remote methods

Should we allow refinements on remote methods like we do with associations.

Given a Post

class Post < ActiveRecord::Base
  has_many :comments
  def top_comments
     comments.order_by(:like_count)
  end
end

And its Comment:

class Comment < ActiveRecord::Base
   scope :edited, -> { where.not(updated_at: nil) }
end

Could we?

Post.first.top_comments.edited 

Recommend Projects

  • React photo React

    A declarative, efficient, and flexible JavaScript library for building user interfaces.

  • Vue.js photo Vue.js

    ๐Ÿ–– Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.

  • Typescript photo Typescript

    TypeScript is a superset of JavaScript that compiles to clean JavaScript output.

  • TensorFlow photo TensorFlow

    An Open Source Machine Learning Framework for Everyone

  • Django photo Django

    The Web framework for perfectionists with deadlines.

  • D3 photo D3

    Bring data to life with SVG, Canvas and HTML. ๐Ÿ“Š๐Ÿ“ˆ๐ŸŽ‰

Recommend Topics

  • javascript

    JavaScript (JS) is a lightweight interpreted programming language with first-class functions.

  • web

    Some thing interesting about web. New door for the world.

  • server

    A server is a program made to process requests and deliver data to clients.

  • Machine learning

    Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.

  • Game

    Some thing interesting about game, make everyone happy.

Recommend Org

  • Facebook photo Facebook

    We are working to build community through open source technology. NB: members must have two-factor auth.

  • Microsoft photo Microsoft

    Open source projects and samples from Microsoft.

  • Google photo Google

    Google โค๏ธ Open Source for everyone.

  • D3 photo D3

    Data-Driven Documents codes.