What does `!a ? b` mean in a Nix expression? (i.e., Boolean negation of an attribute set) - nix

The snippet below got me confused because I know that the question mark in the function head is to supply default values, but in the if block it seems as if Boolean negation is attempted on an attribute set. This is not possible therefore I presume that I'm misreading this expression.
{ a ? null
, b ? 27
, ... }#args:
if !args ? a then
# ...
else
# ...

!a ? b is equivalent to !(a ? b). To break it down:
nix-repl> a = { b = "0118 999 881 999 119 725...3"; }
nix-repl> a ? b # Does the `a` attribute set have
true # have an attribute named `b`?
nix-repl> !(a ? b) # Negate the above results
false
nix-repl> !a ? b # `?` operator has higher
false # precedence then `!`, hence
# the parens can be omitted
The relevant Nix expression language operators in the if block:
+--------------+---------------+--------------------------------+------------+
|Name | Syntax | Description | Precedence |
+--------------+---------------+--------------------------------+------------+
|Has Attribute | e ? attrpath | Test whether set e contains | |
| | | attribute denoted by attrpath; | 4 |
| | | return true or false. | |
+--------------+---------------+--------------------------------+------------+
|Not | ! e | Boolean negation. | 8 |
+--------------+---------------+--------------------------------+------------+

Related

Validation of linked booleans in model - Rails 5.2

My application has two linked boolean fields in one of the data tables that mark categories into which the record may fall. That is, these two fields may be true or false, and I require one (and only one) of them to be true for the record to be valid. (In case anyone's wondering, there may be more categories in future, so a simple binary option with a single field won't work for long and I want to solve the root problem.)
The options for the values and their overall validity in my application are thus:
field a | 0 | 1 | 0 | 1
field b | 0 | 0 | 1 | 1
========================================
valid? | N | Y | Y | N
I've got the following validation in place in the model:
validates :a, presence: true, unless: :b
validates :b, presence: true, unless: :a
This catches some error cases by ensuring that at least one of the fields is true. However, this validation allows both fields also to be true, which I don't want.
Is there any way to enforce this final validation option in the model (the fourth column in the table up top)?
At present, I'm catching it in the controller while the record's being created as follows (forcing "both true" to "both false", which the model will catch and reject):
if params[:word][:a] == "1" && params[:word][:b] == "1"
params[:word][:a] = "0"
params[:word][:b] = "0"
end
but I'm sure there must be a more elegant way to do it!
You could try a custom validator like so:
validate :a_xor_b
...
def a_xor_b
errors.add(:a_xor_b, "only one must be true") unless a ^ b
end
Essentially you want the XOR operator.
EDIT (in response to #jvillian 's comment):
If you want to make sure there is only one true value in a number of boolean values I suggest something like the following using .inject:
xor_fields = %i[a b c ...]
def validate_xor
result = xor_fields.inject(false) do |acc, curr|
val = send(curr)
return errors.add(:xor, 'Multiple true values') if acc && val
acc ^ val # in this approach equivalent to acc || val
end
errors.add(:xor, 'No true value') unless result
end

Print object attributes in a table in rails console

I know there are some tools like hirb which let you print the contents of a database table in a table format in the console.
But this does not work with object attributes, i.e. those defined in the model, not in the database. Is there some similar tool that lets print these attributes, in proper layout format in the rails console similar to hirb?
For added flexibility, you can use something like terminal-table. With it, you can display anything in a table.
table = Terminal::Table.new do |t|
t << ['One', 1]
t << :separator
t.add_row ['Two', 2]
t.add_separator
t.add_row ['Three', 3]
end
puts table
# +-------+---+
# | One | 1 |
# +-------+---+
# | Two | 2 |
# +-------+---+
# | Three | 3 |
# +-------+---+

Ruby on Rails: accessing and expanding a SQL query via a model method

The Ruby on Rails application I am working on has an exchange_rate table that I wish to access while doing calculations to convert one currency to another. The table looks like this:
id | base_currency_id | currency_id | exchange_rate |
---+------------------+-------------+---------------+
2 | 1 | 2 | 0.9345 |
3 | 1 | 3 | 0.5000 |
The exchange_rate model looks like this:
class ExchangeRate < ActiveRecord::Base
include ActiveModel::ForbiddenAttributesProtection
validates :base_currency_id, :currency_id, :exchange_rate, presence: true
...
def self.matching_exchange_rate(base_currency)
ExchangeRate.where("base_currency_id = ?",base_currency)
end
# Commented out as I can't get it to work yet:
# def self.matching_exchange_rate (base_currency, target_currency_id)
# where("base_currency_id = ?",base_currency " and currency_id = ?",target_currency_id)
# end
I have been invoking/calling the incomplete-but-working function matching_exchange_rate like this:
x_rate = ExchangeRate.matching_exchange_rate(user_currency_id)
puts "x_rate = " + x_rate.to_s
This displays in the log as:
x_rate = #<ExchangeRate::ActiveRecord_Relation:0x0000010b434c78>
I would love help with either of these questions:
(1) How do I change the matching_exchange_rate method so that it uses two input parameters? I have tried a large number of different combinations--one of which is in the commented-out code above--but so far I have had no luck.
(2) How do I access the exchange_rate from return value? (e.g., the ActiveRecord_Relation)
Thanks in advance for any help,
Pierre
You can use pluck to select exchange_rate with supplied condition as follows:
def self.matching_exchange_rate(base_currency, target_currency_id)
where(
"base_currency_id = ? and currency_id = ?", base_currency,target_currency_id
).pluck(
'exchange_rate'
).first
end
The code above is replacement to your commented out method. As far as the following output you've posted is concerned:
x_rate = #<ExchangeRate::ActiveRecord_Relation:0x0000010b434c78>
where returns an ActiveRecord::Relation. You could have used first method on your AR query to get reference to the first ExchangeRate record on which you could have called exchange_rate on as follows:
x_rate = ExchangeRate.matching_exchange_rate(user_currency_id).first.exchange_rate
puts "x_rate = " + x_rate.to_s

How can I implement a form (login) that is visible on every page?

Suppose I want my _Layout to include a login form instead of the default separate login view. How should I do this? Searching around has given me very vague/uncertain answers.
___________________________________
| _______________________________ |
| | Logo _____ | |
| | | | | |
| | |Login| | |
| | |_____| | |
| |_______________________________| |
| |
| Content |
| |
| |
| Footer |
|___________________________________|
Currently I have created a LoginController and corresponding LoginViewModel to handle the given username/password combo. Then the form POSTS to LoginController/Index. This solution is riddled with problems. For example, how do I provide the correct model to the _Layout? How do I perform validation? What about handling ReturnUrl?
Creating an abstract class for every other single model to inherit from, then giving it to _Layout, seems ugly.
Not the cleanest solution:
The form itself submits to a designated controller/action:
#Using Html.BeginForm("Login", "Account")
...
End Using
The controller has only the HTTPPOST action:
Public Class AccountController
' POST: /Account/Login
<HttpPost>
Function Login(ByVal viewModel as LoginViewModel) As ActionResult
WebSecurity.Login(viewModel.Username, model.Password, False)
TempData("LoginViewModel") = viewModel
Return RedirectToLocal(viewModel.ReturnUrl)
End Function
End Class
Then in my _Layout where I include the login partial:
#Code
Dim model As LoginViewModel
If TempData("LoginViewModel") IsNot Nothing Then
model = TempData("LoginVieWModel")
Else
model = New LoginViewModel
End If
Html.RenderPartial("_LoginPartial", model)
End Code

What Does ActiveRecord::MultiparameterAssignmentErrors Mean?

I have a rails form with a datetime_select field. When I try to submit the form, I get the following exception:
ActiveRecord::MultiparameterAssignmentErrors in WidgetsController#update
1 error(s) on assignment of multiparameter attributes
If it's a validation error, why don't I see an error on the page?
This is in Rails 2.0.2
It turns out that rails uses something called Multi-parameter assignment to transmit dates and times in small parts that are reassembled when you assign params to the model instance.
My problem was that I was using a datetime_select form field for a date model field. It apparently chokes when the multi-parameter magic tries to set the time on a Date object.
The solution was to use a date_select form field rather than a datetime_select.
Super hack, but I needed to solve this problem right away for a client project. It's still a bug with Rails 2.3.5.
Using either date_select or datetime_select, if you add this to your model in the initialize method, you can pre-parse the passed form-serialized attributes to make it work:
def initialize(attributes={})
date_hack(attributes, "deliver_date")
super(attributes)
end
def date_hack(attributes, property)
keys, values = [], []
attributes.each_key {|k| keys << k if k =~ /#{property}/ }.sort
keys.each { |k| values << attributes[k]; attributes.delete(k); }
attributes[property] = values.join("-")
end
I am using this with a nested, polymorphic, model. Here's a question I had showing the models I'm using. So I needed accepts_nested_attributes_for with a datetime.
Here's the input and output using the console:
e = Event.last
=> #<Event id: 1052158304 ...>
e.model_surveys
=> []
e.model_surveys_attributes = [{"survey_id"=>"864743981", "deliver_date(1i)"=>"2010", "deliver_date(2i)"=>"2", "deliver_date(3i)"=>"11"}]
PRE ATTRIBUTES: {"survey_id"=>"864743981", "deliver_date(1i)"=>"2010", "deliver_date(2i)"=>"2", "deliver_date(3i)"=>"11"}
# run date_hack
POST ATTRIBUTES: {"survey_id"=>"864743981", "deliver_date"=>"2010-2-11"}
e.model_surveys
=> [#<ModelSurvey id: 121, ..., deliver_date: "2010-02-11 05:00:00">]
>> e.model_surveys.last.deliver_date.class
=> ActiveSupport::TimeWithZone
Otherwise it was either null, or it would throw the error:
1 error(s) on assignment of multiparameter attributes
Hope that helps,
Lance
This is not a bug in Rails, it is the intended behavior of the multi-parameter attribute writer. I'm willing to bet that the original poster's deliver_date field in the database is a varchar as opposed to a date or datetime type. ActiveRecord uses each part of the multi-parameter attribute to send to the new method of the serialized type. The number 1, 2, 3, etc indicates the constructor parameter position and the "i" tells ActiveRecord to call to_i on the parameter before passing it to the constructor. In this case they are all "i's" because DateTime.new(year, month, day) expects three Integers not three Strings.
If the deliver_date column in the database isn't a type that's serialized to a DateTime then ActiveRecord will throw a ActiveRecord::MultiparameterAssignmentErrors exception because String.new(2010,2,11) won't be successful.
Source:
https://github.com/rails/rails/blob/v3.0.4/activerecord/lib/active_record/base.rb#L1739
ActiveRecord throws the MultiparameterAssignmentErrors exception when you try to set an invalid date to a models attribute.
Try to pick a Nov 31 date from the date_select or datetime_select dropdown and you will get this error.
Like Zubin I've seen this exception when the form submits a month as a month name rather than a numerical month string (eg. October rather than 10).
One user agent I've encountered seems to submit the contents of the option tag rather than the value attribute:
Mozilla/5.0 (SymbianOS/9.2; U;
Series60/3.1 NokiaE66-1/300.21.012;
Profile/MIDP-2.0
Configuration/CLDC-1.1 )
AppleWebKit/413 (KHTML, like Gecko)
Safari/413
So in the case of submitting a multi-parameter date from a helper generated select (from date_select helper) your params will have:
"event"=> {
"start_on(2i)"=>"October",
"start_on(3i)"=>"19",
"start_on(1i)"=>"2010"
}
This creates an exception: ActiveRecord::MultiparameterAssignmentErrors: 1 error(s) on assignment of multiparameter attributes
Most user agents will correctly submit:
"event"=> {
"start_on(2i)"=>"10",
"start_on(3i)"=>"19",
"start_on(1i)"=>"2010"
}
This error can also occur with webrat/cucumber when filling in form data using a table.
eg this doesn't work:
When I fill in the following:
| report_from_1i | 2010 |
| report_from_2i | January |
| report_from_3i | 1 |
| report_to_1i | 2010 |
| report_to_2i | February |
| report_to_3i | 1 |
but this does:
When I fill in the following:
| report_from_1i | 2010 |
| report_from_2i | 1 |
| report_from_3i | 1 |
| report_to_1i | 2010 |
| report_to_2i | 2 |
| report_to_3i | 1 |
In my case the ActiveRecord am/pm plugin caused the error through an incorrect alias_method_chain resulting in an StackLevelTooDeep exception.
The plugin was included by the unobtrusive_date_picker plugin.
The look into this before hacking away.

Resources