Meet a Gilt Technologist: Patrick Losco, Software Engineer
When did you start working at Gilt?
I moved to NYC right out of college at the end of 2009, and started working at Gilt not long after that. At the time Gilt was still a startup, and was building out its quality assurance operations. I was hired to be a manual tester, which meant testing small features and changes on a day-to-day basis. Eventually I branched out into automation.

In my early days at Gilt, I got automation and release management experience, which involved code coverage from Selenium tests, migrating code from an engineer’s commit to testing in stage, and getting the code to production. In 2011, I realized that I wanted more coding experience than that, so I enrolled in a Java Web Services course at NYU’s School of Professional Studies. Earning my certificate really helped me measure and prove that I knew how to code. I highly recommend that course, by the way.
Was Gilt supportive of your Java study?
Yes. I mean, it was all on me that I pushed myself to do it–no one else was going to do that for me. But there’s a lot of support here. There’s no traditional ladder to climb–working here is all about making sure you push yourself to new levels. People want to see you succeed.
More recently, I took the Scala class that Gilt hosted at our NYC office. It’s great that the company is investing in teaching us new skills. Even nicer was that the class was completely free and open to anyone. I just wish it had been longer than one day.
After you finished the Java certificate, how did you apply your new skills on the job?
Other engineers here knew that I wanted to learn how to code, so one of my colleagues taught me front-end development, and Yoni [Goldberg, Popeye Team lead engineer] taught me about service-side/back-end coding.
While still enrolled in the course I started diving into front-end development, and helped out with small features. After I got my Java certificate, I started taking on features by myself, and eventually was promoted to software engineer. I’ve since been a member of Gilt’s Popeye team, which works on the Login & Registration experience, SEO optimizations, and the new Insider loyalty program. Because of my QA experience I’ve been helping Popeye with our automation coverage while gradually becoming a full-time developer and taking on new features with the Scrum team. Like all of Gilt’s engineers, I’m expected to take product leadership and own my features.
How do you decide which projects to work on?
Because I’m still a junior engineer, I look for the smaller projects that can result in a higher ROI on a low amount of input for changing code. Recently I worked on Gilt’s wait-list page; the project involved UX, and no other engineers were free to take it, so I took it up.
How did Yoni and Team Popeye support you?
Yoni gave me the push and said, ‘it’s a great project, no one’s doing it, I think you should take it on.’ I was pretty skeptical about doing it by myself at first, but I dove into it and, piece by piece, it started to come along. All the engineers on my team know how to write code in service-side, so they helped me figure out how to do something scalable while not jeopardizing performance.
Because of the small projects I worked on prior to wait-list, I knew my way around the code base. The work was really fun. I had to wait a bit for the rollout of other features until I could turn on my own–but this was good, because I wasn’t sure how great my work was, or if it would cause problems. Having to wait gave me time to make sure my work was iron-clad and address edge cases that came up over time.
Old wait list:
New Waitlist:
How do you generally respond to setbacks?
You can’t call anything that happens “bad,” because you learn from every experience. Bugs can hurt revenue and your metrics, but it’s one of those things where, if you do something wrong, you learn never to do it again. At Gilt, we evaluate why things sometimes go wrong, as opposed to taking a “move on and don’t look back” approach. We try to put as many safeguards in place as possible, to prevent problems.
To avoid issues, we have strong code reviews, high levels of test coverage and great tools for deploys.
Doesn’t code review act like a personal progress measurement of sorts?
Yes, kind of. Starting out, I had tons of iterations, and then they became fewer and fewer. That’s when I knew I was getting better. My goal was to have fewer complaints.
The engineers looking over my code were hard on me, but this was helpful to me in getting the fundamentals down and knowing why things are done a certain way. Even seemingly minor issues like a missing space they would call out, just so I’d be up to the standards they were expecting. You don’t write code for yourself–you write it for others, or for yourself a year or two from now.
In one word, how would you describe Gilt’s work culture?
Empowering. People here are willing to help you do what interests you. If you identify a need, and want to help fill that need, the company responds. That’s what gives you more opportunities to succeed.
Any advice for engineers who are just starting out?
Technology’s always changing, but it keeps you on your game. You’re always learning something. That’s what an engineer is–someone who can pick up a project and figure it out, solve the problem, without knowing much of anything about it.
The hardest time for a young or new developer is the beginning. But over time, and with perseverance, you’ll gain confidence and know that you can build something out. Pick fun projects, and enjoy what you’re doing.


No new messages!
You have {{pending.messages}} new messages
No pending visits!
You have {{pending.visits}} new visits to view
No pending calls!
You have {{pending.calls}} new calls to make
No new translations!
You have {{pending.translations}} translations to do
Enroll Participant
Find Participant
Upcoming Visits


The privacy preserving functionality of bonuskaart.com comes from a combination of two effects.
Firstly, by using a different bonuskaart for every purchase, personal information will be fragmented. If there are n cards in the database, each bonuskaart will only contain 1/nth of a single person's personal information.
The second part comes from the fact that each bonuskaart can be used by an arbitrary amount of people. When trying to identify a single person, every other person using the same bonuskaart adds noise. The more people using the same card, the noisier the data becomes, the more private it becomes.
When you visit bonuskaart.com for the first time, it will send a request for a new bonuskaart. Upon reception, the server will randomly select a barcode and send it to the client. When the client receives this bonuskaart, they will store it in a cookie. This cookie will expire at 23:59 of that day.
When visiting the website again, the website will check for cookies. If it finds one, it will use bonsukaart stored in the cookie instead of requesting a new one. The reuse of the same bonuskaart is required for the handheld scanners. To unlock a scanner one scans a bonuskaart, but to checkout you need to scan the same bonuskaart at a payment terminal.
Doesn't the cookie store identifiable information?
Yes, but this cookie never leaves your device.
The checking for cookies happens on the client (i.e. your smartphone).
Take a look at the code, if you want to see for yourself.
Wouldn't a single card for all the users add more noise?
Yes. However, if two people with the same barcode in the same store, want to use the handheld scanners it may cause problems.
I don't know what the exact behavior would be, but I suspect it would prevent you taking out two scanners with the same bonuskaart.
Another possibility is that, at checkout, the system would confuse you for the other customer, making you pay for someone else's groceries.
This project is really awesome/horrible, can I contribute?
Absolutely! please find the repository at github.com.
Can I donate a Bonuskaart?
Yes, please do so!.
You can fill in the numbers below your barcode here


|
Upload your next photo to Unsplash
|
|
|
What can we expect from your next photo 🌄?
|
|
|
|
Hey Smiles Davis,
The community misses you! Here’s a quick summary of what’s been happening on your Unsplash account since you last logged in:
|
|
|
Total views last month
+428,874.0
|
|
|
Downloads last month
+1,606
|
Total photos
11
|
|
|
Days since joining Unsplash
114
|
In collections
482
|
|
|
|
|
|
|
|
Trending Topics
If you're looking for themes to share, the search terms that have had the highest increase in search frequency over the last 30 days are:
- Summer (+734%)
- Beach (+199%)
- Fireworks (+155%)
- Friends (+116%)
- Father (+105%)
|
- Christmas (+105%)
- Ice cream (+103%)
- Pool (+91%)
- Winter (+60%)
- Sea (+58%)
|
|
These photos just hit a 1M views 👀
|
|
|


Police say no foul play suspected in dead body last week in Fort Myers
by WFTX Digital TeamFORT MYERS, Fla. -- Police have identified the man found dead in a Fort Myers field last week.
According to Fort Myers Police, the body was found November 13th in a field near Palm Avenue and Metro Parkway.
They now say he was 51-year-old Curtis Thomas.
There is no suspicion of foul play and next of kin have been notified.


<%include src="activity-card-upload/activity-card-upload.html"%>
<%include src="activity-card-post/activity-card-post.html"%>
<%include src="activity-card-facebook/activity-card-facebook2.html"%>
<%include src="activity-card-twitter/activity-card-twitter-2.html"%>
<%include src="activity-card-post/activity-card-post2.html"%>
<%include src="activity-card-birthday/activity-card-birthday.html"%>
<%include src="activity-card-facebook/activity-card-facebook.html"%>
<%include src="activity-card-post/activity-card-post3.html"%>
<%include src="activity-card-facebook/activity-card-facebook3.html"%>
<%include src="activity-card-post/activity-card-post4.html"%>
<%include src="activity-card-post/activity-card-post5.html"%>
<%include src="activity-card-post/activity-card-post6.html"%>
<%include src="activity-card-twitter/activity-card-twitter-1.html"%>
<%include src="activity-card-birthday/activity-card-birthday.html"%>
<%include src="activity-card-post/activity-card-post7.html"%>
<%include src="activity-card-post/activity-card-post8.html"%>
<%include src="activity-card-facebook/activity-card-facebook4.html"%>
<%include src="activity-card-twitter/activity-card-twitter-3.html"%>


Project Results
Main model (Model 1) with whole brain mask
Main model (Model 1) with FPC mask
View these results in Firefox if you have issues.
Source IPython files are also provided for each model (see inside the folders).


Grand Marshal Week 2017
Rank your candidates
This is unofficial polling, please vote at official stations on April 6
Voting tutorial
Live Results
Grand
Marshall
President
of the Union
Undergraduate
President
Class of 2018
President
Vice President
Senator
Representative
Class of 2019
President
Vice President
Senator
Representative
Class of 2020
President
Vice President
Senator
Representative
Alumni
President
Vice President
Secretary
Treasurer
Others
Independent Senator
Greek Senator
Graduate Senator
Graduate Representative
{% if request.user.is_authenticated %}
{% csrf_token %}
{% endif %}


|
||||||||||
| PREV NEXT | FRAMES NO FRAMES |
| Packages that use DTM | |
|---|---|
| org.apache.xml.dtm | |
| org.apache.xml.dtm.ref | |
| org.apache.xml.dtm.ref.dom2dtm | |
| org.apache.xml.dtm.ref.sax2dtm | |
| org.apache.xpath | Implementation of XPath; for the most part, only classes meant for public use are found at this root level of the XPath packages. |
| org.apache.xpath.axes | Implementation of XPath LocationPath support -- primary classes are LocPathIterator and UnionPathIterator. |
| org.apache.xpath.objects | Implementation of XPath polymorphic type objects -- this package will grow as XPath objects are expanded to support XML Schema data types. |
| org.apache.xpath.patterns | Implementation of XPath nodeTest support, and XSLT pattern matching support. |
| Uses of DTM in org.apache.xml.dtm |
|---|
| Methods in org.apache.xml.dtm that return DTM | |
|---|---|
abstract DTM |
DTMManager.createDocumentFragment()
Creates a DTM representing an empty DocumentFragment object. |
abstract DTM |
DTMManager.getDTM(int nodeHandle)
Get the instance of DTM that "owns" a node handle. |
DTM |
DTMIterator.getDTM(int nodeHandle)
Get an instance of a DTM that "owns" a node handle. |
abstract DTM |
DTMManager.getDTM(javax.xml.transform.Source source,
boolean unique,
DTMWSFilter whiteSpaceFilter,
boolean incremental,
boolean doIndexing)
Get an instance of a DTM, loaded with the content from the specified source. |
| Methods in org.apache.xml.dtm with parameters of type DTM | |
|---|---|
abstract int |
DTMManager.getDTMIdentity(DTM dtm)
%TBD% Doc NEEDSDOC @param dtm NEEDSDOC ($objectName$) @return |
short |
DTMWSFilter.getShouldStripSpace(int elementHandle,
DTM dtm)
Test whether whitespace-only text nodes are visible in the logical view of DTM . |
abstract boolean |
DTMManager.release(DTM dtm,
boolean shouldHardDelete)
Release a DTM either to a lru pool, or completely remove reference. |
| Uses of DTM in org.apache.xml.dtm.ref |
|---|
| Classes in org.apache.xml.dtm.ref that implement DTM | |
|---|---|
class |
DTMDefaultBase
The DTMDefaultBase class serves as a helper base for DTMs. |
class |
DTMDefaultBaseIterators
This class implements the traversers for DTMDefaultBase. |
class |
DTMDefaultBaseTraversers
This class implements the traversers for DTMDefaultBase. |
class |
DTMDocumentImpl
This is the implementation of the DTM document interface. |
| Fields in org.apache.xml.dtm.ref declared as DTM | |
|---|---|
DTM |
DTMNodeProxy.dtm
The DTM for this node. |
(package private) DTM |
DTMNamedNodeMap.dtm
The DTM for this node. |
protected DTM |
DTMTreeWalker.m_dtm
DomHelper for this TreeWalker |
private DTM |
DTMAxisIterNodeList.m_dtm
|
protected DTM[] |
DTMManagerDefault.m_dtms
Map from DTM identifier numbers to DTM objects that this manager manages. |
private DTM |
DTMChildIterNodeList.m_parentDTM
|
| Methods in org.apache.xml.dtm.ref that return DTM | |
|---|---|
DTM |
DTMManagerDefault.createDocumentFragment()
Method createDocumentFragment NEEDSDOC (createDocumentFragment) @return |
DTM |
DTMNodeProxy.getDTM()
NON-DOM: Return the DTM model |
DTM |
DTMManagerDefault.getDTM(int nodeHandle)
Return the DTM object containing a representation of this node. |
DTM |
DTMManagerDefault.getDTM(javax.xml.transform.Source source,
boolean unique,
DTMWSFilter whiteSpaceFilter,
boolean incremental,
boolean doIndexing)
Get an instance of a DTM, loaded with the content from the specified source. |
| Methods in org.apache.xml.dtm.ref with parameters of type DTM | |
|---|---|
void |
DTMManagerDefault.addDTM(DTM dtm,
int id)
Add a DTM to the DTM table. |
void |
DTMManagerDefault.addDTM(DTM dtm,
int id,
int offset)
Add a DTM to the DTM table. |
int |
DTMManagerDefault.getDTMIdentity(DTM dtm)
Given a DTM, find the ID number in the DTM tables which addresses the start of the document. |
ExpandedNameTable |
DTMManagerDefault.getExpandedNameTable(DTM dtm)
return the expanded name table. |
boolean |
DTMManagerDefault.release(DTM dtm,
boolean shouldHardDelete)
Release the DTMManager's reference(s) to a DTM, making it unmanaged. |
void |
DTMTreeWalker.setDTM(DTM dtm)
Set the DTM to be traversed. |
| Constructors in org.apache.xml.dtm.ref with parameters of type DTM | |
|---|---|
DTMAxisIterNodeList(DTM dtm,
DTMAxisIterator dtmAxisIterator)
Public constructor: Wrap a DTMNodeList around an existing and preconfigured DTMAxisIterator |
|
DTMChildIterNodeList(DTM parentDTM,
int parentHandle)
Public constructor: Create a NodeList to support DTMNodeProxy.getChildren(). |
|
DTMNamedNodeMap(DTM dtm,
int element)
Create a getAttributes NamedNodeMap for a given DTM element node |
|
DTMNodeProxy(DTM dtm,
int node)
Create a DTMNodeProxy Node representing a specific Node in a DTM |
|
DTMTreeWalker(org.xml.sax.ContentHandler contentHandler,
DTM dtm)
Constructor. |
| Uses of DTM in org.apache.xml.dtm.ref.dom2dtm |
|---|
| Classes in org.apache.xml.dtm.ref.dom2dtm that implement DTM | |
|---|---|
class |
DOM2DTM
The DOM2DTM class serves up a DOM's contents via the
DTM API. |
| Uses of DTM in org.apache.xml.dtm.ref.sax2dtm |
|---|
| Classes in org.apache.xml.dtm.ref.sax2dtm that implement DTM | |
|---|---|
class |
SAX2DTM
This class implements a DTM that tends to be optimized more for speed than for compactness, that is constructed via SAX2 ContentHandler events. |
class |
SAX2DTM2
SAX2DTM2 is an optimized version of SAX2DTM which is used in non-incremental situation. |
class |
SAX2RTFDTM
This is a subclass of SAX2DTM which has been modified to meet the needs of Result Tree Frameworks (RTFs). |
| Uses of DTM in org.apache.xpath |
|---|
| Methods in org.apache.xpath that return DTM | |
|---|---|
DTM |
XPathContext.createDocumentFragment()
Creates an empty DocumentFragment object. |
DTM |
XPathContext.getDTM(int nodeHandle)
Get an instance of a DTM that "owns" a node handle. |
DTM |
NodeSetDTM.getDTM(int nodeHandle)
Get an instance of a DTM that "owns" a node handle. |
DTM |
XPathContext.getDTM(javax.xml.transform.Source source,
boolean unique,
DTMWSFilter wsfilter,
boolean incremental,
boolean doIndexing)
Get an instance of a DTM, loaded with the content from the specified source. |
DTM |
XPathContext.getGlobalRTFDTM()
Get a DTM to be used as a container for a global Result Tree Fragment. |
DTM |
XPathContext.getRTFDTM()
Get a DTM to be used as a container for a dynamic Result Tree Fragment. |
| Methods in org.apache.xpath with parameters of type DTM | |
|---|---|
XObject |
Expression.execute(XPathContext xctxt,
int currentNode,
DTM dtm,
int expType)
Execute an expression in the XPath runtime context, and return the result of the expression. |
int |
XPathContext.getDTMIdentity(DTM dtm)
%TBD% Doc |
boolean |
XPathContext.release(DTM dtm,
boolean shouldHardDelete)
Release a DTM either to a lru pool, or completely remove reference. |
| Uses of DTM in org.apache.xpath.axes |
|---|
| Fields in org.apache.xpath.axes declared as DTM | |
|---|---|
protected DTM |
LocPathIterator.m_cdtm
The dtm of the context node. |
private DTM |
AxesWalker.m_dtm
The DTM for the root. |
| Methods in org.apache.xpath.axes that return DTM | |
|---|---|
DTM |
NodeSequence.getDTM(int nodeHandle)
|
DTM |
LocPathIterator.getDTM(int nodeHandle)
Get an instance of a DTM that "owns" a node handle. |
DTM |
AxesWalker.getDTM(int node)
Get the DTM for this walker. |
| Methods in org.apache.xpath.axes with parameters of type DTM | |
|---|---|
void |
AxesWalker.setDefaultDTM(DTM dtm)
Set the DTM for this walker. |
| Uses of DTM in org.apache.xpath.objects |
|---|
| Fields in org.apache.xpath.objects declared as DTM | |
|---|---|
private DTM |
DTMXRTreeFrag.m_dtm
|
| Methods in org.apache.xpath.objects that return DTM | |
|---|---|
(package private) DTM |
DTMXRTreeFrag.getDTM()
|
| Uses of DTM in org.apache.xpath.patterns |
|---|
| Methods in org.apache.xpath.patterns with parameters of type DTM | |
|---|---|
private boolean |
StepPattern.checkProximityPosition(XPathContext xctxt,
int predPos,
DTM dtm,
int context,
int pos)
New Method to check whether the current node satisfies a position predicate |
XObject |
StepPattern.execute(XPathContext xctxt,
int currentNode,
DTM dtm,
int expType)
Execute an expression in the XPath runtime context, and return the result of the expression. |
XObject |
NodeTest.execute(XPathContext xctxt,
int context,
DTM dtm,
int expType)
Tell what the test score is for the given node. |
XObject |
FunctionPattern.execute(XPathContext xctxt,
int context,
DTM dtm,
int expType)
Test a node to see if it matches the given node test. |
protected boolean |
StepPattern.executePredicates(XPathContext xctxt,
DTM dtm,
int currentNode)
Execute the predicates on this step to determine if the current node should be filtered or accepted. |
protected XObject |
StepPattern.executeRelativePathPattern(XPathContext xctxt,
DTM dtm,
int currentNode)
Execute the match pattern step relative to another step. |
|
||||||||||
| PREV NEXT | FRAMES NO FRAMES |


Heroes, hard to find
Don’t make the news
till they’re doing time
Schemers, turn the screw
They get the word
and they drop on you
Lay down the law, black up the night
Run up a score,
the vice is right
Dealers looking ’round
Wheel is turning round coming round on you
Some sin for gold,
some sin for shame
Some sin for cash,
some sin for gain
Some sin for wine,
some sin for pain
But I ain’t gonna be the fool
who’s gonna have to sin for nothin’
Nothing at all
Nero, evil mind
He was born way before his time
Heat on down below
You get it right and away you go
You gotta lay down the rules,
push up the price
Turn on the heat,
put you on ice
Dealers looking ’round
Wheel is turning round coming round for you
Some sin for gold,
some sin for shame
Some sin for cash,
some sin for gain
Some sin for wine,
some sin for pain
But I ain’t gonna be the fool
who’s gonna have to sin for nothin’
Some sin for gold,
some sin for shame
Some sin for cash,
some sin for gain
Some sin for wine,
some sin for pain
But I ain’t gonna be the fool
who’s gonna have to sin for nothin’
Ain’t gonna sin for nothin’
You get nothing for nothing
Ain’t gonna sin for nothin’
It don’t make no sense


Toggle navigation
Capacity Management Information System
CI report
{{p.name}}
Pick a product on the left.
{{ci.name}}
{{ci.description}}
Instances
{{instance}}
Measurements
{{measurement.name}}


--- layout: post status: publish published: true title: Cubs Change Ticket Give-Away Rules author: display_name: Corey Fineran login: Corey email<EMAIL_ADDRESS>url: '' author_login: Corey author_email<EMAIL_ADDRESS>wordpress_id: 7000 wordpress_url: http://ivyenvy.com/?p=7000 date: '2012-04-02 21:06:12 +0000' date_gmt: '2012-04-03 02:06:12 +0000' categories: - The Cubs - Cubs fans tags: - cubs - Wrigley Field - MLB - Twitter - Major League Baseball - Ticket Give-away comments: - id: 5449 author: ryanativyenvydotcom author_email<EMAIL_ADDRESS>author_url: http://firstorderhistorians.wordpress.com date: '2012-04-08 22:52:52 +0000' date_gmt: '2012-04-09 03:52:52 +0000' content: Like I said in my response to that post, the Cubs are similar to the Dallas Cowboys - they have fans everywhere.  What amuses me is that nobody from the MLB office seems to catch that. And it's a pretty hard thing to miss. ---
Last weekend, I wrote a post about the distance rule for tickets given away by the Cubs. According to the official rules (at the time), if you reside more than 100 miles from Wrigley Field, you are not eligible to win free tickets.
(Click to enlarge)
The Cubs have informed me that they’ve changed this rule. It was originally a rule that was handed down by Major League Baseball and the Cubs have received an exemption and the rule now reads:
(Click to enlarge)
This is a great move by the Cubs. The number of Cubs fans outside of the 100 mile radius is enormous. The Cubs have shown their loyalty those of us that live a distance away. It would have been easy for the organization to hear these complaints and dismiss them. However, they took the time to address the situation and make the change.
Follow the Cubs on Twitter - @Cubs for news and potentially free tickets, regardless of your location.


"CHAMPION "(1 Samuel 17:4, 23), properly “the man between the two,” denoting the position of Goliath between the two camps. Single combats of this kind at the head of armies were common in ancient times. In ver. 51 this word is the rendering of a different Hebrew word, and properly denotes “a mighty man.”
"CHAMPION "(1 Samuel 17:4, 23), properly “the man between the two,” denoting the position of Goliath between the two camps. Single combats of this kind at the head of armies were common in ancient times. In ver. 51 this word is the rendering of a different Hebrew word, and properly denotes “a mighty man.”


|
Product
|
Price
|
Quantity
|
Remove
|
|---|---|---|---|
Modal ScarfCategory: Accessories |
$79.00 | 3 | |
Banjara JacketCategory: Jackets |
$100.00 | 3 | |
Marina ClucthCategory: Clutch | $79.00 | 3 |
If you have a coupon code, please enter it in the box below
If you have some information for the seller you can leave them in the box below
Shipping and additional costs are calculated based on values you have entered.


The cost of running the 5etools server is covered by the advertisements run on 5e.tools, but if you wish to toss some spare gold at the development team (of one), you are welcome to do so. Direct donations are done through cryptocurrency for the safety of both parties; Coinbase is a reputable site where you can exchange money for crypto and carry out transactions.
BTC: 35kpgQDUaZWy4p6Q1VhQ6D25aZePf7JRQW
ETH: 0x8247D70C092C9834790E580655Ab1FdEe26bdeee
LTC: MSUXsUS1DwejDvovb1NDtDsdErPF2d7Txb


Hancock : A Single Sign On Server in Sinatra
A while back I blogged about the flatirons openid provider and how it was inspired by something we were doing at Engine Yard. We needed a single sign on provider and chose to use OpenID as the sso protocol. Since this is heavily influenced by openid I will often use the word “provider” to describe an SSO server. I also use the word “consumer” to describe an SSO enabled application.
So we pretty much drew up a diagram of how openid works and removed the steps we felt were unnecessary(decision/acceptance steps). We then extended it by adding auto discovery of the openid url. Here’s a somewhat up to date diagram that shows off how things work. You’ll notice that the user-agent(browser) never specifies its identity url, it’s automatically provided from the SSO server.
I’m pleased to announce a new piece of software that might interest you if you need a single sign on solution for your projects. It’s called hancock and it’s available(like all good things) on github. Here’s a quick run down of what it offers you:
- a rubygem that provides all the functionality you need for an sso server
- an sso server as an extendable sinatra app, aka rack middleware
- a required whitelist for consumer application access
- configurable sreg response parameters to customize what information is exposed to clients
- can hook into anything that datamapper supports as a user/consumer store.
- trivial deployment as a rack application under passenger
- simple user signup with email verification
- application sessions don't overlap by using .domain.com style cookies
This is all implemented in about 400 lines of ruby code using the sinatra framework. With the release of Sinatra <IP_ADDRESS> programmers can write sinatra applications in a more modular fashion. The killer feature is that these modular applications are actually rack applications. This allows for services to be written in sinatra that can be deployed as either a standalone rack application or used as middleware in frameworks like merb or rails. Hancock is an example standalone rack application. I’ll be blogging about sinatra as middleware in the days to come.
Hancock requires that you provide atleast three things to get it going. This is normally provided in your rackup file.
- database connection info, specifically the datamapper connect string
- mailer info
- a custom layout file
Hancock assumes that you’re going to provide the layout that gives your site its customized look and feel. The hancock gem itself provides all of the forms for authentication. If you’re going to be serving static assets like images, stylesheets, or javascript files you’ll also need to set the public attribute. Here’s what my rackup file looks like on my server.
That’s your whole app.
The Hancock gem provides a sinatra application as a class named Hancock::App. You should inherit from this class and implement a landing page at “/”. The mailer configuration functions a lot like the merb-mailer gem, if you’re having issues google around for merb-mailer help.
The Hancock gem also provides a class named Hancock::Consumer that represents a consumer application that is to be accepted by your hancock application. The :url attribute MUST match up to the return_to parameter in the openid negotiation. If you’re using the hancock-client gem then it should take care of this for you. The main thing to remember about this class is that if you ever receive the Forbidden message on your SSO provider you should double check that the host is allowed to access the provider.
Your user model is exposed as Hancock::User. Right now there is simple sign up through web forms. If you want to test the provider without setting up the full email configuration then you should run your application in development mode. When run under development mode the registration url that would normally be sent via email is embedded as a comment. I found that it made it really easy to test.
The Hancock gem doesn’t have perfect tests, but I’ll be damned if we aren’t shooting for it. You’ll find both unit and acceptance tests done in rspec, rack-test, webrat, and cucumber. Even if you’re not interested in an SSO server the tests might help clarify a few things if you’re a testing junkie.
As I mentioned earlier, the killer feature of sinatra <IP_ADDRESS> is that sinatra apps are modular rack applications. While hancock is a pretty cool example of the power and simplicity of sinatra; its use as middleware in other frameworks is where I expect to see sinatra shine for the foreseeable future. Even if you’re stuck on a never ending rails project with no chance of innovation in sight, go learn about rack. I think you’ll be surprised where you can start leveraging it. Besides, writing sinatra is fun.
So what good is a simple SSO server if you don’t have a super simple client library? I bet if we did it as a simple sinatra app then we could turn it into middleware. Then we could hook our rails and merb apps up. Yeah, that’d be sweet.
I’m gonna follow this up in the next few days with something covering the hancock-client gem. This gem provides a sinatra application that can be run standalone or as middleware in other apps. I’ll prolly follow that up with two short articles on “how you get your middleware running in (rails|merb).”