Best ruby-on-rails questions in February 2012

How do RVM and RBENV actually work?

14 votes

I am interested in how RVM and RBEnv actually work. Obviously they swap between different versions of Ruby and gemsets, but how is this achieved? I had assumed they were simply updating symlinks, but having delved into the code (and I must admit my knowledge of Bash is superficial) they appear to be doing more than this.

Short explanation: rbenv works by hooking into your environment's PATH. The concept is simple, but the devil is in the details; full scoop below.

First, rbenv creates shims for all the commands (ruby, irb, rake, gem and so on) across all your installed versions of Ruby. This process is called rehashing. Every time you install a new version of Ruby or install a gem that provides a command, run rbenv rehash to make sure any new commands are shimmed.

These shims live in a single directory (~/.rbenv/shims by default). To use rbenv, you need only add the shims directory to the front of your PATH:

export PATH="$HOME/.rbenv/shims:$PATH"

Then any time you run ruby from the command line, or run a script whose shebang reads #!/usr/bin/env ruby, your operating system will find ~/.rbenv/shims/ruby first and run it instead of any other ruby executable you may have installed.

Each shim is a tiny Bash script that in turn runs rbenv exec. So with rbenv in your path, irb is equivalent to rbenv exec irb, and ruby -e "puts 42" is equivalent to rbenv exec ruby -e "puts 42".

The rbenv exec command figures out what version of Ruby you want to use, then runs the corresponding command for that version. Here's how:

  1. If the RBENV_VERSION environment variable is set, its value determines the version of Ruby to use.
  2. If the current working directory has an .rbenv-version file, its contents are used to set the RBENV_VERSION environment variable.
  3. If there is no .rbenv-version file in the current directory, rbenv searches each parent directory for an .rbenv-version file until it hits the root of your filesystem. If one is found, its contents are used to set the RBENV_VERSION environment variable.
  4. If RBENV_VERSION is still not set, rbenv tries to set it using the contents of the ~/.rbenv/version file.
  5. If no version is specified anywhere, rbenv assumes you want to use the "system" Ruby—i.e. whatever version would be run if rbenv weren't in your path.

(You can set a project-specific Ruby version with the rbenv local command, which creates a .rbenv-version file in the current directory. Similarly, the rbenv global command modifies the ~/.rbenv/version file.)

Armed with an RBENV_VERSION environment variable, rbenv adds ~/.rbenv/versions/$RBENV_VERSION/bin to the front of your PATH, then execs the command and arguments passed to rbenv exec. Voila!

For a thorough look at exactly what happens under the hood, try setting RBENV_DEBUG=1 and running a Ruby command. Every Bash command that rbenv runs will be written to your terminal.


Now, rbenv is just concerned with switching versions, but a thriving ecosystem of plugins will help you do everything from installing Ruby to setting up your environment, managing "gemsets" and even automating bundle exec.

I am not quite sure what IRC support has to do with switching Ruby versions, and rbenv is designed to be simple and understandable enough not to require support. But should you ever need help, the issue tracker and Twitter are just a couple of clicks away.

Disclosure: I am the author of rbenv, ruby-build, and rbenv-vars.

RESTful DCI contexts in Rails

9 votes

I first learned about Data, context, and interaction (DCI) through this blog post. Fascinated by the concept, I endeavored to build it in to my next Rails application. Since DCI works in tandem with MVC, I thought it wouldn't be too hard to make the API RESTful at the same time. So I made a RESTful resource, Report and extend it with various contexts. The way I implemented contexts in Rails was by creating a directory, /app/contexts/, for modules which extend the controller actions. So my reports_controller.rb looks like this:

class ReportsController < ApplicationController
  before_filter :only => :new do |c|
    c.switch_context("submission")
  end

  # GET /reports
  def index
    @context.report_list
  end

  # GET /reports/1
  def show
    @context.display_report
  end

  # GET /reports/new
  def new
    @context.new_report
  end

  # GET /reports/1/edit
  def edit
    @context.edit_report
  end

  # POST /reports
  def create
    @context.create_report
  end

  def update
    @context.update_report
  end

  # DELETE /reports/1
  def destroy
    @context.destroy_report
  end

  protected

  def switch_context(context_name)
    session[:context] = context_name
    context = session[:context].camelize.constantize
    @context ||= self.extend context
  end
end

And in the application_controller.rb I set the context with a before_filter:

class ApplicationController < ActionController::Base
  before_filter :contextualize
  protect_from_forgery

  protected

  # Sets the context of both current_user and self
  # by extending with /app/roles/role_name
  # and /app/contexts/context_name respectively
  def contextualize
    # Extend self (ActionController::Base) with context
    if session[:context]
      context_class = session[:context].camelize.constantize
      if current_user.allowed_contexts.include?(context_class)
        context_class = current_user.context if context_class == Visiting
      else
        context_class = Visiting
      end
    else
      context_class = current_user.context
    end
    @context ||= self.extend context_class
  end
end

Notice I extend current_user with a Role in addition to the controller context.

Here's how it works:

  1. A user logs in.
  2. The user's role is RegisteredUser.
  3. RegisteredUser's default context is Search (as defined in /app/roles/registered_user.rb).
  4. Inside the Search context, the user can only view published reports.
  5. The user presses the "create new report" button and the context is changed to Submission and stored in the current_user's session.
  6. The user then proceeds to submit a report through a multi-step form.
  7. Each time the user saves the report by stepping through the form the /app/contexts/submission.rb context handles the action.

The are several other contexts (review, editorial, etc.) and roles (co-author, editor, etc.).

So far this approach has worked well for the most part. But there is a flaw: when a user opens multiple browser windows and changes contexts in one of them, all of the other windows will be in the wrong context. This could be a problem if the user is in the middle of the multi-step form and then opens a window in the Search context. When he switches back to the form and hits "Next", the controller will perform the action defined by the Search context instead of the Submission context.

There are 2 possible ways around this that I can think of:

  1. Namespace the Report resource with the context name. So the user would visit URL's such as /search/reports and /submission/reports/1. This doesn't seem RESTful to me and I would rather keep the URL's as clean as possible.
  2. Put the context name in a hidden field. This method requires developers to have remember to put the hidden field in every form on the site, and it doesn't work for GET requests.

Are there any other ways around this problem, or better overall implementations?

I know of this project, but it's too limited for our needs.

If you want to allow for multiple contexts then obviously you must put the information which determines the current context in some storage which is not shared between tabs. Sessions, as implemented in Rack/Rails, use cookies, and cookies are shared between tabs.

Just put the context into something, that is not shared. How about a context=viewer URL parameter?

To talk REST I think it is arguable whether or not a resource is the same or not in different contexts. One could argue that a report for a "Visiting" user is different from a report for an "Administering" user. In that case a RESTy approach would probably namespace the requests (which again puts the context into the URL), e.g. /visiting/reports/1 vs /administering/reports/1.

And a third way to put the context into the URL would be to use it as part of the domain name.

AWS::S3::S3Object.url_for - How to do this with the new AWS SDK Gem?

8 votes

I've been using this forever with paperclip and aws-s3:

  def authenticated_url(style = nil, expires_in = 90.minutes)
      AWS::S3::S3Object.url_for(attachment.path(style || attachment.default_style), attachment.bucket_name, :expires_in => expires_in, :use_ssl => true)
  end

The new paperclip uses the AWS-SDK gem, which breaks this giving the error:

undefined method `url_for' for AWS::S3:Class

Anyone know how to get this method to work with the new AWS-SDK gem?

Thanks

To generate a url using the aws-sdk gem you should use the AWS::S3Object#url_for method.
You can access the S3Object instance from a paperclip attachment using #s3_object. The snippet below should resolve your issue.

def authenticated_url(style = nil, expires_in = 90.minutes)
  attachment.s3_object(style).url_for(:read, :secure => true, :expires => expires_in).to_s
end

Adding Additional Headers to Carrierwave for Amazon s3 Encryption

7 votes

In short
In short I want to know if I can send additional headers through a carrierwave and fog connection to Amazon s3?

In depth
I recently found that amazon supports Client and Server side encryption of files. more info » http://docs.amazonwebservices.com/AmazonS3/latest/dev/SSEUsingRESTAPI.html

I'm currently using carrierwave in a rails app to upload files to amazon s3.
For server side encryption amazon asks for a header of x-amz-server-side-encryption=AES256 added to the request.

So I'm looking to figure out how to send additional headers through with my carrierwave and fog.

My thought was that maybe I could use the fog_attribute config line something like the following and maybe that might work but I'm not sure the fog_attribute is for partiular attribute or just a blanket header section.

config.fog_attributes = {'x-amz-server-side-encryption' => 'AES256','Cache-Control'=>'max-age=315576000'}  # optional, defaults to {}

I believe that should actually be correct, note however that I don't believe the server side encryption stuff has been released, so you would need to use edge fog to get this behavior. I hope to do a release soon though and then it should be good to go. If you find that you still can't get it working on edge let me know though and we'll try and see what can be done.

Proper SCSS Asset Structure in Rails

7 votes

So, I have an app/assets/stylesheets/ directory structure that looks something like this:

   |-dialogs
   |-mixins
   |---buttons
   |---gradients
   |---vendor_support
   |---widgets
   |-pages
   |-structure
   |-ui_elements

In each directory, there are multiple sass partials (usually *.css.scss, but one or two *.css.scss.erb).

I might be assuming a lot, but rails SHOULD automatically compile all the files in those directories because of *= require_tree . in application.css, right?

I recently have tried restructuring these files by removing all color variables and placing them in a file in the root app/assets/stylesheets folder (_colors.css.scss). I then created a file in the root app/assets/stylesheets folder called master.css.scss which looks like this:

// Color Palette 
@import "colors";

// Mixins
@import "mixins/buttons/standard_button";
@import "mixins/gradients/table_header_fade";
@import "mixins/vendor_support/rounded_corners";
@import "mixins/vendor_support/rounded_corners_top";
@import "mixins/vendor_support/box_shadow";
@import "mixins/vendor_support/opacity";

I don't really understand how rails handles the order of asset compilation, but it's obviously not in my favor. It appears none of the files realize they have any variables or mixins being imported, and so it throws errors and I can't compile.

Undefined variable: "$dialog_divider_color".
  (in /home/blah/app/assets/stylesheets/dialogs/dialog.css.scss.erb)

Undefined mixin 'rounded_corners'.
  (in /home/blah/app/assets/stylesheets/widgets.css.scss)

The variable $dialog_divider_color is clearly defined in _colors.css.scss, and _master.css.scss is importing colors and all my mixins. But apparently rails didn't get that memo.

Is there some way I can fix these errors, or will I need to resort to putting all my variable definitions back into each individual file, as well as all the mixin imports?

Unfortunately, this guy doesn't seem to think it's possible, but I'm hoping he's wrong. Any thoughts are greatly appreciated.

The problem with CSS is, you do not want to automatically add all files. The order of which your sheets are loaded and processed by the browser is essential. So you will always end up explicitly importing all your css.

For example, lets say you have a reset.css sheet to default all those nasty browser default css styles to something that is the same for all browsers. If you just randomly include this sheet somewhere in your css imports, it will then not only override the browser default styles, but also any styles defined in earlier imports.

This goes the same for your variables and mixins, if they are imported too late, they can not be found by sheets that were imported before them.

I generally use this approach:

  • Rename you application .css to .scss
  • Remove the require tree directives so you application.scss looks like:
/*
*= require_self
*/

Then start including your css. You can either have mulitple files for the includes, like give every directory one, or just all put then in you application.scss file. For example, if you are using twitters bootstrap and a few css sheets of your own, you have to import bootstrap first, because it has a sheet to reset styles. So you add @import "bootstrap/bootstrap.scss"; to your application.scss.

The bootstrap.scss file looks like:

// CSS Reset
@import "reset.scss";

// Core
@import "variables.scss";
@import "mixins.scss";

// Grid system and page structure
@import "scaffolding.scss";

// Styled patterns and elements
@import "type.scss";
@import "forms.scss";
@import "tables.scss";
@import "patterns.scss";

And your application.scss file will end up like:

/*
 *= require_self
*/
@import "bootstrap/bootstrap.scss";
@import "my_model.css.scss";
@import "my_other_model.css.scss";

Because of the order of the imports, you can now use the variables, loaded with @import "variables.scss"; in any other .scss file imported after it. So they can be used in type.scss in the bootstrap folder but also in my_model.css.scss.

How Can I Tell Controller Specs to Use the Signed OAuth Request

7 votes

I am building a 2-Legged OAuth provider for my api. Everything is hooked up properly and I can make signed calls from the rails console. The problem I have is that I am having trouble integrating OAuth into the controller_spec.

Here is an example of a working call on my server:

coneybeare $ rails c test
Loading test environment (Rails 3.2.0)
rails test: main 
>> consumer = OAuth::Consumer.new("one_key", "MyString", :site => [REDACTED])
# => #<OAuth::Consumer:0x007f9d01252268 @key="one_key", @secret="MyString", @options={:signature_method=>"HMAC-SHA1", :request_token_path=>"/oauth/request_token", :authorize_path=>"/oauth/authorize", :access_token_path=>"/oauth/access_token", :proxy=>nil, :scheme=>:header, :http_method=>:post, :oauth_version=>"1.0", :site=>[REDACTED]}>  

ruby: main 
>> req = consumer.create_signed_request(:get, "/api/v1/client_applications.json", nil)
# => #<Net::HTTP::Get GET>  

ruby: main 
>> res = Net::HTTP.start([REDACTED]) {|http| http.request(req) }
# => #<Net::HTTPOK 200 OK readbody=true>  

ruby: main 
>> puts res.body
{"client_applications":[{"id":119059960,"name":"FooBar1","url":"http://test1.com"},{"id":504489040,"name":"FooBar2","url":"http://test2.com"}]}
# => nil  

And here is what I am doing in my controller tests:

require 'oauth/client/action_controller_request'
describe Api::ClientApplicationsController do
  include OAuthControllerSpecHelper
  …
  … 
    it "assigns all client_applications as @client_applications" do
      consumer = OAuth::Consumer.new("one_key", "MyString", :site => [REDACTED])
      ActionController::TestRequest.use_oauth=true
      @request.configure_oauth(consumer)
      @request.apply_oauth!
      puts "request.env['Authorization'] = #{@request.env['Authorization']}"
      get :index, {:api_version => 'v1', :format => :json}
      response.should be_success # Just this for now until I can get authorization, then proper controller testing
    end
end

The output of that test:

request.env['Authorization'] = OAuth oauth_consumer_key="one_key", oauth_nonce="gzAbvBSWyFtIYKfuokMAdu6VnH39EHeXvebbH2qUtE", oauth_signature="juBkJo5K0WLu9mYqHVC3Ar%2FATUs%3D", oauth_signature_method="HMAC-SHA1", oauth_timestamp="1328474800", oauth_version="1.0"
1) Api::ClientApplicationsController GET index assigns all client_applications as @client_applications
   Failure/Error: response.should be_success
     expected success? to return true, got false

And the corresponding server call from the rails log:

Processing by Api::ClientApplicationsController#index as JSON
  Parameters: {"api_version"=>1}
  Rendered text template (0.0ms)
Filter chain halted as #<OAuth::Controllers::ApplicationControllerMethods::Filter:0x007f85a51a8858 @options={:interactive=>false, :strategies=>:two_legged}, @strategies=[:two_legged]> rendered or redirected
Completed 401 Unauthorized in 15ms (Views: 14.1ms | ActiveRecord: 0.0ms)
   (0.2ms)  ROLLBACK

I just can't figure out why it's not working :/ Am I making an obvious mistake?

Turns out that the best way to test my controller was the simplest as well. Instead of trying to sign each test so the controller gets the right information (something that indeed does belong in a request spec not a controller spec), I figured out that I could just give the controller the information it needed manually.

To do this, I simply had to stub 2 methods:

fixtures :client_applications
before(:each) do
  @client_application1 = client_applications(:client_application1)
  Api::ClientApplicationsController::Authenticator.any_instance.stub(:allow?).and_return(true)
  controller.stub(:client_application).and_return(@client_application1)
end

Stubbing the allow? method caused the rack auth to be fooled into thinking it was authenticated. allow? also set the client_application based on the credentials though, so I had to stub that as well. Now that the auth is out of the way, I can test my controller properly.

Best analytics/data visualization library for MongoDB

7 votes

In your opinion, what is the best library to crunch data AND build the web based reports in this context:

  • Data: documents in MongoDB, less than 1M documents, time is a key dimension in the data
  • Framework: Rails (but I'm opened to others)

There are tons of Javascript data visualization libraries, but ideally I'm looking for something that integrates "off-the-shelf" with MongoDB, and that uses modern technologies (HTML5) vs. old (Flash).

If I don't find anything MongoDB specific, I might go with either Google Chart or D3.js, which looks awesome but maybe too "young".

Since you already specified D3.js, i heartily recommend Cube Time series visualization tool kit that built on

  • Mongodb
  • D3
  • Node
  • websocket

Its still in early development. But looks very promising. And one of the cool feature is it packed with websocket, this will be a interesting choice for real time data visualization for web. Have a look

Omniauth Session expires when browser is closed

6 votes

In my rails 3 app I use Omniauth for the user authentication part (fb/twitter).

Actually I follow this:

https://github.com/RailsApps/rails3-mongoid-omniauth

https://github.com/RailsApps/rails3-mongoid-omniauth/wiki/Tutorial

But, when I close the browser session expires and I need to login again. How can I keep the session for returning users?

Any help would be greatly appreciated!

Devise offers this functionality through its Rememberable module. OmniAuth integrates easily with it through the (you'd never guess it) OmniAuth module. It's even mentioned in the second link you posted!

Is there a way to access method arguments in Ruby?

6 votes

New to Ruby and ROR and loving it each day, so here is my question since I have not idea how to google it (and I have tried :) )

we have method

def foo(first_name, last_name, age, sex, is_plumber)
    # some code
    # error happens here
    logger.error "Method has failed, here are all method arguments #{SOMETHING}"    
end

So what I am looking for way to get all arguments passed to method, without listing each one. Since this is Ruby I assume there is a way :) if it was java I would just list them :)

Output would be:

Method has failed, here are all method arguments {"Mario", "Super", 40, true, true}

In Ruby 1.9.2 you can use the parameters method on a method to get the list of parameters for that method. This will return a list of pairs indicating the name of the parameter and whether it is required.

e.g.

If you do

def foo(x, y)
end

then

method(:foo).parameters # => [[:req, :x], [:req, :y]]

You can use the special variable __method__ to get the name of the current method. So within a method the names of its parameters can be obtained via

args = method(__method__).parameters.map { |arg| arg[1] }

You could then display the name and value of each parameter with

logger.error "Method failed with " + args.map { |arg| "#{arg} = #{eval arg}" }.join(', ')

Is there a .NET equivalent to Rails Console?

6 votes

Rails Console is so useful for direct sanity-checking of your model. Is there an ASP.NET MVC equivalent?

Is it possible to mimic Rails Console behaviour using LinqPAD?

awesome - I discovered LinqPAD 4.38.03 (latest beta version) works perfectly well as a Rails Console substitute!

My ASP.NET MVC3 project is based on Entity Framework 4.2 (using the "database first" approach) which Linqpad integrates nicely with. I am able to reference my assembly as a connection and query the model, controller, repositories etc. interactively, just like in Rails Console!

These were my steps

  1. In the Connection manager (to the left) click "Add connection"
  2. click the radio labelled "use a typed data context from your own assembly"
  3. click "Entity Framework dbContext POCO (4.1/4.2)", then "next"
  4. use "browse" to locate the "path to custom assembly" (in your project)
  5. click "choose" to select the dbContext class from your assembly
  6. click "choose" to locate your project config file in "path to application config file"
  7. type an optional connection name, click "next"

Finally, in your query window select your new assembly connection as the "Database" and that's it! You can now work with your assembly interactively.

For example, to inspect and test a controller: (first, in Query Properties, add a reference to System.Web.Mvc)

var controller = MyProject.Controllers.CustomerController();
controller.Index().Dump();

to "post" some data

var customer = new Customer() {name = "Brian"};
controller.Create(customer);

to see your new Customer in the database

Customers.Dump();

or if you have a repository

var repo = new Repository();
repo.GetCustomers().Dump();

Application displays wrong hours and minutes

6 votes

At first: thats not issue of wrong zone. In irb and database everything is fine. Problem occurs when I want to display dates (created_at, updated_at and all defined by myself in every model) in my views. I was trying to set time zone in application.rb and remove time formats from initializers and that didn't solved my issue.

schema information generated by Annotate gem:

#  created_at                      :datetime
#  updated_at                      :datetime
#  publish_at                      :datetime

from irb:

1.9.2-p290 :004 > Time.zone
 => (GMT+00:00) UTC 
1.9.2-p290 :005 > Time.zone.now
 => Fri, 24 Feb 2012 12:14:04 UTC +00:00 
1.9.2-p290 :006 > Time.now
 => 2012-02-24 13:14:07 +0100

Examples:

1.9.2-p290 :007 > Article.last
  Article Load (0.3ms)  SELECT `articles`.* FROM `articles` ORDER BY `articles`.`id` DESC LIMIT 1
 => #<... created_at: "2012-02-24 12:04:24", updated_at: "2012-02-24 12:04:24", publish_at: "2012-02-24 12:04:24"...> 

App displays:

Created_at 2012-02-24 12:02, Updated_at 2012-02-24 12:02, Publish_at 2012-02-24 12:02,

1.9.2-p290 :008 > Article.first
  Article Load (0.5ms)  SELECT `articles`.* FROM `articles` LIMIT 1
=> #<...created_at: "2012-01-30 10:28:07", updated_at: "2012-02-08 17:20:41", publish_at: "2012-02-08 17:20:33"...> 

App displays:

Created_at 2012-01-30 10:01, Updated_at 2012-02-08 17:02, Publish_at 2012-02-08 17:02

this applies whole app (including active_admin)

Sorry for my English ;) Any ideas what is the reason of my problem?

I've got it :) That was silly, but maybe someone will need reminder: App gets format from locale yml file first (thats only way to set date format in active admin; formats from initializers doesn't matter):

time:
  formats:
    default: "%Y-%m-%d"
    short: "%b %d"
    long: "%Y-%m-%d %H:%m"

and ofc minutes in long format should be %M (not %m)

Nightly batch network task on Heroku

5 votes

We're working on a Rails project on Heroku that needs to scrape and process data each night for each user. This requires many Internet accesses per user, and we're hoping to be able to support tens of thousands of users. While there's a fair bit of parsing, calculating, and writing to databases involved, we expect that most of the task's time will be spent waiting on data from the network.

What's the best general approach to doing this task while minimizing both wallclock time and Heroku fees? Obviously either concurrency or async networking will be needed to take advantage of the time spent waiting for the network, but how should we go about it? We're thinking in terms of a database-backed queue with forked worker processes, but that may not be the best approach—or may not even be possible on Heroku.

Heroku supports Delayed Job, I would start there. You can then do the following:

  • create a job class that does the processing for a single user
  • schedule a nightly cron that creates a job for every user in your system
  • auto-scale your workers to accommodate the job queue (workless or similar should be able to do this for you. If not, you may have to roll some custom code.)

You'll need to play with your workers/jobs ratio to figure out the sweet spot for optimizing across db load, wallclock time and heroku costs.

If you're finding that each job spends too much time sitting around waiting for network, take a look at eventmachine. Jobs are just ruby code, so you can play whatever parallelization tricks you want here, Heroku shouldn't limit you in any way.

This setup would be a pretty good baseline to get to as it shouldn't take very long to spin up and you'll probably learn a bit about your work load from it.

You might find out that 1 job/user doesn't make sense, and that you need n jobs per user (one job per property or something). Without knowing your exact usecase it's hard to say up front, that's why I'm assuming a 1-1 mapping.

I should also point out that the new Heroku stack supports queueing systems other than Delayed Job (scroll to bottom).

Rails 3.1 Asset Pipeline not precompiling files from a different directory

5 votes

I currently have a Rails 3.1 app that hosts multiple sites for several different customers. Each site is represented by a model that knows the domain name and path to where the assets are stored, along with some other info.

I've been using app/sites/domain-name as a location for storing assets, views and locales that are specific to a given site, and are running a custom middleware and controller actions for modifying the load path for Sprockets, setting up the view paths and so on.

middleware (loaded using config.middleware.use "Configurator" in application.rb):

class Configurator
  def initialize(app)
    @app = app
  end

  def call(env)
    @env = env
    clear_existing_paths
    prepend_local_assets
    @app.call(env)
  end

  def current_site
    # find site using @env
  end

  def current_site_path
    Rails.root.join('app', 'sites', current_site.path)
  end

  def clear_existing_paths
    paths = Rails.application.assets.paths
    Rails.application.assets.clear_paths
    paths.each do |path|
      next if path.include?("app/sites/")
      Rails.application.assets.append_path path
    end
  end

  def prepend_local_assets
    path = current_site_path.join('assets')
    return unless Dir.exists?(path)

    ['images', 'javascripts', 'misc', 'stylesheets'].each do |subdir|
      asset_dir = path.join(subdir).to_s
      next unless Dir.exists?(asset_dir)
      next if     Rails.application.assets.paths.include? asset_dir

      Rails.application.assets.prepend_path asset_dir
    end
  end
end

Asset bits from application.rb:

module MyApp
  class Application < Rails::Application
    ...
    config.middleware.use "Configurator"

    config.assets.enabled = true 
    config.assets.version = '1.0'
  end
end

And environments/production.rb:

MyApp::Application.configure do
  config.serve_static_assets = false
  config.assets.compress = true
  config.assets.compile = true
  config.assets.digest = true
  # Defaults to Rails.root.join("public/assets")
  # config.assets.manifest = YOUR_PATH
end

The problem is that while this setup works, it prevents me from precompiling the assets that is not shared with the entire app, making them be generated over and over again, from the looks of it.

Is there any way I could tell the precompiler to find the assets located here, and create a version of those as well? Each site has a site.css.scss and a site.js.coffee file that might require other assets inside the site-dir. Would be nice if I could get it precompiled to public/assets/domain-name/site.(js|css), so I could easily set up a separate subdomain for assets down the line when I need to optimize further

Final solution (2012-02-22)

After implementing what was suggested by Brian, I have ended up with

Main stylesheet/javascript stored in app/sites/<sitename>/assets/<shortname>/site.css|js, where sitename is the domain for this site, and shortname is the main part of the domain, with no subdomain or com|org|net|ccTLD.

Modified all views and stylesheets to prepend shortname to my asset paths.

In config/application.rb:

{ "sitename" => "shortname", ... }.each_pair do |domain, short|
  %w{stylesheets javascripts}.each do |dir|
    config.assets.paths << Rails.root.join("app/sites/#{domain}/assets/#{dir}").to_s
  end # Had to specify each dir to make it work
  config.assets.precompile += ["#{short}/site.css", "#{short}/site.js"]
end

When running rake assets:precompile this creates public/assets/shortname filled with all the assets for that site, and public/assets have all the shared assets as well. Works great for my needs.

And since everything ended up in public/assets, I was able to drop the Configurator-middleware, since the default configuration was able to find all the assets

I think the problem is, by adding each site as a path, sprockets only finds the first site.scss

I've tried this using compass, but it should be the same for plain sprockets. I haven't tried your configurator approach, but it looks straightforward to adapt my arrangement to it.

Can you change your app/sites/* to the more standard file arrangement?

./app/assets/javascripts/application.js
./app/assets/stylesheets/screen.css.scss
./app/assets/stylesheets/othersite/screen.css.scss

Change your config/application.rb, and add each of your sites. This will pregenerate all styles, on each of your hosts:

config.assets.precompile += ['screen.css', 'othersite/screen.css']

In your view/layouts/application, you'll need to configure the path to sitename:

= stylesheet_link_tag '[your sitename here]/screen'

After I rake assets:clean and precompile, I see this in public:

./assets/othersite/screen.css
./assets/othersite/screen.css.gz
./assets/screen.css
./assets/screen.css.gz

How to get the total number of pages of the existing pdf in ruby on rails.. any idea?

5 votes

How to get the total number of pages of the existing pdf in rails.. any idea?

You can use pdf-reader (also available as a ruby gem). Usage is as simple as

reader = PDF::Reader.new("somefile.pdf")
puts reader.page_count

Code Editor API (with line number) and code highlighter

5 votes

How to get and open a file file In a Editor with Line Number as Below:

So, my text File could open in editor below and can save my changes on edit.

   ---------------------------------------------------------
 1 |                                                       |
 2 |                                                       |
 3 |                                                       |
 4 |                                                       |
 5 |                                                       |
   ---------------------------------------------------------

Is there any Code Editor(API/Plugin) available online for doing this?

I Used CodeMirror. It's the highlighter used by Google for their API playground.It has given me line number for my code to be editing too.

I've tried it and it works well, and according to their site it works in the following browsers:

  • Firefox 1.5 or higher
  • Internet Explorer 6 or higher
  • Safari 3 or higher
  • Opera 9.52 or higher
  • Chrome

Cheers !

Is String#pluralize idempotent?

5 votes

That is, for any String string, does the following hold?

string.pluralize == string.pluralize.pluralize

pluralize is NOT idempotent. I can prove it by example (courtesy of a personal Facebook posting that hit some language geeks).

"taxi".pluralize
=> "taxis"
"taxis".pluralize
=> "taxes"
"taxi".pluralize.pluralize
=> "taxes"

So "taxi" (the thing that drives you around) to "taxis" (an arrangement or order) to "taxes" (the proper pluralization of "taxis"). I'm sure there are other examples, but they are certainly hard to come by.

Not looking for score or acceptance on this answer, but I couldn't really fit this nicely into the comments on Ryan's post.