Rails Cache has unexpected side effects

Posted on Updated on

Unexpected
Unexpected

While working on a rails 4 site I noticed something strange. If I cache an ActiveRecord object and retrieve it twice the objects will have the same object id.

Here’s an example:

def get_data_no_marshal
cache_key = 'Some random cache key'
result = Rails.cache.fetch(cache_key, race_condition_ttl: 10) do
# Load resource and pre-load associations
Survey.includes(:survey_meta,
:q_groups=>[
:q_questions=>[
:q_answers,
:q_attributes
]
]).where(:sid=>sid.to_i).first
end
end
result_1 = get_data_no_marshal()
result_2 = get_data_no_marshal()
puts result1.object_id
puts result2.object_id
# And there you have it. When I run this my objects will have the same object_id,
# which is unfortunate, because the plan is to alter them and have them return
# different results. The solution I came up with was Marshal!
def get_data_and_marshal
cache_key = 'Some random cache key'
result = Rails.cache.fetch(cache_key, race_condition_ttl: 10) do
# Load resource and pre-load associations
Survey.includes(:survey_meta,
:q_groups=>[
:q_questions=>[
:q_answers,
:q_attributes
]
]).where(:sid=>sid.to_i).first
end
result = Marshal.load(Marshal.dump(result))
end
result_1 = get_data_and_marshal()
result_2 = get_data_and_marshal()
puts result1.object_id
puts result2.object_id
#It adds a little bit of overhead, but it fixed the problem for me.

Bypassing ActiveRecord::DangerousAttributeError

Posted on Updated on

In Rails 4 if you try to access a Model with a column name that is a reserved word you are going to receive the error “ActiveRecord::DangerousAttributeError”

The most common solution I have seen is to use the ‘safe_attributes’ gem, unfortunately it has not been updated in 2 years, and I have read reports of people saying it is not compatible with Rails 4.

Here is the quick and dirty hack that I put together for PostgreSQL to bypass the error and allow active_record to access the column by a different name:

class MyModel < ActiveRecord::Base
    # Manually select all of the columns in the table
    default_scope{
        select('col1, col2, attribute as xattribute, col3, col4')}
end

# Reach inside of model
# and change the bad column name
MyModel.columns.find{|col|col.name=='attribute'}.
    instance_variable_set(:@name, 'xattribute')

Notice:

This code may make your hair fall out or kill your cat; coder discretion is advised.

Particularly, you should be aware that certain features of ActiveRecord may cause this configuration to throw an error. One example would be:


MyModel.all.count

ActiveRecord::StatementInvalid: PG::SyntaxError: ERROR:

As long as you are aware of this, and you absolutely *must* be able to access the data, its not a huge price to pay.

Ruby Exception Classes Cheat Sheet

Posted on Updated on

I’ve seen several sites that describe the error classes that come in the standard library, but no where that gave a concise description on when to use them.

Rub Docs:

The built-in subclasses of Exception are:

  • NoMemoryError
  • ScriptErrorScriptError is the superclass for errors raised when a script can not be executed because of a LoadError, NotImplementedError or a SyntaxError. Note these type of ScriptErrors are not StandardError and will not be rescued unless it is specified explicitly (or its ancestor Exception).
    • LoadError: Raised when a file required (a Ruby script, extension library, …) fails to load.
    • NotImplementedError: Raised when a feature is not implemented on the current platform. For example, methods depending on the fsync or fork system calls may raise this exception if the underlying operating system or Ruby runtime does not support them.
    • SyntaxError: Raised when encountering Ruby code with an invalid syntax.
  • SignalException: Raised when a signal is received.
    • Interrupt: Raised with the interrupt signal is received, typically because the user pressed on Control-C (on most posix platforms). As such, it is a subclass of SignalException.
  • StandardError — default for rescue: The most standard error types are subclasses of StandardError. A rescue clause without an explicit Exception class will rescue all StandardErrors (and only those).
    • ArgumentError: Raised when the arguments are wrong and there isn’t a more specific exception
      • Ex: passing the wrong number of arguments
      • Ex: passing an argument that is not acceptable:
    • IndexError: Raised when the given index is invalid.
    • IOError: Raised when an IO operation fails.
      • EOFError: Raised by some IO operations when reaching the end of file. Many IO methods exist in two forms, one that returns nil when the end of file is reached, the other raises EOFError EOFErrorEOFError is a subclass of IOError.
    • LocalJumpError: Raised when Ruby can’t yield as requested.
    • NameError: Raised when a given name is invalid or undefined.
      • NoMethodError: Raised when a method is called on a receiver which doesn’t have it defined and also fails to respond with method_missing.
    • RangeError: Raised when a given numerical value is out of range.
      • FloatDomainError: Raised when attempting to convert special float values (in particular infinite orNaN) to numerical classes which don’t support them.
    • RegexpError: Raised when given an invalid regexp expression.
    • RuntimeError — default for raise: A generic error class raised when an invalid operation is attempted.
    • SecurityError: Raised when attempting a potential unsafe operation, typically when the $SAFE level is raised above 0
    • SystemCallError is the base class for all low-level platform-dependent errors.The errors available on the current platform are subclasses of SystemCallError and are defined in the Errno module.
    • SystemStackError: Raised in case of a stack overflow
    • ThreadError: Raised when an invalid operation is attempted on a thread.
    • TypeError: Raised when encountering an object that is not of the expected type
    • ZeroDivisionError: Raised when attempting to divide an integer by 0.
  • SystemExit: Raised by exit to initiate the termination of the script.
  • fatal – impossible to rescue

LimeSurvey on Ubuntu 14.04

Posted on Updated on

Recently I tried to run LimeSurvey on Ubuntu 14.04, but ran into cryptic errors. First I installed the dependencies: (with a postgres database)

sudo apt-get install apache2 php5 php5-pgsql php5-imap php5-gd php5-ldap

After everything was installed and ready to go I copied my limeSurvey files over to /var/www/html/surveys. When I went to http://localhost/surveys though LimeSurvey completely skipped the installation step and threw an error because the database did not exist. Feeling adventurous I manually created the database and grabbed some popcorn to see what would happen. It threw this error next:

The table "{{settings_global}}" for active record class "SettingGlobal" cannot be found in the database.

If I tried to run the Yii php console inside of LimeSurvey I ran into this error:

Uncaught exception 'CException' with message 'Application base path "protected" is not a valid directory.

The hint is right there, Application base path “protected”. It has something to do with either .htacces or FollowSymLinks. I need to figure out which one caused the issue at some point.

Long story short all of this was caused by something completely unrelated to LimeSurvey and the database. Ubuntu has changed the default Apache configuration. The solution that worked for me was to make sure that .htaccess files are enabled and FollowSymLinks is enabled in apache:

In /etc/apache2/sites-enabled/000-default.conf


    Options FollowSymLinks
    AllowOverride None


 
    Options FollowSymLinks MultiViews 
    AllowOverride All 
    Order allow,deny 
    allow from all 


It seems that the LimeSurvey Installation process will fail horribly, ideally it could throw an error message and give the user a hint at what is going on.

Gist

Posted on Updated on

Here’s a quick little app I wrote to back up our PostgreSQL databases.

https://gist.github.com/hattwj/11405978

Fun with port forwarding on Win7

Posted on Updated on

I tried for several hours to figure out the best way to enable port forwarding in Windows7. I normally use Linux, so I was admittedly a little out of my comfort zone, but you wouldn’t think that something simple like port forwarding would be a problem. It is. Most all of my google-fu returned results related to setting up port forwarding on a router (from a computer that just happens to be running windows 7). How is that even helpful?

Then I learned about the netsh.exe program. Ugg! I have no doubt that it is possible to set up a simple port forward with this tool, but an hour and a half later I hadn’t gotten it figured out. I’m not quite sure what the problem was, but I could not get it to forward the data to a different host.

Time to resort to open software. I installed Cygwin in windows and then installed socat. Socat is possibly related to the (in)famous netcat utility and really simplifies things.

#Cygwin forward connections in windows to our Linux VM
#send tcp connections at port 5500 to our vm on port 22
socat TCP-LISTEN:5500,fork TCP:my_nat_blocked_vm:22

# Create a reverse tunnel
# Connect to local machine and allow access to port 443
ssh -R 4499:localhost:443 user@my_local_address:5500

# Access restricted port on local machine
wget https://localhost:4499/

SSH append key to known_hosts

Posted on Updated on

A system I work on has rotating backup servers with different ssh host keys, so depending on the time of year etc… when I SSH in to the address I could be served with one of several servers. Essentially I need to have a single host with multiple key entries in my known_hosts file.
ssh-keyscan -t rsa example.com >> ~/.ssh/known_hosts

CopyPasta: Connectbot localhost disconnects

Posted on Updated on

I use LinuxOnAndroid on my Android phone and use it for a shamefully large amount of my personal programming projects. Its pretty handy seeing as I spend around 2 hours on the bus a day, so I can write code on the go. LinuxOnAndroid allows me to connect to my phone via ssh and from there I am pretty much at home with vim and the rest of my command line development tools. The problem though is that my favorite ssh client for Android has a pretty annoying bug for users who happen to be connecting to localhost.

The gist of it is that if you lose internet connectivity or transition between connection methods (wifi -> 3g->4g etc…) Connectbot will cut your connection to localhost.

The full details of the bug are here:
https://code.google.com/p/connectbot/issues/detail?id=417

The generous folks that posted this bug also posted a FIX for the source code… 2 years ago. The problem still has not been fixed, so I set about trying to compile the application with this patch last night.

First, let me admit that I am not an Android or JAVA developer so it took me a while to get everything compiled. Oh and then signed (what a pain!). But if anyone is interested in using connectbot to connect to localhost on android without hosing their connections here is a link to the compiled and signed apk file.

Download:

connectbot_signed_test.apk

command-not-found has crashed

Posted on Updated on

I had a strange error on an Ubuntu 12.04 vm that a hosting provider set up for me. the message was:

$ llds
Sorry, command-not-found has crashed! Please file a bug report at:
https://bugs.launchpad.net/command-not-found/+filebug
Please include the following information with the report:

After searching around I found that it is likely related to the locale not being properly set which causes the command-not-found python script to break. The fix for the problem was to properly set my locale.

This is not a complete fix, but somehow it worked for me:

sudo locale-gen en_US

Copy Pasta: Install MATE desktop on Ubuntu 12.04 precise

Posted on Updated on

Most sites incorrectly list the repo address for MATE.

I am a unity refugee, I tried it for a while and really wanted to like it. I miss the configurability of GNOME2. I’ve tried gnome3(buggy), KDE (Awesome, but not at stable as I remember gnome2 being), Unity (Wish i could configure things again). Now i am migrating to MATE. Wish me luck.

# BAD URL
#sudo add-apt-repository “deb http://packages.mate-desktop.org/repo/ubuntu precise main”

# Remove repo/ from in between domain name and ubuntu will fix it
sudo add-apt-repository “deb http://packages.mate-desktop.org/ubuntu precise main”

sudo apt-get update
sudo apt-get install mate-archive-keyring
sudo apt-get update
sudo apt-get install mate-core
sudo apt-get install mate-desktop-environment