{"id":13850632,"url":"https://github.com/DAddYE/foreverb","last_synced_at":"2025-07-12T22:31:26.005Z","repository":{"id":56847508,"uuid":"2001649","full_name":"DAddYE/foreverb","owner":"DAddYE","description":"Small daemon framework for ruby, with logging, error handler, scheduling and much more.","archived":false,"fork":false,"pushed_at":"2017-02-06T14:31:51.000Z","size":283,"stargazers_count":519,"open_issues_count":6,"forks_count":21,"subscribers_count":12,"default_branch":"master","last_synced_at":"2024-11-20T18:48:01.568Z","etag":null,"topics":[],"latest_commit_sha":null,"homepage":"https://github.com/DAddYE/foreverb","language":"Ruby","has_issues":true,"has_wiki":null,"has_pages":null,"mirror_url":null,"source_name":null,"license":null,"status":null,"scm":"git","pull_requests_enabled":true,"icon_url":"https://github.com/DAddYE.png","metadata":{"files":{"readme":"README.md","changelog":"CHANGES.md","contributing":null,"funding":null,"license":null,"code_of_conduct":null,"threat_model":null,"audit":null,"citation":null,"codeowners":null,"security":null,"support":null}},"created_at":"2011-07-05T17:09:02.000Z","updated_at":"2024-10-18T14:03:17.000Z","dependencies_parsed_at":"2022-09-12T11:51:04.741Z","dependency_job_id":null,"html_url":"https://github.com/DAddYE/foreverb","commit_stats":null,"previous_names":[],"tags_count":33,"template":false,"template_full_name":null,"repository_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DAddYE%2Fforeverb","tags_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DAddYE%2Fforeverb/tags","releases_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DAddYE%2Fforeverb/releases","manifests_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/repositories/DAddYE%2Fforeverb/manifests","owner_url":"https://repos.ecosyste.ms/api/v1/hosts/GitHub/owners/DAddYE","download_url":"https://codeload.github.com/DAddYE/foreverb/tar.gz/refs/heads/master","host":{"name":"GitHub","url":"https://github.com","kind":"github","repositories_count":225809025,"owners_count":17527426,"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","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-04T20:01:21.170Z","updated_at":"2024-11-22T03:31:43.071Z","avatar_url":"https://github.com/DAddYE.png","language":"Ruby","funding_links":[],"categories":["Ruby"],"sub_categories":[],"readme":"# Foreverb\n\nSmall daemon framework **for ruby**, with logging, error handler, scheduling and much more.\n\nMy inspiration was [forever for node.js](https://raw.github.com/indexzero/forever) written by Charlie Robbins.\nMy scheduling inspiration was taken from [clockwork](https://github.com/adamwiggins/clockwork) written by Adam Wiggins.\n\n## Why?\n\nThere are some alternatives, one of the best is [resque](https://github.com/defunkt/resque), so why another daemons framework?\nIn my servers I've several daemons and what I need is:\n\n* easily watch the process (memory, cpu)\n* easily manage exceptions\n* easily see logs\n* easily start/stop/restart daemon\n* no blocking jobs\n* no blocking queue\n\nAs like [sinatra](https://github.com/sinatra/sinatra) and [padrino](https://github.com/padrino/padrino-framework) I need a\n**thin** framework to do these jobs in few seconds. This mean that:\n\n* I can create a new job quickly\n* I can watch, start, stop it quickly\n\nSo, if you have my needs, **Forever** can be the right choice for you.\n\n## Install:\n\n``` sh\n$ gem install foreverb\n```\n\n## Deamon Example:\n\nPlace your script under your standard directory, generally on my env is _bin_ or _scripts_.\n\nIn that case is: ```bin/foo```\n\n``` rb\n#!/usr/bin/ruby\nrequire 'rubygems' unless defined?(Gem)\nrequire 'forever'\nrequire 'mail'\n\nForever.run do\n  ##\n  # You can set these values:\n  #\n  # dir  \"foo\"     # Default: File.expand_path('../../', __FILE__)\n  # file \"bar\"     # Default: __FILE__\n  # log  \"bar.log\" # Default: File.expand_path(dir, '/log/[file_name].log')\n  # pid  \"bar.pid\" # Default: File.expand_path(dir, '/tmp/[file_name].pid')\n  #\n\n  on_error do |e|\n    Mail.deliver do\n      delivery_method :sendmail, :location =\u003e `which sendmail`.chomp\n      to      \"d.dagostino@lipsiasoft.com\"\n      from    \"exceptions@lipsiasoft.com\"\n      subject \"[Foo Watcher] #{e.message}\"\n      body    \"%s\\n  %s\" % [e.message, e.backtrace.join(\"\\n  \")]\n    end\n  end\n\n  before :each do # or if you prefer before :all\n    require 'bundler/setup'\n    require 'foo'\n    Foo.start_loop\n  end\nend\n```\n\nAssign right permission:\n\n``` sh\n$ chmod +x bin/foo\n```\n\nstart the daemon:\n\n``` sh\n$ bin/foo\n```\n\nyou should see an output like:\n\n``` sh\n$ bin/foo\n=\u003e Process demonized with pid 19538\n```\n\nyou can stop it:\n\n``` sh\n$ bin/foo stop\n=\u003e Found pid 19538...\n=\u003e Killing process 19538...\n```\n\n## Scheduling\n\nYou can use `every` method to schedule repetitive tasks.\n\nEvery allow the option `:at` to specify hour or minute and the option `:last` to specify when the `every` must start to loop.\n\n`:last`: can be nil or a Time class. Default is 0.\u003cbr /\u003e\n`:at`: can be nil, a string or an array of formatted strings. Default is nil.\n\n``` rb\nevery 1.second,   :at =\u003e '19:30'            # =\u003e every second since 19:30\nevery 1.minute,   :at =\u003e ':30'              # =\u003e every minute but first call wait xx:30\nevery 5.minutes,  :at =\u003e '18:'              # =\u003e every five minutes but first call was at 18:xx\nevery 1.day,      :at =\u003e ['18:30', '20:30'] # =\u003e every day only at 18:30 and 20:30\nevery 60.seconds, :last =\u003e Time.now         # =\u003e will be fired 60 seconds after you launch the app\n```\n\nRemember that `:at`:\n\n* accept only 24h format\n* you must always provide the colon `:`\n\nSo looking our [example](https://github.com/DAddYE/foreverb/blob/master/examples/sample):\n\n``` rb\nForever.run do\n  dir File.expand_path('../', __FILE__) # Default is ../../__FILE__\n\n  before :all do\n    puts \"All jobs will wait me for 1 second\"; sleep 1\n  end\n\n  every 10.seconds, :at =\u003e \"#{Time.now.hour}:00\" do\n    puts \"Every 10 seconds but first call at #{Time.now.hour}:00\"\n  end\n\n  every 1.seconds, :at =\u003e \"#{Time.now.hour}:#{Time.now.min+1}\" do\n    puts \"Every one second but first call at #{Time.now.hour}:#{Time.now.min}\"\n  end\n\n  every 10.seconds do\n    puts \"Every 10 second\"\n  end\n\n  every 20.seconds do\n    puts \"Every 20 second\"\n  end\n\n  every 15.seconds do\n    puts \"Every 15 seconds, but my task require 10 seconds\"; sleep 10\n    # This doesn't block other jobs and your queue !!!!!!!\n  end\n\n  every 10.seconds, :at =\u003e [\":#{Time.now.min+1}\", \":#{Time.now.min+2}\"] do\n    puts \"Every 10 seconds but first call at xx:#{Time.now.min}\"\n  end\n\n  on_error do |e|\n    puts \"Boom raised: #{e.message}\"\n  end\n\n  on_exit do\n    puts \"Bye bye\"\n  end\nend\n```\n\nRunning the example with the following code:\n\n``` sh\n$ examples/sample; tail -f -n 150 examples/log/sample.log; examples/sample stop\n```\n\nyou should see:\n\n```\n=\u003e Pid not found, process seems doesn't exist!\n=\u003e Process demonized with pid 11509 with Forever v.0.2.0\n[14/07 15:46:56] All jobs will will wait me for 1 second\n[14/07 15:46:57] Every 10 second\n[14/07 15:46:57] Every 20 second\n[14/07 15:46:57] Every 15 seconds, but my task require 10 seconds\n[14/07 15:47:00] Every one second but first call at 15:47\n[14/07 15:47:00] Every 10 seconds but first call at xx:47\n[14/07 15:47:01] Every one second but first call at 15:47\n[14/07 15:47:02] Every one second but first call at 15:47\n[14/07 15:47:03] Every one second but first call at 15:47\n[14/07 15:47:04] Every one second but first call at 15:47\n[14/07 15:47:05] Every one second but first call at 15:47\n[14/07 15:47:06] Every one second but first call at 15:47\n[14/07 15:47:07] Every 10 second\n[14/07 15:47:07] Every one second but first call at 15:47\n[14/07 15:47:08] Every one second but first call at 15:47\n[14/07 15:47:09] Every one second but first call at 15:47\n[14/07 15:47:10] Every 10 seconds but first call at xx:47\n[14/07 15:47:10] Every one second but first call at 15:47\n[14/07 15:47:11] Every one second but first call at 15:47\n[14/07 15:47:12] Every 15 seconds, but my task require 10 seconds\n...\n[14/07 15:47:42] Every 15 seconds, but my task require 10 seconds\n[14/07 15:47:42] Every one second but first call at 15:47\n[14/07 15:47:43] Every one second but first call at 15:47\n[14/07 15:47:44] Every one second but first call at 15:47\n[14/07 15:47:45] Every one second but first call at 15:47\n[14/07 15:47:46] Every one second but first call at 15:47\n[14/07 15:47:47] Every 10 second\n^C\n=\u003e Found pid 11509...\n=\u003e Killing process 11509...\n[14/07 15:48:40] Bye bye\n```\n\n## Filters\n\nIn foreverb we have a couple of filters, `before` and `after`, like rspec you should be able to filter `before :all` or `before :each`.\n\n``` rb\nbefore :all do\n  puts \"This will be ran only at start\"\nend\n\nbefore :each do\n  puts \"Do that before each job\"\nend\n\n# ...  here jobs ...\n\nafter :all do\n  puts \"This will be ran only at shutdown\"\nend\n\nafter :each do\n  puts \"Do that after each job\"\nend\n```\n\n## CLI\n\n### Help:\n\n``` sh\n$ foreverb help\nTasks:\n  foreverb help [TASK]                       # Describe available tasks or one specific task\n  foreverb list                              # List Forever running daemons\n  foreverb restart [DAEMON] [--all] [--yes]  # Restart one or more matching daemons\n  foreverb start [DAEMON] [--all] [--yes]    # Start one or more matching daemons\n  foreverb stop [DAEMON] [--all] [--yes]     # Stop one or more matching daemons\n  foreverb tail [DAEMON]                     # Tail log of first matching daemon\n  foreverb update [DAEMON] [--all] [--yes]   # Update config from one or more matching daemons\n  foreverb version                           # show the version number\n```\n\n### List daemons:\n\n``` sh\n$ foreverb list\n     RUNNING  /Developer/src/Extras/githubwatcher/bin/githubwatcher\n     RUNNING  /Developer/src/Extras/foreverb/examples/sample\nReading config from: /Users/DAddYE/.foreverb\n```\n\n### Monitor daemons (with ps):\n\n``` sh\n$ foreverb list -m\nPID   RSS     CPU    CMD\n5528  168 Mb  0.1 %  Forever: /Developer/src/Extras/githubwatcher/bin/githubwatcher\n5541  18 Mb   0.0 %  Forever: /Developer/src/Extras/foreverb/examples/sample\n```\n\n### Stop daemon(s):\n\n``` sh\n$ foreverb stop foo\nDo you want really stop Forever: bin/foo  with pid 19538? y\nKilling process Forever: bin/foo  with pid 19538...\n\n$ foreverb stop --all -y\nKilling process Forever: /usr/bin/githubwatcher with pid 2824\nKilling process Forever: examples/sample with pid 2836\n```\n\n### Start daemon(s):\n\n``` sh\n$ foreverb start github\nDo you want really start /Developer/src/Extras/githubwatcher/bin/githubwatcher? y\n=\u003e Found pid 5528...\n=\u003e Killing process 5528...\n=\u003e Process demonized with pid 14925 with Forever v.0.2.2\n```\n\nas for stop we allow `--all` and `-y`\n\n### Restart daemon(s)\n\n``` sh\n$ foreverb restart github\nDo you want really restart /Developer/src/Extras/githubwatcher/bin/githubwatcher? y\n=\u003e Found pid 5528...\n=\u003e Killing process 5528...\n=\u003e Process demonized with pid 14925 with Forever v.0.2.2\n```\n\nas for stop we allow `--all` and `-y`\n\n### Tail logs\n\n``` sh\n$ foreverb tail github\n[22/07 11:22:17] Quering git://github.com/DAddYE/lipsiadmin.git...\n[22/07 11:22:17] Quering git://github.com/DAddYE/lightbox.git...\n[22/07 11:22:17] Quering git://github.com/DAddYE/exception-notifier.git...\n[22/07 11:22:17] Quering git://github.com/DAddYE/lipsiablog.git...\n[22/07 11:22:17] Quering git://github.com/DAddYE/purple_ruby.git...\n```\n\nyou can specify how many lines show with option `-n`, default is `150`\n\n### Update config\n\nThis command would be helpful if you change `pid` `log` path, in this way the global config file `~/.foreverb` will be update\nusing latest informations from yours deamons\n\nNote that you can personalize the config file setting `FOREVER_PATH` matching your needs.\n\n``` sh\n$ foreverb update github\nDo you want really update config from /Developer/src/Extras/githubwatcher/bin/githubwatcher? y\n```\n\nas for stop we allow `--all` and `-y`\n\n## HACKS\n\n### Bundler\n\nBundler has the bad behavior to load `Gemfile` from your current path, so if your `daemons` (ex: [githubwatcher](https://github.com/daddye/githubwatcher))\nis shipped with their own `Gemfile` to prevent errors you must insert that line:\n\n``` ruby\nENV['BUNDLE_GEMFILE'] = File.expand_path('../../Gemfile', __FILE__) # edit matching your Gemfile path\n```\n\n### Rails/Padrino prevent memory leaks\n\nI highly suggest to use `fork` and `before` filters when you are using `forever` with frameworks, this since running same job on our ruby will eat a lot of\nram, so the better way that I found is that:\n\n```rb\nForever.run :fork =\u003e true do\n  before :each do\n    require '/config/boot' # here the rails/padrino environment\n  end\n\n  every 10.seconds, :at =\u003e ['12:00', '00:00'] do\n    Project.all(\u0026:perform_long_task)\n  end\n\n  every 1.minute do\n    Account.all.map(\u0026:send_emails)\n  end\nend\n```\n\nThis is similar to create a new process i.e.:\n\n```rb\nProcess.fork do\n  require '/config/boot'\n  my_long_jobs\n  Project.all(\u0026:perform_long_task)\nend\nProcess.waitall\n```\n\n## /etc/init.d script sample\n\nUse the following script if you want **foreverb** to fire up all of your daemons at boot time in Linux:\n\n```#!/bin/sh\n### BEGIN INIT INFO\n# Provides:          foreverb\n# Required-Start:    $local_fs $remote_fs\n# Required-Stop:     $local_fs $remote_fs\n# Default-Start:     2 3 4 5\n# Default-Stop:      S 0 1 6\n# Short-Description: foreverb initscript\n# Description:       foreverb\n### END INIT INFO\n\n# Do NOT \"set -e\"\n\nDAEMON=\"foreverb\"\nUSER=\"username\"\nSCRIPT_NAME=\"/etc/init.d/foreverb-username\"\n\ncase \"$1\" in\n  start)\n  su -l $USER -c \"$DAEMON start --all --yes\"\n  ;;\n  stop)\n  su -l $USER -c \"$DAEMON stop --all --yes\"\n  ;;\n  restart)\n  su -l $USER -c \"$DAEMON restart --all --yes\"\n  ;;\n  *)\n  echo \"Usage: $SCRIPT_NAME {start|stop|restart}\" \u003e\u00262\n  exit 3\n  ;;\nesac\n\n:\n```\n\nYou'll have to create one script per each user foreverb runs on.\nAfter creating the file, make it executable:\n\n    chmod +x /etc/init.d/foreverb-username\n\nand add it to the system's boot:\n\n* RedHat:\n  ```\n  sudo /sbin/chkconfig --level 345 foreverb-username on\n  ```\n\n* Debian/Ubuntu:\n  ```\n  sudo /usr/sbin/update-rc.d -f foreverb-username defaults\n  ```\n\n* Gentoo:\n  ```\n  sudo rc-update add foreverb-username default\n  ```\n\n\n## Extras\n\nTo see a most comprensive app running _foreverb_ + _growl_ see [githubwatcher gem](https://github.com/daddye/githubwatcher)\n\n## Author\n\nDAddYE, you can follow me on twitter [@daddye](http://twitter.com/daddye) or take a look at my site [daddye.it](http://www.daddye.it)\n\n## Copyright\n\nCopyright (C) 2011 Davide D'Agostino - [@daddye](http://twitter.com/daddye)\n\nPermission is hereby granted, free of charge, to any person obtaining a copy of this software and\nassociated documentation files (the “Software”), to deal in the Software without restriction, including without\nlimitation the rights to use, copy, modify, merge, publish, distribute, sublicense, and/or sell copies of the Software,\nand to permit persons to whom the Software is furnished to do so, subject to the following conditions:\n\nThe above copyright notice and this permission notice shall be included in all copies or substantial portions of the Software.\n\nTHE SOFTWARE IS PROVIDED “AS IS”, WITHOUT WARRANTY OF ANY KIND, EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES\nOF MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM,\nDAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION WITH THE\nSOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.\n","project_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDAddYE%2Fforeverb","html_url":"https://awesome.ecosyste.ms/projects/github.com%2FDAddYE%2Fforeverb","lists_url":"https://awesome.ecosyste.ms/api/v1/projects/github.com%2FDAddYE%2Fforeverb/lists"}