Qugstart Blog | Ruby, Rails, Nerdy, Life

CAT | Ruby and Rails

Just installed Redmine for our project management / code tracking with Git. I must say it’s pretty nice being able to setup multiple projects easily (unlike Trac). The system is designed fairly well with a very user-friendly interface. And plus, it’s Rails !!

Anyways… too much on Redmine! This post is to describe how to setup a Git repository for use with Redmine.

The important thing to note is that the Git repository MUST BE on the same server as your Redmine setup. So, you probably need to have access to that server!
This was not ideal for me because our Git server does not have Ruby/Rails, etc. installed on it. That’s okay! You just need to clone the repository as a BARE repository on the same machine that your Redmine app is running on. Let’s see how:

Pick a place on your Redmine app server to house all your bare Git repos.
I will choose “/var/local/git_copies” for my example.

Note: Make sure that your permissions allow for your web-user to access these Git repos. My web-user is ‘build’.

# Change to build user (see above)
$ su - build
# Create the directory
$ mkdir /var/local/git_copies

Now, create your git clone as a bare repository clone.
Note: a bare repository will not have your actual files. It will just contain the standard git folders

# Goto your git_copies directory
$ cd /var/local/git_copies
# Make a bare clone of the repo
$ git clone --bare ssh://git@reposerver/usr/local/git_root/foo-project.git

Change into your project and configure remote branch tracking for your local copy.

$ cd foo-project.git
$ git remote add origin ssh://git@reposerver/usr/local/git_root/foo-project.git

Now, normally you want to sync up the repo, but you cannot do a normal git fetch && git merge into a bare repo.
Instead, do a fetch and reset the HEAD to point to the remote branch commit. You need the ‘–soft’ flag or else you will see errors!

$ git fetch origin
$ git reset --soft refs/remotes/origin/master

Now, remember you need to manually sync your new Git repo to have the changes appear on Redmine. A better idea would be to create a cronjob that does this automatically. Especially with more than 1 repository, automating this process will save much time. Here’s the basic idea:

# Add the following to your crontab
*/30 * * * * cd /var/local/git_copies/foo-project.git && git fetch origin && git reset --soft refs/remotes/origin/master > /dev/null

Instead of doing many times in your crontab, maybe it would be easier to setup a bash script to run:

#!/bin/bash
# Not tested! Use at your own risk, and change your GIT_ROOT and "*.git" to fit your setup.
GIT_ROOT=/var/local/git_copies

cd $GIT_ROOT
ls *.git | while read repo; do
  cd $repo && git fetch origin && git reset --soft refs/remotes/origin/master > /dev/null && cd $GIT_ROOT
done

Then, just add this one script to your crontab and have it run every N minutes as you desire!

This site is helpful and worth checking out as well:
Reference on synchronizing 2 git repositories

That’s it, let me know how it goes!

, , Hide

So you have some files or a new Rails application, and you want to add this to a new shared remote Git repository. (I’m assuming you have access to your server and are setting up a remote repo over ssh.)

I know I can never remember how to do it, so here’s a post for me and hopefully you!

Create a local Git repository in your application for your local files.

#On local machine
cd foo_project
git init
git add *
git commit -m "My initial commit message"

Now, create the repository on your Git server. All of my git repositories are owned by a user git and located at /usr/local/git_root/. You can change these things accordingly to match your server setup.

#On remote machine (Git remote repository)
sudo su - git
cd /usr/local/git_root/

Create your new project git repo as a bare Git repository

mkdir foo-project.git
cd foo-project.git/
git --bare init

Make sure permissions are set properly. These are common options I use for my shared repositories

git config core.sharedrepository 1
git config receive.denyNonFastforwards true
find objects -type d -exec chmod 02770 {} \;
#The core.sharedrepository flag tells git to keep everything group readable and writable.
#The receive.denyNonFastforwards flag makes sure that merges can't happen when you push to the repo. You have to do the merges on your local machine, and then push the result.

Now, go back to your local repository, and add the newly created remote repository so it tracks from the remote repository (origin).

#On local machine, in your git project
git remote add origin ssh://git@example.com:2227/usr/local/git_root/foo_project.git
git push origin master

Now, to ensure that your local branch is tracking when you do a fetch, you need to use -f option to force a new local branch to be created even though it already exists.

#Switch to origin/master so you don't get any error about "fatal: Cannot force update the current branch."
git checkout origin/master
#Create the local "master" branch that is tracking the "origin/master" branch
git branch -f master origin/master
#Switch back to your "master" branch
git checkout master

There you have it. You should be able to push changes to origin and fetch changes to your local copy!

, , , Hide

It sounds easy, but I tried a lot of things before finding the solution I used.

I tried using:

`host 66.249.67.49` or
`nslookup 66.249.67.49`

These were fine, but it seems a bit hacky to use the shell. Also, it would require some sort of parsing to get the hostname that I want.

Browsing the web, I found a couple solutions that almost worked.

s = Socket.getaddrinfo('66.249.67.49',nil)
hostname = s[0][2]

This solution worked in IRB, worked in console, but for some reason would not work when I was running my mongrel server and trying to perform the exact same method call from a web-browser. ( I still don’t know why).

Digging around a bit more, I came up with this simple solution:

host = Resolv.new.getname('66.249.67.49')

Is it really that easy?? Give it a shot and let me know your thoughts !!

, , , , , , , , Hide

In a recent Rails project, I was trying to figure out how to allow users to view any email’s “Date:” header in his/her own timezone. Here’s a short explanation about how I accomplished this using Ruby’s TZInfo gem!

Purpose:
If an email was sent out from New York, the header would contain a Date string like the following (notice the offset):

Fri, 24 Oct 2008 18:35:07 -0400 (EST)

Now, it makes sense for a user in Los Angeles to be able to view the Date/time as 15:35 pm, while a user in New York City should be able to view it as 18:35pm.

Here’s what I did.

Of course, I installed TZinfo first:

%>sudo gem install tzinfo

Each user should have a timezone associated. Try the following method for a list of timezones in US:

 >>TZInfo::Country.get('US').zone_identifiers

Let’s take two users as an example. We have lakersfan is on the west coast, and knicksfan is on the east coast:

>> lakersfan.timezone = TZInfo::Timezone.get('America/Los_Angeles')
>> knicksfan.timezone = TZInfo::Timezone.get('America/New_York')

Given a date/time string like the following (could be taken from the email header or elsewhere):

>>timestring = "Fri, 24 Oct 2008 18:35:07 -0400 (EST)"

first call Time.parse(timestring) which will create a time object with offset info. **Note that this is different from calling timestring.to_time (which will disregard offset info and store the Time object as UTC).

>> timestring = "Fri, 24 Oct 2008 18:35:07 -0400 (EST)"
>> Time.parse(timestring)
=> Fri Oct 24 18:35:07 -0400 2008 #Stores the Time object with offset
>> timestring.to_time
=> Fri Oct 24 18:35:07 UTC 2008 #Incorrect - the offset is ignored

Now, once you’ve got the Time object in proper format (with offset), you can just call the ‘utc’ method to convert the time into UTC format:

>> utctime = Time.parse(timestring).utc

Lastly, once the time is in UTC format, you can use any user’s timezone object to call ‘utc_to_local’ method and convert that utc time into the user’s timezone!

>> lakersfan.tz.utc_to_local(utctime)
=> Fri Oct 24 15:35:07 UTC 2008
>> knicksfan.tz.utc_to_local(utctime)
=> Fri Oct 24 18:35:07 UTC 2008

Voila! Lakers fan (west coast) sees the time as 15:35, while the Knicks fan (on east coast) sees the time as 18:35 – the same as the email header.

, , , Hide

After spending a good couple hours + trying to figure out why my functional tests were failing when working with cookies, I came across a couple great resources for rails + testing cookies.

Here’s a good basic explanation of using cookies on Pluit solutions website. Then I came across the Robby on Rails blog that led me to understand (and misunderstand) the asssert_cookie plugin.

However, I was still having a lot of trouble figuring out why I was setting my @request.cookies['foo'] in my functional test, but later (in the same test method) checking for cookies['foo'] and not finding anything in the cookie jar!

I then tried setting cookies['foo'] in my controller test, but found that when I did a check for the existance of that cookie in my actual controller code, it was not there! I thought that I should be able to set up a cookie in a test and have my controller recognize it. This is true, however, not by setting cookies['foo'].

So… I opened up a blank controller and functional test and began testing out different combinations to find out exactly what was going on. I hope this little example helps to save someone else’s precious time. Here it goes…

This is the controller I will be referring to for my example tests:

========= controller
  def foo
    if cookies['oreo'] == 'yesitis'
      @info =  "Oreos are tasty"
    else
      @info = "Only Milk here"
    end
     render :nothing => true
  end

1. In your functional test, do not test for presence of a cookie after a get request to a particular action, unless the action you are testing specifically sets up that cookie. In other words, if your controller action is not setting the cookie, do not test for that cookie’s presence.

In the above controller, no cookies are ever being set. Therefore, testing for the presence of a cookie after the get request (in a test method) will return false.

In other words, the cookie is recognized by the controller, however, notice that cookies['oreo'] is nil after the get request:

  def test_oreo
    @request.cookies['oreo'] = CGI::Cookie.new("oreo", "yesitis")
    get :foo
    assert_equal({}, @response.cookies)
    assert_equal(nil, cookies['oreo'])
    #Assert cookie will be nil here so do not test it:
    #assert_cookie :o reo
    puts assigns['info'] #"Oreos are tasty"
  end

Note:
1. After your get request, the @response.cookies will be empty
2. After your get request, cookies['oreo'] will still be nil.
3. After your get request, you cannot use assert_cookie because it will be nil

2. If your controller action expects a cookie to be there, you cannot set it up in your functional test by assigning:

  • cookies['oreo'] = true (incorrect) OR
  • @request.cookies['oreo'] = true (incorrect)
  • The only way to set up your cookie for a subsequent request, so that your controller will see it is:

    • @request.cookies['oreo'] = CGI::Cookies.new(’oreo’, true) (correct)
      #INCORRECT
      def test_oreo
        cookies['oreo'] = 'yesitis'
        get :foo
        puts assigns['info'] #"Only Milk here"
      end
    
    #INCORRECT
      def test_oreo
        cookies['oreo'] =  CGI::Cookie.new("oreo", "yesitis")
        get :foo
        puts assigns['info'] #"Only Milk here"
      end
    
    #INCORRECT
      def test_oreo
        @request.cookies['oreo'] = 'yesitis'
        get :foo
        puts assigns['info'] #"Only Milk here"
      end

    The only way to set up your cookie in a functional test and have your controller recognize it.

    #CORRECT
      def test_oreo
        @request.cookies['oreo'] = CGI::Cookie.new("oreo", "yesitis")
        get :foo
        puts assigns['info'] # Will print out "Oreos are tasty"
      end

    3. Avoid the temptation to use symbols in your tests with cookies.

    • cookies[:foo] -> BAD
    • cookies['foo'] -> GOOD

    4. Only use assert_cookie (plugin: see above for link) if you are testing that the controller action is creating a new cookie.

    5. If you are testing multiple actions and redirects from one action to another action in the same controller, you do not need to set the cookie before each action, explicitly. The cookie will remain in the @request object that you created in the setup method of your test.

    EXAMPLE TWO (Testing persistent cookies)
    You DONT set up the cookie for each subsequent request.
    It does “persist” throughout your test-method because you are
    using the same @request object!!

    ========controller
      def foo
        if cookies['oreo'] == 'yesitis'
    	puts "Foo has OREO"
        else
     	puts "Foo has NOTHING"
        end
          redirect_to :action => :bar
          return true
      end
    
      def bar
        if cookies['oreo'] == 'yesitis'
          puts "Oreos are tasty"
        else
          puts "Only Milk here"
        end
         render :nothing => true
      end
    
    ========= Test
      def test_oreo
       @request.cookies['oreo'] =  CGI::Cookie.new('oreo', "yesitis")
        get :foo    #OUTPUTS: Foo has OREO
    
        # COOKIE PERSISTS HERE!!
        # Don't need to explicitly set cookie again in the
        # @request.cookies object
        get :bar   #OUTPUTS: Oreos are tasty
      end

    , , , , , , , , Hide

    Find it!

    Theme Design by devolux.org
    To top