• Stars
    star
    2,415
  • Rank 19,045 (Top 0.4 %)
  • Language
    Ruby
  • License
    MIT License
  • Created over 4 years ago
  • Updated about 1 month ago

Reviews

There are no reviews yet. Be the first to send feedback to the community and the maintainers!

Repository Details

Notifications for Ruby on Rails applications

Noticed

๐ŸŽ‰ Notifications for your Ruby on Rails app.

Build Status Gem Version

Important

โš ๏ธ Upgrading from V1? Read the Upgrade Guide!

Noticed is a gem that allows your application to send notifications of varying types, over various mediums, to various recipients. Be it a Slack notification to your own team when some internal event occurs or a notification to your user, sent as a text message, email, and real-time UI element in the browser, Noticed supports all of the above (at the same time)!

Noticed implements two top-level types of delivery methods:

  1. Individual Deliveries: Where each recipient gets their own notification
Show Example

Letโ€™s use a car dealership as an example here. When someone purchases a car, a notification will be sent to the buyer with some contract details (โ€œCongrats on your new 2024 XYZ Model...โ€), another to the car sales-person with different details (โ€œYou closed X deal; your commission is Yโ€), and another to the bank handling the loan with financial details (โ€œNew loan issued; amount $20,000...โ€). The event (the car being sold) necessitates multiple notifications being sent out to different recipients, but each contains its own unique information and should be separate from the others. These are individual deliveries.

  1. Bulk Deliveries: One notification for all recipients. This is useful for sending a notification to your Slack team, for example.
Show Example Letโ€™s continue with the car-sale example here. Consider that your development team created the car-sales application that processed the deal above and sent out the notifications to the three parties. For the sake of team morale and feeling the โ€˜winsโ€™, you may want to implement a notification that notifies your internal development team whenever a car sells through your platform. In this case, youโ€™ll be notifying many people (your development team, maybe others at your company) but with the same content (โ€œsomeone just bought a car through our platform!โ€). This is a bulk delivery. Itโ€™s generally a single notification that many people just need to be made aware of.

Bulk deliveries are typically used to push notifications to other platforms where users are managed (Slack, Discord, etc.) instead of your own.

Delivery methods we officially support:

Bulk delivery methods we support:

๐ŸŽฌ Screencast

Watch Screencast

๐Ÿš€ Installation

Run the following command to add Noticed to your Gemfile:

bundle add "noticed"

Generate then run the migrations:

rails noticed:install:migrations
rails db:migrate

๐Ÿ“ Usage

Noticed operates with a few constructs: Notifiers, delivery methods, and Notification records.

To start, generate a Notifier:

rails generate noticed:notifier NewCommentNotifier

Usage Contents

Notifier Objects

Notifiers are essentially the controllers of the Noticed ecosystem and represent an Event. As such, we recommend naming them with the event they model in mind โ€” be it a NewSaleNotifier, ChargeFailureNotifier, etc.

Notifiers must inherit from Noticed::Event. This provides all of their functionality.

A Notifier exists to declare the various delivery methods that should be used for that event and any notification helper methods necessary in those delivery mechanisms. In this example weโ€™ll deliver by :action_cable to provide real-time UI updates to usersโ€™ browsers, :email if theyโ€™ve opted into email notifications, and a bulk notification to :discord to tell everyone on the Discord server thereโ€™s been a new comment.

# ~/app/notifiers/new_comment_notifier.rb

class NewCommentNotifier < Noticed::Event
  deliver_by :action_cable do |config|
    config.channel = "NotificationsChannel"
    config.stream = :some_stream
  end

  deliver_by :email do |config|
    config.mailer = "CommentMailer"
    config.if = ->(recipient) { !!recipient.preferences[:email] }
  end

  bulk_deliver_by :discord do |config|
    config.url = "https://discord.com/xyz/xyz/123"
    config.json = -> {
      {
        message: message,
        channel: :general
      }
    }
  end

  notification_methods do
    # I18n helpers
    def message
      t(".message")
    end

    # URL helpers are accessible in notifications
    # Don't forget to set your default_url_options so Rails knows how to generate urls
    def url
      user_post_path(recipient, params[:post])
    end
  end
end

For deeper specifics on setting up the :action_cable, :email, and :discord (bulk) delivery methods, refer to their docs: action_cable, email, and discord (bulk).

Delivery Method Configuration

Each delivery method can be configured with a block that yields a config object.

Procs/Lambdas will be evaluated when needed and symbols can be used to call a method.

When a lambda is passed, it will not pass any arguments and evaluates the Proc in the context of the Noticed::Notification

If you are using a symbol to call a method, we pass the notification object as an argument to the method. This allows you to access the notification object within the method. Your method must accept a single argument. If you don't need to use the object you can just use (*).

Show Example
class CommentNotifier < Noticed::Event
  deliver_by :ios do |config|
    config.format = :ios_format
    config.apns_key = :ios_cert
    config.key_id = :ios_key_id
    config.team_id = :ios_team_id
    config.bundle_identifier = Rails.application.credentials.dig(:ios, :bundle_identifier)
    config.device_tokens = :ios_device_tokens
    config.if = -> { recipient.send_ios_notification? }
  end

  def ios_format(apn)
    apn.alert = { title:, body: }
    apn.mutable_content = true
    apn.content_available = true
    apn.sound = "notification.m4r"
    apn.custom_payload = {
      url:,
      type: self.class.name,
      id: record.id,
      image_url: "" || image_url,
      params: params.to_json
    }
  end

  def ios_cert(*)
    Rails.application.credentials.dig(:ios, Rails.env.to_sym, :apns_token_cert)
  end

  def ios_key_id(*)
    Rails.application.credentials.dig(:ios, Rails.env.to_sym, :key_id)
  end

  def ios_team_id(*)
    Rails.application.credentials.dig(:ios, Rails.env.to_sym, :team_id)
  end

  def ios_bundle_id(*)
    Rails.application.credentials.dig(:ios, Rails.env.to_sym, :bundle_identifier)
  end

  def ios_device_tokens(notification)
    notification.recipient.ios_device_tokens
  end

  def url
    comment_thread_path(record.thread)
  end
end

class Recipient < ApplicationRecord # or whatever your recipient model is
  has_many :ios_device_tokens

  def send_ios_notification?
    # some logic
  end
end

More examples are in the docs for each delivery method.

Required Params

While explicit / required parameters are completely optional, Notifiers are able to opt in to required parameters via the required_params method:

class CarSaleNotifier < Noticed::Event
  deliver_by :email { |c| c.mailer = "BranchMailer" }

  # `record` is the Car record, `Branch` is the dealership
  required_params :branch

  # To validate the `:record` param, add a validation since it is an association on the Noticed::Event
  validates :record, presence: true
end

Which will validate upon any invocation that the specified parameters are present:

CarSaleNotifier.with(record: Car.last).deliver(Branch.last)
#=> Noticed::ValidationError("Param `branch` is required for CarSaleNotifier")

CarSaleNotifier.with(record: Car.last, branch: Branch.last).deliver(Branch.hq)
#=> OK

Helper Methods

Notifiers can implement various helper methods, within a notification_methods block, that make it easier to render the resulting notification directly. These helpers can be helpful depending on where and how you choose to render notifications. A common use is rendering a userโ€™s notifications in your web UI as standard ERB. These notification helper methods make that rendering much simpler:

<div>
  <% @user.notifications.each do |notification| %>
    <%= link_to notification.message, notification.url %>
  <% end %>
</div>

On the other hand, if youโ€™re using email delivery, ActionMailer has its own full stack for setting up objects and rendering. Your notification helper methods will always be available from the notification object, but using ActionMailerโ€™s own paradigms may fit better for that particular delivery method. YMMV.

URL Helpers

Rails url helpers are included in Notifiers by default so you have full access to them in your notification helper methods, just like you would in your controllers and views.

But don't forget, you'll need to configure default_url_options in order for Rails to know what host and port to use when generating URLs.

Rails.application.routes.default_url_options[:host] = 'localhost:3000'

Translations

We've also included Railsโ€™ translate and t helpers for you to use in your notification helper methods. This also provides an easy way of scoping translations. If the key starts with a period, it will automatically scope the key under notifiers, the underscored name of the notifier class, and notification. For example:

From the above Notifier...

class NewCommentNotifier < Noticed::Event
  # ...

  notification_methods do
    def message
      t(".message")
    end
  end

  # ...
end

Calling the message helper in an ERB view:

<%= @user.notifications.last.message %>

Will look for the following translation path:

# ~/config/locales/en.yml

en:
  notifiers:
    new_comment_notifier:
      notification:
        message: "Someone posted a new comment!"

Or, if you have your Notifier within another module, such as Admin::NewCommentNotifier, the resulting lookup path will be en.notifiers.admin.new_comment_notifier.notification.message (modules become nesting steps).

Tip: Capture User Preferences

You can use the if: and unless: options on your delivery methods to check the user's preferences and skip processing if they have disabled that type of notification.

For example:

class CommentNotifier < Noticed::Event
  deliver_by :email do |config|
    config.mailer = 'CommentMailer'
    config.method = :new_comment
    config.if = ->{ recipient.email_notifications? }
  end
end

If you would like to skip the delivery job altogether, for example if you know that a user doesn't support the platform and you would like to save resources by not enqueuing the job, you can use before_enqueue.

For example:

class IosNotifier < Noticed::Event
  deliver_by :ios do |config|
    # ...
    config.before_enqueue = ->{ throw(:abort) unless recipient.registered_ios? }
  end
end

Tip: Extracting Delivery Method Configurations

If you want to reuse delivery method configurations across multiple Notifiers, you can extract them into a module and include them in your Notifiers.

# /app/notifiers/notifiers/comment_notifier.rb
class CommentNotifier < Noticed::Event
  include IosNotifier
  include AndriodNotifer
  include EmailNotifier

  validates :record, presence: true
end

# /app/notifiers/concerns/ios_notifier.rb
module IosNotifier
  extend ActiveSupport::Concern

  included do
    deliver_by :ios do |config|
      config.device_tokens = ->(recipient) { recipient.notification_tokens.where(platform: :iOS).pluck(:token) }
      config.format = ->(apn) {
        apn.alert = "Hello world"
        apn.custom_payload = {url: root_url(host: "example.org")}
      }
      config.bundle_identifier = Rails.application.credentials.dig(:ios, :bundle_id)
      config.key_id = Rails.application.credentials.dig(:ios, :key_id)
      config.team_id = Rails.application.credentials.dig(:ios, :team_id)
      config.apns_key = Rails.application.credentials.dig(:ios, :apns_key)
      config.if = -> { recipient.ios_notifications? }
    end
  end
end

Shared Delivery Method Options

Each of these options are available for every delivery method (individual or bulk). The value passed may be a lambda, a symbol that represents a callable method, a symbol value, or a string value.

  • config.if โ€” Intended for a lambda or method; runs after the wait if configured; cancels the delivery method if returns falsey
  • config.unless โ€” Intended for a lambda or method; runs after the wait if configured; cancels the delivery method if returns truthy
  • config.wait โ€” (Should yield an ActiveSupport::Duration) Delays the job that runs this delivery method for the given duration of time
  • config.wait_until โ€” (Should yield a specific time object) Delays the job that runs this delivery method until the specific time specified
  • config.queue โ€” Sets the ActiveJob queue name to be used for the job that runs this delivery method

๐Ÿ“จ Sending Notifications

Following the NewCommentNotifier example above, hereโ€™s how we might invoke the Notifier to send notifications to every author in the thread about a new comment being added:

NewCommentNotifier.with(record: @comment, foo: "bar").deliver(@comment.thread.all_authors)

This instantiates a new NewCommentNotifier with params (similar to ActiveJob, any serializable params are permitted), then delivers notifications to all authors in the thread.

โœจ The record: param is a special param that gets assigned to the record polymorphic association in the database. You should try to set the record: param where possible. This may be best understood as โ€˜the record/object this notification is aboutโ€™, and allows for future queries from the record-side: โ€œgive me all notifications that were generated from this commentโ€.

This invocation will create a single Noticed::Event record and a Noticed::Notification record for each recipient. A background job will then process the Event and fire off a separate background job for each bulk delivery method and each recipient + individual-delivery-method combination. In this case, thatโ€™d be the following jobs kicked off from this event:

  • A bulk delivery job for :discord bulk delivery
  • An individual delivery job for :action_cable method to the first thread author
  • An individual delivery job for :email method to the first thread author
  • An individual delivery job for :action_cable method to the second thread author
  • An individual delivery job for :email method to the second thread author
  • Etc...

Custom Noticed Model Methods

In order to extend the Noticed models you'll need to use a concern and a to_prepare block:

# config/initializers/noticed.rb
module NotificationExtensions
  extend ActiveSupport::Concern

  included do
    belongs_to :organization

    scope :filter_by_type, ->(type) { where(type:) }
    scope :exclude_type, ->(type) { where.not(type:) }
  end

  # You can also add instance methods here
end

Rails.application.config.to_prepare do
  # You can extend Noticed::Event or Noticed::Notification here
  Noticed::Event.include EventExtensions
  Noticed::Notification.include NotificationExtensions
end

The NotificationExtensions class could be separated into it's own file and live somewhere like app/models/concerns/notification_extensions.rb.

If you do this, the to_prepare block will need to be in application.rb instead of an initializer.

# config/application.rb
module MyApp
  class Application < Rails::Application

    # ...

    config.to_prepare do
      Noticed::Event.include Noticed::EventExtensions
      Noticed::Notification.include Noticed::NotificationExtensions
    end
  end
end

โœ… Best Practices

Renaming Notifiers

If you rename a Notifier class your existing data and Noticed setup may break. This is because Noticed serializes the class name and sets it to the type column on the Noticed::Event record and the type column on the Noticed::Notification record.

When renaming a Notifier class you will need to backfill existing Events and Notifications to reference the new name.

Noticed::Event.where(type: "OldNotifierClassName").update_all(type: NewNotifierClassName.name)
# and
Noticed::Notification.where(type: "OldNotifierClassName::Notification").update_all(type: "#{NewNotifierClassName.name}::Notification")

๐Ÿš› Delivery Methods

The delivery methods are designed to be modular so you can customize the way each type gets delivered.

For example, emails will require a subject, body, and email address while an SMS requires a phone number and simple message. You can define the formats for each of these in your Notifier and the delivery method will handle the processing of it.

Individual delivery methods:

Bulk delivery methods:

No Delivery Methods

Itโ€™s worth pointing out that you can have a fully-functional and useful Notifier that has no delivery methods. This means that invoking the Notifier and โ€˜sendingโ€™ the notification will only create new database records (no external surfaces like email, sms, etc.). This is still useful as itโ€™s the database records that allow your app to render a userโ€™s (or other objectโ€™s) notifications in your web UI.

So even with no delivery methods set, this example is still perfectly available and helpful:

<div>
  <% @user.notifications.each do |notification| %>
    <%= link_to notification.message, notification.url %>
  <% end %>
</div>

Sending a notification is entirely an internal-to-your-app function. Delivery methods just get the word out! But many apps may be fully satisfied without that extra layer.

Fallback Notifications

A common pattern is to deliver a notification via a real (or real-ish)-time service, then, after some time has passed, email the user if they have not yet read the notification. You can implement this functionality by combining multiple delivery methods, the wait option, and the conditional if / unless option.

class NewCommentNotifier < Noticed::Event
  deliver_by :action_cable
  deliver_by :email do |config|
    config.mailer = "CommentMailer"
    config.wait = 15.minutes
    config.unless = -> { read? }
  end
end

Here a notification will be created immediately in the database (for display directly in your appโ€™s web interface) and sent via ActionCable. If the notification has not been marked read after 15 minutes, the email notification will be sent. If the notification has already been read in the app, the email will be skipped.

A note here: notifications expose a #mark_as_read method, but your app must choose when and where to call that method.

You can mix and match the options and delivery methods to suit your application specific needs.

๐Ÿšš Custom Delivery Methods

If you want to build your own delivery method to deliver notifications to a specific service or medium that Noticed doesnโ€™t (or doesnโ€™t yet) support, youโ€™re welcome to do so! To generate a custom delivery method, simply run

rails generate noticed:delivery_method Discord

This will generate a new DeliveryMethods::Discord class inside the app/notifiers/delivery_methods folder, which can be used to deliver notifications to Discord.

class DeliveryMethods::Discord < ApplicationDeliveryMethod
  # Specify the config options your delivery method requires in its config block
  required_options # :foo, :bar

  def deliver
    # Logic for sending the notification
  end
end

You can use the custom delivery method thus created by adding a deliver_by line with a unique name and class option in your notification class.

class MyNotifier < Noticed::Event
  deliver_by :discord, class: "DeliveryMethods::Discord"
end
Turbo Stream Custom Delivery Method Example

A common custom delivery method in the Rails world might be to Delivery to the web via turbo stream.

Note: This example users custom methods that extend the Noticed::Notification class.

See the Custom Noticed Model Methods section for more information.

# app/notifiers/delivery_methods/turbo_stream.rb

class DeliveryMethods::TurboStream < ApplicationDeliveryMethod
  def deliver
    return unless recipient.is_a?(User)

    notification.broadcast_update_to_bell
    notification.broadcast_replace_to_index_count
    notification.broadcast_prepend_to_index_list
  end
end
# app/models/concerns/noticed/notification_extensions.rb

module Noticed::NotificationExtensions
  extend ActiveSupport::Concern

  def broadcast_update_to_bell
    broadcast_update_to(
      "notifications_#{recipient.id}",
      target: "notification_bell",
      partial: "navbar/notifications/bell",
      locals: { user: recipient }
    )
  end

  def broadcast_replace_to_index_count
    broadcast_replace_to(
      "notifications_index_#{recipient.id}",
      target: "notification_index_count",
      partial: "notifications/notifications_count",
      locals: { count: recipient.reload.notifications_count, unread: recipient.reload.unread_notifications_count }
    )
  end

  def broadcast_prepend_to_index_list
    broadcast_prepend_to(
      "notifications_index_list_#{recipient.id}",
      target: "notifications",
      partial: "notifications/notification",
      locals: { notification: self }
    )
  end
end

Delivery methods have access to the following methods and attributes:

  • event โ€” The Noticed::Event record that spawned the notification object currently being delivered
  • record โ€” The object originally passed into the Notifier as the record: param (see the โœจ note above)
  • notification โ€” The Noticed::Notification instance being delivered. All notification helper methods are available on this object
  • recipient โ€” The individual recipient object being delivered to for this notification (remember that each recipient gets their own instance of the Delivery Method #deliver)
  • config โ€” The hash of configuration options declared by the Notifier that generated this notification and delivery
  • params โ€” The parameters given to the Notifier in the invocation (via .with())

Validating config options passed to Custom Delivery methods

The presence of delivery method config options are automatically validated when declaring them with the required_options method. In the following example, Noticed will ensure that any Notifier using deliver_by :email will specify the mailer and method config keys:

class DeliveryMethods::Email < Noticed::DeliveryMethod
  required_options :mailer, :method

  def deliver
    # ...
    method = config.method
  end
end

If youโ€™d like your config options to support dynamic resolution (set config.foo to a lambda or symbol of a method name etc.), you can use evaluate_option:

class NewSaleNotifier < Noticed::Event
  deliver_by :whats_app do |config|
    config.day = -> { is_tuesday? "Tuesday" : "Not Tuesday" }
  end
end

class DeliveryMethods::WhatsApp < Noticed::DeliveryMethod
  required_options :day

  def deliver
    # ...
    config.day #=> #<Proc:0x000f7c8 (lambda)>
    evaluate_option(config.day) #=> "Tuesday"
  end
end

Callbacks

Callbacks for delivery methods wrap the actual delivery of the notification. You can use before_deliver, around_deliver and after_deliver in your custom delivery methods.

class DeliveryMethods::Discord < Noticed::DeliveryMethod
  after_deliver do
    # Do whatever you want
  end
end

๐Ÿ“ฆ Database Model

The Noticed database models include several helpful features to make working with notifications easier.

Notification

Class methods/scopes

(Assuming your user has_many :notifications, as: :recipient, class_name: "Noticed::Notification")

Sorting notifications by newest first:

@user.notifications.newest_first

Query for read or unread notifications:

user.notifications.read
user.notifications.unread

Marking all notifications as read or unread:

user.notifications.mark_as_read
user.notifications.mark_as_unread

Instance methods

Mark notification as read / unread:

@notification.mark_as_read
@notification.mark_as_read!
@notification.mark_as_unread
@notification.mark_as_unread!

Check if read / unread:

@notification.read?
@notification.unread?

Associating Notifications

Adding notification associations to your models makes querying, rendering, and managing notifications easy (and is a pretty critical feature of most applications).

There are two ways to associate your models to notifications:

  1. Where your object has_many notifications as the recipient (who you sent the notification to)
  2. Where your object has_many notifications as the record (what the notifications were about)

In the former, weโ€™ll use a has_many to :notifications. In the latter, weโ€™ll actually has_many to :events, since records generate notifiable events (and events generate notifications).

We can illustrate that in the following:

class User < ApplicationRecord
  has_many :notifications, as: :recipient, dependent: :destroy, class_name: "Noticed::Notification"
end

# All of the notifications the user has been sent
# @user.notifications.each { |n| render(n) }

class Post < ApplicationRecord
  has_many :noticed_events, as: :record, dependent: :destroy, class_name: "Noticed::Event"
  has_many :notifications, through: :noticed_events, class_name: "Noticed::Notification"
end

# All of the notification events this post generated
# @post.notifications

ActiveJob Parent Class

Noticed uses its own Noticed::ApplicationJob as the base job for all notifications. In the event that you would like to customize the parent job class, there is a parent_class attribute that can be overridden with your own class. This should be done in a noticed.rb initializer.

Noticed.parent_class = "ApplicationJob"

Handling Deleted Records

Generally we recommend using a dependent: ___ relationship on your models to avoid cases where Noticed Events or Notifications are left lingering when your models are destroyed. In the case that they are or data becomes mis-matched, youโ€™ll likely run into deserialization issues. That may be globally alleviated with the following snippet, but use with caution.

class ApplicationJob < ActiveJob::Base
  discard_on ActiveJob::DeserializationError
end

Customizing the Database Models

You can modify the database models by editing the generated migrations.

One common adjustment is to change the IDs to UUIDs (if you're using UUIDs in your app).

You can also add additional columns to the Noticed::Event and Noticed::Notification models.

# This migration comes from noticed (originally 20231215190233)
class CreateNoticedTables < ActiveRecord::Migration[7.1]
  def change
    create_table :noticed_events, id: :uuid do |t|
      t.string :type
      t.belongs_to :record, polymorphic: true, type: :uuid
      t.jsonb :params

      # Custom Fields
      t.string :organization_id, type: :uuid, as: "((params ->> 'organization_id')::uuid)", stored: true
      t.virtual :action_type, type: :string, as: "((params ->> 'action_type'))", stored: true
      t.virtual :url, type: :string, as: "((params ->> 'url'))", stored: true

      t.timestamps
    end

    create_table :noticed_notifications, id: :uuid do |t|
      t.string :type
      t.belongs_to :event, null: false, type: :uuid
      t.belongs_to :recipient, polymorphic: true, null: false, type: :uuid
      t.datetime :read_at
      t.datetime :seen_at

      t.timestamps
    end

    add_index :noticed_notifications, :read_at
  end
end

The custom fields in the above example are stored as virtual columns. These are populated from values passed in the params hash when creating the notifier.

๐Ÿ™ Contributing

This project uses Standard for formatting Ruby code. Please make sure to run standardrb before submitting pull requests.

Running tests against multiple databases locally:

DATABASE_URL=sqlite3:noticed_test rails test
DATABASE_URL=trilogy://root:@127.0.0.1/noticed_test rails test
DATABASE_URL=postgres://127.0.0.1/noticed_test rails test

๐Ÿ“ License

The gem is available as open source under the terms of the MIT License.

More Repositories

1

simple_calendar

A wonderfully simple calendar gem for Rails
Ruby
1,476
star
2

jumpstart

Easily jumpstart a new Rails application with a bunch of great features by default
HTML
1,200
star
3

tailwindcss-stimulus-components

A set of StimulusJS components for TailwindCSS apps similar to Bootstrap JS components.
JavaScript
1,083
star
4

madmin

A robust Admin Interface for Ruby on Rails apps
JavaScript
514
star
5

receipts

Easy receipts and invoices for your Rails applications
Ruby
495
star
6

simple_discussion

A simple, extensible Rails forum
Ruby
368
star
7

prefixed_ids

Friendly Prefixed IDs for your Ruby on Rails models
Ruby
198
star
8

revise_auth

A pure Rails authentication system like Devise.
Ruby
192
star
9

stack_rescue

A gem for Rails to automatically search Google for exceptions raised in your Rails applications
Ruby
171
star
10

esbuild-rails

Esbuild Rails plugin
JavaScript
137
star
11

signalman

Development tools for Ruby on Rails
Ruby
92
star
12

beginnerbounties.com

Small Projects for Junior Developers
Ruby
77
star
13

dotfiles

My personal dotfiles for macOS: zsh, MacVim, iterm, and more.
Vim Script
70
star
14

stimulus-slimselect

A Stimulus wrapper around SlimSelect
JavaScript
66
star
15

python-apt

This is a Python library interface to libapt, which allows you to query and manipulat APT package repository information using the Python programming language.
C++
63
star
16

combined_time_select

A Rails time_select like Google Calendar with combined hour and minute time_select
Ruby
60
star
17

logbot

A minimal IRC bot for logging channels
Python
59
star
18

textual-solarized

A port of the Solarized theme to Textual IRC
JavaScript
31
star
19

simple_calendar-ajax-example

An example Rails application to demonstrate SimpleCalendar AJAX next/previous links
Ruby
29
star
20

scheduled_tweets

Ruby
28
star
21

gorails-forum

Ruby
24
star
22

betterminitest.com

Learn how to write better Minitest tests
CSS
21
star
23

Rails-Learning

List of resources to learning this amazing platform for building top-quality web apps with Ruby on Rails! โค
20
star
24

railshackathon.com

The RailsHackathon.com website
HTML
19
star
25

ferrum_pdf

A PDF generator for Rails using Ferrum & headless Chrome
Ruby
19
star
26

trello-stimulus-reflex

A trello clone using Stimulus Reflex
Ruby
17
star
27

asdf-vars

An asdf extension that safely sets global and per-project environment variables, based upon rbenv
Shell
17
star
28

railsconf-2020-actiontext

Railsconf 2020 example app for showcasing ActionText
Ruby
16
star
29

nine_to_five

Ruby
14
star
30

oauthable

Setup, test, and implement the OAuth connections to over 70 different APIs.
Ruby
13
star
31

gorails-ruby

An interactive CLI for GoRails, Railsbytes, Jumpstart, and more.
Ruby
12
star
32

animatedgif.me-v2

Ruby
11
star
33

keryx

Python
11
star
34

current_projects

Lists a user's active github projects
Ruby
9
star
35

israilsdead.com

It's pretty self explanatory I think
HTML
9
star
36

urlgrabber

A high-level cross-protocol url-grabber
Python
8
star
37

Sublime-Ruby-on-Rails.tmLanguage

8
star
38

fluttrly

An elegantly simple todo list
Ruby
8
star
39

zapier

Zapier API Rubygem
Ruby
8
star
40

excid3-wordpress-theme

My wordpress theme for excid3.com
PHP
7
star
41

simpyl_pastebin

A simple python pastebin
Python
7
star
42

animatedgifme

A ruby library for http://animatedgif.me
Ruby
7
star
43

weather_underground

A Ruby gem for the Weather Underground API
Ruby
7
star
44

wistia-player-speed

Add 2x, 1.5x, 0.5x speeds and skip 30 seconds controls to your Wistia video player
HTML
7
star
45

mail_drop

Record and view Rails ActionMailer emails sent in development
Ruby
7
star
46

stimulus-js-2.0

A look at Stimulus JS 2.0's new Values and CSS Classes APIs
Ruby
6
star
47

railsconf-2021

Realtime Rails applications with Hotwire & ActionMailbox
Ruby
6
star
48

superleggera

Monitor your CSS in development to create a whitelist for PurgeCSS in Rails with Webpacker
Ruby
6
star
49

deploy_test

See how to deploy Rails to Ubuntu with Capistrano
Ruby
5
star
50

feedforward.io

Curating articles that inspire discussion
Ruby
5
star
51

clickable-simple_calendar

An example how to make the dates clickable with simple_calendar for creating new events
Ruby
5
star
52

particlesjs-rails-example

Ruby
5
star
53

webhookmonitor.com

A tool for recording and monitoring webhooks
Ruby
4
star
54

rpi-pms5003

Raspberry Pi air quality monitoring with PMS5003 and Ruby
Ruby
4
star
55

chef-rails-stack

Chef Solo recipes to setup a Rails stack on Ubuntu 12.04 LTS
Ruby
4
star
56

cfps

Conference talk submissions (CFPs)
4
star
57

landing

My new website landing page
CSS
4
star
58

vuejs-rails-markdown-field

Use VueJS to add markdown previews to any Rails form input field or text area.
Ruby
4
star
59

react-todolist

A react todo list application with Ruby on Rails
Ruby
4
star
60

better_resources

Resources routes that use the same for new and edit so users get less 404s
Ruby
3
star
61

rails-5.2-actiontext

Testing ActionText in Rails 5.2
Ruby
3
star
62

irc_bothost

A Django web application for maintaining python IRC bots
Python
3
star
63

simple_calendar-example

A simple calendar gem example with Rails 6
Ruby
3
star
64

sponsor-hub

A hub for your GitHub sponsors
Ruby
3
star
65

excid3

3
star
66

simple_calendar-full-day-example

Ruby
3
star
67

download_counter

A download counter implemented in Rails3 with an administration interface
Ruby
3
star
68

administrate-trix-editor

Episode 211: Adding the Trix Editor as an Administrate Custom Field
Ruby
3
star
69

excid3.com

Moving to Jekyll
HTML
3
star
70

gemspec-cheatsheet

An interactive GemSpec cheat sheet
3
star
71

reply_parser

Parser for email replies
Ruby
3
star
72

animatedgif.me

With Great Gifs Comes Great Responsibility
Ruby
3
star
73

turbolinks-3-example

Ruby
3
star
74

imageme

Ruby
2
star
75

unwrapt

A pure python layer for offline package managers
Python
2
star
76

chromium

cluster rendering
C
2
star
77

googleweather

Ruby client for the undocumented Google Weather API
Ruby
2
star
78

lithoslabs.com

2
star
79

keryx_profiler

A website to maintain user submitted profiles for Keryx
Ruby
2
star
80

cultivate

Ruby
2
star
81

blogspam

A Ruby and/or Rails client for Blogspam.net
Ruby
2
star
82

pastebot

An IRC bot that accepts pastes, uploads them, and notifies recipients
Python
2
star
83

excid3bot

A hubot
CoffeeScript
2
star
84

cdnjs-rails

A mechanized way to pull in one to many CDNJS libraries with fall back to local installed versions.
Ruby
2
star
85

panopticode

Social Volt Contest Entry
Ruby
2
star
86

hatchbox-marketing

2
star
87

rugby

A Ruby IRC bot
Ruby
2
star
88

trackler

A package tracker visualizing progress on Google Maps
Ruby
2
star
89

No-Net-Debs

List and download deb packages for updates (dist-upgrade included) or new package(s) for a computer without Internet. Hosting in case http://ubuntu.no.sapo.pt/nonetdebs/nonetdebs goes down.
2
star
90

fluttr-desktop

Fluttr desktop application in Python and GTK
Python
2
star
91

omniauth-stripeplatform

A strategy for omniauth and the new beta Stripe.com platofrm.
Ruby
1
star
92

simple_map

Ruby
1
star
93

hacker_rank

A Ruby interface to the HackerRank / InterviewStreet API
Ruby
1
star
94

anonykron

anonykron.com
Ruby
1
star
95

confab

A NodeJS IRC client in the browser
JavaScript
1
star
96

taip-parser

Ruby
1
star
97

ipcommerce

Ruby
1
star
98

omniauth-linode

An Omniauth Strategy for Linode's v4 API
Ruby
1
star
99

ice_rocket

A Rubygem for the IceRocket API
Ruby
1
star
100

combined_time_select-example

Ruby
1
star