{"id":30107083,"url":"https://github.com/benignware/attendable","last_synced_at":"2025-08-10T01:33:03.988Z","repository":{"id":14793189,"uuid":"17515246","full_name":"benignware/attendable","owner":"benignware","description":"The attendable-plugin let's you add members to a model and easily build rsvp actions","archived":false,"fork":false,"pushed_at":"2014-08-20T15:42:38.000Z","size":440,"stargazers_count":2,"open_issues_count":0,"forks_count":1,"subscribers_count":4,"default_branch":"master","last_synced_at":"2023-08-12T18:37:41.682Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/benignware.png","metadata":{"files":{"readme":"README.md","changelog":null,"contributing":null,"funding":null,"license":"MIT-LICENSE","code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2014-03-07T13:45:18.000Z","updated_at":"2023-08-12T18:37:41.683Z","dependencies_parsed_at":"2022-08-19T16:20:24.168Z","dependency_job_id":null,"html_url":"https://github.com/benignware/attendable","commit_stats":null,"previous_names":[],"tags_count":2,"template":null,"template_full_name":null,"purl":"pkg:github/benignware/attendable","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benignware%2Fattendable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benignware%2Fattendable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benignware%2Fattendable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benignware%2Fattendable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/benignware","download_url":"https://codeload.github.com/benignware/attendable/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benignware%2Fattendable/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269663297,"owners_count":24455801,"icon_url":"https://github.com/github.png","version":null,"created_at":"2022-05-30T11:31:42.601Z","updated_at":"2022-07-04T15:15:14.044Z","status":"online","status_checked_at":"2025-08-09T02:00:10.424Z","response_time":111,"last_error":null,"robots_txt_status":"success","robots_txt_updated_at":"2025-07-24T06:49:26.215Z","robots_txt_url":"https://github.com/robots.txt","online":true,"can_crawl_api":true,"host_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub","repositories_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories","repository_names_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repository_names","owners_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners"}},"keywords":[],"created_at":"2025-08-10T01:32:17.892Z","updated_at":"2025-08-10T01:33:03.961Z","avatar_url":"https://github.com/benignware.png","language":"Ruby","funding_links":[],"categories":[],"sub_categories":[],"readme":"Attendable\n==========\n\nThe attendable-plugin let's you add members to a model and easily setup rsvp actions\n\nUsage\n-----\n\nInstall the attendable-plugin by adding it to your Gemfile:\n```\ngem 'attendable', github: 'rexblack/attendable'\n```\n\nCreate an example model\n```\nrails g scaffold Event title:string date:date\n```\n\nGenerate your member model\n```\nrails g attendable:member EventMember\n```\n\nYou may also add custom attributes, for example:\n```\nrails g attendable:member EventMember role:string\n```\n\nDon't forget to run migrate after creating your models:\n```\nrake db:migrate\n```\n\nNow let your main model acts as attendable:\n```\n# app/models/event.rb\nclass Event \u003c ActiveRecord::Base\n  acts_as_attendable :event_members, by: :users\nend\n```\nThis will add a has_many-association named 'event_members' to your model.\n \nUse the 'by'-option to specify your invitee model by its symbolized plural name. The invitee class defaults to 'User'.\n\nMethods\n-------\nBesides the member association, calling acts_as_attendable generates the following methods on your model object: \n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth\u003eName\u003c/th\u003e\n    \u003cth\u003eDescription\u003c/th\u003e\n    \u003cth\u003eReturn\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eis_member?(invitee)\u003c/td\u003e\n    \u003ctd\u003eReturns true if the specified invitee is a member of the attendable instance\u003c/td\u003e\n    \u003ctd\u003eBoolean\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eis_attending?(invitee)\u003c/td\u003e\n    \u003ctd\u003eReturns true if the specified invitee is attending\u003c/td\u003e\n    \u003ctd\u003eBoolean\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003ehas_declined?(invitee)\u003c/td\u003e\n    \u003ctd\u003eReturns true if the specified invitee has declined\u003c/td\u003e\n    \u003ctd\u003eBoolean\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eattendees\u003c/td\u003e\n    \u003ctd\u003eReturns a list of all attending invitees\u003c/td\u003e\n    \u003ctd\u003eArray\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003einvite(invitee)\u003c/td\u003e\n    \u003ctd\u003eInvite user to the attendable instance. Returns the corresponding member object\u003c/td\u003e\n    \u003ctd\u003eObject\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eaccept_invitation(invitation_token, invitee)\u003c/td\u003e\n    \u003ctd\u003eAccept the invitation for the specified invitee. Returns the corresponding member object\u003c/td\u003e\n    \u003ctd\u003eArray\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n\nBasic Example\n-------------\nIn the following example, we'll build a simple rsvp-action where any user can attend to an event. \n\nGenerate models\n```\nrails g scaffold Event title:string date:date\nrails g attendable:member EventMember\nrake db:migrate\n```\n\nSetup routes\n```\n# config/routes.rb\nresources :events do\n  member do\n    get 'rsvp'\n  end\nend\n```\n\nMake the model attendable:\n```\n# app/models/event.rb\nclass Event \u003c ActiveRecord::Base\n  acts_as_attendable :event_members, by: :users\nend\n```\n\nSetup controller actions\n```\n# app/controllers/event_controller.rb\nclass EventsController \u003c ApplicationController\n  \n  before_action :set_event, only: [:show, :edit, :update, :destroy, :rsvp]\n  \n  ...\n  \n  def create\n    @event = Event.new(event_params)\n    # automatically add the creator of the event as an attending member\n    @event.event_members.build({invitee: current_user, rsvp_status: :attending})\n  end\n  \n  ...\n  \n  def rsvp\n    # find current_user member\n    event_member = @event.event_members.where([\"invitee_id = ?\", current_user.id])[0]\n    if event_member\n      event_member.rsvp_status = params[:rsvp_status]\n    else\n      # no member, so create one\n      event_member = @event.event_members.build({invitee: current_user, rsvp_status: :attending})\n    end\n    if event_member.save\n      redirect_to @event, notice: 'Status was successfully updated.'\n    else\n      redirect_to @event, notice: 'Status could not be saved.'\n    end\n    \n  end\n  \nend\n```\n\nShow a list of attendees on the event's page and provide a link to update the user's rsvp status:\n```\n# app/views/events/show.html.haml\n\n\u003c% @event.attendees.each do |user| %\u003e\n  \u003cp\u003eUser #\u003c%= user.id.to_s %\u003e\u003c/p\u003e\n\u003c% end %\u003e\n\n\u003c%= link_to(@event.is_attending?(current_user) ? \"Refuse\" : \"Attend\", rsvp_event_path(@event, {rsvp_status: @event.is_attending?(current_user) ? :declined : :attending})) %\u003e\n```\n\n\nInvitation Example\n------------------\n\nThe attendable-plugin also let's you invite users to a specific attendable instance - even if they're not registered yet.\n\nThis is achieved by an invitation_token which is generated with the member in conjuction with an invitation_key that serves as identity.\n\nIn this example, only invited users may attend or refuse the event.\n\nExtend the above example as follows... \n\nAdd the mail gem to your Gemfile and run bundle install\n```\ngem \"mail\"\n```\n\nConfigure mail to work in development\n```\n# config/environments/development.rb\nMyApplication::Application.configure do\n  ...\n  # mailer settings\n  config.mailer_sender = \"xxx@xxx.com\"  \n  config.action_mailer.perform_deliveries = true\n  config.action_mailer.delivery_method = :smtp\n  config.action_mailer.logger = nil\n  config.action_mailer.default_url_options = { :host =\u003e \"localhost:3000\" }\n  config.action_mailer.smtp_settings = {\n    :address =\u003e \"smtp.xxx.com\",\n    :port =\u003e \"25\",\n    :domain =\u003e \"xxx.com\",\n    :user_name =\u003e \"xxx@xxx.com\",\n    :password =\u003e \"xxx\",\n    :authentication =\u003e :plain \n  }\n  ...\nend\n```\n\nGenerate the event mailer\n```\nrails g mailer EventMailer\n```\n\nSetup the invitation method\n```\n# app/mailers/event_mailer.rb\nclass EventMailer \u003c ActionMailer::Base\n  default from: \"from@example.com\"\n  \n  def invitation(email, event, member, inviter)\n    @event = event\n    @inviter = inviter\n    @attend_url = rsvp_event_path(@event, invitation_token: member.invitation_token, rsvp_status: :attending, only_path: false)\n    mail(to: email, subject: 'Event invitation')\n  end\n  \nend\n```\n\nSetup the invitation text email\n```\n# app/views/event_mailer/invitation.text.erb\nWelcome friend,\n\nYou were invited to an event.\nTo attend the event, please click this link: \u003c%= @attend_url %\u003e\n\nThanks for joining and have a great day!\n```\n\nSetup the invitation html email\n```\n# app/views/event_mailer/invitation.html.erb\n\u003c!DOCTYPE html\u003e\n\u003chtml\u003e\n  \u003chead\u003e\n    \u003cmeta content='text/html; charset=UTF-8' http-equiv='Content-Type' /\u003e\n  \u003c/head\u003e\n  \u003cbody\u003e\n    \u003ch1\u003eWelcome friend,\u003c/h1\u003e\n    \u003cp\u003e\n      You were invited to an event.\n    \u003c/p\u003e\n    \u003cp\u003e\n      To attend the event, please click this link: \u003c%= @attend_url %\u003e\n    \u003c/p\u003e\n    \u003cp\u003eThanks for joining and have a great day!\u003c/p\u003e\n  \u003c/body\u003e\n\u003c/html\u003e\n```\n\n\nAdd 'invite' to event routes\n```\n# config/routes.rb\nresources :events do\n  member do\n    get 'rsvp'\n    post 'invite'\n  end\nend\n```\n\nAdd 'invite' action to controller and modify rsvp action to accept invitation_token.\n```\n# app/controllers/event_controller.rb\nclass EventsController \u003c ApplicationController\n  \n  before_action :set_event, only: [:show, :edit, :update, :destroy, :rsvp, :invite]\n \n  ...\n  \n  def rsvp\n    # find current_user member\n    if params[:invitation_token]\n      # find member by invitation_token\n      event_member = @event.accept_invitation(params[:invitation_token], current_user)\n      if (!event_member)\n        # invalid token\n      end\n    else\n      # find by id\n      event_member = @event.event_members.where([\"invitee_id = ?\", current_user.id])[0]\n    end\n    if event_member\n      event_member.rsvp_status = params[:rsvp_status]\n    else\n      # user is not permitted to rsvp the event\n    end\n    if event_member.save\n      redirect_to @event, notice: 'Status was successfully updated.'\n    else\n      redirect_to @event, notice: 'Status could not be saved.'\n    end\n  end\n  \n  def invite\n    params[:invitations][:emails].split(\",\").each do |email|\n      # find user-member by email or use email as invitation_key  \n      if member = @event.invite(User.find_by_email(email) || email)\n        # send invitation email\n        EventMailer.invitation(email, @event, member, current_user).deliver\n      end\n    end\n    respond_to do |format|\n      format.html { redirect_to @event, notice: 'Invitations have been successfully sent.' }\n      format.json { head :no_content }\n    end\n  end\n  \nend\n```\n\nProvide a dialog for sending invitations using haml and a bootstrap3-modal:\n```\n.modal.fade#send-event-invitations{tabindex:-1, role:\"dialog\", aria:{labelledby:\"send-event-invitations-label\", hidden:\"true\"}}\n  .modal-dialog\n    = simple_form_for :invitations, url: invite_event_path(@event), :html=\u003e { class: 'modal-content' } do |f|\n      .modal-header\n        %button.close{type:\"button\", data: {dismiss:\"modal\"}, aria:{hidden:\"true\"}}='\u0026times'.html_safe\n        %h4.modal-title#send-event-invitations-label='Send invitations'\n      .modal-body\n        = f.input :emails\n      .modal-footer\n        %button.btn.btn-default{type:\"button\", data:{dismiss:\"modal\"}}='Close'\n        = f.submit 'Send', class: 'btn btn-primary'\n        \n%button.btn.btn-primary{data: {toggle:\"modal\", target:\"#send-event-invitations\"}}='Send invitations'\n```\n\n   ","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenignware%2Fattendable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbenignware%2Fattendable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenignware%2Fattendable/lists"}