diff --git a/app/controllers/organizations_controller.rb b/app/controllers/organizations_controller.rb new file mode 100644 index 0000000..c258d20 --- /dev/null +++ b/app/controllers/organizations_controller.rb @@ -0,0 +1,74 @@ +class OrganizationsController < ApplicationController + before_action :set_organization, only: [:show, :edit, :update, :destroy] + + # GET /organizations + # GET /organizations.json + def index + @organizations = Organization.all + end + + # GET /organizations/1 + # GET /organizations/1.json + def show + end + + # GET /organizations/new + def new + @organization = Organization.new + end + + # GET /organizations/1/edit + def edit + end + + # POST /organizations + # POST /organizations.json + def create + @organization = Organization.new(organization_params) + + respond_to do |format| + if @organization.save + format.html { redirect_to @organization, notice: 'Organization was successfully created.' } + format.json { render action: 'show', status: :created, location: @organization } + else + format.html { render action: 'new' } + format.json { render json: @organization.errors, status: :unprocessable_entity } + end + end + end + + # PATCH/PUT /organizations/1 + # PATCH/PUT /organizations/1.json + def update + respond_to do |format| + if @organization.update(organization_params) + format.html { redirect_to @organization, notice: 'Organization was successfully updated.' } + format.json { head :no_content } + else + format.html { render action: 'edit' } + format.json { render json: @organization.errors, status: :unprocessable_entity } + end + end + end + + # DELETE /organizations/1 + # DELETE /organizations/1.json + def destroy + @organization.destroy + respond_to do |format| + format.html { redirect_to organizations_url } + format.json { head :no_content } + end + end + + private + # Use callbacks to share common setup or constraints between actions. + def set_organization + @organization = Organization.find(params[:id]) + end + + # Never trust parameters from the scary internet, only allow the white list through. + def organization_params + params.require(:organization).permit(:url, :name, :about) + end +end diff --git a/app/helpers/organizations_helper.rb b/app/helpers/organizations_helper.rb new file mode 100644 index 0000000..24cc9a8 --- /dev/null +++ b/app/helpers/organizations_helper.rb @@ -0,0 +1,2 @@ +module OrganizationsHelper +end diff --git a/app/models/meetup_api.rb b/app/models/meetup_api.rb new file mode 100644 index 0000000..9c1c7c5 --- /dev/null +++ b/app/models/meetup_api.rb @@ -0,0 +1,19 @@ +class MeetupApi + BASE_URL = 'https://api.meetup.com' + AUTH = "sign=true&key=#{ENV['MEETUP_KEY']}" + + def self.is_meetup_url(url) + url =~ /meetup.com/ + end + + def self.group(url) + group_name = URI.parse(url).path.split('/').second + url = "#{BASE_URL}/#{group_name}?#{AUTH}" + response = HTTParty.get(url) + + # watch out for the rate limit + #p response.headers + + response.parsed_response + end +end diff --git a/app/models/organization.rb b/app/models/organization.rb new file mode 100644 index 0000000..1b59e01 --- /dev/null +++ b/app/models/organization.rb @@ -0,0 +1,12 @@ +class Organization < ActiveRecord::Base + before_create :check_url + + def check_url + if MeetupApi.is_meetup_url(url) + group = MeetupApi.group(url) + self.name = group['name'] + self.about = group['description'] + self.meetup_id = group['id'] + end + end +end diff --git a/app/views/organizations/_form.html.slim b/app/views/organizations/_form.html.slim new file mode 100644 index 0000000..bf1cdd9 --- /dev/null +++ b/app/views/organizations/_form.html.slim @@ -0,0 +1,34 @@ += form_for @organization, :html => {class:'form'} do |f| + - if @organization.errors.any? + #error_explanation + h2 = "#{pluralize(@organization.errors.count, "error")} prohibited this organization from being saved:" + ul + - @organization.errors.full_messages.each do |message| + li = message + + // URL + .icon-group + .icon + .icon-content + .form-group + = f.text_field :url, class:'form-control', placeholder:'Organization URL', tabindex: autoTabIndex + + // Name + .icon-group + .icon + .icon-content + .form-group + = f.text_field :name, class:'form-control', placeholder:'Organization Name', tabindex: autoTabIndex + + // About + .icon-group + .icon + i.glyphicon.glyphicon-info-sign + .icon-content + .form-group + = f.text_area :about, class:'form-control', placeholder:'A description of the organization', rows: 5, tabindex: autoTabIndex + + .icon-group + .icon + .icon-content + = f.submit diff --git a/app/views/organizations/edit.html.slim b/app/views/organizations/edit.html.slim new file mode 100644 index 0000000..b224196 --- /dev/null +++ b/app/views/organizations/edit.html.slim @@ -0,0 +1,8 @@ +h1 Editing organization + +== render 'form' + += link_to 'Show', @organization +'| += link_to 'Back', organizations_path + diff --git a/app/views/organizations/index.html.slim b/app/views/organizations/index.html.slim new file mode 100644 index 0000000..8d47367 --- /dev/null +++ b/app/views/organizations/index.html.slim @@ -0,0 +1,19 @@ +h1 Listing organizations + +table + thead + tr + th + th + th + + tbody + - @organizations.each do |organization| + tr + td = link_to 'Show', organization + td = link_to 'Edit', edit_organization_path(organization) + td = link_to 'Destroy', organization, data: {:confirm => 'Are you sure?'}, :method => :delete + +br + += link_to 'New Organization', new_organization_path diff --git a/app/views/organizations/index.json.jbuilder b/app/views/organizations/index.json.jbuilder new file mode 100644 index 0000000..2dada5b --- /dev/null +++ b/app/views/organizations/index.json.jbuilder @@ -0,0 +1,4 @@ +json.array!(@organizations) do |organization| + json.extract! organization, :id + json.url organization_url(organization, format: :json) +end diff --git a/app/views/organizations/new.html.slim b/app/views/organizations/new.html.slim new file mode 100644 index 0000000..f08446b --- /dev/null +++ b/app/views/organizations/new.html.slim @@ -0,0 +1,6 @@ +.icon-container + .icon-group + .icon + .icon-content + h3 New organization + == render 'form' diff --git a/app/views/organizations/show.html.slim b/app/views/organizations/show.html.slim new file mode 100644 index 0000000..c9dcf8d --- /dev/null +++ b/app/views/organizations/show.html.slim @@ -0,0 +1,17 @@ +p#notice = notice + +h2 Name +.div + = link_to @organization.name, @organization.url + +h2 Description +.div + = simple_format(@organization.about) + +h2 URL +.div + = link_to @organization.url, @organization.url + += link_to 'Edit', edit_organization_path(@organization) +'| += link_to 'Back', organizations_path diff --git a/app/views/organizations/show.json.jbuilder b/app/views/organizations/show.json.jbuilder new file mode 100644 index 0000000..e9560cd --- /dev/null +++ b/app/views/organizations/show.json.jbuilder @@ -0,0 +1 @@ +json.extract! @organization, :id, :created_at, :updated_at diff --git a/config/routes.rb b/config/routes.rb index ff7f101..38690ee 100644 --- a/config/routes.rb +++ b/config/routes.rb @@ -6,6 +6,8 @@ resources :events get 'calendar', to: 'events#calendar' + resources :organizations + # The priority is based upon order of creation: first created -> highest priority. # See how all your routes lay out with "rake routes". diff --git a/db/migrate/20150323044944_remove_location_from_events.rb b/db/migrate/20150323044944_remove_location_from_events.rb index e71659e..d77aebf 100644 --- a/db/migrate/20150323044944_remove_location_from_events.rb +++ b/db/migrate/20150323044944_remove_location_from_events.rb @@ -1,10 +1,5 @@ class RemoveLocationFromEvents < ActiveRecord::Migration def change - def up - remove_column :events, :location - end - def down - add_column :events, :location, :string - end + remove_column :events, :location end end diff --git a/db/migrate/20150822155300_create_organizations.rb b/db/migrate/20150822155300_create_organizations.rb new file mode 100644 index 0000000..3754cf3 --- /dev/null +++ b/db/migrate/20150822155300_create_organizations.rb @@ -0,0 +1,12 @@ +class CreateOrganizations < ActiveRecord::Migration + def change + create_table :organizations do |t| + t.string :name + t.text :about + t.string :url + t.string :meetup_id + + t.timestamps null: false + end + end +end diff --git a/db/migrate/20150823235104_add_organization_to_events.rb b/db/migrate/20150823235104_add_organization_to_events.rb new file mode 100644 index 0000000..22ab61c --- /dev/null +++ b/db/migrate/20150823235104_add_organization_to_events.rb @@ -0,0 +1,5 @@ +class AddOrganizationToEvents < ActiveRecord::Migration + def change + add_reference :events, :organization, index: true, foreign_key: true + end +end diff --git a/db/schema.rb b/db/schema.rb index 8d77b54..4852043 100644 --- a/db/schema.rb +++ b/db/schema.rb @@ -11,19 +11,21 @@ # # It's strongly recommended that you check this file into your version control system. -ActiveRecord::Schema.define(version: 20150323211739) do +ActiveRecord::Schema.define(version: 20150823235104) do create_table "events", force: :cascade do |t| - t.string "title", limit: 255 + t.string "title" t.datetime "start" t.datetime "finish" - t.string "location", limit: 255 - t.string "ticketurl", limit: 255 + t.string "ticketurl" t.datetime "created_at" t.datetime "updated_at" t.text "description" + t.integer "organization_id" end + add_index "events", ["organization_id"], name: "index_events_on_organization_id" + create_table "location_events", force: :cascade do |t| t.integer "location_id" t.integer "event_id" @@ -48,6 +50,15 @@ t.string "long_address" end + create_table "organizations", force: :cascade do |t| + t.string "name" + t.text "about" + t.string "url" + t.string "meetup_id" + t.datetime "created_at", null: false + t.datetime "updated_at", null: false + end + create_table "user_events", force: :cascade do |t| t.integer "user_id", null: false t.integer "event_id" @@ -59,16 +70,16 @@ add_index "user_events", ["user_id"], name: "index_user_events_on_user_id" create_table "users", force: :cascade do |t| - t.string "email", limit: 255, default: "", null: false - t.string "encrypted_password", limit: 255, default: "", null: false - t.string "reset_password_token", limit: 255 + t.string "email", default: "", null: false + t.string "encrypted_password", default: "", null: false + t.string "reset_password_token" t.datetime "reset_password_sent_at" t.datetime "remember_created_at" - t.integer "sign_in_count", default: 0, null: false + t.integer "sign_in_count", default: 0, null: false t.datetime "current_sign_in_at" t.datetime "last_sign_in_at" - t.string "current_sign_in_ip", limit: 255 - t.string "last_sign_in_ip", limit: 255 + t.string "current_sign_in_ip" + t.string "last_sign_in_ip" t.datetime "created_at" t.datetime "updated_at" end diff --git a/spec/controllers/organizations_controller_spec.rb b/spec/controllers/organizations_controller_spec.rb new file mode 100644 index 0000000..d284b82 --- /dev/null +++ b/spec/controllers/organizations_controller_spec.rb @@ -0,0 +1,159 @@ +require 'rails_helper' + +# This spec was generated by rspec-rails when you ran the scaffold generator. +# It demonstrates how one might use RSpec to specify the controller code that +# was generated by Rails when you ran the scaffold generator. +# +# It assumes that the implementation code is generated by the rails scaffold +# generator. If you are using any extension libraries to generate different +# controller code, this generated spec may or may not pass. +# +# It only uses APIs available in rails and/or rspec-rails. There are a number +# of tools you can use to make these specs even more expressive, but we're +# sticking to rails and rspec-rails APIs to keep things simple and stable. +# +# Compared to earlier versions of this generator, there is very limited use of +# stubs and message expectations in this spec. Stubs are only used when there +# is no simpler way to get a handle on the object needed for the example. +# Message expectations are only used when there is no simpler way to specify +# that an instance is receiving a specific message. + +RSpec.describe OrganizationsController, type: :controller do + + # This should return the minimal set of attributes required to create a valid + # Organization. As you add validations to Organization, be sure to + # adjust the attributes here as well. + let(:valid_attributes) { + skip("Add a hash of attributes valid for your model") + } + + let(:invalid_attributes) { + skip("Add a hash of attributes invalid for your model") + } + + # This should return the minimal set of values that should be in the session + # in order to pass any filters (e.g. authentication) defined in + # OrganizationsController. Be sure to keep this updated too. + let(:valid_session) { {} } + + describe "GET #index" do + it "assigns all organizations as @organizations" do + organization = Organization.create! valid_attributes + get :index, {}, valid_session + expect(assigns(:organizations)).to eq([organization]) + end + end + + describe "GET #show" do + it "assigns the requested organization as @organization" do + organization = Organization.create! valid_attributes + get :show, {:id => organization.to_param}, valid_session + expect(assigns(:organization)).to eq(organization) + end + end + + describe "GET #new" do + it "assigns a new organization as @organization" do + get :new, {}, valid_session + expect(assigns(:organization)).to be_a_new(Organization) + end + end + + describe "GET #edit" do + it "assigns the requested organization as @organization" do + organization = Organization.create! valid_attributes + get :edit, {:id => organization.to_param}, valid_session + expect(assigns(:organization)).to eq(organization) + end + end + + describe "POST #create" do + context "with valid params" do + it "creates a new Organization" do + expect { + post :create, {:organization => valid_attributes}, valid_session + }.to change(Organization, :count).by(1) + end + + it "assigns a newly created organization as @organization" do + post :create, {:organization => valid_attributes}, valid_session + expect(assigns(:organization)).to be_a(Organization) + expect(assigns(:organization)).to be_persisted + end + + it "redirects to the created organization" do + post :create, {:organization => valid_attributes}, valid_session + expect(response).to redirect_to(Organization.last) + end + end + + context "with invalid params" do + it "assigns a newly created but unsaved organization as @organization" do + post :create, {:organization => invalid_attributes}, valid_session + expect(assigns(:organization)).to be_a_new(Organization) + end + + it "re-renders the 'new' template" do + post :create, {:organization => invalid_attributes}, valid_session + expect(response).to render_template("new") + end + end + end + + describe "PUT #update" do + context "with valid params" do + let(:new_attributes) { + skip("Add a hash of attributes valid for your model") + } + + it "updates the requested organization" do + organization = Organization.create! valid_attributes + put :update, {:id => organization.to_param, :organization => new_attributes}, valid_session + organization.reload + skip("Add assertions for updated state") + end + + it "assigns the requested organization as @organization" do + organization = Organization.create! valid_attributes + put :update, {:id => organization.to_param, :organization => valid_attributes}, valid_session + expect(assigns(:organization)).to eq(organization) + end + + it "redirects to the organization" do + organization = Organization.create! valid_attributes + put :update, {:id => organization.to_param, :organization => valid_attributes}, valid_session + expect(response).to redirect_to(organization) + end + end + + context "with invalid params" do + it "assigns the organization as @organization" do + organization = Organization.create! valid_attributes + put :update, {:id => organization.to_param, :organization => invalid_attributes}, valid_session + expect(assigns(:organization)).to eq(organization) + end + + it "re-renders the 'edit' template" do + organization = Organization.create! valid_attributes + put :update, {:id => organization.to_param, :organization => invalid_attributes}, valid_session + expect(response).to render_template("edit") + end + end + end + + describe "DELETE #destroy" do + it "destroys the requested organization" do + organization = Organization.create! valid_attributes + expect { + delete :destroy, {:id => organization.to_param}, valid_session + }.to change(Organization, :count).by(-1) + end + + it "redirects to the organizations list" do + organization = Organization.create! valid_attributes + delete :destroy, {:id => organization.to_param}, valid_session + expect(response).to redirect_to(organizations_url) + end + end + +end diff --git a/spec/factories.rb b/spec/factories.rb index 82a1464..98d4c4a 100644 --- a/spec/factories.rb +++ b/spec/factories.rb @@ -38,4 +38,4 @@ user end -end \ No newline at end of file +end diff --git a/spec/helpers/organizations_helper_spec.rb b/spec/helpers/organizations_helper_spec.rb new file mode 100644 index 0000000..601faf0 --- /dev/null +++ b/spec/helpers/organizations_helper_spec.rb @@ -0,0 +1,15 @@ +require 'rails_helper' + +# Specs in this file have access to a helper object that includes +# the OrganizationsHelper. For example: +# +# describe OrganizationsHelper do +# describe "string concat" do +# it "concats two strings with spaces" do +# expect(helper.concat_strings("this","that")).to eq("this that") +# end +# end +# end +RSpec.describe OrganizationsHelper, type: :helper do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/models/organization_spec.rb b/spec/models/organization_spec.rb new file mode 100644 index 0000000..44c20ed --- /dev/null +++ b/spec/models/organization_spec.rb @@ -0,0 +1,5 @@ +require 'rails_helper' + +RSpec.describe Organization, type: :model do + pending "add some examples to (or delete) #{__FILE__}" +end diff --git a/spec/requests/organizations_spec.rb b/spec/requests/organizations_spec.rb new file mode 100644 index 0000000..ad3e993 --- /dev/null +++ b/spec/requests/organizations_spec.rb @@ -0,0 +1,10 @@ +require 'rails_helper' + +RSpec.describe "Organizations", type: :request do + describe "GET /organizations" do + it "works! (now write some real specs)" do + get organizations_path + expect(response).to have_http_status(200) + end + end +end diff --git a/spec/routing/organizations_routing_spec.rb b/spec/routing/organizations_routing_spec.rb new file mode 100644 index 0000000..b786485 --- /dev/null +++ b/spec/routing/organizations_routing_spec.rb @@ -0,0 +1,35 @@ +require "rails_helper" + +RSpec.describe OrganizationsController, type: :routing do + describe "routing" do + + it "routes to #index" do + expect(:get => "/organizations").to route_to("organizations#index") + end + + it "routes to #new" do + expect(:get => "/organizations/new").to route_to("organizations#new") + end + + it "routes to #show" do + expect(:get => "/organizations/1").to route_to("organizations#show", :id => "1") + end + + it "routes to #edit" do + expect(:get => "/organizations/1/edit").to route_to("organizations#edit", :id => "1") + end + + it "routes to #create" do + expect(:post => "/organizations").to route_to("organizations#create") + end + + it "routes to #update" do + expect(:put => "/organizations/1").to route_to("organizations#update", :id => "1") + end + + it "routes to #destroy" do + expect(:delete => "/organizations/1").to route_to("organizations#destroy", :id => "1") + end + + end +end diff --git a/spec/views/organizations/edit.html.slim_spec.rb b/spec/views/organizations/edit.html.slim_spec.rb new file mode 100644 index 0000000..e2b8cef --- /dev/null +++ b/spec/views/organizations/edit.html.slim_spec.rb @@ -0,0 +1,14 @@ +require 'rails_helper' + +RSpec.describe "organizations/edit", type: :view do + before(:each) do + @organization = assign(:organization, Organization.create!()) + end + + it "renders the edit organization form" do + render + + assert_select "form[action=?][method=?]", organization_path(@organization), "post" do + end + end +end diff --git a/spec/views/organizations/index.html.slim_spec.rb b/spec/views/organizations/index.html.slim_spec.rb new file mode 100644 index 0000000..9b22d4c --- /dev/null +++ b/spec/views/organizations/index.html.slim_spec.rb @@ -0,0 +1,14 @@ +require 'rails_helper' + +RSpec.describe "organizations/index", type: :view do + before(:each) do + assign(:organizations, [ + Organization.create!(), + Organization.create!() + ]) + end + + it "renders a list of organizations" do + render + end +end diff --git a/spec/views/organizations/new.html.slim_spec.rb b/spec/views/organizations/new.html.slim_spec.rb new file mode 100644 index 0000000..205ca2e --- /dev/null +++ b/spec/views/organizations/new.html.slim_spec.rb @@ -0,0 +1,14 @@ +require 'rails_helper' + +RSpec.describe "organizations/new", type: :view do + before(:each) do + assign(:organization, Organization.new()) + end + + it "renders new organization form" do + render + + assert_select "form[action=?][method=?]", organizations_path, "post" do + end + end +end diff --git a/spec/views/organizations/show.html.slim_spec.rb b/spec/views/organizations/show.html.slim_spec.rb new file mode 100644 index 0000000..1ba1fcc --- /dev/null +++ b/spec/views/organizations/show.html.slim_spec.rb @@ -0,0 +1,11 @@ +require 'rails_helper' + +RSpec.describe "organizations/show", type: :view do + before(:each) do + @organization = assign(:organization, create(:organization)) + end + + it "renders attributes in
" do + render + end +end