Trying to figure out nested scopes in Rails routing - ruby-on-rails-3

I have a site where I'm making URLs look like the following (for users to view information):
/:state/:data_type/:category/:subcategory/:item_id
data_type is one of "directory", "articles", or "documents". The idea is to store information for each state, based on categories and subcategories, and viewable as such.
Any particular piece of that is a valid path. For instance:
/IN - show a home page for Indiana (controller region_home, action index)
/IN/directory - list of categories and subcategories for Indiana (controller region_categories)
/IN/directory/schools - list of subcategories under the "schools" category for Indiana (controller is region_subcategories)
/IN/directory/schools/elementary - list of elementary schools in Indiana (controller is region_directory, action index)
/IN/directory/schools/elementary/10-meridian-street - drilled down to the specific school (controller is region_directory, action is show)
This is mainly for showing the information. I already have "slugs" created for all the regions, categories, subcategories, etc. That part works.
I can actually make it work like this:
match ':region_slug/directory(/:category_slug(/:subcategory_slug(/:directory_entry_id)))' => 'region_directory#index', :as => :region_directory
The issue is that every request goes to the same controller/action, which then has to dispatch the request.
I can cut it into pieces:
match ':region_slug' => 'region_home#index'
match ':region_slug/directory' => 'region_categories#index'
match ':region_slug/directory/:category_slug' => 'region_subcategories#index'
match ':region_slug/directory/:category_slug/:subcategory_slug' => 'region_directory#index'
match ':region_slug/directory/:category_slug/:subcategory_slug/:directory_entry_id' => 'region_directory#show'
But that breaks DRY, and I have to repeat that for articles, etc. I can't figure out how to do this with scopes. Any ideas?

Related

Scope/Filter collection from route

I've got a standard resourceful route setup for 'Invoice' however I'm looking to add in the ability to filter the invoice records based on their state.
/invoices - shows all invoices
/invoices/unpaid - shows all unpaid invoices
/invoices/paid - shows all paid invoices.
/invoices/3 - shows invoice #3
I've gotten this working no problem with an explicitly defined match route.
match "/invoices/pending" => "invoices#index", :state => 'pending'
However, with a growing number of possible states this means modifying the routes regularly, and also means I'm repeating myself quite a lot.
My next attempt was to make this route a little more dynamic with named params in the match route.
match "/invoices/:state" => "invoices#index"
However this then negates the /invoices/id route and trying to look for /invoices/3 finds no records as it's searching based on the state parameter.
Can anyone help with defining a filter route such as this that'll work dynamically?
Add regular expression as constraint in the routes:
match "/invoices/:id" => "invoices#show", :id => /\d+/
match "/invoices/:state" => "invoices#index"
It should select the only-number id-s for show, and the rest for the index.
Try to use routing constraints . Something like
match "/invoices/:id" => "invoices#show", constraint: { id: /\d+/ }
match "/invoices/:state" => "invoices#index", constraint: { state: /\w+/ }
I decided to take a slightly different route (excuse the pun) with this, by adding a constraint that rather then using REGEX used a method to look at possible states direct on the state machine.
# State based routes, which match the state machine dynamicly.
match "/invoices/:state" => "invoices#index", constraints: lambda { |r|
# Get an array if potential state names.
states = Invoice.state_machine.states.map &:name
# See if the request state name matches or not.
states.include?(r.params[:state].parameterize.underscore.to_sym)
}
# Resource routes go here.
This basically means that if I now go looking for /invoices/paid or /invoices/unpaid I get my expected index action, with the state variable set as expecetd.
However, doing something like /invoices/pay_soon which is not a valid event returns a 404.
Pretty happy with that solution, but thanks to the other suggestions which got me on to the track of using a constraint.

Querying for Customers using the API

Using the command line shopify application
include ShopifyAPI
Customer.first(:params => {:query => "email:foo#fizz.com", :fields => "email"})
=> #<ShopifyAPI::Customer:0x00000100b75448 #attributes={"email"=>"poohbear#gmail.com"}, #prefix_options={}, #persisted=true>
I am curious if it is possible to have a query where you provide an email and the result is either empty set (not found) or a record of a customer? I am always getting a customer record back no matter what my query is. That makes it hard to determine if a Customer exists or not from outside the shop.
You're hitting the wrong endpoint. Customer.first queries the index endpoint (admin/customers.json) whereas you're trying to do a search (admin/customers/search.json).
The index endpoint doesn't understand the query param, so it ignores it and you get the regular index.
To fix this, you need to specify the endpoint you're trying to hit. Docs are here, Shopify-specific examples are here. Also here:
ShopifyAPI::Customer.find(:all, :from => :search, :params => { :q => "John" })
This is an old post, but Shopify's docs are pretty spotty. You need to use the search API endpoint AND you will need to quote the email address you are searching for, e.g.
/admin/customers/search.json?query=email:"email#domain.com"
If you don't quote the email address, you may get lots of similar email addresses.
See https://community.shopify.com/c/Shopify-APIs-SDKs/Find-a-Customer-by-Email-via-API-Call/td-p/141661

Yii - one controller multiple urls

I'm trying to create two Yii models that are very similar and can share the same database table. One is a 'question' and one is an 'article'. They both share a title, body, and author. 'Question' has an additional field in the table that 'article' does not need to interact with called follow_up.
Most methods and validation is the same, but there are some tiny differences that could easily be done with if statements. The main problem I'm seeing is the URL, I want separate URLs like site.com/question and site.com/article but have them both interact with the same model, controller, and views.
How can this be done?
Use the urlManager component in the Yii config to set routes for /article and /question to go to the same controller, and then either use different actions or different parameters to differentiate between the two. Since you said they are almost identical, I would suggest different parameters and a single action as follows:
array(
...
'components' => array(
...
'urlManager' => array(
'question/<\d+:id>' => 'mycontroller/myaction/type/question/id/<id>',
'article/<\d+:id>' => 'mycontroller/myaction/type/article/id/<id>',
),
),
);
Of course you'll have to modify that for your needs, but that's the general setup. Look here for more information: http://www.yiiframework.com/doc/guide/1.1/en/topics.url

Ruby on Rails: Basic parameterized queries and URL formation

I'm trying to learn how to query a rails database and return the results as JSON. In my example, I want to query the data using the parameters, city and state.
So far, in my controller, I have gotten the following action to work.
def state
#bathrooms = Bathroom.where("state = ?" ,params[:state])
respond_to do |format|
format.json { render :json => #bathrooms }
format.js { render :nothing => true }
end
end
This is also my routing entry.
match '/bathrooms/state/:state',
:controller => "bathrooms",
:action => "state"
I can call this resource with the following URL:
http://localhost:3000/bathrooms/state/CA.json
That's all good but I don't know how to query by more than one parameter. Adding and AND clause in the controller seems to be easy enough.
BUT....I don't know how to
a.) Correctly write the routing entry?
b.) What would the URL look like if I tested it in a browser?
I've tried to understand rake routes but I must be missing something.
Could someone provide a basic example for what the action should look like? What the routing entry should look like and what does the URL to access the resource look like?
Again, if written in SQL, this is what I would like to be returned.
SELECT * from bathrooms WHERE city='Chicago' AND state = 'IL'
Any help appreciated.
You don't have to pass everything by the route - the URL also support GET parameters - those are the parameters you usually see after the question mark in the URL. You can add those GET parameters without changing your routes: http://localhost:3000/bathrooms/state/IL.json?city=Chicago. Then your can access the city parameter via params[:city]. but in your case, I think it will be better to use http://localhost:3000/bathrooms/index.json?state=IL&city=Chicago. You'll also need to change your routing to
match '/bathrooms/index',
:controller=>:bathrooms,
:action=>:index
and put the code in the index method of BathroomsController. You access the parameters the same - but the concept is different - you don't enter a state and look for bathrooms by city, you just look for bathrooms by state and city.
Anyways, you don't want to write the URL by hand - you want to a Rails helper or an HTML form generate it:
link_to "bathroom in Chicago, IL",:controller=>:bathrooms,:action=>:index,:state=>'IL',:city=>'Chicago'
If you want to use a form(to let the users choose their own state and city), you need to set it's method to GET:
form_tag {:controller=>:bathrooms,:action=>:index},:method=>:get do
and put state and city as fields.
It's also worth noting that while you can use SQL's AND to perform a search by multiple fields, you can also chain where methods: Bathroom.where(:state=>params[:state]).where(:city=>params[:city]).
You can put any arbitrary parameters in your querystring.
For example:
http://localhost:3000/bathrooms/state/CA.json?city=Chicago
your query looks like this:
#bathrooms = Bathroom.where("state = ? and city= ?" ,params[:state], params[:city])

Advice about Rails 3 - Routes - Restfull

I'm building a app to manage some products, so I have a Product model.
We sell, buy and when a product breakes, we send it to the manufacturer for repair.
So, I've created a Movimentation model, that will take care of all ins and outs of a product.
When we bought a product, a In record is generated for each product on the Movimentation table. Out for sells and repairs.
I'm trying to make it the more restfull possible, and that's why I'm confused.
How should I do it? A resource :movimentation ? Or should I create a "product_in" and a "product_out" controllers?
I want urls to be like "movimentation/:to_where/:direction" (in/out) and so draw the views correctly.
Any toughts?
you could have a movimentation_controller with in/out actions and :to_where param. so for the routes you have two choices:
#if you have other REST actions do this:
resources :movimentation do
member do
get :in
get :out
end
end
#or go with a match:
match '/movimentation/in/:to_where' => 'movimentation#in'
match '/movimentation/out/:to_where' => 'movimentation#out'
cheers,
A.