I am teaching myself Crystal-lang and I came across a section in the documentation that I don't quite understand.
Here is the documetation page.
On that page it gives the following code:
class Person
#age = 0
def initialize(#name : String)
end
end
This is followed by the following statement:
This will initialize #age to zero in every constructor. This is useful to avoid duplication, but also to avoid the Nil type when reopening a class and adding instance variables to it.
Can someone please explain, or show me an example of the bolded behaviour? I'm not certain I understand what it means by "reopening a class and adding an instance variable to it".
Here is an example of reopening a class, and adding an instance variable to it:
class Person
#age = 0
def initialize(#name : String)
end
end
# Usually in another file
class Person
def gender=(gender : String)
#gender = gender
end
def gender
#gender
end
end
person = Person.new("RX14")
typeof(person.gender) # => String | Nil
person.gender # => nil
person.gender = "???"
person.gender # => "???"
We add the #gender instance variable, which is not initialized in the def initialize. The compiler infers the type of #gender to be String | Nil, since it is assigned to a string in gender=, but it is not initialized in the constructor, meaning it can also be nil.
However, we can add a default value to the #gender instance variable, which applies to all constructors, define before or after the default:
class Person
#age = 0
def initialize(#name : String)
end
end
# Usually in another file
class Person
#gender = "unknown"
def gender=(gender : String)
#gender = gender
end
def gender
#gender
end
end
person = Person.new("RX14")
typeof(person.gender) # => String
person.gender # => "unknown"
person.gender = "???"
person.gender # => "???"
This avoids the #gender variable getting the String | Nil type, since it is initialized to "unknown" when Person is constructed. Since Nil types are often avoided, this is an important tool to have.
Related
I have yet another problem with testing in Elixir and Phoenix. I have a module which represents users which looks like so:
defmodule AppExample.Accounts.User do
use Ecto.Schema
import Ecto.Changeset
alias SecuritytrailsApi.Accounts.User
schema "users" do
field :email, :string
field :password, :string, virtual: true
field :password_hash, :string
field :last_login, :naive_datetime
timestamps()
end
...
def registration_changeset(%User{} = user, attrs \\ %{}) do
user
|> changeset(attrs) # validating and castnig email
|> validate_required([:password])
|> put_password_hash # creates password hash from password
|> set_default_date
end
defp set_default_date(chanegset) do
case changeset do
%Ecto.Changeset{valid?: true} ->
put_change(changeset, :last_login, Ecto.DateTime.utc)
_ -> changeset
end
end
end
And let's say I have an Accounts context which is responsible for creating new accounts. Like so:
defmodule AppExample.Accounts do
def create_user(attrs \\ %{}) do
%User{}
|> User.registration_changeset(attrs)
|> Repo.insert
end
end
So as you can ese, the field last_login is inserted automatically using Ecto.DateTime.utc to get the now() time. So, my questions are the following:
1) Is this the correct way of adding a default now() value to a field in Ecto?
2) How can I write tests for this? I don't know how can I get the value of that particular field. Please check the example below for details.
defmodule AccountsTest do
#valid_attrs %{email: "mail#mail.com", password: "123123"}
test "create_user/1 creates a new users with last_login set to now" do
assert {:ok, %User{} = user} = Accounts.create_user(#valid_attrs)
assert user.email == "mail#mail.com"
assert user.last_login == ### ????? How can I get that NOW value here for the test
end
end
1) Is this the correct way of adding a default now() value to a field in Ecto?
You can default this field to now() in a migration instead of computing its value manually.
alter table(:users) do
modify :last_login, :string, default: fragment("now()")
end
2) How can I write tests for this?
You could assert that the value is in the last e.g. 15 seconds:
assert DateTime.diff(DateTime.utc_now, user.last_login, :second) <= 15
In Theory: When game_start is called and the user inputs 'easy' the variable quiz_paragraph should assume the value of easy_paragprah
In Actuality: The value of quiz_paragraph is set to "
I can verify that user_difficulty is being set properly and is feeding 'easy' properly into set_difficulty. The if statement runs but doesn't alter the value of quiz_paragraph.
Can anyone tell me what I am missing here?
# Easy Paragraph
easy_paragraph = '''\n___1___ is a programming ___2___ used to solve simple and complex problems. ___1___ like
many languages can be used to display a message to a user using ___3___. Try it sometime.
To display this common message by typing ___3___ '___4___ World!' \n'''
# Init Variables
user_difficulty = ""
quiz_paragraph = ""
# Ask user difficulty
def ask_difficulty():
user_difficulty = raw_input("What difficulty level would you like? ")
return user_difficulty
# Difficulty sets returned Paragraph
def set_difficulty(difficulty):
if difficulty == "easy":
quiz_paragraph = easy_paragraph
return quiz_paragraph
else:
print '''Sorry, that is not a valid choice.'''
# Start the game
def game_start():
set_difficulty(ask_difficulty())
print quiz_paragraph
# Game Start Test
game_start()
It looks like you have an issue with scope.
The quiz_paragraph variable within set_difficulty, is not the same as the 'global' quiz_paragraph. Although they currently share the same name, you could rename the one inside set_difficulty to foo and you would have the same result.
You need to set the 'global' quiz_paragraph to the value returned by set_difficulty, or a better another local variable within game_start. That is,
def game_start():
quiz_paragraph = set_difficulty(ask_difficulty())
print quiz_paragraph
or
def game_start():
my_difficulty = set_difficulty(ask_difficulty())
print my_difficulty
I'm getting the error:
Wrong number of arguments (2 for 1)
On my Task model when I defined my method to update all status of tasks. What is the correct syntax?
class Task < ActiveRecord::Base
belongs_to :user
def self.toggle(user, groups)
groups.each do |status, ids|
user.tasks.update_all({status: status.to_s}, {id: ids}) #=> error here
end
end
end
class GroupIdsByStatus
def self.group(options = {})
result = Hash.new {|h,k| h[k] = []}
options.reduce(result) do |buffer, (id, status)|
buffer[status.to_sym] << id
buffer
end
result
end
end
class TasksController < ApplicationController
def toggle
groups = GroupIdsByStatus.group(params[:tasks])
Task.toggle(current_user, groups)
redirect_to tasks_path
end
end
The method update_all receives a single Hash as its sole argument. You need to put all of the updates into the single argument:
user.tasks.update_all(status: status.to_s, id: ids)
# The above line of code is identical to:
# user.tasks.update_all( {status: status.to_s, id: ids} ) # < Notice the curly braces
More information on this method is shown in the Rails Relation Docs
I'm using Lupa for filtering (Lupa is framework independent, easy to test, benchmarks well against has_scope, etc.).
How can I chain scopes with OR logic in Lupa? This works great when the params are known ahead of time, but not when the filters are user-determined.
Perhaps the solution is in the dates_between section of the docs, but I cannot seem to figure it out.
I have a form that allows users to select the gender of an animal - male, female - using checkboxes. I want users to be able to check one, the other, or both and return the intuitive result.
Lupa's author's blog (#edelpero) nudged me in the right direction, but I feel either I am misunderstanding something or the docs might be improved (probably the former). Maybe some type-o's below as I'm simplifying from an app I'm kicking through, but here is a solution:
form
= form_tag pups_path, method: :get do
= check_box_tag 'male', 'm'
= check_box_tag 'female', 'f'
# add a hidden field so that the gender method in the scope class is called
= hidden_field_tag 'gender', 'anything' # tag must have a value
= submit_tag :search
controller
class PupsController < ApplicationController
def index
#pups = PupSearch.new(Pup.all).search(search_params)
end
protected
def search_params
params.permit(:male, :female, :gender) # permit all three
end
end
scope class
class WalkSearch < Lupa::Search
class Scope
def male
# leave blank to avoid chaining; handled in gender below
end
def female
# leave blank to avoid chaining; handled in gender below
end
def gender
# standard procedure for OR logic
scope.joins(:pup).where('pups.male_female' => [
search_attributes[:male],
search_attributes[:female]
])
end
end
end
How do you update an instance variable from inside a block?
E.g.
def initialize_people(people)
people.each do |person|
person = "Bob" if person.nil?
end
end
#first = "Adam"
#second = "Eve"
#third = nil
people = [#first, #second, #third]
initialize_people(people)
puts people
# outputs
# Adam
# Eve
# would like it to output
# Adam
# Eve
# Bob
The expression:
people.each |person|
Defines person locally to the loop. So, all your loop did is set the locally scoped person to Bob when it was found to be nil. You could do something like this:
def initialize_people(people)
people.each_with_index do |person, i|
people[i] = "Bob" if person.nil?
end
end
Or shorter:
i = people.find_index(nil)
people[i] = "Bob" if i