Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Auto API generation #324

Open
wants to merge 2 commits into
base: master
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
63 changes: 63 additions & 0 deletions lib/volt/controllers/restfull_base_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
require 'volt/controllers/http_controller'

module Volt
# Allow you to create controllers that act as http endpoints
class RestfullBaseController < HttpController

before_action :setup_model
before_action :setup_new_resource, only: [:create]
before_action :setup_buffered_resource, only: [:update]
before_action :setup_resource, only: [:show, :destroy]

private

attr_reader :model, :resource

def self.model(model = :not_set)
if model == :not_set
@model
else
@model = model.to_sym
end
end

def collection_name
model.pluralize
end

def collection
store.send(collection_name)
end

def resource_params
params.send(:"_#{model}")
end

def setup_model
@model = self.class.model || params._model.try(:to_sym)
unless @model
render text: "No model given", status: :internal_server_error
stop_chain
end
end

def setup_new_resource
@resource = collection.new(resource_params)
end

def setup_resource
@resource = collection.where(id: params.id).first.sync
unless @resource
head :not_found
stop_chain
end
end

def setup_buffered_resource
setup_resource
@resource = @resource.buffer
end

end
end

42 changes: 42 additions & 0 deletions lib/volt/controllers/simple_json_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,42 @@
require 'volt/controllers/restfull_base_controller'

module Volt
# Allow you to create controllers that act as http endpoints
class SimpleJsonController < RestfullBaseController

def index
render json: { collection_name => collection.all.to_a.sync }
end

def show
render json: { model => resource.to_h }
end

def create
collection.append(resource).then do
# TODO http_controllers should be able to get routes via params
head :created #, location: params_to_url(:get, component: params._component, controller: params._controller, id: resource.id)
end.fail do |err|

end
end

def update
resource.update(resource_params)
resource.save!.then do
head :no_content
end.fail do

end
end

def destroy
resource.destroy.then do
head :no_content
end.fail do |err|

end
end

end
end
2 changes: 2 additions & 0 deletions lib/volt/volt/core.rb
Original file line number Diff line number Diff line change
@@ -1,3 +1,5 @@
# Require in the core volt classes that get used on the server
require 'volt/controllers/http_controller'
require 'volt/controllers/restfull_base_controller'
require 'volt/controllers/simple_json_controller'
require 'volt/server/rack/http_request'
2 changes: 2 additions & 0 deletions spec/apps/kitchen_sink/app/api/config/dependencies.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# Specify which components you wish to include when
# this component loads.
10 changes: 10 additions & 0 deletions spec/apps/kitchen_sink/app/api/config/initializers/boot.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,10 @@
# Place any code you want to run when the component is included on the client
# or server.

# To include code only on the client use:
# if RUBY_PLATFORM == 'opal'
#
# To include code only on the server, use:
# unless RUBY_PLATFORM == 'opal'
# ^^ this will not send compile in code in the conditional to the client.
# ^^ this include code required in the conditional.
1 change: 1 addition & 0 deletions spec/apps/kitchen_sink/app/api/config/routes.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1 @@
# See https://github.com/voltrb/volt#routes for more info on routes
20 changes: 20 additions & 0 deletions spec/apps/kitchen_sink/app/api/controllers/main_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
module Api
class MainController < Volt::ModelController
def index
# Add code for when the index view is loaded
end

def about
# Add code for when the about view is loaded
end

private

# the main template contains a #template binding that shows another
# template. This is the path to that template. It may change based
# on the params._controller and params._action values.
def main_path
"#{params._component || 'main'}/#{params._controller || 'main'}/#{params._action || 'index'}"
end
end
end
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
module Api
class ApiController < Volt::SimpleJsonController

end
end
5 changes: 5 additions & 0 deletions spec/apps/kitchen_sink/app/api/views/main/index.html
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
<:Title>
Component Index

<:Body>
<h1>Component Index</h1>
3 changes: 3 additions & 0 deletions spec/apps/kitchen_sink/app/main/config/routes.rb
Original file line number Diff line number Diff line change
Expand Up @@ -34,5 +34,8 @@
# Route for file uploads
client '/upload', controller: 'upload', action: 'index'

# Simple JSON API specs
rest '/api/issues', component: 'api', controller: 'api', model: 'issue'

# The main route, this should be last. It will match any params not previously matched.
client '/', {}
3 changes: 3 additions & 0 deletions spec/apps/kitchen_sink/app/main/models/issue.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
class Issue < Volt::Model
field :name, String
end
171 changes: 171 additions & 0 deletions spec/controllers/restfull_base_controller_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,171 @@
require 'spec_helper'

if RUBY_PLATFORM != 'opal'
require 'volt/controllers/restfull_base_controller'
require 'volt/server/rack/http_request'
require 'volt/server/rack/http_resource'

describe Volt::RestfullBaseController do
class TestRestfulController < Volt::RestfullBaseController

attr_accessor :the_model, :the_collection, :the_collection_name, :the_resource_params

def model_test
self.the_model = model
end

def collection_test
self.the_collection = collection
self.the_collection_name = collection_name
end

def params_test
self.the_resource_params = resource_params
end

end

class StaticRestfulController < Volt::RestfullBaseController
attr_accessor :the_model

model :issue

def model_test
self.the_model = model
end
end

class ImplementedRestfulController < Volt::RestfullBaseController
attr_accessor :the_resource

def create
self.the_resource = resource
end

def update
self.the_resource = resource
end

def show
self.the_resource = resource
end

def destroy
self.the_resource = resource
end
end

let(:app) { ->(env) { [404, env, 'app'] } }

def request(url='http://example.com/issues')
Volt::HttpRequest.new(
Rack::MockRequest.env_for(url, 'CONTENT_TYPE' => 'application/json;charset=utf-8'))
end


let(:controller) { TestRestfulController.new(volt_app, {}, request) }

before(:each) do
store.issues.reverse.each do |issue|
issue.destroy
end
end

it 'should set the model from the params' do
controller = TestRestfulController.new(
volt_app, {model: 'issue'}, request
)
controller.perform(:model_test)
expect(controller.the_model).to eq(:issue)
end

it 'should use a static model if set' do
controller = StaticRestfulController.new(
volt_app, {model: 'none'}, request
)
controller.perform(:model_test)
expect(controller.the_model).to eq(:issue)
end

it 'should set the collection based on the model name' do
controller = TestRestfulController.new(
volt_app, {model: 'issue'}, request)

controller.perform(:collection_test)
expect(controller.the_collection_name).to eq(:issues)
expect(controller.the_collection).to be_kind_of(Volt::ArrayModel)
expect(controller.the_collection.new).to be_kind_of(Issue)
end

it 'should set the correct resource_params based on the model name' do
issue = { name: 'test' }
controller = TestRestfulController.new(
volt_app, {model: 'issue', issue: issue }, request
)
controller.perform(:params_test)
expect(controller.the_resource_params).to eq(issue)
end

it 'should setup a new instance of a model with the given params for the create action' do
issue = { name: 'test' }
controller = ImplementedRestfulController.new(
volt_app, {model: 'issue', issue: issue }, request
)
controller.perform(:create)
expect(controller.the_resource).to be_kind_of(Issue)
expect(controller.the_resource.root).to be_kind_of(Issue)
new_issue = controller.the_resource.to_h
new_issue.delete(:id)
expect(new_issue).to eq(issue)
end

it 'should setup a buffer of a model for the update action' do
issue = store.issues.create({ name: 'test' }).sync
controller = ImplementedRestfulController.new(
volt_app, { model: 'issue', id: issue.id }, request
)
controller.perform(:update)
expect(controller.the_resource.to_h).to eq(issue.to_h)
expect(controller.the_resource.root).to eq(store)
expect(controller.the_resource.buffer?).to be(true)
end

it 'should setup the model for the show action' do
issue = store.issues.create({ name: 'test' }).sync
controller = ImplementedRestfulController.new(
volt_app, { model: 'issue', id: issue.id }, request
)
controller.perform(:show)
expect(controller.the_resource.to_h).to eq(issue.to_h)
expect(controller.the_resource.root).to eq(store)
expect(controller.the_resource.buffer?).to be(false)
end

it 'should setup the model for the delete action' do
issue = store.issues.create({ name: 'test' }).sync
controller = ImplementedRestfulController.new(
volt_app, { model: 'issue', id: issue.id }, request
)
controller.perform(:show)
expect(controller.the_resource.to_h).to eq(issue.to_h)
expect(controller.the_resource.root).to eq(store)
expect(controller.the_resource.buffer?).to be(false)
end

it 'should respond with http 404 not found if the resource could not be found' do
controller = ImplementedRestfulController.new(
volt_app, { model: 'issue', id: 0 }, request
)
response = controller.perform(:show)
expect(response.status).to eq(404)
end

it 'should respond with http 500 internal server error no model is set' do
controller = ImplementedRestfulController.new(
volt_app, { }, request
)
response = controller.perform(:show)
expect(response.status).to eq(500)
end
end
end
Loading