RESTful nested conventional routing - ruby-on-rails

I have the model:
User -1---n- Transaction(amount,description, date)
User -1---n- TransactionImport -1---n- TransactonImportField(name,value)
(personal expense tracking app).
What I want to achieve is this:
User opens URL and pastes the CSV with the list of transactions.
User submits it.
System extracts data from CSV into TransactionImport (row) + TransactionImportField (cell).
User can choose which column means what (amount, description, date) from the imported data in TransactionImport(Field).
User click save and the system transfers TransactionImport into the Transaction.
What I can't seem to get right is the fact that step 3 creates multiple records of TransactionImport (and related TransactionImportField).
So doing POST /transaction_imports?csv=abcd is expected to produce one record if we would be RESTful. But the code is supposed to be something like this:
# TransactionImportsController
def create
result = TransactionImports.parse(params[:csv])
flash[:notice] = result.message
redirect_to transaction_imports_path
end
I am probably approaching the task from a wrong angle as I feel that implementation doesn't fit in tp the inherited_resources.
Could you please advise what would be the most conventional way of implementing this?
Thanks,
Dmytrii.

REST/HTTP has no expectation that doing POST will only create one record. That maybe the default rails behaviour, but you should not constrain your design because of that.

Related

How do I generate all new records for a given model randomly using pre-defined options?

I'd like one of my models, Stones, to be generated at random using pre-defined options I've stored in a set of arrays and hashes. Instead of Create using params from the URL, I'd like new Stones to always be defined using this random generation process. I don't need any user input at all, except that each stone belongs to a given player.
I'm still new to rails; where should I put all this code? I know enough to be able to define the arrays and hashes and randomly select from them when I need to, but I'm not sure where and how to replace the part of the code that draws params from URLs and fills in a new record before it is saved. I know controllers are supposed to be skinny, so do I do this in the model?
Apologies if this is a duplicate. I searched extensively and couldn't find an applicable solution.
Thanks for any help!
I would create a service for this. Something like:
# app/services/stone_creator.rb
class RandomStoneCreator
RANDOM_FOOS = ['bar', 'baz', 'bat']
def self.call(user)
Stone.create!({
foo: RANDOM_FOOS.sample,
user: user
})
end
end
And then anywhere that you need a new random stone you can call it like:
random_stone = RandomStoneCreator.call(current_user)

Rails DRY RESTful design of API endpoints with filters

I am developming a JSON API with Rails 4.2 which looks like this:
GET api/v1/car => app/controller/api/v1/car_controller#index
GET api/v1/car/:id/installment => app/controller/api/v1/carresources/installment_controller#index
Now i would like to extend these endpoint with filters.
api/v1/carresources/installment#index:
all installments for a specific car during a specific time period
all installments for a specific car by payment type (cash, mobile money, <- mobile money provider)
api/v1/car#index:
all cars by region
all cars by dealer
all cars by loan schema
The way i implemented is the following:
app/controller/api/v1/car_controller.rb
def index
res = []
if params.key?('start_date') and parms.key?('end_date')
res = Car.index_period(params['start_date'], params['stop_date']
elsif params.key?('loanstructure')
res = Car.index_loan_strucuture(params['loan_strucuture'])
....
end
end
Which is working, but not such a nice solution to put completely semantically different logic behind these if-"graves".
It would be possible to create a new endpoint for each filter, but i have the feeling that this bloats the routing and controller structure.
I would also like to avoid these if clauses, because these is also some authorization on the users role going on -which i skipped showing - which is also done with if clauses
Is there another clever way, or should i spent an extra route for each filter, where i then need to copy the whole authorization structure.
Many thanks in advance
Many thanks in advance
OP I would suggest breaking the filtering code little separately by handling the request.query.params in a filter class.
This filter class helps in de-coupling the code by
Allowing validation of the filters which are being passed so you can have a parseFilters function which does that.
You can use the hashtable which points to
{('start_date','end_date'): "index_period(params['start_date'], params['stop_date']", etc }
this will help you to apply the filters directly from the parsefilters.
This will ensure that your code is decoupled and also can be reused in other scenarios.

Rails Limit Model To 1 Record

I am trying to create a section in my app where a user can update certain site wide attributes. An example is a sales tax percent. Even though this amount is relatively constant, it does change every few years.
Currently I have created a Globals model with attributes I want to keep track of. For example, to access these attributes where needed, I could simply do something like the following snippet.
(1+ Globals.first.sales_tax) * #item.total
What is the best way to handle variables that do not change often, and are applied site wide? If I use this method is there a way to limit the model to one record? A final but more sobering question.......Am I even on the right track?
Ok, so I've dealt with this before, as a design pattern, it is not the ideal way to do things IMO, but it can sometimes be the only way, especially if you don't have direct disk write access, as you would if deployed on Heroku. Here is the solution.
class Global < ActiveRecord::Base
validate :only_one
private
def only_one
if Global.count >= 1
errors.add :base, 'There can only be one global setting/your message here'
end
end
end
If you DO have direct disk access, you can create a YAML config file that you can read/write/dump to when a user edits a config variable.
For example, you could have a yaml file in config/locales/globals.yml
When you wanted to edit it, you could write
filepath = "#{Rails.root}/config/locales/globals.yml"
globals = YAML.load(File.read("#{Rails.root}/config/locales/globals.yml"))
globals.merge!({ sales_tax: 0.07 })
File.write(filepath) do |f|
f.write YAML.dump(globals)
end
More on the ruby yaml documentation
You could also use JSON, XML, or whatever markup language you want
It seems to me like you are pretty close, but depending on the data structure you end up with, I would change it to
(1+ Globals.last.sales_tax) * #item.total
and then build some type of interface that either:
Allows a user to create a new Globals object (perhaps duplicating the existing one) - the use case here being that there is some archive of when these things changed, although you could argue that this should really be a warehousing function (I'm not sure of the scope of your project).
Allows a user to update the existing Globals object using something like paper_trail to track the changes (in which case you might want validations like those presented by #Brian Wheeler).
Alternatively, you could pivot the Global object and instead use something like a kind or type column to delineate different values so that you would have:
(1+ Globals.where(kind: 'Colorado Sales Tax').last) * #item.total
and still build interfaces similar to the ones described above.
You can create a create a class and dump all your constants in it.
For instance:
class Global
#sales_tax = 0.9
def sales_tax
#sales_tax
end
end
and access it like:
Global.sales_tax
Or, you can define global variables something on the lines of this post

Want to count number of visits on my blogs

I want to count the number of visits on my blog? Can someone please suggest the overall method to implement this feature?
It is just an idea. You can add a count_view column in the database into blogs table with default value 0.
And in the show action of BlogsController add the following code
def show
#blog = Blog.where('id = ?', params[:id]).first
#blog.update_column('count_view', #blog.count_view + 1) if #blog.present?
end
You can modify this logic as per your requirement.
You can check the hit counter gem or the impressionist gem.
You could also use an existing (free) analytics solutions if you want to get much more data than the number of times the action was called (please note that if the same user refreshes the browser 5 times, you get 5 hits):
http://www.google.com/analytics/
Using these you can get data like unique visitors, referral URL, locations data, browser, OS, and a lot of different stuff to make informed decisions. There are several other options (paid, free, real time) available as well:
https://mixpanel.com
https://www.kissmetrics.com/

Dynamic nested form elements based on inputting a starting number

Like many others, I'm new to Rails and have a question. I'm working on a sample tracker for a small analytical lab. I would like users to submit batches consisting of many samples. I want the front page to be a simple gateway into batch submission. My general plan is:
Front page asks for number of samples in batch. User enters number and hits submit.
A form is generated where the user can enter batch information (Sampling date, experiment name, batch model stuff). Under the batch fields there should be as many fields for individual sample IDs as the user indicated in the first step.
User fills all this out and the batch and its samples are created upon submission.
My feeling is that the homepage should pass some sort of parameter to the batches controller, which then iteratively builds the samples while the model has a method to iteratively build form elements for the view. Is this thinking correct? How could I pass a parameter that isn't directly related to any models or controllers? I could find any similar questions, but if anyone can link me to a solution for a similar problem or a Railscast or something I'd be very grateful!
There's no need to back a form with a model. For your view, you'll just want something like this example (in Haml):
- form_tag new_batch_path, :method => "get" do
= label_tag(:sample_count, "Number of samples:")
= text_field_tag(:sample_count, 3)
= submit_tag("Get Started!")
And then in your controller, and the new_batch view, you can just reference params[:sample_count]
- (params[:sample_count] || 5).to_i.times do |n| ...
Because this isn't tied to a model (and nothing's being saved anyway) you can't use model validations to check the value. If you do want to verify, you'll do the verification in the batches controller - either as a before_filter, or just inline:
#sample_count = params[:sample_count].to_i
unless (1..10).include? #sample_count
flash[:error] = "A batch must contain between 1 and 10 samples."
redirect_to root_url
end
Note that nil.to_i, "".to_i and rubbish like "ajsdgsd".to_i all equal 0, so unless you want people to be able to specify 0 samples, this code is fairly robust
Have a look at these Railscasts series:
Nested Model Form: Part 1, Part 2
Complex Forms: Part 1, Part 2, Part 3
The "Nested Model Form" screencasts are newer, so I'd go with these ones first.

Resources