How to add an extra value into a Rails param - ruby-on-rails

I'm trying to solve a problem with getting all the values saved to my database. Here's how my application is setup is
before_filter :load_make, only: :create
def create
#record = #make.message.new(my_params)
#record.save
end
def load_make
make_id = params[:message] ? params[:message][:make_id] : params[:make_id]
#make = Car.find_by(custom_make: make_id)
#make ||= Car.find_by(id: make_id).tap
end
def my_params
params.require(:message).permit(:message_type, :message_date, :make_id)
end
My problem is that I want to create another method that does a GET request from a different API that returns a created_at date and also save it on create, similar to:
def lookup_car
Car.car_source.find(id: my_params[:make_id]).created_at
# returns a datetime value
end
I'm looking for advice on what's the best way to put this into the my_params method :message_date and save it along with everything else on the create action?
Schema for model:
create_table "messages", force: :cascade do |t|
t.integer "make_id"
t.string "message", limit: 255
t.datetime "created_at"
t.datetime "updated_at"
t.string "message_type", limit: 255
t.datetime "message_date"
end

Firstly, you should NOT really change / update / insert to the created_at since Rails does that for you. If anything, I suggest you adding another column.
Secondly, you should NOT do and create / update on a Get request.
Other than that, adding another field to your params is easy as long as you have that column ready. Not sure how your models are structure, but here you can do something along the line like this below. Let's say you have message_date column in your whatever model, you can do this:
def my_params
message_date_param = { message_date: Time.now }
params.require(:message).permit(:message_type, :message_date, :make_id).merge(message_date)
end

I wrote this pretty late at night when I probably wasn't making much sense, but essentially what I was wanting was to update another value at same time save was called on the #record. I solved this by using attributes, which updates but doesn't call save (since I was doing that already below).
For example:
def create
car = Car.car_source.find(id: my_params[:make_id]).created_at
#record.attributes = { message_date: car }
#record.save
end
Also much thanks to all the other comments. I've completely refactored this code to be more rails idiomatic.

Related

How to store values of an array inside a table in ruby on rails?

I have two tables named "imports" and "import_details". Inside the "imports" table are log files that have been uploaded using carrierwave. I have a method that reads the log file and extracts the data that I need from each entry (I used regex for this). My regex expression has a total of 12 capture groups, and each capture group is a value that I want to store inside the "import_details" table.
My problem is I don't know how to iterate through each of them and store them in each row after the regex expression has reached the 12th capture group.
My create_imports.rb:
class CreateImports < ActiveRecord::Migration[5.2]
def change
create_table :imports do |t|
t.string :name
t.string :attachment
t.timestamps
end
end
end
create_import_details.rb:
class CreateImportDetails < ActiveRecord::Migration[5.2]
def change
create_table :import_details do |t|
t.string :controller
t.string :controllermethod
t.string :ipaddress
t.datetime :datetime
t.string :methodname
t.string :sessionid
t.string :cycletime
t.string :restimeview
t.string :restimerecord
t.integer :statuscode
t.string :statusname
t.string :urladdress
t.timestamps
end
end
end
My method for reading a log file:
def processimport
path = Dir.glob('/sample/file/path/'#import = Import.find(params[:id])'/*.log').first
regex = /sampleregexexpression/
samplefile = File.open(path)
string = File.read(samplefile)
string.scan(regex).each do|x|
puts x
end
end
What my processimport method currently does is only to print out the values of each capture group that my regex gets. Any idea how to loop through them and store in a database?
A sample response of
string.scan(regex).each do|x|
puts x
end
looks like this:
SampleController
create
10.910.992.227
2020-12-01 12:00:00
POST
12mnd9adkmc82js9akjnas98sdv3
0.12995 (7 reqs/sec)
0.00027 (0%)
0.09836 (75%)
201
Created
[https://www.soap.com/api/sample.xml]
AnotherController
index
888.12.445.247
2020-12-01 12:00:00
GET
ertye73do928hxksmsu2edjejend783k
0.00905 (110 reqs/sec)
0.00007 (0%)
0.00281 (31%)
200
OK
[https://www.samplecloud.com/api/anothersample.xml?sample_id=9002&after=2020-12-01 12:00:00Z&page=1]
ExampleController
index
838.33.55.776
2020-12-01 12:00:00
GET
7282849jfjdkdo2a29snxmmjscnssdn8
0.23466 (4 reqs/sec)
0.15961 (68%)
0.06614 (28%)
200
OK
[https://customer.example.com/en/example]
ThisIsAController
show
992.334.556.1
2020-12-01 12:00:00
GET
jasd7839njsdnlkal3898259adansdn
0.26166 (3 reqs/sec)
0.13863 (52%)
0.11492 (43%)
200
OK
[https://www.sample.com/en/soap/shampoo]
In this example, there are 4 log file entries inside one log file, each of them start with the Controller (capture group 1) and end with URL address (capture group 12). The goal is to store in the database table the first set of capture groups in one row, the second set in the next row, and so on.
I would create a hash of the values you want in the loop:
my_hash = {}
string.scan(regex).each do|x|
my_hash[:key] = x
end
as long as each key matches exactly with a column in your table you can call
ImportDetails.create(my_hash)

How does Ruby's block syntax work?

I'm new in Ruby and trying to understand this syntax:
create_table :posts do |t|
t.string :title
t.string :content
t.string :likes
t.string :comments
t.timestamps null: false
end
I fully understand what this code is doing, but I don't understand how it works. More specifically, I understand that create_table is a method and :posts is a parameter, but I don't understand the rest of the code.
Brace for it :)
create_table is a method. :posts is a symbol that is passed as parameter. Parenthesis are optional, so it looks odd but it is a simple method call.
Everything between do and end is a code block. It is one of the many ways how to pass code as an argument to a method. Code blocks are great for common case. Other similar (but different) ways to do it is to use Proc or lambda or ->.
|t| is an argument passed into the code block by create_table. create_table will execute your code block, and will pass a table object as single argument to it. You chose to name that object t.
Now inside your code block you are calling method string on object t and passing it symbol as an argument. You are doing it four times. Again parenthesis are optional.
You are calling timestamps method on same object t. Here you are passing it one parameter, which is a Hash with the value { :null => false }.
Not only parenthesis are optional, but also curly braces are optional when passing hash as last or only parameter to a method.
null: false, is a shortcut syntax for { :null => false }.
So all of the above is equivalent to:
create_table(:posts) do |t|
t.string(:title)
t.string(:content)
t.string(:likes)
t.string(:comments)
t.timestamps({:null => false})
end
Let's first forget about Active Record and focus on the code structure itself. Here is a super simple version of that structure.
class MyBuilder
def initialize
# keys are property names, values are options
#properties = {}
end
def property(name, options={})
#properties[name] = options
end
def build
# For simplicity, just return all properties
#properties
end
end
def create_thing(name)
puts "Begin creating #{name}"
builder = MyBuilder.new
puts "Let user use the builder to define properties"
yield builder
puts "Consume the builder"
properties = builder.build
puts "Persist changes to #{name}..."
# For simplicity just print them out
p properties
puts 'done'
end
create_thing :bar do |builder|
builder.property :counter, color: 'brown'
builder.property :wine_storage, texture: 'wood'
end
Please type the code above by hand to grab some feel.
Although the code above has nothing to do with Active Record, it has the same structure as the migration.
When ever create_table is called, it instantiates a builder (of type TableDefinition), and "pushes" that builder to the block (by yielding it) in order to let user define the tables columns. The builder is consumed later by create_table when the user is done defining the columns.
One of the struggles with learning Ruby is that, like smalltalk et al, you get to pass code around as well as data.
One way you can pass code to a method is with a code block.
You can then call the code block in the method definition with yield which says "insert this block of code in place of yield":
def do_it
yield
end
do_it { 2 + 4 }
=> 6
You also get to send parameters into the code block from the method definition.
That's where the |t| comes in:
def do_it_with_ten
yield 10
end
do_it_with_ten { |t| (2 + 4) * t }
=> 60
Note that the curly braces are equivalent to do..end.
I'm guessing that this is the code you found with yield in it:
def create_table(name, options = {})
table_definition = TableDefinition.new(self)
table_definition.primary_key(options[:primary_key] || "id") unless options[:id] == false
yield table_definition
if options[:force]
drop_table(name) rescue nil
end
create_sql = "CREATE#{' TEMPORARY' if options[:temporary]} TABLE "
create_sql << "#{name} ("
create_sql << table_definition.to_sql
create_sql << ") #{options[:options]}"
execute create_sql
end
This is exactly what you're looking for. This is the definition of the create_table method we are calling. You can see the yield with the parameter table_definition.

default values not working same on creation and updation - Ruby on Rails

I have model user.rb like below:
class User < ActiveRecord::Base
before_save :set_default
def set_default
self[:is_admin] || 0
self[:is_active] || 1
end
end
Now when i create u new user , it inserts is_admin = null , is_active = null . While when i update user and send parameters is_admin= true , is_active = true or whatever values it updates it correctly . I did some R&D and found that i should do the following
def set_default
self.is_admin || 0
self.is_active || 1
end
Now the case becomes totally alternate . Means now values are inserting correctly on creation . but updation makes no difference . I also tried it with the following
def set_default
self.is_admin ||= 0
self.is_active ||= 1
end
But still no help . Kindly explain what's going on . Any help will be appreciated .
If you always want those fields to start with that default data, I would move your defaults to the schema.
def change
create_table :users do |t|
t.boolean :is_admin, default: false, null: false
t.boolean :is_active, default: true, null: false
...
end
end
This will set the defaults on your users table without needing the extra cost/complication of a callback. This will also nicely blow up if you try to set either of those to nil which would produce unexpected behaviors in your application.
Hope that helps.

Rails 4 - order of param parsing with a custom paperclip attachment processor

I have a content table with a paperclip image attachment along with some user-selected cropping settings:
create_table "content", force: true do |t|
t.string "title"
t.string "image_file_name"
t.string "image_content_type"
t.integer "image_file_size"
t.datetime "image_updated_at"
t.integer "crop_x"
t.integer "crop_y"
t.integer "crop_w"
t.integer "crop_h"
end
In order to process the user-specified image cropping on the server-side, I have a custom paperclip processor, which I sourced from here:
module Paperclip
class CustomCropper < Thumbnail
def initialize(file, options = {}, attachment = nil)
super
#current_geometry.width = target.crop_w
#current_geometry.height = target.crop_h
end
def target
#attachment.instance
end
def transformation_command
crop_command = [
'-crop',
"#{target.crop_w}x" \
"#{target.crop_h}+" \
"#{target.crop_x}+" \
"#{target.crop_y}",
'+repage'
]
crop_command + super
end
end
end
My problem is that the crop_x,y,w,h params are parsed after the image attachment, therefore the custom paperclip processor sees nil for all the fields and doesn't crop the image properly.
# rails processes the image_xxx params before the crop_xxx params
#content = Content.new(content_params)
Is there a clean way to tell Rails to process the crop-boundary fields before the attachment image? Or is there a different solution that essentially accomplishes this?
For the life of me, I couldn't figure out a "clean" way. What I ended up doing was saving the crop_ values before calling an update/save with the attachment.
For example:
def update
if article_params[:image_crop_y].present?
#article.image_crop_y = article_params[:image_crop_y]
#article.image_crop_x = article_params[:image_crop_x]
#article.image_crop_w = article_params[:image_crop_w]
#article.image_crop_h = article_params[:image_crop_h]
#article.save
end
if #article.update(article_params)
...
end
end
Like I said, not the "cleanest" way, but it was effective for me.

current_user association seems to work in view but not controller

The rundown: I have a user model which belongs_to a state model (region). The state model rb file is basically empty except for: has_many users. The db is seeded with the state info, and when a user registers they can select their region; all works fine.
Now I'm trying to use devise's current_user method in one of my controllers to check if the current_user's state_id is a match to one of those in the array.
class FinancesController < ApplicationController
def index
#user = current_user
if #user.state_id == ['53', '54', '60']
flash.now[:notice] = "User is from one of the accepted regions"
else
end
end
end
Schema: (the dots are just there to indicate that there's more than just the two lines)
create_table "users", force: true do |t|
.........
t.string "email", null: false
.....
.....
t.integer "state_id"
.....
end
Unfortunately it doesn't work, and will always use the "else" even if the user is of one of the accepted values in the array.
For example, the user I'm testing with has a state_id of 54. In my view I can use <%= current_user.state_id %> which will print "54" on the page.
I believe the problem may be with the array ['53', '54', '60'] because I tried just having one ID there instead of the array, and it seemed to produce the proper results. Perhaps I should be using a for loop to cycle through and check each number in the array if such is the case?
You are checking a string value against an array.
You would probably want to do this instead:
if [53, 54, 60].include? #user.state_id
Use include method:
['53', '54', '60'].include?(#user.state_id)

Resources