Rails Cache has unexpected side effects
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
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
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.
The built-in subclasses of Exception are:
- NoMemoryError
- ScriptError: ScriptError is the superclass for errors raised when a script can not be executed because of a
LoadError
,NotImplementedError
or aSyntaxError
. Note these type ofScriptErrors
are notStandardError
and will not be rescued unless it is specified explicitly (or its ancestorException
).- 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
orfork
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
.
- 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
- 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.
- StopIteration: Raised to stop the iteration, in particular by Enumerator#next. It is rescued byKernel#loop.
- IOError: Raised when an IO operation fails.
- 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
.
- NoMethodError: Raised when a method is called on a receiver which doesn’t have it defined and also fails to respond with
- 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.
- FloatDomainError: Raised when attempting to convert special float values (in particular
- 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.
- ArgumentError: Raised when the arguments are wrong and there isn’t a more specific exception
- SystemExit: Raised by
exit
to initiate the termination of the script. - fatal – impossible to rescue
LimeSurvey on Ubuntu 14.04
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
Here’s a quick little app I wrote to back up our PostgreSQL databases.
Fun with port forwarding on Win7
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
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
command-not-found has crashed
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
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