{"id":13879920,"url":"https://github.com/benignware/schedulable","last_synced_at":"2025-08-10T01:33:03.046Z","repository":{"id":15158865,"uuid":"17886480","full_name":"benignware/schedulable","owner":"benignware","description":"Handling recurring events in rails","archived":false,"fork":false,"pushed_at":"2018-09-20T09:22:18.000Z","size":6042,"stargazers_count":92,"open_issues_count":28,"forks_count":52,"subscribers_count":7,"default_branch":"master","last_synced_at":"2024-11-24T08:33:01.086Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":null,"language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":"doomspork/elixir-school","license":"mit","status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/benignware.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGELOG.md","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-19T00:00:54.000Z","updated_at":"2024-04-03T05:14:09.000Z","dependencies_parsed_at":"2022-08-29T01:30:38.700Z","dependency_job_id":null,"html_url":"https://github.com/benignware/schedulable","commit_stats":null,"previous_names":[],"tags_count":9,"template":false,"template_full_name":null,"purl":"pkg:github/benignware/schedulable","repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benignware%2Fschedulable","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benignware%2Fschedulable/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benignware%2Fschedulable/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benignware%2Fschedulable/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/benignware","download_url":"https://codeload.github.com/benignware/schedulable/tar.gz/refs/heads/master","sbom_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/benignware%2Fschedulable/sbom","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":269663300,"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":"2024-08-06T08:02:39.103Z","updated_at":"2025-08-10T01:33:02.972Z","avatar_url":"https://github.com/benignware.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"schedulable\n===========\n\n\u003e Handling recurring events in rails. \n\n\n## Install\n\nPut the following into your Gemfile and run `bundle install`\n```cli\ngem 'ice_cube'\ngem 'schedulable'\n```\n\nInstall schedule migration and model\n```cli\nrails g schedulable:install\n```\n\n## Basic Usage\n\nCreate an event model\n```cli\nrails g scaffold Event name:string\n```\n\nDon't forget to migrate your database at this point in order to reflect the changes: `rake db:migrate`\n\nOpen `app/models/event.rb` and add the following to configure your model to be schedulable:\n\n```ruby\n# app/models/event.rb\nclass Event \u003c ActiveRecord::Base\n  acts_as_schedulable :schedule\nend\n```\nThis will add an association to the model named 'schedule' which holds the schedule information. \n\n### Schedule Model\nThe schedule-object respects the following attributes.\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth\u003eName\u003c/th\u003e\u003cth\u003eType\u003c/th\u003e\u003cth\u003eDescription\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003erule\u003c/td\u003e\u003ctd\u003eString\u003c/td\u003e\u003ctd\u003eOne of 'singular', 'daily', 'weekly', 'monthly'\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003edate\u003c/td\u003e\u003ctd\u003eDate\u003c/td\u003e\u003ctd\u003eThe date-attribute is used for singular events and also as startdate of the schedule\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003etime\u003c/td\u003e\u003ctd\u003eTime\u003c/td\u003e\u003ctd\u003eThe time-attribute is used for singular events and also as starttime of the schedule\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eday\u003c/td\u003e\u003ctd\u003eArray\u003c/td\u003e\u003ctd\u003eDay of week. An array of weekday-names, i.e. ['monday', 'wednesday']\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003eday_of_week\u003c/td\u003e\u003ctd\u003eHash\u003c/td\u003e\u003ctd\u003eDay of nth week. A hash of weekday-names, containing arrays with indices, i.e. {:monday =\u003e [1, -1]} ('every first and last monday in month')\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003einterval\u003c/td\u003e\u003ctd\u003eInteger\u003c/td\u003e\u003ctd\u003eSpecifies the interval of the recurring rule, i.e. every two weeks\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003euntil\u003c/td\u003e\u003ctd\u003eDate\u003c/td\u003e\u003ctd\u003eSpecifies the enddate of the schedule. Required for terminating events.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003ecount\u003c/td\u003e\u003ctd\u003eInteger\u003c/td\u003e\u003ctd\u003eSpecifies the total number of occurrences. Required for terminating events.\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n## Forms\n\nUse schedulable's built-in helpers to setup your form.\n\n### FormBuilder\n\nSchedulable extends FormBuilder with a 'schedule_select'-helper and should therefore seamlessly integrate it with your existing views:\n\n```erb\n\u003c%# app/views/events/_form.html.erb %\u003e\n\u003c%= form_for(@event) do |f| %\u003e\n\n  \u003cdiv class=\"field\"\u003e\n    \u003c%= f.label :name %\u003e\u003cbr\u003e\n    \u003c%= f.text_field :name %\u003e\n  \u003c/div\u003e\n  \n  \u003cdiv class=\"field\"\u003e\n    \u003c%= f.label :schedule %\u003e\u003cbr\u003e\n    \u003c%= f.schedule_select :schedule %\u003e\n  \u003c/div\u003e\n  \n  \u003cdiv class=\"actions\"\u003e\n    \u003c%= f.submit %\u003e\n  \u003c/div\u003e\n\u003c% end %\u003e\n```\n\n#### Customize markup\nYou can customize the generated markup by providing a hash of html-attributes as `style`-option. For wrappers, also provide a `tag`-attribute.\n* field_html\n* input_html\n* input_wrapper\n* label_html\n* label_wrapper\n* number_field_html\n* number_field_wrapper\n* date_select_html\n* date_select_wrapper\n* collection_select_html\n* collection_select_wrapper\n* collection_check_boxes_item_html\n* collection_check_boxes_item_wrapper\n\n#### Integrate with Bootstrap\n\nThe schedulable-formhelper has built-in-support for Bootstrap. Simply point the style-option of schedule_input to `bootstrap` or set it as default in config. \n\n```erb\n\u003c%= f.schedule_select :schedule, style: :bootstrap %\u003e\n```\n\n#### Custom inputs\nCustomize datetime-controls by passing FormBuilder-methods as a hash to the `input_types`-option. \nSee below example for integrating [date_picker](https://github.com/benignware/date_picker) with schedulable:\n\n```erb\n\u003c%# app/views/events/_form.html.erb %\u003e\n  \n\u003cdiv class=\"field form-group\"\u003e\n  \u003c%= f.label :schedule %\u003e\u003cbr\u003e\n  \u003c%= f.schedule_select :schedule, style: :bootstrap, until: true, input_types: {date: :date_picker, time: :time_picker, datetime: :datetime_picker} %\u003e\n\u003c/div\u003e\n```\n\n#### Options\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth\u003eName\u003c/th\u003e\u003cth\u003eType\u003c/th\u003e\u003cth\u003eDescription\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003ecount\u003c/td\u003e\u003ctd\u003eBoolean\u003c/td\u003e\u003ctd\u003eSpecifies whether to show 'count'-field\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003einput_types\u003c/td\u003e\u003ctd\u003eHash\u003c/td\u003e\u003ctd\u003eSpecify a hash containing custom form builder methods. Defaults to `{date: :date_select, time: :time_select, datetime: :datetime_select}`. The interface of the custom form builder method must either match `date_select` or `date_field`-methods\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003einterval\u003c/td\u003e\u003ctd\u003eBoolean\u003c/td\u003e\u003ctd\u003eSpecifies whether to show 'interval'-field\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003estyle\u003c/td\u003e\u003ctd\u003eHash\u003c/td\u003e\u003ctd\u003eSpecifies a hash of options to customize markup. By providing a string, you can point to a prefined set of options. Built-in styles are :bootstrap and :default.\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003euntil\u003c/td\u003e\u003ctd\u003eBoolean\u003c/td\u003e\u003ctd\u003eSpecifies whether to show 'until'-field\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n### SimpleForm\nAlso provided with the plugin is a custom input for simple_form. Make sure, you installed [SimpleForm](https://github.com/plataformatec/simple_form) and executed `rails generate simple_form:install`.\n\n\n```cli\nrails g schedulable:simple_form\n```\n\n```erb\n\u003c%# app/views/events/_form.html.erb %\u003e\n\u003c%= simple_form_for(@event) do |f| %\u003e\n  \n  \u003cdiv class=\"field\"\u003e\n    \u003c%= f.label :name %\u003e\u003cbr\u003e\n    \u003c%= f.text_field :name %\u003e\n  \u003c/div\u003e\n  \n  \u003cdiv class=\"field\"\u003e\n    \u003c%= f.label :schedule %\u003e\u003cbr\u003e\n    \u003c%= f.input :schedule, as: :schedule %\u003e\n  \u003c/div\u003e\n\n  \u003cdiv class=\"actions\"\u003e\n    \u003c%= f.submit %\u003e\n  \u003c/div\u003e\n  \n\u003c% end %\u003e\n```\n\n#### Integrate with Bootstrap\n\nSimple Form has built-in support for Bootstrap as of version 3.0.0. \nAt time of writing it requires some a little extra portion of configuration to make it look as expected:\n\n```ruby\n# config/initializers/simple_form_bootstrap.rb\n\n# Inline date_select-wrapper for Bootstrap\nconfig.wrappers :horizontal_select_date, tag: 'div', class: 'form-group', error_class: 'has-error' do |b|\n  b.use :html5\n  b.optional :readonly\n  b.use :label, class: 'control-label'\n  b.wrapper tag: 'div', class: 'form-inline' do |ba|\n    ba.use :input, class: 'form-control'\n    ba.use :error, wrap_with: { tag: 'span', class: 'help-block' }\n    ba.use :hint,  wrap_with: { tag: 'p', class: 'help-block' }\n  end\nend\n\n# Include date_select-wrapper in mappings\nconfig.wrapper_mappings = {\n  datetime: :horizontal_select_date,\n  date: :horizontal_select_date,\n  time: :horizontal_select_date\n}\n```\n\n#### Custom inputs\n\nYou can customize datetime-controls by passing simple_form-inputs as a hash to the `input_types`-option. See form helper for similar example \n\nFor integrating [date_picker](https://github.com/benignware/date_picker) with schedulable and simple_form, you can also run date_picker's simple_form-generator to create a default `date_time`-input:\n```cli\nrails g date_picker:simple_form date_time\n```\n\n#### Options\n\n\u003ctable\u003e\n  \u003ctr\u003e\n    \u003cth\u003eName\u003c/th\u003e\u003cth\u003eType\u003c/th\u003e\u003cth\u003eDescription\u003c/th\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003ecount\u003c/td\u003e\u003ctd\u003eBoolean\u003c/td\u003e\u003ctd\u003eSpecifies whether to show 'count'-field\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003einput_types\u003c/td\u003e\u003ctd\u003eHash\u003c/td\u003e\u003ctd\u003eSpecify a hash containing custom simple form inputs. Defaults to `{date: :date_time, time: :date_time, datetime: :date_time}`.\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003einterval\u003c/td\u003e\u003ctd\u003eBoolean\u003c/td\u003e\u003ctd\u003eSpecifies whether to show 'interval'-field\u003c/td\u003e\n  \u003c/tr\u003e\n  \u003ctr\u003e\n    \u003ctd\u003euntil\u003c/td\u003e\u003ctd\u003eBoolean\u003c/td\u003e\u003ctd\u003eSpecifies whether to show 'until'-field\u003c/td\u003e\n  \u003c/tr\u003e\n\u003c/table\u003e\n\n### Sanitize parameters\n\nAdd schedule-attributes to the list of strong parameters in your controller:\n```\n# app/controllers/event_controller.rb\ndef event_params\n  params.require(:event).permit(:name, schedule_attributes: Schedulable::ScheduleSupport.param_names)\nend\n```\nNote: If you don't use `schedule` as attribute name, you need to rename `schedule_attributes` accordingly.\n\n\n## Accessing IceCube\nYou can access ice_cube-methods directly via the schedule association:\n\n```ruby\n\u003c%# app/views/events/show.html.erb %\u003e\n\u003cp\u003e\n  \u003cstrong\u003eSchedule:\u003c/strong\u003e\n  \u003c%# Prints out a human-friendly description of the schedule, such as %\u003e \n  \u003c%= @event.schedule %\u003e\n\u003c/p\u003e\n```\n\n```ruby\n# Prints all occurrences of the event until one year from now\nputs @event.schedule.occurrences(Time.now + 1.year)\n# Export to ical\nputs @event.schedule.to_ical\n```\nSee [IceCube](https://github.com/seejohnrun/ice_cube) for more information.\n\n## Internationalization\n\nSchedulable is bundled with translations in english and german which will be automatically initialized with your app.\nYou can customize these messages by running the locale generator and edit the created yml-files:\n\n```cli\nrails g schedulable:locale de\n```\n\n### Date- and Time-Messages\nAppropriate datetime translations should be included.\nBasic setup for many languages can be found here:\n[https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale](https://github.com/svenfuchs/rails-i18n/tree/master/rails/locale).\n\n\n### IceCube-Messages\nAn internationalization-branch of ice_cube can be found here: \n[https://github.com/joelmeyerhamme/ice_cube](https://github.com/joelmeyerhamme/ice_cube):\n\n```ruby\ngem 'ice_cube', git: 'git://github.com/joelmeyerhamme/ice_cube.git', branch: 'international' \n```\n\n## Persist Occurrences\n\nSchedulable allows for persisting occurrences and associate them with your event model. \nThe Occurrence Model model must include an attribute of type 'datetime' with name 'date' as well as a polymorphic association to the schedulable event model.\nSimply use the occurrence generator for setting up the appropriate model:\n\n```ruby\nrails g schedulable:occurrence EventOccurrence\n```\n\nOn the other side, pass the association to your schedulable event model by using the `occurrences`-option of the `acts_as_schedulable`-method:\n\n\n```ruby\n# app/models/event.rb\nclass Event \u003c ActiveRecord::Base\n  acts_as_schedulable :schedule, occurrences: :event_occurrences\nend\n```\n\nThis will create the corresponding has_many-association and also add `remaining_event_occurrences` and `previous_event_occurrences`-methods.\n\nInstances of remaining occurrences are persisted when the parent-model is saved.\nOccurrences records will be reused if their datetime matches the saved schedule. \nPast occurrences stay untouched.\n\n### Terminating and non-terminating events\nAn event is terminating if an until- or count-attribute has been specified. \nSince non-terminating events have infinite occurrences, we cannot build all occurrences at once ;-)\nSo we need to limit the number of occurrences in the database. \nBy default this will be one year from now. \nThis can be configured via the 'build_max_count' and 'build_max_period'-options. \nSee notes on configuration. \n\n### Automate build of occurrences\nSince we cannot build an infinite amount of occurrences, we will need a task that adds occurrences as time goes by. \nSchedulable comes with a rake-task that performs an update on all scheduled occurrences. \n\n```cli\nrake schedulable:build_occurrences\n```\n\nYou may add this task to crontab. \n\n#### Use 'whenever' to schedule the generation of occurrences\n\nWith the 'whenever' gem this can be easily achieved. \n\n```ruby\ngem 'whenever', :require =\u003e false\n```\n\nGenerate the 'whenever'-configuration file:\n\n```cli\nwheneverize .\n```\n\nOpen up the file 'config/schedule.rb' and add the job:\n\n```ruby\n# config/schedule.rb\nset :environment, \"development\"\nset :output, {:error =\u003e \"log/cron_error_log.log\", :standard =\u003e \"log/cron_log.log\"}\n\nevery 1.day do\n  rake \"schedulable:build_occurrences\"\nend\n```\n\nWrite to crontab:\n\n```cli\nwhenever -w\n```\n\n## Configuration\nGenerate the configuration file\n\n```cli\nrails g schedulable:config\n```\n\nOpen 'config/initializers/schedulable.rb' and edit options as needed:\n\n```ruby\nSchedulable.configure do |config|\n  # Build occurrences\n  config.max_build_count = 0\n  config.max_build_period = 1.year\n  # SimpleForm\n  config.simple_form = {\n    input_types: {\n   \t  date: :date_time,\n   \t  time: :date_time,\n   \t  datetime: :date_time\n    }\n  }\n  # Generic Form helper\n  config.form_helper = {\n    style: :default\n  }\nend\n```\n\n\n## Changelog\nSee the [Changelog](CHANGELOG.md) for recent enhancements, bugfixes and deprecations.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenignware%2Fschedulable","html_url":"https://awesome.ecosyste.ms/projects/github.com%2Fbenignware%2Fschedulable","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2Fbenignware%2Fschedulable/lists"}