Scope/Filter collection from route - ruby-on-rails-3

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.

Related

can't get relation's name via listdata

I am not sure why I can't get the columns from my other tables via my relations. I was thinking is it because of my scope? After i had a default scope in my models, everything seems to be out of place, even if i use resetscope() at some places. Some sections I can't get to my relation columns; when that happens, I'd have to use Model::model->findbypk(n)->name.. that doesn't look pretty.
the id shows if i don't have the relations, but the name is blank when i put the relation name.
CHtml::listData(Model::model()->findAll(),'product_id','main.product_name'),
my model defaultscope is pretty basic:
return array(
'condition'=>'store_id1=:store_id OR store_id2=:store_id' ,
'params' => array(':store_id' => $store_id)
);
You can change the way you use your model like below:
Model::model()->with('main')->findAll();

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 recognise timestamp column

I'm using giix to extend model (and crud) behavior. In this I would like to handle columns of type timestamp (that already exist in my model) specifically, rather like autoincrement fields are handled. (Ignored and not shown, that is.) However, there is no property $column->isTimestamp. I would like to add this somewhere, but I'm rather at loss what the best place for this would be. Do I put it in giix somewhere, or do I have to extend the column-baseclass?
Edit: I want to ignore them from every view, for every table. Since this is a lot of work, and it's something I always want, I'd like to automate it. Adapting the generators seems to make most sense, but I'm not sure what the best way to do it would be.
Here is the process:
Extend your database schema, if you are on MySQL it is CMysqlSchema.
Extend CMysqlColumnSchema and add a "isTimestamp" attribute.
In your CMysqlSchema sub-class extend createColumn and test for a timestamp, you'll see that Yii makes simple string comparisons here to set it's own flags. Set "isTimestamp" in your CMysqlColumnSchema here.
Tell Yii to use your schema driver like this in your components section in the config:
'db' => array(
'connectionString' => 'mysql:host=localhost;dbname=database',
'username' => '',
'password' => '',
'driverMap' => array('mysql' => 'CustomMysqlSchema'),
),
You will need to query the column schema, I've not used giix but find where it is generating the views, it should be looping through either the model attributes or the underlying table schema.
If it is looping through the schema:
//you can also ask Yii for the table schema with Yii::app()->db->schema->getTable[$tableName];
if ('timestamp' === $tableSchema->columns[$columnName]->dbType)
continue; //skip this loop iteration
If it loops over the attributes:
$dbType = Yii::app()->db->schema->getTable[$model->tableName]->columns[$modelAttribute]->dbType;
if ('timestamp' === $dbType)
continue; //skip this loop iteration

How do you load collections that live on a rails' action besides index?

Batmanjs sample project Classifieds used to do this by passing a :url option to override the default resource name, but this has now been deprecated.
Classifieds.Ad.load {url: "/ads/search.json?q=#{params.q}"}, (error, records) =>
throw error if error
#set 'searchAds', records
I have seen most people pass query params to the index action and handle logic depending on the filters passed. Is there a working example somewhere of how to load a collection from a rails action other than index?

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])