I have a parameter defined as such:
param :purchased, [true, false]
This leads to the following error:
Invalid parameter 'purchased' value true: Must be one of: true, false.
I have also tried:
param :purchased, [true, false, 'true', 'false']
but it didn't change anything.
Unfortunately also I cannot cover this in the tests as there the sent boolean is always cast into a String. Then the error looks like this:
Invalid parameter 'purchased' value "true": Must be one of: true, false.
Can anybody help here? Also I would be happy to just disable the validation of Apiepie as I am only interested in the documentation part.
Related
So I'm working on trying to learn GraphQL for ruby for a project.
I've almost got some parts of it up and running, but I'm having issues with other parts. There are plenty of tutorials out there that cover ultra-basics, but none of them seem to expand in the right directions.
I have a mutation to update my user. So far so good. I can look up the user by their ID, and update a single specific field. I can extend that to updating two fields.
What I cannot do, and this is looking insane, is generalize those fields -- at all. My user model will wind up with over 20 fields attached to it -- phone numbers, addresses, job title, etc etc.
When I create the mutation, I have to define the arguments that go into the resolve method. So far so good. I then define the fields the mutation can return. Again, so far so good.
Then I get to the actual resolve method.
The initial syntax isn't bad. def resolve(user_id:, name:, email:). Then you discover that despite setting required to false, you have to include all the values. You need to specify default values for the optional variables. So it becomes def resolve(user_id:, name: null, email: null) -- but that actually nulls out those values, you can't do partial updates. Worse yet, imagine having 20 fields you have to set this way. You can play games by trying to convert the arguments into a dictionary and rejecting null values -- but then you can't set properties to nil if they need to be nil again.
The solution: a double splat operator. Your syntax becomes def resolve(user_id:, **args). From what I can tell, it turns all remaining named arguments into a dictionary -- and I think unnamed arguments would become an array. Not sure how it would react with a mix of the two.
Full model becomes:
argument :user_id, ID, required: true#, loads: Types::UserType
argument :name, String, required: false
argument :email, String, required: false
field :user, Types::UserType, null: true
field :errors, Types::UserType, null: true
def resolve(user_id:, **args)
user = User.find(user_id)
if user.update(args)
{
user: user,
errors: []
}
else
{
user: nil,
errors: user.errors.full_messages
}
end
end
end
My app is a social platform and I want to have the support-team being able to maintain a blacklist for nicknames. Therefore I have a model Blacklist that contains the different names.
Now I tried with the validates_exclusion_of to map these into the in: but it raises the following error,
wrong number of arguments (given 1, expected 0)
validates_exclusion_of :nickname, in: -> {
where(Blacklist.select(:name).map(&:name).uniq.to_s)
}, message: "This nickname is not allowed"
You don't need to wrap the values to exclude within a lambda, it'll work as is if you pass just the Blacklist AR query result.
validates_exclusion_of :nickname,
in: Blacklist.select(:name).map(&:name).uniq.to_s,
message: 'This nickname is not allowed'
Notice, you can use the ActiveRecord::Calculations#pluck in order to get just the names from Blacklist, getting rid of the select and map combination, and use ActiveRecord::QueryMethod#distinct to get non-repeated values.
With that you don't need the uniq and to_s step. The last one, because the validation is waiting for an enumerable object, and you'd be passing a string.
Something like:
validates_exclusion_of :nickname,
in: Blacklist.distinct.pluck(:name),
message: 'This nickname is not allowed'
The where you're using on the validation won't work, as it's applying a WHERE statement without passing a column name to check, so you'll get an ActiveRecord::StatementInvalid.
I am very curious about what this expected behavior is for Rails 4.2 and I have not been able to find an answer to my question.
I'm adding validation to a model on all the time attributes. I want them to ONLY accept integers, not numerical strings. The data type in my schema for this attribute is an integer. I have my validation like so:
RANGE = 0..59
validates :start_minute, inclusion: { in: RANGE }, numericality: true
I've tried these other validations as well. I get the same result.
validates_numericality_of :start_minute, inclusion: { 0..59, only_integer: true }
validates :start_minute, inclusion: { in: 0..59 }, numericality: { only_integer: true }
When I pass my params to my controller from the request spec, start_minute is "12". BUT when I look at the created object, the start_minute is 12.
According to this article by ThoughtBot:
"This is because Active Record automatically type casts all input so that it matches the database schema. Depending on the type, this may be incredibly simple, or extremely complex."
Shouldn't the object not be able to be created? Is the typecasting taking precedence of my validation? Or is there something wrong with my validation? I appreciate any insight to this as I haven't been able to determine what is happening here. I've also created a model spec for this and I'm still able to create a new object with numerical strings.
Thank you for any insight you can give on this. I am still learning the magic of Rails under the hood.
From the rails docs it says,
If you set :only_integer to true, then it will use the
/\A[+-]?\d+\z/
What it(only_integer validator) does is that it validates that the format of value matches the regex above and a string value that contains only numbers like '12' is a match(returns a truthy value which is 0 and passes the validation).
2.3.1 :001 > '12' =~ /\A[+-]?\d+\z/
=> 0
My delete api takes a boolean query params /users?force=true. In my controller, I added the following for apipie documentation
param :force, [true, false], required: false, desc: "query parameter."
When I make the api call, I get
Apipie::ParamInvalid (Invalid parameter 'force' value "true": Must be one of: <code>true</code>, <code>false</code>.).
I tried passing /users?force=1, /users?force, but 1 is treated as "1" and not passing anything is treated as nil and both calls fail. How do I make the validation pass?
Note: I am aware that the api definition is not restful.
The param you are passing through ends up being "true" as in string, not a boolean, that's why it's failing. Without any type casting, rails has got no idea that you're trying to pass boolean, not a string.
You should whitelist "true", "false" as strings in valid options like:
param :force, [true, false, "true", "false"], required: false, desc: "query parameter."
I ran into a bug where i left out a space like this
#entries = [#<Entry id: 3806, approved: false, submitted: false]
#entries.any?&:submitted? => true
#entries.any? &:submitted? => false
how does the space change the behavior. Entry.submitted? => false for all the elements in the array, the second line has the desired behavior.
The problem is that & is also a Unary Operator that has a precedence.
When you do this:
#entries.any?&:submitted?
You are actually doing is this:
(#entries.any?)&(:submitted?)
Which results in true. Since there are entries in #entries and the symbol :submitted? has a truth value of true.
When you do this:
#entries.any? &:submitted?
What you are actually doing is this:
#entries.any?(&:submitted?)
Which is what you really want.
The reason #pilcrow got his to work is that he/she was using the following class:
class Entry
def submitted?
true
end
end
To reproduce the result, use the following class:
class Entry
def submitted?
false
end
end
P.S: Same thing with #fl00r's example:
[1,2,nil].any?&:nil? and [1,2,nil].any? &:nil?
[1,2,nil].any? results in true and :nil? has a truth value of true, so the results are the same since [1,2,nil] also contains a nil value, but the calculations are different.