diff --git a/.env.development b/.env.development new file mode 100644 index 0000000..06fc35a --- /dev/null +++ b/.env.development @@ -0,0 +1,3 @@ +OWNER_EMAIL: 'coolemail@example.com' +BIKE_INDEX_URL: 'http://lvh.me:3000' +BIKE_INDEX_TOKEN: 'asdfklkjasfdljkd3asdfjkl2asdf' diff --git a/Gemfile b/Gemfile index 797f887..dbb355a 100644 --- a/Gemfile +++ b/Gemfile @@ -7,6 +7,7 @@ gem 'devise' gem 'faraday', '0.9.1' gem 'oauth2', '1.0.0' gem 'dotenv-rails' +gem 'sidekiq' #SCSS & Bootstrap gem 'bootstrap-sass', '2.3.2.0' @@ -27,12 +28,15 @@ group :test, :development do gem 'better_errors' gem 'assert_difference' gem 'binding_of_caller' - gem 'webmock', '1.21.0' # Uncomment this line on OS X. gem 'growl', '1.0.3' end +group :test do + gem 'webmock', '1.21.0' +end + gem 'uglifier', '2.1.1' gem 'jquery-rails', '3.0.4' gem 'jquery-turbolinks' diff --git a/Gemfile.lock b/Gemfile.lock index 71c8f06..8f62103 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -46,6 +46,8 @@ GEM rack (>= 1.0.0) rack-test (>= 0.5.4) xpath (~> 2.0) + celluloid (0.16.0) + timers (~> 4.0.0) childprocess (0.5.6) ffi (~> 1.0, >= 1.0.11) coderay (1.1.0) @@ -56,6 +58,7 @@ GEM coffee-script-source execjs coffee-script-source (1.9.1.1) + connection_pool (2.1.3) crack (0.4.2) safe_yaml (~> 1.0.0) debug_inspector (0.0.2) @@ -87,6 +90,7 @@ GEM haml (4.0.6) tilt hike (1.2.3) + hitimes (1.2.2) i18n (0.7.0) jbuilder (1.0.2) activesupport (>= 3.0.0) @@ -149,6 +153,9 @@ GEM rake (10.4.2) rdoc (3.12.2) json (~> 1.4) + redis (3.2.1) + redis-namespace (1.5.1) + redis (~> 3.0, >= 3.0.4) responders (1.1.2) railties (>= 3.2, < 4.2) rspec (3.2.0) @@ -188,6 +195,12 @@ GEM multi_json (~> 1.0) rubyzip (< 1.0.0) websocket (~> 1.0.4) + sidekiq (3.3.3) + celluloid (>= 0.16.0) + connection_pool (>= 2.1.1) + json + redis (>= 3.0.6) + redis-namespace (>= 1.3.1) slop (3.6.0) sprockets (2.12.3) hike (~> 1.2) @@ -201,6 +214,8 @@ GEM thor (0.19.1) thread_safe (0.3.5) tilt (1.4.1) + timers (4.0.1) + hitimes treetop (1.4.15) polyglot polyglot (>= 0.3.1) @@ -252,6 +267,7 @@ DEPENDENCIES sass-rails (~> 4.0.0) sdoc (= 0.3.20) selenium-webdriver (= 2.35.1) + sidekiq turbolinks (= 1.1.1) uglifier (= 2.1.1) webmock (= 1.21.0) diff --git a/app/assets/javascripts/application.js b/app/assets/javascripts/application.js index 2e9b715..8b82d89 100644 --- a/app/assets/javascripts/application.js +++ b/app/assets/javascripts/application.js @@ -14,6 +14,6 @@ //= require jquery_ujs //= require turbolinks //= require_tree . -//= require bootstrap +//= require bootstrap //= require bootstrap-datepicker -//= require bootstrap-select +//= require bootstrap-select diff --git a/app/assets/javascripts/bikes.coffee b/app/assets/javascripts/bikes.coffee new file mode 100644 index 0000000..8cb0589 --- /dev/null +++ b/app/assets/javascripts/bikes.coffee @@ -0,0 +1,6 @@ +seatTubeIn = $(".seat-tube-in") +seatTubeCm = $(".seat-tube-cm") + +seatTubeIn.on("change", () -> + seatTubeCm.val(parseFloat(seatTubeIn.val())*2.54) +) diff --git a/app/assets/javascripts/bikes.js b/app/assets/javascripts/bikes.js deleted file mode 100644 index dee720f..0000000 --- a/app/assets/javascripts/bikes.js +++ /dev/null @@ -1,2 +0,0 @@ -// Place all the behaviors and hooks related to the matching controller here. -// All this logic will automatically be available in application.js. diff --git a/app/models/bike.rb b/app/models/bike.rb index 0ad6bcb..321802b 100644 --- a/app/models/bike.rb +++ b/app/models/bike.rb @@ -12,7 +12,7 @@ class Bike < ActiveRecord::Base end def client - client = Client.find_by bike_id: self.id + Client.find_by bike_id: self.id end def ready_for_pickup? @@ -35,22 +35,7 @@ class Bike < ActiveRecord::Base def post_to_bike_index return true if self.bike_index_id.present? - - conn = Faraday.new(:url => "#{ENV['BIKE_INDEX_URL']}") do |faraday| - faraday.request :url_encoded - faraday.response :logger - faraday.adapter Faraday.default_adapter - end - - response = conn.post do |req| - req.url "/api/v2/bikes?access_token=#{ENV['BIKE_INDEX_TOKEN']}" - req.headers['Content-Type'] = 'application/json' - req.body = BikeIndexBikeGenerator.create_bike_index_bike(self) - end - - self.update_attribute :bike_index_id, JSON.parse(response.body)['bike']['id'] if response.status == 201 - - + BikeIndexLogger.perform_async(self.id) end end diff --git a/app/services/bike_index_bike_generator.rb b/app/services/bike_index_bike_generator.rb deleted file mode 100644 index ef42d1c..0000000 --- a/app/services/bike_index_bike_generator.rb +++ /dev/null @@ -1,13 +0,0 @@ -class BikeIndexBikeGenerator - def self.create_bike_index_bike(bike) - { - serial: bike.serial_number, - manufacturer: bike.brand, - owner_email: ENV["OWNER_EMAIL"], - color: bike.color, - is_for_sale: bike.purpose != "freecyclery", - frame_model: bike.model, - no_notify: true - }.to_json - end -end diff --git a/app/workers/bike_index_logger.rb b/app/workers/bike_index_logger.rb new file mode 100644 index 0000000..28ac639 --- /dev/null +++ b/app/workers/bike_index_logger.rb @@ -0,0 +1,32 @@ +class BikeIndexLogger + include Sidekiq::Worker + def perform(bike_id) + bike = Bike.find(bike_id) + + conn = Faraday.new(:url => "#{ENV['BIKE_INDEX_URL']}") do |faraday| + faraday.request :url_encoded + faraday.response :logger + faraday.adapter Faraday.default_adapter + end + + response = conn.post do |req| + req.url "/api/v2/bikes?access_token=#{ENV['BIKE_INDEX_TOKEN']}" + req.headers['Content-Type'] = 'application/json' + req.body = BikeIndexLogger.create_bike_index_bike(bike) + end + + bike.update_attribute :bike_index_id, JSON.parse(response.body)['bike']['id'] if response.status == 201 + end + + def self.create_bike_index_bike(bike) + { + serial: bike.serial_number, + manufacturer: bike.brand, + owner_email: ENV["OWNER_EMAIL"], + color: bike.color, + is_for_sale: bike.purpose != "freecyclery", + frame_model: bike.model, + no_notify: true + }.to_json + end +end diff --git a/spec/factories/bikes.rb b/spec/factories/bike_factory.rb similarity index 100% rename from spec/factories/bikes.rb rename to spec/factories/bike_factory.rb diff --git a/spec/factories/clients.rb b/spec/factories/client_factory.rb similarity index 100% rename from spec/factories/clients.rb rename to spec/factories/client_factory.rb diff --git a/spec/factories/users.rb b/spec/factories/user_factory.rb similarity index 100% rename from spec/factories/users.rb rename to spec/factories/user_factory.rb diff --git a/spec/models/bike_spec.rb b/spec/models/bike_spec.rb index 0d0d9cd..36dd492 100644 --- a/spec/models/bike_spec.rb +++ b/spec/models/bike_spec.rb @@ -1,18 +1,15 @@ require 'spec_helper' describe Bike do - it "posts to bike index" do + describe "#post_to_bike_index" do + it "calls BikeIndexLogger if no bike_index_id is present" do + expect(BikeIndexLogger).to receive(:perform_async) + create(:bike, bike_index_id: nil) + end - WebMock.stub_request(:post, "http://lvh.me:3000/api/v2/bikes?access_token=asdfklkjasfdljkd3asdfjkl2asdf") - .and_return(:status => 201, :body => {bike: {id: 1}}.to_json, :headers => {}) - bike = create(:bike, bike_index_id: nil) - expect(bike.bike_index_id).to eq(1) - - end - it "assigns bike_index_id after posting to bike index" do - WebMock.stub_request(:post, "http://lvh.me:3000/api/v2/bikes?access_token=asdfklkjasfdljkd3asdfjkl2asdf") - .and_return(:status => 400, :body => {error: "error"}.to_json, :headers => {}) - bike = create(:bike, bike_index_id: nil) - expect(bike.bike_index_id).to_not be_present + it "returns true if bike_index_id is present" do + bike = create(:bike, bike_index_id: 1) + expect(bike.post_to_bike_index).to be_truthy + end end end diff --git a/spec/services/bike_index_bike_generator_spec.rb b/spec/services/bike_index_bike_generator_spec.rb deleted file mode 100644 index ac91004..0000000 --- a/spec/services/bike_index_bike_generator_spec.rb +++ /dev/null @@ -1,42 +0,0 @@ -require 'spec_helper' - -describe BikeIndexBikeGenerator do - it "creates a bike with required attributes" do - bike = create(:bike) - - bikeIndexBikeJson = BikeIndexBikeGenerator.create_bike_index_bike(bike) - - bikeIndexBike = JSON.parse(bikeIndexBikeJson) - - expect(bikeIndexBike["serial"]).to eq(bike.serial_number) - expect(bikeIndexBike["manufacturer"]).to eq(bike.brand) - expect(bikeIndexBike["frame_model"]).to eq(bike.model) - expect(bikeIndexBike["owner_email"]).to eq(ENV["OWNER_EMAIL"]) - expect(bikeIndexBike["no_notify"]).to be_truthy - expect(bikeIndexBike["color"]).to eq(bike.color) - - end - - describe "freecyclery bikes" do - it "is_for_sale is false" do - bike = create(:bike, purpose: "freecyclery") - bikeIndexBikeJson = BikeIndexBikeGenerator.create_bike_index_bike(bike) - - bikeIndexBike = JSON.parse(bikeIndexBikeJson) - - expect(bikeIndexBike["is_for_sale"]).to be_falsey - end - end - - describe "sale bikes" do - it "is_for_sale is true" do - bike = create(:bike, purpose: "sale") - bikeIndexBikeJson = BikeIndexBikeGenerator.create_bike_index_bike(bike) - - bikeIndexBike = JSON.parse(bikeIndexBikeJson) - - expect(bikeIndexBike["is_for_sale"]).to be_truthy - end - end - -end diff --git a/spec/workers/bike_index_logger_spec.rb b/spec/workers/bike_index_logger_spec.rb new file mode 100644 index 0000000..ced57b4 --- /dev/null +++ b/spec/workers/bike_index_logger_spec.rb @@ -0,0 +1,63 @@ +require 'spec_helper' + +describe BikeIndexLogger do + context "#create_bike_index_bike" do + it "creates a bike with required attributes" do + bike = build(:bike) + allow(Bike).to receive(:find).and_return(bike) + + bikeIndexBikeJson = BikeIndexLogger.create_bike_index_bike(bike) + + bikeIndexBike = JSON.parse(bikeIndexBikeJson) + + expect(bikeIndexBike["serial"]).to eq(bike.serial_number) + expect(bikeIndexBike["manufacturer"]).to eq(bike.brand) + expect(bikeIndexBike["frame_model"]).to eq(bike.model) + expect(bikeIndexBike["owner_email"]).to eq(ENV["OWNER_EMAIL"]) + expect(bikeIndexBike["no_notify"]).to be_truthy + expect(bikeIndexBike["color"]).to eq(bike.color) + + end + + describe "freecyclery bikes" do + it "is_for_sale is false" do + bike = build(:bike, purpose: "freecyclery") + allow(Bike).to receive(:find).and_return(bike) + bikeIndexBikeJson = BikeIndexLogger.create_bike_index_bike(bike) + bikeIndexBike = JSON.parse(bikeIndexBikeJson) + expect(bikeIndexBike["is_for_sale"]).to be_falsey + end + end + + describe "sale bikes" do + it "is_for_sale is true" do + bike = build(:bike, purpose: "sale") + allow(Bike).to receive(:find).and_return(bike) + bikeIndexBikeJson = BikeIndexLogger.create_bike_index_bike(bike) + bikeIndexBike = JSON.parse(bikeIndexBikeJson) + expect(bikeIndexBike["is_for_sale"]).to be_truthy + end + end + end + + it "assigns bike_index_id on successful response from bike index" do + WebMock.stub_request(:post, "http://lvh.me:3000/api/v2/bikes?access_token=asdfklkjasfdljkd3asdfjkl2asdf") + .and_return(:status => 201, :body => {bike: {id: 1}}.to_json, :headers => {}) + allow_any_instance_of(Bike).to receive(:post_to_bike_index) + bike = create(:bike, bike_index_id: nil) + allow(Bike).to receive(:find).and_return(bike) + BikeIndexLogger.new.perform(bike.id) + expect(bike.bike_index_id).to eq(1) + end + + it "does not assign bike_index_id on unsuccessful response from bike index" do + WebMock.stub_request(:post, "http://lvh.me:3000/api/v2/bikes?access_token=asdfklkjasfdljkd3asdfjkl2asdf") + .and_return(:status => 400, :body => {error: "error"}.to_json, :headers => {}) + allow_any_instance_of(Bike).to receive(:post_to_bike_index) + bike = create(:bike, bike_index_id: nil) + allow(Bike).to receive(:find).and_return(bike) + BikeIndexLogger.new.perform(bike.id) + expect(bike.bike_index_id).to_not be_present + end + +end