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)
Related
I'm trying to implement a query type that can search by name of the record instead of id. Here's its definition in query_type.rb.
# Get game by name
field :game_by_name, Types::GameType, null: false do
argument :name, String, required: true
end
def game_by_name(name:)
Game.where(name: name) //find a game using the name attribute
end
But when I run:
query {
gameByName(name: "League of Legends") {
id
name
}
}
I get the following error.
Failed to implement Game.id, tried:\n\n
- `Types::GameType#id`, which did not exist\n
- `Game::ActiveRecord_Relation#id`, which did not exist\n
- Looking up hash key `:id` or `\"id\"` on `#<Game::ActiveRecord_Relation:0x00007f5644442888>`, but it wasn't a Hash\n\n
To implement this field, define one of the methods above (and check for typos)\n
This is odd because the following query type works perfectly.
# Get game by ID
field :game_by_id, Types::GameType, null: false do
argument :id, ID, required: true
end
def game_by_id(id:)
Game.find(id)
end
Here's game_type.rb:
module Types
class GameType < Types::BaseObject
field :id, ID, null: false
field :name, String, null: false
end
end
How do I go about fixing this? Thank you!
I stumbled upon this, chasing a similar error. Not sure if you solved it or not, but I believe the issue is in the query type definition.
You're telling it to return a type of Types::GameType, but the Active Record query returns an Active Record Relation, which is a collection. So, graphql is expecting a single instance of Game, but is instead receiving a collection. Graphql is then trying to map the returned value from the query to the type definition, but is unable to. The best hint is from this line:
Looking up hash key `:id` or `\"id\"` on `#<Game::ActiveRecord_Relation:0x00007f5644442888>`, but it wasn't a Hash..
Graphql is trying to assign :id to the ActiveRecord_Relation and it can't do it.
Two paths forward, depending on how you want the API to behave. Do you want it to return 1 record or many?
Wrapping the Types::GameType within brackets will tell graphql it's a collection and to iterate over the records
# Get game by name
field :game_by_name, [Types::GameType], null: false do
argument :name, String, required: true
end
def game_by_name(name:)
Game.where(name: name) //find a game using the name attribute
end
or have Active Record return just 1 record, something like...
# Get game by name
field :game_by_name, Types::GameType, null: false do
argument :name, String, required: true
end
def game_by_name(name:)
Game.where(name: name).limit(1).first //find a game using the name attribute
end
I know this is months old, but just putting it out there for anyone else who stumbles upon this question, like I did!
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)
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.
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.
I am trying to make an app in Rails 4. I use simple form for forms and country_select gem for country lists.
I have an address model, which includes this method:
def country_name
self.country = ISO3166::Country[country]
country.translations[I18n.locale.to_s] || country.name
end
When I try to save a new address, I get this error:
undefined method `translations' for "Australia":String
Can anyone see what's wrong with this method definition?
If I change my view to:
<% if #profile.addresses.any? %>
<%= #profile.addresses.first.country.titlecase %>
<% else %>
<span class="profileeditlink">
<%= link_to "Add your location", new_address_path %>
</span>
<% end %>
Then the record displays - but as AU instead of Australia (which is what the method in the address model provides).
Address table:
create_table "addresses", force: :cascade do |t|
t.string "unit"
t.string "building"
t.string "street_number"
t.string "street"
t.string "city"
t.string "region"
t.string "zip"
t.string "country"
t.boolean "main_address"
t.boolean "project_offsite"
t.string "time_zone"
t.float "latitude"
t.float "longitude"
t.integer "addressable_id"
t.integer "addressable_type"
t.datetime "created_at", null: false
t.datetime "updated_at", null: false
end
add_index "addresses", ["addressable_type", "addressable_id"], name: "index_addresses_on_addressable_type_and_addressable_id", unique: true, using: :btree
TAKING JAEHYEN'S SUGGESTION,
I changed my country name method in the address model to:
def country_name
# self.country = ISO3166::Country(country)
# country.translations[I18n.locale.to_s] || country.name
iso_country = ISO3166::Country.find_by_name[country] # `country` should be name like 'Australia'
iso_country.translations[I18n.locale.to_s] || iso_country.name
end
I get this error:
undefined method `translations' for nil:NilClass
ANOTHER ATTEMPT:
I found this resource: http://www.scriptscoop.net/t/4ee6d5ef4577/displaying-countries-using-country-select-gem-in-rails-4.html
I tried changing my form input to:
<%= f.country_select :country, priority: [ "Australia", "New Zealand", "United Kingdom" ] %>
It still just displays the country code instead of the country name. I'm stuck.
ANOTHER ATTEMPT
I found this post:
Rails Simple_Form: How to show long name of the country
The answer in this post suggests defining country_name as:
def country_name
country = ISO3166::Country[country_code]
country.translations[I18n.locale.to_s] || country.name
end
This is slightly different to my previous attempts, however, when I try this, I get this error:
undefined local variable or method `country_code' for #<Address:0x007fbae5bfb290>
I tried changing the method to:
def country_name
self.country = ISO3166::Country[country_code]
country.translations[I18n.locale.to_s] || country.name
end
This gives the same error as the formulation that does not use 'self'. I think these attempts don't work because the attribute in my address table is called 'country'.
When i change the method to:
def country_name
self.country = ISO3166::Country[country]
country.translations[I18n.locale.to_s] || country.name
end
I get the error with the word 'translations'. When I delete '.translations' from the method, I get an error with the word 'name'.
I'm losing my marbles trying to figure this out.
ANOTHER ATTEMPT
I tried adding the countries gem to my gem file (above country_select).
Nothing changes when I bundle this gem. Same problem.
ANOTHER ATTEMPT
Trying again (as I originally had the method defined), but with countries gem installed (above country_select gem):
def country_name
self.country = ISO3166::Country[country]
country.translations[I18n.locale.to_s] || country.name
end
I get this error: undefined method `translations' for "Cayman Islands":String
This is the same problem that I originally started with, so I don't think that adding the countries gem has helped advance toward a solution.
This code work well for me. I had country column in my User table.
On my model :-
def country_name
country = self.country
ISO3166::Country[country]
end
in the form :-
<%= form_for #user do |f| %>
<%= country_select("user", "country") %>
<% end %>
You Question is not enough, there are some point that should make error.
First,
def country_name
self.country = ISO3166::Country[country]
country.translations[I18n.locale.to_s] || country.name
end
In this methods, you should make clear country / self.country variable or instance. I can not imagine country is ISO3166::Country instance or String
Second,
Make clear it use ISO code or country name as hash key
Update:
you have country column that is string.
calling self.country = ISO3166::Country[country] means assigning ISO3166::Country instance in country(string) variable. so it makes error.
I do not know what you expect.
But you should use key of ISO03166::Country as ISO code. like ISO3166::Country['AU']. And you can not assign this in self.country.
def country_name
iso_county = ISO3166::Country[country] # `country` should be iso_code
iso_country.translations[I18n.locale.to_s] || iso_country.name
end
If you have country name, not ISO code in country column. use ISO3166::Country.find_by_name
def country_name
iso_county = ISO3166::Country.find_by_name(country) # `country` should be name like 'Australia'
iso_country.translations[I18n.locale.to_s] || iso_country.name
end
It looks like the answer will be a certain combination of your previous attempts.
Country_select uses the country codes instead of full country names:
country_select("user", "country", priority_countries: ["GB", "FR", "DE"])
This is shown on the github page for that gem:
https://github.com/stefanpenner/country_select
Since the select returns a country code then we can assume that the country attribute will be a country code and not a full country name.
With this in mind we'll modify a previous answer:
def country_name
iso_country = ISO3166::Country[country] # `country` should be code like 'AU'
iso_country.translations[I18n.locale.to_s] || iso_country.name
end
It's best practice to avoid creating a variable with the same name as an attribute in that model, so stick with iso_country instead of country in your testing.
The issue that you've been encountering is that when you assign the ISO3166::Country object to an object's attribute (self.country = ISO3166::Country[country]) it's not assigning the ISO3166::Country object itself to that attribute, but instead is assigning the ISO3166::Country object's name to it.
I tested this in Rails console:
user = User.new
#<User:0x007ff7de29d1b8
id: nil,
...
user.country = ISO3166::Country['AU']
#<ISO3166::Country:0x007ff7de3c6a08
#data=
{"continent"=>"Australia",
...
user.country
"Australia"
As you can see, it assigned the country name (not the country object) to the attribute. Therefore when you try to access country.translations or country.name it will throw an error because it is accessing the address attribute and not the country object. So definitely keep the variable name different from the attribute name.
One last thing, if you do have the country name then use the method find_country_by_name instead of find_by_name since find_by_name will return an array, while find_country_by_name will return a Country object. This should not be necessary in your current scenario, but keep it in mind if you need to use it later.