Showing posts with label rails. Show all posts
Showing posts with label rails. Show all posts

20140605

pow and byebug - staying in the web app dev env heaven: an update for ruby 2.1

The previous version (for ruby 1.9) is here.

It seems ruby-debug has fallen out of favour with the community and the replacement is byebug.
So here's my follow-up on the old post.

The steps to enable byebugging in your app dev env:
  1. echo export POW_WORKERS=1 >> ~/.powconfig  # make sure pow runs only one app process — globally
  2. cd <your project directory>
  3. Add byebug to your bundle (and remove ruby-debug if exists).
  4. Paste the following snippet at the end of your config/environments/development.rb — for rails, or in the appropriate development environment initialisation module — for the platform of your choice:
  5. echo export RUBY_DEBUG_PORT=10007 >> .powenv  # make sure we enable debugging with a machine-unique port, e.g. 10007 (you should pick different ports for different projects)
  6. touch tmp/restart.txtThen make any request to actually restart your server.
  7. byebug -R localhost:10007 -d  # you're back in business, go wild with debugger's in your code

20140330

mail_view for human — and also for automatic — testing

I've come to use a nice gem called mail_view for visual testing of email templates in Rails.
I think it is totally legit and desirable to re-use that code in the views testing suite, so here we go:
(provided that your MailView's subclass is MailPreview, put this in ./spec/views/mail_spec.rb)

This just checks for the lack of exceptions and any content, but it's a starting point.

p.s. A nice complementary pull request of mine is here.

20140116

I18n testing outfit for Rails 3.2 (also Rails 4.0-stable)

Now put this in config/initializers/i18n_test.rb and spec it!

N.B. Curiously enough, a similar technique is needed for 4.0 as well.

P.S. Tested and working with Rails 4.0-stable, yay!

20121119

subdomains in rails apps: a current brief

Subdomains (or second level domains) are still a nice way to present separate interfaces to a single web app — commonly to give a certain kind of customers the feel-n-touch of a dedicated app install.

Here are most of the related aspects based on a particular Rails3 project:

  • some model Group, representing a customer, or rather a group of users, has a string column domain (migration not shown, but don’t forget to index by that column)

    in app/models/group.rb:

    has_many :users
    before_validation :downcase_domain, :if => :domain_changed?
    validates :domain, presence: true, uniqueness: true, length: {maximum: 255}, format: /^[0-9a-z-]+$/
    
    
    def host
      "#{domain}.#{ROOT_DOMAIN}"
    end
    
    
    protected
    def downcase_domain
      self.domain = domain.to_s.downcase
    end
    
  • under assumption that you’re using devise and your (group’s) user identity model is User (updated for everchanging devise 2.0.4)

    in app/models/user.rb:

    belongs_to :group
    devise :database_authenticatable, ..., :request_keys => [:app_subdomain]
    
    
    def self.find_for_authentication(conditions={})
      group = Group.find_by_domain(conditions.delete(:app_subdomain))
      return nil unless group.present?
      conditions[:group_id] = group.id
      super
    end
    
  • you’ll have some admin interface where the groups are managed (e.g. active_admin, not recommended)

    in app/admin/groups.rb:

    link_to group.domain, root_url(host: group.host)
    
  • you will certainly want to do something specific in config/routes.rb

  • you’ll want some handy helper method, to know which guvnor you’re serving

    in app/controllers/application_controller.rb:

    helper_method :current_group
    def current_group
      return @current_group if defined? @current_group
      @current_group = request.app_subdomain && Group.find_by_domain(request.app_subdomain)
    end
    
  • you’ll need an initializer of some sort to set a constant and monkey-patch the request class, so…

    in config/initializers/subdomain.rb:

    ROOT_DOMAIN ||= ENV['ROOT_DOMAIN'] or raise "ROOT_DOMAIN must be set"
    
    
    # (): paranoid monkey patching :()
    class ActionDispatch::Request
      def app_subdomain
        return @app_subdomain if defined? @app_subdomain
        @@app_hostname_regex ||= /^(?:([0-9a-z-]+).)?#{Regexp.escape(ROOT_DOMAIN)}$/
        raise 'Wrong domain' unless host.downcase =~ @@app_hostname_regex
        @app_subdomain = $1
      end
    end
    
  • then, for your production (and staging) environment on, say, heroku, you’ll have to setup your lovely app domain name (with wildcard subdomains) and set the environment variable ROOT_DOMAIN to it

  • for test environment, which is also good for the handy circleci

    in config/environments/test.rb:

    ROOT_DOMAIN = 'lvh.me'  # yes, it's a magic domain for 127.0.0.1 //smackaho.st RIP
    
  • for other environment cases, be sure to set either ROOT_DOMAIN or ENV['ROOT_DOMAIN'] as appropriate

  • if you use factories (and girls, factory_girl)

    in spec/factories.rb:

    factory :group do
      sequence(:domain) {|n| "dom-#{n}"}  # or better still, use `forgery` with some smart randomness
      ...
    end
    
  • if you use capybara (2.0.0 at least, recommended) and rspec (rspec-rails 2.12.0 at least)

    in spec/spec_helper.rb: (in Spork.prefork block if you use spork, recommended)

    Capybara.always_include_port = true  # unless you `visit` external sites in your feature specs
    

    and then in some spec/features/..._spec.rb:

    visit("http://some_domain.#{ROOT_DOMAIN}/some_path")
    # or
    visit(some_url host: @group.host)  # if you're playing dirty, using pre-fabricated data and route helpers, recommended
    
  • in some spec/controllers/..._spec.rb you’ll have to include something like this:

    before(:each) do
      request.host = @group.host
    end
    
  • don’t forget the specs for domains in spec/models/group_spec.rb and other relevant places

May your sub-domains be obedient to their master.

20120403

pow, guard and rdebug - staying in the web app dev env heaven: for ruby 1.9 only

The new version (for ruby 2.1) is here.

If you, like me, use pow and guard (with spork of Rails 3 standard setup) for the perfect web app development environment, you might have stumbled upon a problem of debugging the server with rdebug -c which tends to connect to a wrong process even when working on just one project (and that's because spork itself starts the remote debug server by default).

So, firstly, you will probably want to limit your server instances run by pow to 1 by
echo export POW_WORKERS=1 >> ~/.powconfig

Then, to actually enable remote debugging you should place the following in your ./config/environments/development.rb:


And finally, to set the port of your choice for the project,
echo export RUBY_DEBUG_PORT=10007 >> ./.powenv

Now, you are welcome to
touch tmp/restart.txt
and (after a bunch of your CPU's cycles)
rdebug -c -p 10007

You're back in heaven, have a happy stay!

20110624

the boolean virtual attribute's gotcha (a checkbox in a rails form)

I am not sure where to post this, suggestions are welcome.

Whenever you create a virtual boolean attribute in your model, e.g.

attr_writer :some_boolean
def some_boolean; defined?(@some_boolean) ? @some_boolean : true; end  # defaults to true
attr_accessible :some_boolean

And make it a checkbox in the model's input form, e.g. (simple_form, haml)

!= f.input :some_boolean, :as => :boolean

And try to do some reasoning with it, e.g...

after_create { ... if @some_boolean }

You may be surprised as @some_boolean will always resolve to true (actually to 0/1, but both are true in Ruby).

A quick and dirty workaround would be... well... getting your controller dirty quickly:

before_filter :boolean_fix
...
def boolean_fix
  params[:some_model][:some_boolean] = false if params[:some_model] && params[:some_model][:some_boolean] == '0'
end

20110326

acts_as_taggable_on meets thinking_sphinx on rails (and nearly misses it)

Beware, for reasons unknown, instead of
indexes tags.name, :as => :tags
you have to write
indexes taggings.tag.name, :as => :tags
or you'll get too many results.

jQuery autoSuggest vs rails (and acts_as_taggable_on)

While AutoSuggest is quite wonderful as it is, there's also a lot of room for improvement (e.g. I've started using this fork since the original author doesn't seem too community-friendly).

Here's one tip on how to use it with rails (and simple_form) — or rather how to workaround the following issue.
When you write something like $("#post_tag_list").autoSuggest(...); for the first time, you'll expect AS to do all the wow stuff on the client side and have the original input field with the values as a parameter back on the server side, right?
Well, I did.
Unfortunately, you have to work harder: not only you must include the asHtmlID: "tag_list" option in the autoSuggest parameters, but (since that option actually defines the id's suffix only) you'll have to patch your controller allong the lines of:
before_filter :autosuggest_fix
and
def autosuggest_fix
params[:post][:tag_list] = params[:as_values_tag_list] if params[:post]
end