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

Use the JSON API Resources gem to handle api #30

Open
wants to merge 4 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
1 change: 1 addition & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -13,6 +13,7 @@ gem 'skylight' # application performance monitoring
gem 'sprockets', '>= 3.0.0'
gem 'uglifier'
gem 'graph_matching' # max-weight matching algorithm used to pair
gem 'jsonapi-resources' # API is build around this gem

# front-end libraries
gem 'react_on_rails'
Expand Down
4 changes: 4 additions & 0 deletions Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -109,6 +109,9 @@ GEM
hashdiff (0.3.2)
i18n (0.8.1)
json (2.1.0)
jsonapi-resources (0.8.3)
concurrent-ruby
rails (>= 4.0)
lazy_priority_queue (0.1.1)
libv8 (5.3.332.38.5)
listen (3.1.5)
Expand Down Expand Up @@ -300,6 +303,7 @@ DEPENDENCIES
faker
formulaic
graph_matching
jsonapi-resources
listen
mini_racer
normalize-rails
Expand Down
8 changes: 8 additions & 0 deletions app/controllers/api/application_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true
module Api
class ApplicationController < JSONAPI::ResourceController
skip_before_action :ensure_correct_media_type
skip_before_action :ensure_valid_accept_media_type
protect_from_forgery with: :null_session
end
end
5 changes: 5 additions & 0 deletions app/controllers/api/debaters_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# frozen_string_literal: true
module Api
class DebatersController < ApplicationController
end
end
5 changes: 5 additions & 0 deletions app/controllers/api/schools_controller.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# frozen_string_literal: true
module Api
class SchoolsController < ApplicationController
end
end
41 changes: 0 additions & 41 deletions app/controllers/schools_controller.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,50 +2,9 @@
class SchoolsController < ApplicationController
def index
@schools = School.order(:name).map(&:as_json)
respond_to do |format|
format.html { render :index }
format.json { render json: @schools }
end
end

def create
@school = School.new(school_params)
if @school.save
render json: @school
else
render json: @school.errors, status: :unprocessable
end
end

def show
@school = School.find(params[:id]).as_json
respond_to do |format|
format.html { render :show }
format.json { render json: @school }
end
end

def update
@school = School.find(params[:id])
if @school.update(school_params)
render json: @school
else
render json: @school.errors, status: :unprocessable
end
end

def destroy
@school = School.find(params[:id])
if @school.destroy
render json: {}, status: 200
else
render json: {}, status: :unprocessable
end
end

private

def school_params
params.require(:school).permit(:name)
end
end
8 changes: 8 additions & 0 deletions app/resoucres/api/debater_resource.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true
module Api
class DebaterResource < JSONAPI::Resource
attributes :name, :novice
belongs_to :team
belongs_to :school
end
end
6 changes: 6 additions & 0 deletions app/resoucres/api/school_resource.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,6 @@
# frozen_string_literal: true
module Api
class SchoolResource < JSONAPI::Resource
attributes :name
end
end
8 changes: 8 additions & 0 deletions app/resoucres/api/team_resource.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,8 @@
# frozen_string_literal: true
module Api
class TeamResource < JSONAPI::Resource
attributes :name, :seed
has_many :debaters
belongs_to :school
end
end
6 changes: 3 additions & 3 deletions client/app/components/debaters/DebaterContainer.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'
import {Button} from 'react-bootstrap'
import {DebaterForm, DebaterDetail} from './index'
import Debater from '../../resources/Debater'
import {Debater, ApiDebater} from '../../resources/Debater'

export class DebaterContainer extends React.Component {
static propTypes = {
Expand Down Expand Up @@ -35,11 +35,11 @@ export class DebaterContainer extends React.Component {
handleEditClick = () => this.setState({ isEditing: true })

handleDeleteClick = () => {
const debater = new Debater(this.state.debater.id)
const debater = new ApiDebater(this.state.debater.id)
const confirmed = confirm('Are you sure? This will delete all of this debaters stats and affect their team speaks')
if (confirmed) {
debater.destroy()
.then(() => window.location = debater.pathTo().index)
.then(() => window.location = new Debater().pathTo(false).index)
.catch(() => this.setState({ message: 'Could not delete this debater' }))
}
}
Expand Down
2 changes: 1 addition & 1 deletion client/app/components/debaters/DebaterDetail.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react';
import School from '../../resources/School'
import {School} from '../../resources/School'

export const DebaterDetail = (props) => {
const school = new School(props.school.id)
Expand Down
4 changes: 2 additions & 2 deletions client/app/components/debaters/DebaterForm.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import React from 'react'
import {FormControl, ControlLabel, FormGroup, Button} from 'react-bootstrap'
import Debater from '../../resources/Debater'
import {ApiDebater} from '../../resources/Debater'
import SchoolSelectField from '../schools/SchoolSelectField'

export class DebaterForm extends React.Component {
Expand All @@ -27,7 +27,7 @@ export class DebaterForm extends React.Component {

handleSubmit = (event) => {
event.preventDefault()
const debater = new Debater(this.props.id)
const debater = new ApiDebater(this.props.id)
let request = this.props.id ? debater.update : debater.create
request(this.paramsToSubmit())
.then((response) => this.props.handleSuccessfulSubmit(response))
Expand Down
2 changes: 1 addition & 1 deletion client/app/components/debaters/DebaterItem.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react'
import Debater from '../../resources/Debater'
import {Debater} from '../../resources/Debater'

export const DebaterItem = (props) => {
const debater = new Debater(props.id)
Expand Down
13 changes: 4 additions & 9 deletions client/app/components/schools/SchoolDetail.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React, {PropTypes} from 'react'
import {Button} from 'react-bootstrap'
import EditableText from '../shared/EditableText'
import School from '../../resources/School'
import {School, ApiSchool} from '../../resources/School'

class SchoolDetail extends React.Component {
state = {
Expand All @@ -16,17 +16,16 @@ class SchoolDetail extends React.Component {
deleteSchool = (event) => {
event.preventDefault()
let confirmed = confirm('Are you sure? This will delete all of the debaters and judges')
const school = this.getSchoolObject()
if (confirmed) {
school.destroy()
.then((response) => window.location = school.pathTo().index)
new ApiSchool(this.state.school.id).destroy()
.then((response) => window.location = new School.pathTo().index)
.catch(() => this.setState({message: 'Could not delete school.'}))
}
}

handleNameUpdate = (name) => {
if (name.trim() !== this.state.school.name) {
this.getSchoolObject().update({name})
new ApiSchool(this.state.school.id).update({name})
.then((response) => {
this.flashMessage('School updated!')
this.setState({school: response.data})
Expand All @@ -40,10 +39,6 @@ class SchoolDetail extends React.Component {
setTimeout(() => this.setState({message: ''}), 2500)
}

getSchoolObject () {
return new School(this.state.school.id)
}

render () {
return (
<div className="school">
Expand Down
2 changes: 1 addition & 1 deletion client/app/components/schools/SchoolItem.js
Original file line number Diff line number Diff line change
@@ -1,5 +1,5 @@
import React from 'react'
import School from '../../resources/School'
import {School} from '../../resources/School'

export const SchoolItem = (props) => {
return (
Expand Down
4 changes: 2 additions & 2 deletions client/app/components/schools/SchoolListContainer.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import React from 'react'
import _ from 'lodash'
import {SchoolList} from './SchoolList'
import {CreateSchool} from './CreateSchool'
import School from '../../resources/School'
import {ApiSchool} from '../../resources/School'

export class SchoolListContainer extends React.Component {
static propTypes = {
Expand All @@ -17,7 +17,7 @@ export class SchoolListContainer extends React.Component {

handleAddSchool = (event) => {
event.preventDefault()
new School().create({name: this.state.newSchool})
new ApiSchool().create({name: this.state.newSchool})
.then((response) => {
const newSchool = response.data
this.setState({
Expand Down
6 changes: 3 additions & 3 deletions client/app/components/schools/SchoolSelectField.js
Original file line number Diff line number Diff line change
@@ -1,7 +1,7 @@
import React from 'react'
import Autosuggest from 'react-autosuggest'
import {ControlLabel, FormGroup} from 'react-bootstrap'
import School from '../../resources/School'
import {ApiSchool} from '../../resources/School'

const preprocessName = (name) => name.trim().toLowerCase()

Expand Down Expand Up @@ -41,8 +41,8 @@ export default class SchoolSelectField extends React.Component {
}

componentDidMount() {
new School().index('json')
.then((response) => this.setState({schools: response.data}))
new ApiSchool().index()
.then((response) => this.setState({schools: response.data.data}))
}

onSuggestionsClearRequested = () => {
Expand Down
21 changes: 21 additions & 0 deletions client/app/resources/ApiResource.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,21 @@
import Resource from './Resource'

export default class ApiResource extends Resource {
constructor(name, id) {
super(name, id)
}

pathTo = () => {
const listPath = `/api/${this.name}s`
const individualPath = `${listPath}/${this.id}`
return {
index: listPath,
new: `${listPath}/new`,
create: listPath,
show: individualPath,
edit: `${individualPath}/edit`,
update: individualPath,
destroy: individualPath
}
}
}
9 changes: 8 additions & 1 deletion client/app/resources/Debater.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import Resource from './Resource'
import ApiResource from './ApiResource'

export default class Debater extends Resource {
export class Debater extends Resource {
constructor (id) {
super('debater', id)
}
}

export class ApiDebater extends ApiResource {
constructor (id) {
super('debater', id)
}
Expand Down
34 changes: 13 additions & 21 deletions client/app/resources/Resource.js
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ import axios from 'axios'
import ReactOnRails from 'react-on-rails'

export default class Resource {
constructor (name, id) {
constructor(name, id) {
this.name = name
this.id = id
}
Expand All @@ -25,49 +25,41 @@ export default class Resource {
return {authenticity_token: ReactOnRails.authenticityToken()}
}

index = (ext) => {
return axios.get(this.pathTo().index + this._extension(ext), this.authenticityTokenObject())
index = () => {
return axios.get(this.pathTo().index, this.authenticityTokenObject())
}

new = (ext) => {
return axios.get(this.pathTo().new + this._extension(ext), this.authenticityTokenObject())
new = () => {
return axios.get(this.pathTo().new, this.authenticityTokenObject())
}

create = (resourceData, ext) => {
let data = {}
data[this.name] = resourceData
return axios
.post(this.pathTo().create + this._extension(ext), {...data, ...this.authenticityTokenObject()})
.post(this.pathTo().create, {...data, ...this.authenticityTokenObject()})
}

show = (ext) => {
return axios.get(this.pathTo().show + this._extension(ext), this.authenticityTokenObject())
show = () => {
return axios.get(this.pathTo().show, this.authenticityTokenObject())
}

edit = (ext) => {
return axios.get(this.pathTo().edit + this._extension(ext), this.authenticityTokenObject())
edit = () => {
return axios.get(this.pathTo().edit, this.authenticityTokenObject())
}

update = (resourceData, ext) => {
let data = {}
data[this.name] = resourceData
return axios
.put(this.pathTo().update + this._extension(ext), {...data, ...this.authenticityTokenObject()})
.put(this.pathTo().update, {...data, ...this.authenticityTokenObject()})
}

destroy = (ext) => {
destroy = () => {
return axios.request({
url: this.pathTo().destroy + this._extension(ext),
url: this.pathTo().destroy,
method: 'DELETE',
data: this.authenticityTokenObject()
})
}

_extension(extension) {
if (extension) {
return `.${extension}`
} else {
return ''
}
}
}
9 changes: 8 additions & 1 deletion client/app/resources/School.js
Original file line number Diff line number Diff line change
@@ -1,6 +1,13 @@
import Resource from './Resource'
import ApiResource from './ApiResource'

export default class School extends Resource {
export class School extends Resource {
constructor (id) {
super('school', id)
}
}

export class ApiSchool extends ApiResource {
constructor (id) {
super('school', id)
}
Expand Down
Loading