James Crisp

Software dev, tech, mind hacks and the occasional personal bit

Rails ActiveModel, with nested objects and validation

So maybe you have a model that is not backed by a database table? ActiveModel is meant to cover this scenario and give you the usual ActiveRecord goodness and validation.

But the story gets much harder if you want to have nested objects. In a normal ActiveRecord::Base backed model, you can simply say:

accepts_nested_attributes_for :order_lines

and Rails will manage form submitted nested parameters for you.

Life is not so simple, or documented with nested objects on ActiveModel. accepts_nested_attributes_for is not available. But some of the underpinnings are.

So enough talk, how do you make it work? I’ll show you with an Order / OrderLines example.

Note the very special name: order_lines_attributes=. This hooks into the Rails handling of nested form parameters. Also the valid? method propagates the child errors up to the parent object, so that they show up at the top of the form.

Now how do you do the nested form? It’s similar to normal database backed nested records.

Hope this helps, it is not documented anywhere I could find, and worked out mainly though reading the Rails source.

Rails: Removing error divs around labels

Rails makes it very easy to style fields with errors on your form. Unfortunately, the same error DIV with class ‘.field_with_errors’ is applied around labels, as well as inputs/checkboxes/selects. This tends to mess up the layout and double up on error display. To fix this, you can configure the field_error_proc in your application.rb to ignore labels. The code below calls the original error decoration proc for all types of tags except for labels.

The Lane Cove River

The Lane Cove River is the closest major waterway to my place. Over several trips, I have run and kayaked from the source to its mouth, where it joins the Parramatta River/Sydney Harbour.

The Lane Cove River has many beautiful spots. It is divided in two by a weir in the Lane Cove National Park. Above the weir is fresh, below is tidal and briny.

Fifty years ago, it was a much healthier river, which had beautiful swimming spots and even an amusement park called Fairyland above the weir.

Water begins to trickle from among the rocks near Thornleigh
By Wahroonga it is the size of a creek
It grows with multiple little creeks joining, until it becomes passable by kayak under the Ryde Rd Bridge
A pretty waterhole
The remains of Fairyland in Lane Cove National Park (an amusement park until the late 1960s)
The weir
Chatswood West
Near Lane Cove River Kayakers Club
From Riverview, you can just see the Harbour Bridge
Nice houses along the river near Woolwich
The mouth of the Lane Cover River where it joins the Parramatta River/harbour

As well as the beautiful spots, there is a lot of pollution, especially above the weir. Anything in the water above the weir like sticks, trunks or rubbish gets coated with a layer of brown algae.

Orange gunk going into the river from a little side creek
Junk floating in the water
Opaque and milky water above the weir
Lots of invasive weeds along the shoreline and rubbish caught in overhanging trees

There is a multi-council initiative to make the Parramatta River swimmable again by 2025. As someone who sails on the Parramatta River, I love this.

Unfortunately, the Lane Cover River does not have as much support, but there are some groups which are working hard to improve it. Ones I have found so far are:

  • Lane Cove Rivercare – kayakers who meet every 2 months to remove garbage from the river. I joined in early August and we removed piles of rubbish, especially towards the weir.
  • Stringybark Creek Streamwatch – volunteers who do sampling on a creek that is a tributary of the river.
  • Sydney Water faults ([email protected]) – if you smell or spot any leaking sewer pipes or hatches, you can report the location (GPS/Google maps) and they will send a crew out to repair it. I’ve reported in one spot that is always smelly on the edge of the river, and you can see traces of overflow near the hatch. A crew has gone out to fix it quite quickly – I’m impressed!

Lightning Talks at Sydney ALT.NET (30 June)

At next Sydney ALT.NET meetup (30 June), I’ll be giving a couple of short talks from around 7pm:

Building a Rust microservice, dockerising and deploying on Google Kubernetes Engine
Walk through of a little side project, showing you how this tech works and fits together.

Terminal COVID-19 tracker in your postcode
Brought to you in one line, with a little investigation in the Chrome inspector, and some simple terminal commands piped together.

Feel free to tune in on the Twitch stream for some fun!

Now available to watch on YouTube.
You can check out the repo for Rust & k8 on Github.
And the one liner for COVID-19 tracking is:

curl https://nswdac-covid-19-postcode-heatmap.azurewebsites.net/datafiles/data_Cases2.json |ruby -e 'puts STDIN.gets.gsub("},", "},\n")' | grep <your postcode>

Importing Excel 365 CSVs with Ruby on OSX

Up until 2016, Excel for the Mac provided a handy CSV export format called “Windows CSV”, which used iso-8859-1/Windows-1252 encoding. It was reliable, handled simple extended characters like degree signs, and could be read in Ruby with code like:

CSV.foreach(file, encoding:'iso-8859-1', headers:true, skip_blanks:true) do |row|

Unfortunately, support for this format was dropped in Excel 365. Many RiskAssess data files were in this format, as the earlier versions of Excel did not properly export to valid UTF-8.

Excel 365 now has a Save As option of “CSV UTF-8 (Comma-delimited)”. This is UTF-8.. with a few twists! Sometimes it includes a UTF-8 format first character, and sometimes not. Sometimes it includes the UTF-8 format first character plus a BOM (byte order mark), another invisible character. According to Wikipedia “The Unicode Standard permits the BOM in UTF-8 but does not require or recommend its use”. This makes it trickier to import. Code like this:

CSV.foreach(file, encoding:'UTF-8', headers:true, skip_blanks:true) do |row|

will handle the first 2 possibilities, but not the BOM. The BOM ends up as an invisible character in the data, causing trouble. It is possible to import with encoding ‘bom|utf-8’ which will handle this case. Another option is to run data through the dos2unix command (easily installed with brew) which does general tidying including removing the unnecessary BOM.

Also to watch out for, “Windows CSV” format previously used “\r” to denote new lines inside of cells. The new UTF-8 export uses “\n” for new lines inside of cells.

“The Magic of Thinking Big” by David Schwartz

You might think that a book first published in 1959 would be hopelessly dated. But no, “The Magic of Thinking Big” I found an interesting and inspiring read, with most outdated bits easily applicable to more modern parallels (or ignored if too cringeworthy :-). Most chapters have an explanation, supporting stories and clear actions. It’s pretty quick reading as a result, but worth going back over some of the actions more carefully. It is almost funny in parts with how hard the author pushes his points, and full of great anecdotes.

OK, so what is the book about? I’m going to give you some of my favourite bits.

It’s a fine day, and I will succeed

Production in your thought factory is under the charge of two foremen, one of whom we will call Mr Triumph, and the other Mr Defeat. Mr Triumph is in charge of manufacturing positive thoughts. He specializes in producing reasons why you can, why you're qualified, why you will... Mr Defeat produces negative, deprecating thoughts. He is your expert on developing reasons why you can't why you're weak, ...Tell yourself 'Today is a lousy day.'. This signals Mr Defeat into action, and he manufactures some facts to prove you are right.. Mr Defeat is tremendously efficient. In just a few moments, he's got you sold. It is a bad day. 

But tell yourself 'Today is a fine day' and Mr Triumph is signalled forward to act. He tells you 'This is a wonderful day'.. and then it is a good day'. Mr Defeat will convince you that you will fail, while Mr Triumph will demonstrate why you will succeed... 

Now; the more work you give either of these two foremen, the stronger he becomes. If Mr Defeat is given more work to do, he adds personnel and takes up more space in your mind. Eventually, he will take over the entire thought-manufacturing division, and virtually all thought will be of a negative nature. The only wise thing to do is fire Mr Defeat. You don't want him around telling you that you can't you're not up to it, you'll fail and so on... Use Mr Triumph 100% of the time. When any thought enters your mind, ask Mr Triumph to go to work for you. He'll show you how you can succeed...

Let the master thought 'I will succeed' dominate your thinking process. Thinking success conditions your mind to create plans that produce success. Thinking failure does the exact opposite.

Kicking health excusitis

'Bad' health, in a thousand different forms, is used as an excuse for failing to do what a person wants to do, ... failing to achieve success... Looking and looking and looking for an illness often actually produces illness... 

One fellow [with a mild case of diabetes]... belongs to that fraternity of the living dead. Obsessed with a fear of the weather, he is usually ridiculously bundled up. He's afraid of infection, so he shuns anybody who has the slightest sniffle. He's afraid of overexertion, so he does almost nothing. He spends most of his mental energy worry about what might happen. He bores other people telling them "how awful" his problem really is... the other extreme .. he has a severe case, .. but he is not living to be sick. He is living to enjoy his work and have fun. One day he said to me, 'Sure it is an inconvenience, but so is shaving. But I'm not going to think myself to bed. When I take those shots, I just praise the guys who discovered insulin...'

Refuse to talk about your health. The more you talk about an ailment.. the worse it seems to get. Besides, .. it bores people. Refuse to worry about your health... Just being grateful for the health you have is a powerful vaccination against developing new aches and pains and real illness.. Life is yours to enjoy. Don't waste it.


Isolate your fear. Pin it down. Determine exactly what you are afraid of. Then take action. There is some kind of action for any kind of fear. And remember, hesitation only enlarges, magnifies the fear. Take action promptly. Be decisive."

Memory management

Deposit only positive thoughts in your memory bank... everyone encounters plenty of unpleasant, .. discouraging situations.. Most individuals I try to help .. are operating in their own private museum of mental horror. A person can make a mental monster out of almost any unpleasant happening... Don't build mental monsters. Refuse to withdraw the unpleasant thoughts from your memory bank. When you remember situations of any kind, concentrate on the good parts of the experience; forget the bad. bury it. If you find yourself thinking about the negative side, turn your mind off completely.

When meeting important people

Think "We're just two important people sitting down to discuss something of mutual interest and benefit".


Use big, positive, cheerful words and phrases to describe how you feel.. to describe other people... to encourage others.. to outline plans to others. When people hear something like this: "Here is some good news. We face a genuine opportunity.." their minds start to sparkle. But when they hear something like "Whether we like it or not, we've got a job to do", the mind movie is dull and boring and they react accordingly. Promise victory and watch eyes light up. Promise victory and win support. Build castles, don't dig graves!

See what can be, not just what is

Look at things not as they are, but as they can be. Visualization adds value to everything. A big thinker always visualizes what can be done in the future. He isn't stuck with the present... Practice adding value to things, to people and to yourself.

Avoid arguments and petty frustration

Ask "Is it really important?"... "Is it important enough for me to get all worked up about?". Think above trivial things. Focus your attention on big objectives.

Thinking big examples

Views the future as limited .VS. sees the future as promising

Magnifies minor errors. Turns them into big issues .VS. ignores errors of little consequence

Looks for ways to avoid work .VS. Looks for more ways and things to do, especially helping others

Sets goals low .VS. Sets goals high


When you believe something is impossible, your mind goes to work for you to prove why. But when you believe, really believe, something can be done, your mind goes to work for you and helps you find the ways to do it.

Listening & Ideas

Big people monopolize the listening.
Small people monopolize the talking...
ask "How do you feel about it?" "What do you recommend?" "What would you do under these circumstances?"

A leader is a decision-making human machine... ideas of others help to spark your own ideas so your mind is more creative...
Be receptive to new ideas. Be experimental. Try new approaches, be progressive in everything you do... Get stimulated. Associate with people who can help you think of new ideas... mix with people of different occupational and social interests. Circulate in new groups. Discover new and stimulating things to do.

Listen and learn.


When asked, "What are you doing?", the first bricklaer replied, "Laying brick.", the second answered, "Making $9.30 an hour." And the third said, "Me? Why, I'm building the world's greatest cathedral."

Think your work is important. Think this way, and you will receive mental signals on how to do your job better. Think your work is important, and your subordinates will think their work is important too.

People you hang out with

Make your environment work for you, not against you. Don't let suppressive forces - the negative, you can't do it people - make you think defeat.

Take the initiative in building friendships. Introduce yourself to others at every opportunity. Make sure you get the other person's name straight, and make sure he gets your name straight too. Drop a personal note to your new friends you want to know better.

Accept human differences and limitations. Remember the other person has a right to be different.. and don't be a reformer [try and change people].


To activate others, you must first activate yourself. To get enthusiasm about anything - people, places, things - dig into it deeper... Just dig in deeper and you dig up interest.

In everything you do, life it up. Enthusiasm, or lack of it, shows though in everything you do and say... life up your smiles.. your thank you ... your talk.. 

Don't blame others when you receive a setback... instead of sulking or quitting in a huff, [reason] things out. Ask yourself, "What could I do to make myself deserving of the next opportunity?" Don't berate yourself. Plan to win next time.

Spread good news. Broadcasting good news activates you, makes you feel better. Broadcasting good news makes other people feel better too.

Make people feel important

Practice appreciation by letting others know how you depend on them. An earnest "Jim, I don't know what we'd do without you" type of remark.."

Practice appreciation with honest personalised compliments.. compliment people on .. big accomplishments .. [and] little things: their appearance, the way they do their routine work, their ideas, their loyal efforts.. [write] personal notes, make a special phone call or special trip to see them. 

Call people by name.. it gives everyone a boost to be addressed by name. 

Don't hog glory, invest it instead.. pass praise down on to your subordinates, where it will encourage still greater performance. When you share praise, your subordinates know you sincerely appreciate their value. 

Grow the "service first" attitude, and watch money take care of itself. Make it a rule in everything you do: give people more than they expect to get.

When you help others feel important, you help yourself feel important too.

Family time

I've worked out a schedule that enables me to give attention to my family as well as to my work. From 7.30 to 8.30 every evening, I devote my time to my two young children. I play games with them, read them stories, draw, answer questions - anything they want me to do. After an hour with those kids of mine, they're not only satisfied, but I'm 100 fresher. At 8.30 they trot off to bed, and I settle down to work for 2 hours.

At 10.30 I quit working and spend the next hour with my wife. We talk about the kids, her day at work, our plans for the future. This hour, undisturbed by anything is a wonderful way to cap off the day.

I also reserve Sundays for my family. The whole day is theirs. I find my organised program for giving my family the attention it deserves is good not only for them, but also good for me. It gives me new energy.

Concentrate on the biggest qualities in the person [and don't worry about the little things].. do something special for your mate - and do it often.

Be likeable

Learn to remember names... be a comfortable person so there is no strain in being with you. Be an old-shoe kind of individual.. [Be] relaxed easy-going so that things do not ruffle you. Don't be egotistical. Cultivate the quality of being interesting so people will get something of value from their association with you. Study to get the "scratchy" elements out of your personality, even those which you may be unconscious.. heal misunderstanding. Drain off your grievances. Practice liking people until you learn to do so genuinely. Never miss an opportunity to say a word of congratulation upon anyone's achievement, or express sympathy in sorrow or disappointment.

Like people

No person is all good and no person is all bad.. now if we let our thinking go uncontrolled, we can find much to dislike in almost anyone. By the same token, if we manage our thinking properly.. we can find many qualities to like and admire in the same person. Thoughts breed like thoughts. There is real danger if you listen to negative comments about another person, you too will go negative toward that person. [excuse yourself or change the subject]. [If you have negative thoughts about someone] .. say stop... all you must do is think of one positive quality about the individual. In true chain reaction style, this one thought will lead to another and another. And you will be glad.

Get the Action Habit

Action feeds and strengthens confidence; inaction in all forms feeds fear. To fight fear, act. To increase fear - wait, put off, postpone.. Dread making a certain phone call? Make it, and dread disappears. Put if off, and it will get harder and harder to make.

The test of a successful person is not an ability to eliminate all problems before they arise, but to meet and work out difficulties when they do arise. We must be willing to make an intelligent compromise with perfection lest we wait forever before taking action.

Rather than wait for the spirit to move you, sit down and move your spirit. When you want to think, start writing or doodling or diagramming.

Seize the initiative. Be  a crusader. Pick up the ball and run. Be a volunteer. Show that you have the ability and ambition to do.

Turn Defeat into Victory

Study setbacks to pave your way to success. When you lose, learn and then go on to win next time. Research each setback. 

Remember, there is a good side in every situation. Find it. See the good side, and whip discouragement.

Blend persistence with experimentation. Stay with your goal but don't beat your head against a stone wall. Try new approaches. Experiment.


The quickest way to the end is to retire and do nothing. With nothing to live for, no goals, people waste away fast... No medecine in the world .. is as powerful in bringing about long life as is the desire to do something.

The only way to get full power ... is to do what you want to do... gain energy, enthusiasm, mental zip and even better health.

Get a clear fix on where you want to go. Create an image of yourself 10 years from now.

Set goals to get things done.. Progress is made one step at a time.. [Giving up smoking] An hour is easy; forever is difficult. When the hour is up, the smoker simply renews his resolution not to smoke for another hour. Later.. the period is extended to two hours, later to a day. Eventually the goal is won.

On occasion all of us have woken on a Saturday morning with no plans, no agenda.. On days like that we accomplish next to nothing. We aimlessly drift through the day, glad when it's finally over. But when we face the day with plan, we get things done.

Will this help take me where I want to go? If the answer is no, back off; if yes, press ahead.. Prepare to take detours in your stride.


No matter what you do and regardless of your occupation, higher status, higher pay come from one thing: increasing the quality and quantity of your output.. Think, "I can do better" ..[and] ways to do better will appear.

Giving feedback

First I talk to them privately. Second, I praise them for what they are doing well, Third, I point out the one thing at the moment that they could do better and I help them find the way. Fourth I praise them again on their good points. When they walk out of this office, they have been reminded that they are not only pretty good, they can be even better.


Trade minds with the people you want to influence.. "What would I think of this if I exchanged places with the other person?"

Apply the "be-human" rule.. show that you put other people first.. give other people the kind of treatment you like to receive. Ask yourself, "What is the human way to handle this?" Show interest in your subordinates' off-the-job accomplishments. Treat everyone with dignity. Remind yourself that the primary purpose in life is to enjoy it. [Go beyond the call of duty]. Whoever is under a man's power is under his protection, too.

Ask yourself what kind of club, community, school.. would it be if everyone it in acted like you. Think, talk, act, live the way you want your subordinates to .. and they will. Praise your subordinates to your supervisor by putting in plugs for them at every opportunity.

Push for progress.. think improvement in everything you do. Think high standards in everything you do.

Managed solitude pays off. Spend some time alone every day just for thinking [30min or more].

In the words of Publilius Syrus: A wise man will be master of his mind, a fool will be its slave.

Some great questions to ask yourself

Do I think progressively towards my work? Do I appraise my work with the "how can we do it better?" attitude?

Do I praise my company, the people in it, and the products it sells at every possible opportunity?

Are my personal standards with reference to the quantity and quality of my output higher now than three or six months ago?

Am I setting an excellent example for my subordinates, associates, and others I work with?

Is my family happier today than it was three or six months ago?

Am I following a plan to improve my family's standard of living?

Does my family have an ample variety of stimulating activities outside the home?

Do I set an example of "a progressive", a supporter of progress, for my children?

Can I honestly say I am a more valuable person today than three or six months ago?

Am I following an organised self-improvement program to increase my value to others?

Do I have forward-looking goals for at least five years in the future?

Am I a booster in every organisation or group to which I belong?

Have I done anything in the past six months that I honestly feel has improved my community..?

Do I boost worthwhile community projects rather than object, criticise, or complain?

Have I ever taken the lead in bringing about some worth-while improvement in my community?

Do I speak well of my neighbours and fellow citizens?

Fixing ‘Invalid query parameters: invalid %-encoding’ in a Rails App

Sometimes users manually edit query strings in the address bar, and make requests that have invalid encodings. Unfortunately Rails does not handle this neatly and the exception bubbles up. Eg,

ActionView::Template::Error: Invalid query parameters: invalid %-encoding (%2Fsearch%2Fall%Forder%3Ddescending%26page%3D5%26sort%3Dcreated_at)

/rack/lib/rack/utils.rb:127:in `rescue in parse_nested_query'

[Note: This was with Passenger, which passed the request through to the app – your mileage may vary with other servers]

In the case of my app, these corrupted query strings are not that important, but users are receiving 500 server error pages. Sometimes they end up with a bad query string URL cached in browser history, so they keep going back to it rather than to the home page.

A simple solution, that gives a good user experience for my app, is to simply drop the query string on a request completely if it has invalid encoding. See my implementation using Rack middleware below:

Doing a Website Re-design or new look

Having recently being updating the look of RiskAssess, I thought I’d share a few important things to remember.

About 4.5% of people are colour blind. This means a lot of your users! Make sure your site has sufficient contrast in the colour choices. Great tools are available online to help:

Stage the changes as much as possible. Do them incrementally rather than all at once. That way people have time to get used to them, and you have the usual incremental software development benefits like earlier releases with lower risk of bugs.

If you have an established user base, make sure the new design is recognisably connected with the old design, so people don’t feel it’s all changed.

Test on the hardware your users use, and design for it. A design with fancy fonts and subtle colours might look good on a big Retina iMac, but how will it look on old low res LCDs and small netbook/laptop screens?

What are the demographics of your users? If they skew older, then heavier fonts, more contrast etc may be vital for readability.

As always, test on target browsers, screen sizes, mobile, tablet etc to ensure all your users have a good experience.

Ask some of your users for feedback! Yes, really! If you’ve got an established site, you really need to make sure you will be delighting people, not annoying them with a new look. Even if it’s only CSS changes, there’s likely a lot you may not have thought of that your users will spot right away that you’ll want to take on board.

Warn all your users that the change will be happening. Provide screenshots, explain why the new look is better for them, and give everyone the chance to check it out and give feedback and get ready for the change.

Encourage people to give feedback once the new design goes live. Take it on board humbly (even if it hurts), and react quickly to fix any accidental losses of functionality or oversights. Much better if users tell you what they don’t like so you can fix it.

Talk: Introducing Elixir

I’ll be speaking at the Sydney ALT.NET user group next Tues (27 Feb).

Yet another language!?? But wait, Elixir is way cool! Imagine the speed and concurrency of Erlang (it’s build on the Erlang VM), the neatness of functional programming (but only when you want it) and the sexy expressive style of Ruby, all rolled into one attractive language. And, yeah, it has a good web framework too.

Tues 27 Feb from 6pm at ThoughtWorks Sydney office
NOTE NEW LOCATION: 50 Carrington Street, Level 10

RSVP on Meetup (for pizza and beer!)

Check out talk material on Github (slides in VIM 🙂 )

And a you can watch a recording of the talk.

Rails serving big password protected files – Capistrano Rsync & X-Sendfile

Say you’ve got a few app servers, and you want to serve up some largish files from your rails app (eg, pdfs) behind a login screen. Well, you could put them on s3 and redirect the user to s3 with expiring links. However, this would mean the eventual URL the user gets in their browser is going to be a s3 URL that expires in an ugly way with an XML error when the link expires. And if the link (copied from the address bar) is shared around, it’ll work for non-authorised people for a little while. Then when the s3 link expires, the receivers of the link will never get to see your site/product (maybe they might want to register/subscribe), instead they’ll just get a yucky s3 API looking error in XML, and nowhere to go.

Well, where could we put the files? How about using the file system on the app servers?

Two things to solve.. how to efficiently ship the files to the app servers, and how to serve them without tying up expensive Ruby processes.

1. Shipping the files to the app servers with Capistrano and Rsync

If you’re using docker or similar, you might want to bake the files into the images, if there aren’t too huge.

Assuming you’ve got a more classic style of deploy though..

Welcome old friends Capistrano and Rsync. Using Rsync we can ensure we minimise time / data sending files using binary diffs. We can also do the file transfers simultaneously to app app servers using cap. Here’s the tasks I put together. The deploy_pdfs task will even set up the shared directory for us.

We’re sticking the files into the ‘shared/pdfs’ directory created by capistrano on the app servers. Locally, we have them sitting in a ‘pdfs’ directory in the root of the rails app. This might seem inconsistent (and it is), but the reason is due to limitations/security restrictions with X-Sendfile.

2. Serving the files with X-Sendfile/Apache to let Rails get on with other things

So Rails provides a helpful send_file method. Awesome! Just password protect a controller action and then send_file away! Oh but wait, that will tie up our expensive/heavy ruby processes sending files. Fine for Dev, but not so great for production. The good news is we can hand this work off to our lightweight Apache/nginx processes. Since I use Apache/Ubuntu, that’s what I’ll cover here, but the nginx setup is similar. Using the X-Sendfile header, our rails app can tell the web server a path to a file to send to the client.

How to set up Apache & Rails for X-Sendfile

Ok let’s get Apache rolling:

You need to whitelist the path that files can be sent from, and it can’t be a soft link. It needs to be an absolute path on disk. Hence we are using the ‘shared’ directory capistrano creates, rather than a soft linked directory in ‘current’. X-Sendfile header itself lets you send files without a path (just looks for the files in the whitelisted path), but unfortunately we can’t use this as Rails send_file checks the path exists and raises if it can’t find the file.

In your rails app in production.rb add:

  # Hand off to apache to send big files
  config.action_dispatch.x_sendfile_header = 'X-Sendfile'

In development you probably don’t need this since you won’t be using a server that supports x_sendfile. Without this config, rails will just read the file on disk and send it itself.

In a controller somewhere, just use send_file to hand off to Apache. You’ll need to specify the absolute path to the file in the ‘shared’ directory. I’d suggest putting the path to the shared directory in an environment variable or config file (however you do this usually for your app per environment), and then just append the relevant filename on to it. Also, remember to validate the requested filename (I use a whitelist of filenames to be sure), to avoid the possibility of malicious requests getting sent private files they shouldn’t from elsewhere on disk.

Page 1 of 19

Powered by WordPress & Theme by Anders Norén