monolithic app -- becomes
bigger & bigger
- generally try to make a new service
unless it is really, really related to what's
already there
since they are independent, they can be put
in different data centers
json
(others use a messaging system: a giant queue,
where you can throw all you)
to quickly start a service,
setup a "template" to "rails new":
- documentation, testing, code quality,
deployment, monitoring, security
and you can reusing that template
these are apis:
so documentation driven development
docs have to "work" - makes the build green
documentation is the main thing; there is
even a empty scaffolding generator for the
the documentation
description:
reqParams:
properties:
user_id:
description:
require:
type:
example:
resParams:
resCodes:
https://github.com/square/fdoc
growing oos, guided by tests
mock the services
http://ddollar.github.com/foreman/
services.yml
code quality:
https://github.com/square/cane
small, fast and can embed into your
rails app:
http://jetty.codehaus.org/jetty/
use jetpack to package it
to deploy:
jetpack . && cap deploy
https://github.com/square/jetpack
deployed and now it needs monitoring:
airbrake
hoptoad
collect exception and comment on them
monitoring tool:
cubism.js
cool dashboards
https://github.com/square/cubism
use splunk to figure out what's breaking:
search for all your logs from one place
security:
ssl certificates with mutual authentication
ssl certificate for each new service
on ssl certificate there is a key called
organizational unit -> name of service
have a acl for each service -- (25:29) including
all clients allowed to talk to this resource
how to pull out services from the monolithic rails
app: inttially take out the code then do a redirect
and then chop it off.
defcraft
17 January 2013
Notes on SOA @ Square
27 April 2012
Distance between 2 addresses in CoffeeScript
05 December 2010
Two useful scripts
Here are two Ruby scripts that I find useful. Both of them have been tested under Ruby 1.9.2p0. The first is mv_ldown: it moves the most recently downloaded file (from ~/Downloads) into the current directory.
#! /usr/bin/env ruby
src = Dir[File.expand_path('~/Downloads/*')].
sort_by { |x| File.mtime(x) }.
last
abort("nothing in ~/Downloads dir") unless src
dest = File.join(File.absolute_path('.'), File.basename(src))
abort("#{dest} already exists") if File.exists?(dest)
File.rename(src, dest)
puts "#{File.basename src} to ."
The second is sl: it just like the command "ln -s SRC DEST", but you don't have to remember the order of the parameters. The file that exists is the source and the other, the destination.
#! /usr/bin/env ruby
require 'fileutils'
abort("Usage: sl SRC DEST") unless ARGV.length == 2
a, b = ARGV
if File.exists?(a) && File.exists?(b)
abort("Both #{a} & #{b} exists")
elsif File.exists?(a)
src, dest = a, b
elsif File.exists?(b)
src, dest = b, a
else
abort("Neither #{a} nor #{b} exists")
end
src = File.absolute_path src
abort("#{src} not a file") unless File.file?(src)
dest = File.absolute_path dest
system("ln -s #{src} #{dest}")
03 March 2010
Extracting phone numbers from Outlook Msg Files
Recently, I had to extract phone numbers from a bunch of Outlook .MSG files (see here and here).
Looking for an open source solution, I came across MsgParser. It almost did what I needed. (I just had to modify a "protected" field and make it "public" -- I needed to access it from a driver program; there might be a easier way to do this -- maybe make the driver program "run" from the same package as MsgParser and it'll have access to the protected field...) But downloading its dependencies was a bit of a pain.
So here is a Ruby script that does all that for you. You specify a directory where the Outlook .MSG files are. It'll extract all the phone numbers from them and print it. Tested under OS X Leopard; needs curl & java to be installed. Run: ruby msg_extractor.rb MSGS_DIR.
# Released under the GNU GPL.
# http://www.gnu.org/copyleft/gpl.html
require 'fileutils'
DOWNLOADS = %w{
http://auxilii.com/msgparser/download/msgparser-1.6.zip
http://www.freeutils.net/source/jtnef/jtnef-1_6_0.zip
http://apache.ziply.com/poi/release/bin/poi-bin-3.6-20091214.tar.gz
http://www.trieuvan.com/apache/ant/binaries/apache-ant-1.8.0-bin.zip }
JCLASS = 'MyMsgParser'
JAVA_INPUT = 'My.msg'
JAVA = <<END
import java.util.*;
import com.auxilii.msgparser.*;
import com.auxilii.msgparser.attachment.*;
public class #{JCLASS} {
public static void main(String[] args) throws Exception{
MsgParser msgp = new MsgParser();
Message msg = msgp.parseMsg("#{JAVA_INPUT}");
// UNUSED
String fromEmail = msg.getFromEmail();
String fromName = msg.getFromName();
String subject = msg.getSubject();
String body = msg.getBodyText();
String longString = msg.toLongString();
// We modify source of msgparser to make
// properties public
Iterator it = msg.properties.keySet().iterator();
while (it.hasNext()) {
String key = (String) it.next();
Object val = msg.properties.get(key);
System.out.println(key + ": " + val);
}
}
}
END
class MsgExtractor
def initialize(msgs_dir)
@msgs_dir = msgs_dir
@cp = '.:msgparser-1.6/dist/msgparser-1.6.jar:' +
'poi-3.6/poi-3.6-20091214.jar:lib/tnef.jar'
download
javac
end
def download
DOWNLOADS.each do |u|
b = File.basename(u)
if !File.exists?(b)
puts "downloading #{u}"
`curl --silent -O #{u}` unless File.exists?(b)
end
next if File.exists?("#{b}.zipped")
puts "unpacking #{b}"
case b
when /zip$/; `unzip -o #{b}`
when /gz$/; `gzip -dc #{b} | tar -xpvf -`
end
FileUtils.touch("#{b}.zipped")
end
end
def javac
modify_msgparser
@cp.split(':').each do |f|
abort("#{f} doesn't exist") unless File.exists?(f)
end
open("#{JCLASS}.java", "w") { |f| f.write(JAVA) }
`javac -classpath #{@cp} #{JCLASS}.java`
end
def modify_msgparser
jp = 'msgparser-1.6/src/main/java/com/auxilii/msgparser/Message.java'
old1 = 'protected Map<String,Object> properties ' +
'= new HashMap<String,Object>();'
new1 = old1.dup
new1['protected'] = 'public'
modified = File.read(jp)
return if modified[new1]
modified[old1] = new1
puts "modifying #{jp}..."
open(jp, 'w') { |f| f.write(modified) }
FileUtils.cd('msgparser-1.6') do
puts "rebuilding msgparser"
`../apache-ant-1.8.0/bin/ant jar`
FileUtils.mv 'dist/msgparser.jar', 'dist/msgparser-1.6.jar'
end
end
def run(&extract_by)
result = []
Dir[File.join(@msgs_dir, '*.msg')].each do |f|
b = File.basename(f)[0..-5]
puts "extracting #{b}"
FileUtils.cp(f, JAVA_INPUT)
out = `java -classpath #{@cp} #{JCLASS}`
data = extract_by.call(out)
next if data.nil?
break if $INTERRUPTED
result << [b, data]
end
result.sort
end
end
$INTERRUPTED = false
def main
unless ARGV[0]
abort("Usage: ruby msgs_extractor.rb msgs_dir")
end
tmp = ARGV[1] || "msgs_extractor"
Dir.mkdir tmp rescue nil
msgs_dir = File.expand_path ARGV[0]
Dir.chdir tmp
extractor = MsgExtractor.new(msgs_dir)
trap("INT") { $INTERRUPTED = true; puts; }
result = extractor.run { |o| o.scan /\d{7,20}/ } # extract phone numbers
result.each { |n, ph| puts("%20s: %s" % [n, ph.join(", ")]) }
end
if $0 == __FILE__
main
end