Skip to content
David Jones edited this page Mar 18, 2015 · 4 revisions

Creating Notes via the API

We also want to be able to create notes from the API, so let's implement that action.

app/controllers/api/v1/notes_controller.rb

def create
  @note = current_api_user.notes.build note_params
  if @note.save
    render :show
  else
    render json: note.errors, status: :unprocessable_entity
  end
end

Let's not try this out within ElevenNote, but rather create an integration in another app.

API Integration

Wouldn't it be nice if we could click a button in Elevennit, and have a post show up in ElevenNote?

Let's implement a class in Elevennit using HTTParty to consume and post to the ElevenNote api.

First, let's add HTTParty to the Gemfile and run bundler.

Gemfile

gem 'httparty'
$ bundle

Next we'll create a class to consume the API.

app/models/eleven_note.rb

class ElevenNote
  include HTTParty
  include Singleton

  API_BASE = Rails.application.secrets.elevennote_api_base
  API_KEY = Rails.application.secrets['elevennote_api_key']

  base_uri API_BASE

  # ElevenNote.find(note_id)
  def self.find(id)
    get "/notes/#{id}?api_key=#{API_KEY}"
  end

  # post = Post.first
  # ElevenNote.create_from(post)
  def self.create_from(post)
    content = post.link.presence || post.body
    post "/notes",
      body: {
        api_key: API_KEY,
        note: {
          title: post.title,
          body_html: content
        }
      }
  end
end

In this class, we load the configuration from secrets.yml, and implement a class-method to find a note that the API key is authorized to access, and return its JSON response as a Ruby Hash.

We've also implemented a method to send a POST request to ElevenNote's API::V1::NotesController#create action, also, returning the JSON response for the note created.

You may also note that this class does not inherit from ActiveRecord::Base, despite residing in app/models. This is because it doesn't need any of the methods or functionality that it would inherit from Active Record. This class does not interact directly with a database table in this app. This is sometimes called a "Plain Ol' Ruby Object" or PORO class.

It just so happens to be a singleton class, as well. That basically means that we never need to instantiate it directly. There will only ever be one instance of this class throughout the application.

Next, we'll add the URL to ElevenNote to secrets.yml. We'll also add a valid API key. In a real app, the API key would probably be a per-user setting, but let's assume for now that every user of Elevennit can post to the same ElevenNote account.

config/secrets.yml

development:
  secret_key_base: [existing value]
  elevennote_api_base: localhost:3000/api/v1/
  elevennote_api_key: $2a$10$nweqmq4wAAfnPTe.mILRzubq7cTtIt5MEcF7ye121qgYXYN/n/nr6

Let's try it out. Make sure you have ElevenNote running on port 3000 (or whichever port you specified in Elevennit's secrets.yml), and fire up the Elevennit console.

elevennit $ bin/rails c

We should be able to access some notes we own...

[1] pry(main)> ElevenNote.find(1)
=> {"id"=>1,
 "title"=>"Awesome new note",
 "body_text"=>nil,
 "body_html"=>"fooooooo",
 "created_at"=>"2014-11-11T22:06:17.688Z",
 "updated_at"=>"2014-11-11T22:07:29.246Z"}
[2] pry(main)>

...and to create some new ones from our Post records.

[2] pry(main)> post = Post.last
  Post Load (1.0ms)  SELECT  "posts".* FROM "posts"   ORDER BY updated_at ASC LIMIT 1
=> #<Post id: 1, title: "TIL there's an immersive coding experience in Indy...", link: "https://elevenfifty.com/", body: "You should totally check this out. They have a T. ...", created_at: "2014-11-10 20:46:34", updated_at: "2014-11-12 15:18:12", post_type: 0, category_id: 1, user_id: nil>
[3] pry(main)> ElevenNote.create_from post
=> {"id"=>9,
 "title"=>"TIL there's an immersive coding experience in Indy.",
 "body_text"=>"https://elevenfifty.com/",
 "body_html"=>"https://elevenfifty.com/",
 "created_at"=>"2014-11-18T21:17:02.446Z",
 "updated_at"=>"2014-11-18T21:17:02.446Z"}

If it doesn't seem to be loading your api key from secrets.yml (Rails.application.secrets.elevennote_api_key doesn't work from Rails console), then you may need to restart your application, and also restart spring with spring stop. Spring sometimes caches config data even between server restarts. spring stop forces it to reload everything from scratch on next app boot-up.

If it works, create_from should return JSON of the object created in ElevenNote. Look in ElevenNote, and you should see the note created from the Elevennit post.

Pretty cool, huh? You don't want to have to do that from the console though. Let's add an action to Elevennit's PostsController.

_app/controllers/posts_controller.rb_

def save
  ElevenNote.create_from @post
  redirect_to @post, notice: 'Saved to ElevenNote!'
rescue
  redirect_to @post, alert: 'We were unable to save that to ElevenNote.'
end

This isn't a REST action, so we'll need to add a route for it. Since we're creating something, we'll make use the POST HTTP verb.

config/routes.rb

resources :posts do
  member do
    post :save
  end
end

Now we just need to add a link to the actions partial.

_app/views/posts/actions.html.erb

<% if user_signed_in? -%>
  <ul class="actions">
    <li><%= link_to comment_link_text(post.comment_threads.length), post_path(post, anchor: 'comments') %></li>
    <li><%= link_to 'edit', edit_post_path(post) %></li>
    <li>
      <%= link_to 'delete', post_path(post), method: :delete, data: { confirm: 'Are you sure? This cannot be undone.' } %>
    </li>
    <li>
      <%= link_to 'save', save_post_path(post), method: :post %>
    </li>
  </ul>
<% end -%>