Name.random.then yields the random name to a block wherein we create a new user. Following the example from the previous post, we then assign the newly instantiated name inside the #tap block.
Object#tap yields the object it is called upon to a block but the resulting value is the value of the object (not the value of the block). For example:
>1.tap{|obj|putsobj*2}2=>1
The value 1 is passed to the block where we output the result of obj * 2 (2). However, the value of the expression is the value of the object: 1.
Say we have a User class:
classUserattr_accessor:nameend
To create a new user, we might do something like this:
The return value of the above method is a new user object with its name assigned to the value of @name.
You can see we have to create the temporary variable new_user to hold the value of the new user in order for us to assign its name. We then return the value of the temporary variable.
#tap removes the need for the temporary variable.
defuserUser.new.tap{|obj|obj.name=@name}end
In the above example, we call #tap on the newly instantiated user object. Inside the block, we assign a name to the new user. The resulting value is the newly instantiated user object with its name assigned the value of @name.
In Ruby, classes are open. They can be modified at any time. You can add new methods to existing classes and even re-define methods. Rubyists call this monkey-patching.
In the example below, I’ve added String#word_count to return the number of words in a string.
classStringdefword_countself.split.countendend
"Don't put your blues where your shoes should be.".word_count# => 9
However, this change is global. What happens if someone else defines their own version of String#word_count based on different rules? Say, they might want to count the individual parts of a contraction, or ignore digits and any non-word characters.
Refinements allow us to monkey-patch classes and modules locally.
Here, I’ve created a new module and used Module#refine to add word_count to String.
The refinement isn’t available in the global scope:
"Take my shoes off and throw them in the lake.".word_count# => NoMethodError: undefined method `word_count' for…
To activate the refinment, I need to use using:
usingMyStringUtilities"Take my shoes off and throw them in the lake.".word_count# => 10"A pseudonym to fool him".word_count# => 5
If I activate the refinement within a class or module, the refinement is available until the end of the class or module definition.
Below, the refinement is not available at the top level because it is scoped to MyClass.
moduleMyStringUtilitiesrefineStringdodefword_countself.split.countendendendclassMyClassusingMyStringUtilitiesdefself.number_of_words_in(string)string.word_countendend"Out on the wily, windy moors".word_count#=> NoMethodErrorMyClass.number_of_words_in("Out on the wily, windy moors")#=> 6
Audiobooks tend to take up a lot of disk space so often streaming is the only practical method of delivery. Piotr writes about using AWS to convert MP3s and using Cloudfront to handle the streaming.
Often when I begin a project, I find myself rolling back migrations, dropping the database, renaming tables and columns whilst I figure out the shape of the model. If I need to rename a column, I’ll just update or delete the corresponding migration.
However, it is possible to change a column’s name in a migration.
Say, I have a table tasks and I want to change the name of the title column to name. First I ask Rails to generate the migration:
class RenameTitleToNameInTasks < ActiveRecord::Migration[6.0]
def change
end
end
I then remove change and add the up and down migration like so:
class RenameTitleToNameInTasks < ActiveRecord::Migration[6.0]
def up
rename_column :tasks, :title, :name
end
def down
rename_column :tasks, :name, :title
end
end
rename_column takes three arguments. The first is the table name. The second is the existing column name. The third is the new name.
Above, I have defined the up and down migration. That is, the up migration does the renaming bit when we migrate the database. The down migration runs when we roll back the migration – in this instance, it reverts the column’s name back to the original.
Before I run the migration, I give the schema a quick check.
Of course, five minutes later, I might decide that the new name probably isn’t the best and I can run rails db:rollback. On doing so, I can see that the column name has reverted to title.