Class Issue inherits from ActiveRecord (ruby 1.9.3p429 (2013-05-15 revision 40747) [x86_64-linux], Rails 3.2.13). Within this class I attempt to perform the following select:
results = Issue.find_by_sql("select id, notes from mydb.issues;")
results.each do |r|
puts r.notes.class.name
end
The output is NilType in every line.
The field notes is of type text (MySQL, Ver 14.14 Distrib 5.5.31, for debian-linux-gnu (x86_64) using readline 6.2). The is some text in this field, I can see it MySQL Workbench.
Why does it happen? How to access such a field correctly?
May be you have added notes as a attr_protected
class Issue < ActiveRecord::Base
attr_protected :notes
end
issue = Issue.new("notes" => "Some notes here")
issue.notes # => nil
I think you are doing it wrongly, you have already select all the notes from your table, now your results object contains all the notes in an array, so you have to only loop through it :
If you have another 'Note' model, than you have to do this :
results = Issue.find_by_sql("select * from mydb.issues;")
results.each do |r|
puts r.notes.class.name
end
or if you have notes field in your issues table than you should do this :
results = Issue.find_by_sql("select notes from mydb.issues;")
results.each do |r|
puts r.class.name
end
Hope it will help. Thanks
The correct way to retrieve a single column from the database is using pluck. If the following line returns false
Issue.pluck(:notes).any?{ |v| !v.nil? }
# or, since you're using MySQL:
Issue.where('notes IS NOT NULL').pluck(:notes).present?
it's likely that you have some serious problem (database connection which points to another database, f.e.)
Following the pointers provided by #Frederick Cheung and #Salil, I've found that Issue class has the following definition:
delegate :notes, :notes=, :to => :current_journal, :allow_nil => true
Certainly, changing the name notes in the resulting recordset solves the problem.
Related
I have a rails 2 application that I am trying to modify so that before an attribute is written to my MySql DB, it is encoded, and on read, it is decoded (not all attributes, just pre-determined ones).
I have looked at some gems, specifically attr-encrypted, but it doesn't do exactly what I want (I am also trying to avoid re-naming any of my existing table columns, which appears to be a requirement for attr-encrypted).
I have added a before_save filter to my model to do the attribute modification before it is saved to the DB, and I have overridden my attribute getter to do the decode. While this works, I want to do the decode lower in the stack (i.e. right after DB read) in order to have everything function correctly, without requiring system wide changes (it also simplifies the logic when deciding when to encode/decode).
So what it means is that I want to do the following:
1) On DB read, do the reverse, so that if i do a Model.last, the value for my attribute would be the decoded value (without having to explicitly call the attribute getter).
2) Override the find_by_* methods so that doing a search by my encoded attribute will encode the search term first, then do the db query using that value.
How would I go about doing that?
Update: this method unfortunately does not work in Rails 2. Custom serializers were probably added in Rails 3.
Original answer follows:
I think you can try to use a custom serializer as described in this blog post. This feature should be present even in Rails 2 (otherwise I guess these SO questions regarding it would not exist).
Sample serializer which encodes the attribute into Base64:
# app/models/model.rb
class Model < ActiveRecord::Base
serialize :my_attr, MyEncodingSerializer
end
# lib/my_encoding_serializer.rb
class MyEncodingSerializer
require "base64"
def self.load(value)
# called when loading the value from DB
value.present? ? Base64.decode64(value) : nil
end
def self.dump(value)
# called when storing the value into DB
value.present? ? Base64.encode64(value) : nil
end
end
Test in the rails console:
>> Model.create(my_attr: "my secret text")
D, [2016-03-14T07:17:26.493598 #14757] DEBUG -- : (0.1ms) BEGIN
D, [2016-03-14T07:17:26.494676 #14757] DEBUG -- : SQL (0.6ms) INSERT INTO `models` (`my_attr`) VALUES ('bXkgc2VjcmV0IHRleHQ=\n')
D, [2016-03-14T07:17:26.499356 #14757] DEBUG -- : (4.4ms) COMMIT
=> #<Model id: 4, my_attr: "my secret text">
You can see that the my_attr value gets automatically encoded before saving to the DB.
Loading from DB of course works transparently too:
>> Model.last
D, [2016-03-14T07:19:01.414567 #14757] DEBUG -- : Model Load (0.2ms) SELECT `models`.* FROM `models` ORDER BY `models`.`id` DESC LIMIT 1
=> #<Model id: 4, my_attr: "my secret text">
All finder helpers should work too, for example:
>> Model.find_by_my_attr("other text")
D, [2016-03-14T07:20:06.125670 #14757] DEBUG -- : Model Load (0.3ms) SELECT `models`.* FROM `models` WHERE `models`.`my_attr` = 'b3RoZXIgdGV4dA==\n' LIMIT 1
=> nil # nothing found here for wrong my_attr value
>> Model.find_by_my_attr("my secret text")
D, [2016-03-14T07:21:04.601898 #14757] DEBUG -- : Model Load (0.6ms) SELECT `models`.* FROM `models` WHERE `models`.`my_attr` = 'bXkgc2VjcmV0IHRleHQ=\n' LIMIT 1
=> #<Model id: 4, my_attr: "my secret text"> # FOUND!
It looks like rails 2 has the 'after_initialize' callback which should get you what you want (at a bit of a performance hit):
class Model < ActiveRecord::Base
after_initialize do |model|
# your decryption code here
end
end
http://guides.rubyonrails.org/v2.3.11/activerecord_validations_callbacks.html#after-initialize-and-after-find
I have a mysql query that returns this type of data:
{"id"=>1, "serviceCode"=>"1D00", "price"=>9.19}
{"id"=>2, "serviceCode"=>"1D01", "price"=>9.65}
I need to return the id field based on a match of the serviceCode.
i.e. I need a method like this
def findID(serviceCode)
find the row that has the service code and return the ID
end
I was thinking of having a serviceCodes.each do |row| method and loop through and essentially go
if row == serviceCode
return row['id']
end
is there a faster / easier way?
You can use the method Enumerable#find:
service_codes = [
{"id"=>1, "serviceCode"=>"1D00", "price"=>9.19},
{"id"=>2, "serviceCode"=>"1D01", "price"=>9.65}
]
service_codes.find { |row| row['serviceCode'] == '1D00' }
# => {"id"=>1, "serviceCode"=>"1D00", "price"=>9.19}
If you use Rails Active Record as ORM and your Model named Product (only for example),
you can use something like this:
def findID(serviceCode)
Product.select(:id).where(serviceCode: serviceCode).first
end
If you have plain SQL Query in plain ruby class (not recommended), you should change this query to get only the id, as Luiggi mentioned. But aware of SQL Injections if your serviceCode coming from external Requests.
I'm writing a Sinatra application with Sequel. For the first time, I'm trying to add a currency. After looking on-line I used Numeric as a value, of course goes without saying that I need a precision of decimal points.
I know that in MySQL, Numeric and Decimal is the same. So I wonder what I'm doing wrong here. I have the feeling that I need to define the decimal points somehow. Here my model:
# Contracts table: Here the users can define their contracs
DB.create_table?(:contracts, engine: 'InnoDB') do
primary_key :id
Integer :user_id, null: false
String :name, null: false
String :description
Numeric :cost, null: false
end
Every time I try to submit a value with decimal points I get the following error, which I think it's a validation error. I don't use validations explicitly yet, so I think it's either Sequel or MySQL specific.
How should I change my models in order to allow me to add decimal values?
EDIT: As requested I add my controller (routes) file:
class Metro < Sinatra::Base
get "/user_add_contract" do
protected!
haml :user_add_contract
end
post "/user_add_contract" do
protected!
user = session['name']
begin
uid = User.first(username: user)
Contract.create(user_id: uid[:id], name: params['name'], description: params['description'], cost: params['cost'].to_f)
redirect '/user_list_contract'
rescue Exception => e
#error = e
haml :details_error
end
end
end
and the HAML (views):
%form(action="/user_add_contract" method="post")
%fieldset
%legend Φόρμα Νέου Συμβολαίου
%div{class: 'column1of2'}
%ul
%li
%label(for="name")Ονομασία:
%input#name(name="text" name="name")
%li
%label(for="description")Περιγραφή:
%textarea.email#description(type="text" name="description")
%li
%label(foor="cost")Κόστος:
%input#cost(type="number" name="cost")
%li
%input(type="submit" value="Εγγραφη") ή Ακύρωση
thanks
Related answer: How to handle floats and decimal separators with html5 input type number
The step="" attribute defaults to 1 here, meaning that it will truncate everything after the decimal point (or kick back an error, depending on how it's set up and what browser you're using).
Try setting step="0.01", on the input element, assuming that you just want to get to the nearest cent, and see if that works. Everything else in your code looks fine.*
Except that you have "foor" instead of "for" in your cost <label>
In Sequel Pro, created a table using this statement:
CREATE TABLE dogs(
id INT PRIMARY KEY NOT NULL,
name TEXT,
color TEXT
);
*auto increment, under extra in structures, is checked so Sequel Pro generate primary keys automatically*
Using mysql2, I author the method insert, in ruby file classdog.rb, to insert a new dog into Table dogs.
classdog.rb is below in its entirety:
require 'mysql2'
require "debugger"
class Dog
attr_accessor :name, :color, :id,
##db = Mysql2::Client.new(:host => '127.0.0.1', :username => 'root', :database => 'dogs')
def initialize(name, color)
#name = name
#color = color
end
def self.db
##db
end
def db
##db
end
def insert
db.query("INSERT INTO dogs(name, color) VALUE('#{name}', '#{color}')")
end
end
dog = Dog.new("simba", "grey")
puts dog.insert
To check if my code is working, I create this rspec file:
require "./classdog"
describe Dog do
describe "#insert" do
it "should insert a dog into the database" do
dog = Dog.new("simba", "grey")
sql_command = "SELECT * FROM dogs WHERE name = '#{dog.name}'";
row_hash = {"id" => 1, "name" => "simba", "color" => "grey"}
expect(Dog.db.query(sql_command).first).to eq(row_hash)
end
end
end
When I run my spec file in ruby using this command:
rspec spec_classdog.rb
My tests passes.
But there 2 things I don't understand:
The table itself only inserts a new dog when I run my spec file, spec_classdog.rb, using rspec. But when I run my ruby file, classdog.rb, no new dog is inserted.
Why is this happening? I expected that running my ruby file could result in new insertions while rspec is just to check to make sure that my method works. It is because I am not passing the parameters name and color to insert method (meaning something like this: dog.insert("spot", "black")?
When I have the following code in my classdog.rb file:
dog = Dog.new("simba", "grey")
puts dog.inspect
puts dog.name
puts dog.color
puts dog.id
Ruby puts:
Notice that dog.id has no output, as seen very clearly below:
dog = Dog.new("simba", "grey")
puts dog.id
why isn't ruby revealing the the id of dog in dog.id?
Is it because id was set as a primay key when the TABLE dog was created?
Will adding a specific column named dog_id help?
#PeterAlfvin: here is an image showing output of running puts dog.insert
Here's at least some of your problems:
Mysql doesn't auto-create primary key columns for you unless you specify auto_increment on the column
The insert method does not provide an id value, so it will always fail, since id is required to be non-null.
Given the above, any entries in your database were not created by the code you've shown.
Given that you've addressed that issue, then you've got the following:
The id value is only being created by mysql in the database, not in the Ruby Dog object, so it will always be nil in the object unless/until you set it (which you are not currently doing).
It has nothing to do with id being a primary key
Creating a dog_id attribute/column/field would have not effect on this
Ruby is revealing the value of dog.id; it's string representation just happens to be the empty string
The reason why a running spec spec_classdog.rb results in the inserting of a new dog is because my rspec file contains sql statements - therefor running rspec file results in sql statement begin carried out.
My rb file does not contain any rspec statements - classdog.rb simply exists to tell each dog object what I want it to do in Ruby-land. Also remember that in Ruby-land dog objects disappear after it is created & has carried out its call; it does not persist. Hence the need for database - resolves the issue of persistence.
See [this link]: How to create/maintain ID field in Sequel Pro via Ruby and mysql2 for answer to the 2nd part of the question.
Working on a learning management system. NOT a RoR person. Have the line of HAML to generate an average score based on quizzes taken:
="#{(QuizResult.average('score', :conditions => "user_id = #{#user.id}") * 100).round}%"
The quiz_results table has columns for used_id and score.
However, if there are no records in the quiz_results table, the page doesn't render. I want to check if any scores exist for that user id, and if so, to show the average. If none exist, I want to display "No quizzes taken." here's what I have:
19: %td
20: -if QuizResult('score', :conditions => "user_id = #{#user.id}").exists?
21: ="#{(QuizResult.average('score', :conditions => "user_id = #{#user.id}") * 100).round}%"
22: -else
23: %em No quizzes taken
I get the following error:
"ActionView::TemplateError (undefined method `QuizResult' for #ActionView::Base:0x7028c7f5cb88>) on line #20 of app/views/manage_users/show_all_users.haml:"
I've been struggling all day with this. Any suggestions? Thanks in advance, from an RoR noob.
I am guessing that QuizResult is a model class. If that is the case then you cannot use it as a method to look up an instance.
There are various ways that you can look up an object by some condition, for example:
- if QuizResult.find_by_user_id(#user_id).present?
To check for null (or nil as its referred to in Ruby) you can use the nil? method that Ruby itself provides or the Rails convenience method present? which returns true unless the object is nil, an empty string or empty collection.
You might check your quotes. For the condiitions hash, it looks like you should be using single quotes rather than doubles.