mysql and rspec bug - mysql

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.

Related

Rails 2 hook to modify data before it is read/written to MySQL DB

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

Ruby on Rails, save! returns true but database row is unchanged (only in production; works fine in development)

(Using jruby-1.6.8, Rails 2.3.14)
I have a Ruby object, experiment, I'm trying to use to update an existing row in a mysql database with .save!, and even though save! and valid? both return true on the object, the db row is unchanged after doing:
>> result = experiment.save!
=> true
Has anybody ever run into this problem? Also, it works fine in development; production is where the problem is occurring. How can that break it? There is no error to indicate that the row was not updated by save!. The only thing that changes is that the row's updated_at column gets a later time written to it.
I added code later on that grabs the object's attributes individually and does an sql UPDATE with those. This works, but I want to get to the root of the save! problem:
sql = "UPDATE experiment SET attr1 = #{experiment.attr1}, attr2 = #{experiment.attr2} WHERE experimentID = #{experiment.experimentID}"
Experiment.connection.execute(sql)
Here is what the experiment object looks like:
>> puts "#{experiment.inspect}"
=> #<Experiment experimentID: 177, attr1: 13, attr2: 13, attr3: nil ... >
SELECT output of the database row before doing save!:
>> test_sql = "SELECT * FROM experiment WHERE experimentID = #{experiment.experimentID}"
>> sql_hash = Experiment.connection.execute(test_sql)[0]
>> puts "before save: SQL_TEXT: #{sql_hash.inspect}"
=> before save: SQL_TEXT: {"experimentID"=>177, "attr1"=>0, "attr2"=>0, "attr3"=>"", ... }
Then, experiment.save! should update attr1 and attr2 to 13. However, they are still 0.
Here's some of the applicable class declaration:
class Experiment < ActiveRecord::Base
set_table_name :experiment
set_primary_key :experimentID
validates_presence_of :attr1, :attr2
...
end
EDIT: Per commenter's suggestion, I tried setting production logger level to debug and looking at the actual SQL being queried by experiment.save!:
Experiment Update (0.0ms) UPDATE `experiment` SET `updated_at` = '2013-09-02 19:56:11' WHERE `experimentID` = 178
Why would save! choose to not update any of the attributes???
Please let me know if there are any other details I can provide.
Active Record tracks which columns have changed and only saves these to the database. This change tracking is not all knowing, for example
user.name.gsub!(...)
wouldn't be detected as a change prior to rails 4.2.
There are 2 ways around this. One is never to change any Active Record object attribute in place. If you can't do this then you must tell Active Record what you have done, for example
user.name.gsub!(...)
user.name_will_change!
lets Active Record know that the name attribute has changed

Getting a field having `text` type

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.

How to get Ruby MySQL returning PHP like DB SELECT result

So I use the PDO for a DB connection like this:
$this->dsn[$key] = array('mysql:host=' . $creds['SRVR'] . ';dbname=' . $db, $creds['USER'], $creds['PWD']);
$this->db[$key] = new PDO($this->dsn[$key]);
Using PDO I can then execute a MySQL SELECT using something like this:
$sql = "SELECT * FROM table WHERE id = ?";
$st = $db->prepare($sql);
$st->execute($id);
$result = $st->fetchAll();
The $result variable will then return an array of arrays where each row is given a incremental key - the first row having the array key 0. And then that data will have an array the DB data like this:
$result (array(2)
[0]=>[0=>1, "id"=>1, 1=>"stuff", "field1"=>"stuff", 2=>"more stuff", "field2"=>"more stuff" ...],
[1]=>[0=>2, "id"=>2, 1=>"yet more stuff", "field1"=>"yet more stuff", 2=>"even more stuff", "field2"=>"even more stuff"]);
In this example the DB table's field names would be id, field1 and field2. And the result allows you to spin through the array of data rows and then access the data using either a index (0, 1, 2) or the field name ("id", "field1", "field2"). Most of the time I prefer to access the data via the field names but access via both means is useful.
So I'm learning the ruby-mysql gem right now and I can retrieve the data from the DB. However, I cannot get the field names. I could probably extract it from the SQL statement given but that requires a fair bit of coding for error trapping and only works so long as I'm not using SELECT * FROM ... as my SELECT statement.
So I'm using a table full of State names and their abbreviations for my testing. When I use "SELECT State, Abbr FROM states" with the following code
st = #db.prepare(sql)
if empty(where)
st.execute()
else
st.execute(where)
end
rows = []
while row = st.fetch do
rows << row
end
st.close
return rows
I get a result like this:
[["Alabama", "AL"], ["Alaska", "AK"], ...]
And I'm wanting a result like this:
[[0=>"Alabama", "State"=>"Alabama", 1=>"AL", "Abbr"=>"AL"], ...]
I'm guessing I don't have the way inspect would display it quite right but I'm hoping you get the idea by now.
Anyway to do this? I've seen some reference to doing this type of thing but it appears to require the DBI module. I guess that isn't the end of the world but is that the only way? Or can I do it with ruby-mysql alone?
I've been digging into all the methods I can find without success. Hopefully you guys can help.
Thanks
Gabe
You can do this yourself without too much effort:
expanded_rows = rows.map do |r|
{ 0 => r[0], 'State' => r[0], 1 => r[1], 'Abbr' => r[1] }
end
Or a more general approach that you could wrap up in a method:
columns = ['State', 'Abbr']
expanded_rows = rows.map do |r|
0.upto(names.length - 1).each_with_object({}) do |i, h|
h[names[i]] = h[i] = r[i]
end
end
So you could collect up the rows as you are now and then pump that array of arrays through something like what's above and you should get the sort of data structure you're looking for out the other side.
There are other methods on the row you get from st.fetch as well:
http://rubydoc.info/gems/mysql/2.8.1/Mysql/Result
But you'll have to experiment a little to see what exactly they return as the documentation is, um, a little thin.
You should be able to get the column names out of row or st:
http://rubydoc.info/gems/mysql/2.8.1/Mysql/Stmt
but again, you'll have to experiment to figure out the API. Sorry, I don't have anything set up to play around with the MySQL API that you're using so I can't be more specific.
I realize that php programmers are all cowboys who think using a db layer is cheating, but you should really consider activerecord.

Rails 3 - how to find last inserted ID from mysql table

in my controller I have following sequence of commands:
SAVE DATA INTO FIRST TABLE
_get ID of inserted item into table from first step_
SAVE DATA INTO SECOND TABLE WITH ID FROM FIRST COMMAND
if FIRST.save && SECOND.save
do something
And I am wondering, how to get id of item, which is immediately inserted into database... I tried to googling, but I can't find this information...
Thanks in advance for your hints
# SAVE DATA INTO FIRST TABLE
first_instance = FirstModel.new( :foo => :bar )
first_save = first_instance.save
# _get ID of inserted item into table from first step_
first_instance_id = first_instance.id
# SAVE DATA INTO SECOND TABLE WITH ID FROM FIRST COMMAND
second_save = SecondModel.new( :first_model_id => first_instance_id ).save
if first_save && second_save
# do something
end
After saving a model, you can access it's id variable:
#user = User.new
puts #user.id
# => nil
#user.save
puts #user.id
# => 1
Could you just search your database by the updated_at field in your model?
To get the most recent record:
#model1 = Model1.order("updated_at DESC").limit(1)
or better yet, upon saving Model1 in the first place:
#model1 = model1.save
To assign:
#model2.model1_id = #model1.id
Note: if you actually want to save the ID of a specific record, finding the last isn't the best way to go.
This is because another record could be inserted by a different user, right after you inserted Model1 and right before you call Model2.
If you want the two to save together or not at all, you can look into transactions: http://api.rubyonrails.org/classes/ActiveRecord/Transactions/ClassMethods.html
If you're happy with Model1 saving on its own before worrying about Model2, then simply assign the variables as I did above.