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

Meetup API integration #303

Open
wants to merge 10 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
2 changes: 2 additions & 0 deletions .env.sample
Original file line number Diff line number Diff line change
Expand Up @@ -5,3 +5,5 @@ TWITTER_CONSUMER_KEY="your_key"
TWITTER_CONSUMER_SECRET="your_secret"
TWITTER_ACCESS_TOKEN="your_token"
TWITTER_ACCESS_SECRET="your_token_secret"
MEETUP_API_KEY="your_token"
MEETUP_URLNAME="montrealrb"
3 changes: 3 additions & 0 deletions Gemfile
Original file line number Diff line number Diff line change
Expand Up @@ -61,6 +61,9 @@ gem "colorize"
# Authorization system
gem "pundit"

# Meetup API
gem 'meetup_client'

group :development do
gem "annotate"
gem "bullet"
Expand Down
4 changes: 3 additions & 1 deletion Gemfile.lock
Original file line number Diff line number Diff line change
Expand Up @@ -97,7 +97,7 @@ GEM
execjs
coffee-script-source (1.12.2)
colorize (0.8.1)
concurrent-ruby (1.1.3)
concurrent-ruby (1.1.4)
crack (0.4.3)
safe_yaml (~> 1.0.0)
crass (1.0.4)
Expand Down Expand Up @@ -212,6 +212,7 @@ GEM
lumberjack (1.0.12)
mail (2.7.0)
mini_mime (>= 0.1.1)
meetup_client (1.0.0)
memoizable (0.4.2)
thread_safe (~> 0.3, >= 0.3.1)
method_source (0.9.2)
Expand Down Expand Up @@ -463,6 +464,7 @@ DEPENDENCIES
guard-rubocop
jquery-rails
jquery-turbolinks
meetup_client
omniauth-github
omniauth-twitter
pg
Expand Down
7 changes: 7 additions & 0 deletions app/jobs/application_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
class ApplicationJob < ActiveJob::Base
# Automatically retry jobs that encountered a deadlock
# retry_on ActiveRecord::Deadlocked

# Most jobs are safe to ignore if the underlying records are no longer available
# discard_on ActiveJob::DeserializationError
end
15 changes: 15 additions & 0 deletions app/jobs/meetup/base_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,15 @@
module Meetup
class BaseJob < ApplicationJob
queue_as :default

def perform
raise Exception.new('Your class needs to implement #perform')
end

protected
def meetup_client
@meetup_client ||= MeetupApi.new
end

end
end
26 changes: 26 additions & 0 deletions app/jobs/meetup/fetch_events_job.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
module Meetup
class FetchEventsJob < BaseJob

def perform(from_time=Time.now.beginning_of_day, to_time=nil)
@from_time = from_time
@to_time = to_time

fetch_events.each do |meetup_event|
time = DateTime.strptime(meetup_event['time'].to_s, '%Q').in_time_zone
if event = Event.where(starts_at: (time - 1.day)..(time + 1.day)).sort_by{|e| (e.starts_at - time).abs}.first
event.update rsvp_count: meetup_event['yes_rsvp_count']
end
end
end

private
def fetch_events
@events ||= begin
timestamp_range = [(@from_time.to_i * 1000), (@to_time ? @to_time.to_i * 1000 : nil)].compact.join(',')
data = meetup_client.events({ group_urlname: ENV['MEETUP_URLNAME'], status: 'upcoming,past', time: timestamp_range })
data['results']
end
end

end
end
44 changes: 44 additions & 0 deletions app/models/event/meetup.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
# frozen_string_literal: true

# == Schema Information
#
# Table name: events
#
# id :integer not null, primary key
# type :string(255) not null
# starts_at :datetime not null
# created_at :datetime
# updated_at :datetime
#

class Event
class Meetup < ::Event
class NotScheduledYet
def starts_at
date = third_tuesday_of(Time.current)
return third_tuesday_of(date.next_month) if date.end_of_day <= Time.current
date
end

private

def third_tuesday_of(time)
date = time.beginning_of_month.to_date
date = date.succ until date.tuesday?
(date + 2.weeks).in_time_zone + 20.hours
end
end

translates :title, :body
# Someday, we should be able to remove those lines
attribute :title
attribute :body

globalize_accessors locales: I18n.available_locales, attributes: %i(title introduction conclusion)
# validates_translated :title, :introduction, :conclusion, presence: true

def self.next
order(starts_at: :asc).find_by("starts_at > ?", Time.now) || NotScheduledYet.new
end
end
end
42 changes: 0 additions & 42 deletions app/models/meetup.rb

This file was deleted.

3 changes: 3 additions & 0 deletions config/initializers/meetup_client.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
MeetupClient.configure do |config|
config.api_key = ENV['MEETUP_API_KEY']
end
5 changes: 5 additions & 0 deletions db/migrate/20181219144356_add_rsvp_count_to_events.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
class AddRsvpCountToEvents < ActiveRecord::Migration[5.1]
def change
add_column :events, :rsvp_count, :integer, default: 0, after: :sponsorships_count
end
end
3 changes: 2 additions & 1 deletion db/schema.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,7 +10,7 @@
#
# It's strongly recommended that you check this file into your version control system.

ActiveRecord::Schema.define(version: 20181117014059) do
ActiveRecord::Schema.define(version: 20181219144356) do

create_table "contacts", force: :cascade do |t|
t.string "name"
Expand Down Expand Up @@ -46,6 +46,7 @@
t.integer "talks_count", default: 0
t.integer "sponsorships_count", default: 0
t.integer "organizer_id"
t.integer "rsvp_count", default: 0
t.index ["location_id"], name: "index_events_on_location_id"
t.index ["starts_at"], name: "index_events_on_starts_at"
t.index ["user_id"], name: "index_events_on_user_id"
Expand Down
25 changes: 25 additions & 0 deletions spec/jobs/meetup/base_job_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,25 @@
require 'rails_helper'

RSpec.describe Meetup::BaseJob, type: :job do
include ActiveJob::TestHelper

let(:fake_job) { Class.new(described_class) }

describe '#perform' do
it 'raises an error' do
expect { fake_job.perform_now }.to raise_error(Exception)
end
end

describe 'queueing' do
it 'queues the job' do
expect {
fake_job.perform_later
}.to change(ActiveJob::Base.queue_adapter.enqueued_jobs, :size).by(1)
end

it 'queues as :default' do
expect(fake_job.new.queue_name).to eq('default')
end
end
end
31 changes: 31 additions & 0 deletions spec/jobs/meetup/fetch_events_job_spec.rb
Original file line number Diff line number Diff line change
@@ -0,0 +1,31 @@
require 'rails_helper'

RSpec.describe Meetup::FetchEventsJob, type: :job do
let(:job) { described_class.new }
let(:mock_meetup_client) {
Struct.new(:data) do
def events(attrs = {})
data[:events]
end
end
}
let!(:event) { create(:event, starts_at: 2.weeks.from_now.noon) }

it 'attemps to reach the Meetup API' do
expect(job).to receive(:meetup_client) {
mock_meetup_client.new(events: {
'results' => [
{ 'id' => 'foo', 'yes_rsvp_count' => 7, 'time' => (Time.zone.tomorrow.noon.to_i * 1000) },
{ 'id' => 'bar', 'yes_rsvp_count' => 13, 'time' => (1.month.from_now.noon.to_i * 1000) },
{ 'id' => 'baz', 'yes_rsvp_count' => 42, 'time' => (2.weeks.from_now.noon.to_i * 1000) }
],
'meta' => []
})
}

expect {
job.perform
event.reload
}.to change(event, :rsvp_count).from(0).to(42)
end
end
6 changes: 3 additions & 3 deletions spec/models/meetup_spec.rb
Original file line number Diff line number Diff line change
Expand Up @@ -2,14 +2,14 @@

require "rails_helper"

RSpec.describe Meetup, type: :model do
RSpec.describe Event::Meetup, type: :model do
describe ".next" do
it "returns a Meetup::NotScheduledYet if there is upcomming meetups" do
expect(Meetup.next).to be_a Meetup::NotScheduledYet
expect(Event::Meetup.next).to be_a Event::Meetup::NotScheduledYet
end
end

describe Meetup::NotScheduledYet do
describe Event::Meetup::NotScheduledYet do
describe "#starts_at" do
let(:next_meetup) { described_class.new }

Expand Down