repertoire-repertoire-devtools 0.0.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- data/README.txt +0 -0
- data/bin/merb_persist +109 -0
- data/bin/postgresql_crontab_sweep +150 -0
- metadata +63 -0
data/README.txt
ADDED
File without changes
|
data/bin/merb_persist
ADDED
@@ -0,0 +1,109 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
# Adapted from mongrel_rails_persist by Christopher York on 09/16/2008
|
4
|
+
|
5
|
+
# Copyright (c) 2007 Apple Inc. All Rights Reserved.
|
6
|
+
|
7
|
+
# Create a launchd plist to run merb persistently across reboots
|
8
|
+
|
9
|
+
class MerbPersist
|
10
|
+
require 'osx/cocoa'
|
11
|
+
include OSX
|
12
|
+
|
13
|
+
def initialize(args)
|
14
|
+
@merb_root = Dir.pwd
|
15
|
+
@port = 4000
|
16
|
+
@user = nil
|
17
|
+
@group = nil
|
18
|
+
@merb_args = []
|
19
|
+
|
20
|
+
while opt = args.shift
|
21
|
+
case opt
|
22
|
+
when '-m', '--merb-root' then @merb_root = args.shift
|
23
|
+
when '-p', '--port'
|
24
|
+
@port = args.shift
|
25
|
+
[opt, @port].each { |i| @merb_args << i }
|
26
|
+
when '-u', '--user' then @user = args.shift
|
27
|
+
when '-g', '--group' then @group = args.shift
|
28
|
+
when '-d', '--daemonize', '-c', '--cluster-nodes'
|
29
|
+
# Silently remove the daemonize option as it doesn't work well with launchd
|
30
|
+
else
|
31
|
+
[opt, args.shift].each { |i| @merb_args << i }
|
32
|
+
end
|
33
|
+
end
|
34
|
+
|
35
|
+
@label = "edu.mit.hyperstudio.persist.port#{@port}.merb_server"
|
36
|
+
@plist_path = "/Library/LaunchDaemons/#{@label}.plist"
|
37
|
+
end
|
38
|
+
|
39
|
+
def start
|
40
|
+
if ! test(?d,File.dirname(@plist_path))
|
41
|
+
raise unless system('/bin/mkdir', '-m', '0755', @plist_path)
|
42
|
+
raise unless system('/usr/sbin/chown', 'root:wheel', @plist_path)
|
43
|
+
end
|
44
|
+
|
45
|
+
h = {}
|
46
|
+
h['Label'] = @label
|
47
|
+
h['OnDemand'] = false
|
48
|
+
h['KeepAlive'] = true
|
49
|
+
h['ProgramArguments'] = [@merb_root + '/bin/merb', @merb_args].flatten.compact
|
50
|
+
h['WorkingDirectory'] = @merb_root
|
51
|
+
h['UserName'] = @user if @user
|
52
|
+
h['GroupName'] = @group if @group
|
53
|
+
h['StandardErrorPath'] = "/var/log/merb_persist_#{@port}.log"
|
54
|
+
if h['UserName'] || h['GroupName']
|
55
|
+
# Ensure that the log file is writable by the specified user:group
|
56
|
+
system('/usr/bin/touch', h['StandardErrorPath']) unless test(?e, h['StandardErrorPath'])
|
57
|
+
raise unless system('/usr/sbin/chown', "#{h['UserName']}:#{h['GroupName']}", h['StandardErrorPath'])
|
58
|
+
end
|
59
|
+
|
60
|
+
data = NSPropertyListSerialization.dataFromPropertyList_format_errorDescription(h, NSPropertyListXMLFormat_v1_0, nil)
|
61
|
+
plist = NSString.alloc.initWithData_encoding(data, NSUTF8StringEncoding)
|
62
|
+
File.open(@plist_path, 'w') { |f| f.puts plist }
|
63
|
+
|
64
|
+
`/bin/launchctl unload #{@plist_path} >/dev/null 2>&1`
|
65
|
+
raise unless system('/bin/launchctl', 'load', @plist_path)
|
66
|
+
puts "Loaded #{@plist_path}.\nMerb instance should persist across reboot."
|
67
|
+
puts "To stop: #{File.basename($0)} stop -p #{@port}"
|
68
|
+
end
|
69
|
+
|
70
|
+
def stop
|
71
|
+
raise unless system('/bin/launchctl', 'unload', @plist_path)
|
72
|
+
File.delete(@plist_path)
|
73
|
+
puts "Unloaded and deleted #{@plist_path}."
|
74
|
+
end
|
75
|
+
|
76
|
+
def restart
|
77
|
+
raise unless system('/bin/launchctl', 'unload', @plist_path)
|
78
|
+
sleep(2)
|
79
|
+
raise unless system('/bin/launchctl', 'load', @plist_path)
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
if __FILE__ == $0
|
84
|
+
usage = <<EOU
|
85
|
+
Usage: #{File.basename($0)} -h | [start | restart | stop] [options]
|
86
|
+
This is a launchd wrapper for merb.
|
87
|
+
See merb command for details.
|
88
|
+
EOU
|
89
|
+
|
90
|
+
if ARGV.empty? || ARGV.include?('-h') || ARGV.include?('--help')
|
91
|
+
$stderr.puts usage
|
92
|
+
exit
|
93
|
+
end
|
94
|
+
|
95
|
+
begin
|
96
|
+
action = ARGV.shift.to_sym
|
97
|
+
m = MerbPersist.new(ARGV)
|
98
|
+
if m.respond_to?(action)
|
99
|
+
m.send(action)
|
100
|
+
else
|
101
|
+
raise ArgumentError, "#{action}: unknown action"
|
102
|
+
end
|
103
|
+
rescue ArgumentError => e
|
104
|
+
$stderr.puts e.message
|
105
|
+
$stderr.puts usage
|
106
|
+
rescue => e
|
107
|
+
$stderr.puts e.message
|
108
|
+
end
|
109
|
+
end
|
@@ -0,0 +1,150 @@
|
|
1
|
+
#!/usr/bin/ruby
|
2
|
+
|
3
|
+
# Copyright (c) 2008 MIT. All Rights Reserved.
|
4
|
+
|
5
|
+
require "rubygems"
|
6
|
+
require "pg"
|
7
|
+
|
8
|
+
#
|
9
|
+
# Basic support for crontab-type tasks within PostgreSQL
|
10
|
+
#
|
11
|
+
# This script makes it easy to schedule periodic tasks in your PostgreSQL database
|
12
|
+
# Fits a variety of use-cases: updating full text indexes, cleaning up stale caches, expiring accounts, etc.
|
13
|
+
#
|
14
|
+
# Initial Hyperstudio use is to maintain full text indexes for database models with many joins
|
15
|
+
# (1) moves expensive indexing operations outside of the initial database transaction
|
16
|
+
# (2) avoids complicated set of hand-tuned triggers and functions on dependent tables
|
17
|
+
# (2) ensures data consistency and integrity by rebuilding index periodically
|
18
|
+
#
|
19
|
+
# Before running, create the crontab table as follows:
|
20
|
+
#
|
21
|
+
# CREATE TABLE crontab(id SERIAL PRIMARY KEY,
|
22
|
+
# notice TEXT NOT NULL,
|
23
|
+
# task TEXT NOT NULL,
|
24
|
+
# interval INTERVAL NOT NULL,
|
25
|
+
# last_updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT 'epoch',
|
26
|
+
# failing BOOLEAN NOT NULL DEFAULT false,
|
27
|
+
# message TEXT);
|
28
|
+
#
|
29
|
+
# This script makes a single sweep through the crontab list, executing pending tasks. To use,
|
30
|
+
# run as a cron script (standard UNIX), or launchd task (OS X).
|
31
|
+
#
|
32
|
+
# If you would like the script to set up the database table and a basic launchd task, you can use
|
33
|
+
# the following shortcut:
|
34
|
+
# `sudo postgresql_crontab_sweep install <database-name>`
|
35
|
+
# on the command line (OS X only). This will generate the relevant files, tables, and start the
|
36
|
+
# update polling process.
|
37
|
+
#
|
38
|
+
# Each task is executed in a separate transaction, so they fail or succeed separately. If the
|
39
|
+
# task commits, its timestamp is updated. If it aborts, last_updated_at will show the most
|
40
|
+
# recent successful run, and the failing and message fields will be set.
|
41
|
+
#
|
42
|
+
# Parameters: -d <database> -p <port> -h <host> -u <user> -p <password> [ all are optional ]
|
43
|
+
# or: install <database>
|
44
|
+
#
|
45
|
+
# Because this script should only be run locally to the database, it expects postgres to accept
|
46
|
+
# your user without a password prompt (see pg_hba.conf to configure). Usually this user will
|
47
|
+
# be 'postgres', the db admin role. When writing tasks, keep in mind that this user will
|
48
|
+
# execute them. You may need to qualify tables in separate schemas, and grant access to appropriately.
|
49
|
+
#
|
50
|
+
# Christopher York 12/8/2008
|
51
|
+
#
|
52
|
+
|
53
|
+
|
54
|
+
def run(dbparams)
|
55
|
+
# setup and connect
|
56
|
+
cron_conn = PGconn.open(dbparams)
|
57
|
+
forking = false
|
58
|
+
|
59
|
+
# get the pending database cron tasks
|
60
|
+
pending = cron_conn.exec("SELECT id, notice, task, interval, last_updated_at FROM crontab WHERE last_updated_at + interval < now();")
|
61
|
+
|
62
|
+
pending.each do |crontask|
|
63
|
+
# fork a sub-process for each task, with its own database connection
|
64
|
+
forking ||= true
|
65
|
+
fork do
|
66
|
+
task_conn = PGconn.open(dbparams)
|
67
|
+
begin
|
68
|
+
# all postgresql cron tasks take place in their own transaction (which is the reason for this script - not possible in plpgsql)
|
69
|
+
task_conn.transaction do
|
70
|
+
puts crontask[:notice]
|
71
|
+
task_conn.exec("UPDATE crontab SET last_updated_at = now(), failing = false, message = NULL WHERE id = #{ crontask['id'] }")
|
72
|
+
task_conn.exec(crontask['task'])
|
73
|
+
end
|
74
|
+
rescue PGError => e
|
75
|
+
STDERR.puts "ERROR! #{ Time.now } - #{ e.message }"
|
76
|
+
task_conn.exec("UPDATE crontab SET failing = true, message = '#{ e.message }' WHERE id = #{ crontask['id'] }")
|
77
|
+
end
|
78
|
+
task_conn.close
|
79
|
+
exit!
|
80
|
+
end
|
81
|
+
end
|
82
|
+
|
83
|
+
cron_conn.close
|
84
|
+
Process.wait if forking # avoid zombies by waiting for child processes to finish
|
85
|
+
end
|
86
|
+
|
87
|
+
def install(dbparams)
|
88
|
+
launchd_plist = <<-END_OF_PLIST
|
89
|
+
<?xml version="1.0" encoding="UTF-8"?>
|
90
|
+
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
91
|
+
<plist version="1.0">
|
92
|
+
<dict>
|
93
|
+
<key>Label</key>
|
94
|
+
<string>edu.mit.hyperstudio.postgresql.crontab.plist</string>
|
95
|
+
<key>OnDemand</key>
|
96
|
+
<false/>
|
97
|
+
<key>ProgramArguments</key>
|
98
|
+
<array>
|
99
|
+
<string>/usr/bin/postgresql_crontab_sweep</string>
|
100
|
+
<string>-d</string>
|
101
|
+
<string>#{dbparams['dbname']}</string>
|
102
|
+
</array>
|
103
|
+
<key>StartCalendarInterval</key>
|
104
|
+
<dict>
|
105
|
+
<key>StartInterval</key>
|
106
|
+
<integer>5</integer>
|
107
|
+
</dict>
|
108
|
+
</dict>
|
109
|
+
</plist>
|
110
|
+
END_OF_PLIST
|
111
|
+
|
112
|
+
create_table = <<-END_OF_CREATE_TABLE
|
113
|
+
CREATE TABLE crontab(id SERIAL PRIMARY KEY,
|
114
|
+
notice TEXT NOT NULL,
|
115
|
+
task TEXT NOT NULL,
|
116
|
+
interval INTERVAL NOT NULL,
|
117
|
+
last_updated_at TIMESTAMP WITHOUT TIME ZONE NOT NULL DEFAULT 'epoch',
|
118
|
+
failing BOOLEAN NOT NULL DEFAULT false,
|
119
|
+
message TEXT);
|
120
|
+
END_OF_CREATE_TABLE
|
121
|
+
|
122
|
+
File.open('/Library/LaunchDaemons/edu.mit.hyperstudio.postgresql.crontab.plist', 'w') { |f| f.write(launchd_plist) }
|
123
|
+
|
124
|
+
conn = PGconn.open(dbparams)
|
125
|
+
conn.exec(create_table)
|
126
|
+
conn.close
|
127
|
+
end
|
128
|
+
|
129
|
+
|
130
|
+
# parse params, with defaults
|
131
|
+
|
132
|
+
opts = {
|
133
|
+
:dbname => 'hyperstudio_development',
|
134
|
+
:user => 'postgres',
|
135
|
+
:port => 5432,
|
136
|
+
:host => 'localhost'
|
137
|
+
}
|
138
|
+
|
139
|
+
while opt = ARGV.shift
|
140
|
+
case opt
|
141
|
+
when '-d' then opts['dbname'] = ARGV.shift
|
142
|
+
when '-p' then opts['port'] = ARGV.shfit
|
143
|
+
when '-u' then opts['user'] = ARGV.shift
|
144
|
+
when '-p' then opts['password'] = ARGV.shift
|
145
|
+
when 'install' then install(opts) && exit
|
146
|
+
end
|
147
|
+
end
|
148
|
+
|
149
|
+
run(opts)
|
150
|
+
exit
|
metadata
ADDED
@@ -0,0 +1,63 @@
|
|
1
|
+
--- !ruby/object:Gem::Specification
|
2
|
+
name: repertoire-repertoire-devtools
|
3
|
+
version: !ruby/object:Gem::Version
|
4
|
+
version: 0.0.1
|
5
|
+
platform: ruby
|
6
|
+
authors:
|
7
|
+
- Christopher York
|
8
|
+
autorequire:
|
9
|
+
bindir: bin
|
10
|
+
cert_chain: []
|
11
|
+
|
12
|
+
date: 2008-12-15 00:00:00 -08:00
|
13
|
+
default_executable:
|
14
|
+
dependencies:
|
15
|
+
- !ruby/object:Gem::Dependency
|
16
|
+
name: ruby-pg
|
17
|
+
version_requirement:
|
18
|
+
version_requirements: !ruby/object:Gem::Requirement
|
19
|
+
requirements:
|
20
|
+
- - ">"
|
21
|
+
- !ruby/object:Gem::Version
|
22
|
+
version: 0.0.0
|
23
|
+
version:
|
24
|
+
description: Development and server tools for use with Hyperstudio Repertoire
|
25
|
+
email: yorkc@mit.edu
|
26
|
+
executables:
|
27
|
+
- merb_persist
|
28
|
+
- postgresql_crontab_sweep
|
29
|
+
extensions: []
|
30
|
+
|
31
|
+
extra_rdoc_files:
|
32
|
+
- README.txt
|
33
|
+
files:
|
34
|
+
- README.txt
|
35
|
+
has_rdoc: false
|
36
|
+
homepage: http://hyperstudio.mit.edu/repertoire
|
37
|
+
post_install_message:
|
38
|
+
rdoc_options:
|
39
|
+
- --main
|
40
|
+
- README.txt
|
41
|
+
require_paths:
|
42
|
+
- lib
|
43
|
+
required_ruby_version: !ruby/object:Gem::Requirement
|
44
|
+
requirements:
|
45
|
+
- - ">="
|
46
|
+
- !ruby/object:Gem::Version
|
47
|
+
version: "0"
|
48
|
+
version:
|
49
|
+
required_rubygems_version: !ruby/object:Gem::Requirement
|
50
|
+
requirements:
|
51
|
+
- - ">="
|
52
|
+
- !ruby/object:Gem::Version
|
53
|
+
version: "0"
|
54
|
+
version:
|
55
|
+
requirements: []
|
56
|
+
|
57
|
+
rubyforge_project:
|
58
|
+
rubygems_version: 1.2.0
|
59
|
+
signing_key:
|
60
|
+
specification_version: 2
|
61
|
+
summary: Server and development tools for use with Hyperstudio Repertoire
|
62
|
+
test_files: []
|
63
|
+
|