cypriss / mutations Goto Github PK
View Code? Open in Web Editor NEWCompose your business logic into commands that sanitize and validate input.
License: MIT License
Compose your business logic into commands that sanitize and validate input.
License: MIT License
Is it possible to pass an AR record with errors and have the errors be added to the error list?
Something like:
user = User.new(name: "Leo", email: "[email protected]")
unless user.save
add_errors(user)
end
It'd be nice to have that kind of functionality if it is not possible at the moment.
I'm curious to know what others have done in the way of connecting a form to a mutation. In my scenario, I have a single mutation that accepts a large list of inputs for dealing with all of a record's associations. It accepts canonical names (instead of IDs), and performs lookups of those names while building the parent model. This works fantastically for an API, but I'm struggling to understand how to best implement a form around it.
It's clear that I should not use Rails's form helpers (such as form_for, fields_for) since that would require heavy use of accepts_nested_attributes_for in my models and that would defeat the purpose of encapsulating all of my ActiveRecord interaction into a Mutation. So, the only remaining option would be to use the non-model Rails form tag helpers (or no helpers at all) and then I have no way of handling errors.
Has anyone ventured into the land of building complex forms backed by Mutations in Rails?
I noticed this when using Mutations with Rails. If I change a file, Rails reloads all the classes. Then Mutations complains that <class> isn't the right class
, even though it is, conceptually.
require 'mutations'
class Thing; end
class Mutant < Mutations::Command
required { model :thing }
end
thing_1 = Thing.new
p Mutant.run(:thing => thing_1).success? # => true
Object.send(:remove_const, 'Thing')
class Thing; end
thing_2 = Thing.new
p Mutant.run(:thing => thing_1).success? # => true
p Mutant.run(:thing => thing_2).success? # => false
I have a controller inside of app/controllers/api/guides_controller.rb
(it is namespaced as Api::GuidesController
).
I have a mutation within app/mutations/guides/guide_update.rb
as GuideUpdate
(not namespaced).
When I try calling run()
on the mutation I get an error for "uninitialized constant Api::GuidesController::GuideUpdate".
The quick fix for now was to make a mutations.rb
file inside of initializers that does this:
Dir["app/mutations/**/*.rb"].each{|file| load file}
but this is probably a hack. Did I fail to follow a naming convention or something? It's for an opensource project, so feel free to take a look at the source code.
Thanks for maintaining this gem, it's one of the best I've seen in a while.
It would great to be able to handle cases where unpermitted inputs were passed into the mutation; resulting in invalidation. Does anyone else see this as a useful addition to Mutations?
Is it currently possible to describe a hash param allowing arbitrary hash structure? I need to perform pretty complex validations on this param. Having just hash :h without block just return empty hash.
I really like the mutations approach to little service objects, so thank you. I was wondering about the best way to implement something that's a bit like a polymorphic object.
I'd like to make a CreateFooMutation, that checks a type
string parameter, and depending on the contents, defers to a Create{type}FooMutation
for futher validation checking and execution, before returning the outcome back to the original caller.
I can see that I'd want to pass the raw inputs to the second mutation on initialization, but what's the best way to merge all the results back in, including the validations, as if they'd been performed on the first mutation?
Another feature that I've implemented, that I think would be very helpful for users of the mutations gem is current user awareness.
Below is a somewhat contrived example
class CreateBook < Mutations::Command
required do
title :string
end
def execute
model_scope.create(title: title)
end
protected
def model_scope
current_user.present? ? current_user.books : Book
end
end
would be called via:
def create
CreateBook.as(current_user).run(params[:book])
end
If you would be interested in supporting this kind of api, I can speak to a number of great benefits over the approach of passing current user as an input.
Let me know and I will prepare a pull request
Is there anyway to validate an array of hashes of the form:
{
:animal => 'bunny',
:color => 'white',
:age => 1
}
Ideally, I can do:
array do
hash do
string :animal
string :color
integer :age
end
end
Thanks,
Hi,
I needed to write a hash attribute that was a dynamic key/value lookup.
After examining the source, I noticed that you can do wildcard filters, but the feature was not documented.
I've added documentation to the wiki. Apologies if I have misunderstood the feature. I am raising this issue in hopes that someone will proof read my work.
Keep up the great work on this project.
I've a Mutations::Command
subclass that starts out like
class CreateAndPublishBlogPost < Mutations::Command
required do
duck :blog, methods: [:new_post]
hash :post_params do
required do
string :title
end
# listed as optional, but one or the other IS required; see #validate
optional do
string :body
string :image_url
end
end
end
Within the (required) :post_params
hash, only :title
is required; both :body
and :image_url
are optional (ignoring the comment about #validate
for now; we never get that far).
Here's a failing scenario:
The inputs passed in are
{
blog: # SomeObjectInstance; passes
post_params: {
title: 'title' # validates
body: 'body' # validates
}
}
Validation fails when no :image_url value is found; the outcome.errors
object passed back up to the controller contains
[1] pry(#<Blog::PostsController>)> outcome.errors
=> {:post_params=>
{:image_url=>
#<Mutations::ErrorAtom:0x007fc89af206d8
@index=nil,
@key=:image_url,
@message=nil,
@symbol=:empty>}}
which leads to
[2] pry(#<Blog::PostsController>)> outcome.errors[:post_params][:image_url].message
=> "Image Url can't be blank"
Why can't it be?
I saw your library pop up on my Prismatic feed today and I like it! Thanks for sharing it!
As I was reading the README, I realized all your examples use inheritance. So, I copied your implementation of Mutations::Command
to a module and added an included
method and it can now be used as a module include instead of forcing subclass. Is this something you'd like to have or do you prefer the forced inheritance method? I have specs and everything so if you're interested, I'll submit a PR.
P.S. your specs run laser fast, it's ridiculously fun to run them.
@cypriss I'm sure you're a very busy dude... do you need help maintaining this project?
There are growing number of pull requests and other issues that some people would love to be able to help get merged into the main branch.
Any chance of that happening?
I'm working on my own library which subclasses mutations, and I am writing this to see if you would have any interest in merging this particular feature into the mutations gem itself. This will determine some aspects of my approach going forward
class UpdateBook < Mutations::Command
required do
desc "The title of the book"
string :title
optional do
desc "Tags for the book"
array :tags
end
end
end
This gives anyone the ability to generate documentation for the command's interface:
UpdateBook.interface_documentation
This produces (still a WIP)
{
required_inputs: ["name"],
optional_inputs: ["tags"],
schema: {
title: {
type: "string",
description: "The title of the book"
},
tags: {
type: "array",
description: "Tags for the book"
}
}
}
I have considered using just a description key in the options hash but think that this is not the optimal style of writing these kinds of things imo and it isn't inline with the rest of the conventions in my library
Validating hashes with required
and optional
blocks is a bit strange. Example:
# make street2 of address optional
class PropertyCreate < Mutations::Command
required do
string :name
hash :address do
required do
string :street1
string :city
string :state
string :zip
end
end
end
optional do
hash :address do
optional do
string :street2
end
end
end
end
I couldn't get this to work any other way. I have a suggestion for an alternative DSL syntax like this:
class PropertyCreate < Mutations::Command
inputs do
string :name
hash :address do
string :street1
string :street2, optional: true
string :city
string :state
string :zip
end
end
So basically drop the optional
and required
block, and add an extra option to mark the field optional?
The syntax is sort of pleasant, but I don't understand what this library does better than ActiveRecord validations and life-cycle hooks.
It'd be nice to be able to specify specific http status codes in the errors in order to return better responses.
unless outcome.success?
render json: outcome.errors.symbolic, status: outcome.errors.status
end
Any thoughts on this? Something like outcome.errors.status
or outcome.errors.code
A lot has changed since version 0.5.12 in March. Can we get a version bump to 0.5.13?
This is more of a question than an issue, but I'm looking to confirm a good pattern for solving a particular use-case demonstrated with faux example below:
Let's say we have these mutations
# mutations/posts/create.rb
CreatePost
# Adds a new post
# Optionally can supply a comment and attachment
# mutations/posts/comment.rb
PostComment
# Adds a comment to a given post
# Contains additional business-logic, such as email notifications
# mutations/posts/attachment.rb
PostAttachment
# Adds an attachment to a given post
# Contains additional business-logic, such as email notifications
In the scenario above, the CreatePost can optionally be passed inputs to support the creation of a comment and an attachment.
We also need PostComment and PostAttachment mutations isolated because they can be called from other controllers (API endpoints)
Instead of duplicating the logic for interacting with the models in all three mutations and thus having to use callbacks for the business-logic, it would make sense to have the PostComment and PostAttachment mutations each contain the needed business logic- and thus have the CreatePost mutation call the other 2 mutations, when necessary.
class CreateComment
def execute()
@post = Post.create!(inputs)
PostComment.run!(@post, ...) if inputs[:comment]
PostAttachment.run!(@post, ...) if inputs[:attachment]
end
end
Questions about this approach:
class CreatePost
def execute()
ActiveRecord::Base.transaction do
@post = Post.create!(inputs)
PostComment.run!(@post, ...) if inputs[:comment]
PostAttachment.run!(@post, ...) if inputs[:attachment]
end
end
end
So in general, what is the best practice for using Mutations in this kind of scenario? (mutations calling other mutations as part of a larger transaction)
Hey there!
Myself and my colleagues at Intercom are big fans and users of this gem, and wanted to thank you and all the contributors for your work.
In the last few months we've built some monkeypatchsolutions to extend mutations in various ways, some of which we've opened as PRs: #90, #89.
These and other changes live in our codebase as monkeypatches. We think they'd be beneficial to the wider community and we'd love to help get them merged into master and released. On top of that we'd like to offer our communal helping hands in maintaining and otherwise improving the gem.
So I wanted to open an issue to start a discussion about this, and to see if you would be open to some help in maintaining the codebase ๐
Hi there. I'm trying to design a bigger rails3 application, using mutations to abstract away my business logic. Basically i have a "main" app, including only models and mutations, and many rails engines, which only include the views/view-logic and slim controllers which use the mutations from the "main" app to get/update data.
To get everything sorted in a sane way, i namespaced my mutations. For example, there is a model Category
. I have a mutation which returns the root of a Category tree, under the namespace Core::Category::GetRoot
. Given that the "root" of my tree structure is always the first record, the mutation only looks as follows:
class Core::Category::GetRoot < Mutations::Command
def execute
Category.first
end
end
A Unittest in my main-app says everything is okay and it works. But in my Engine-Controller, which uses just Core::Category::GetRoot.run!
, i get the following error:
uninitialized constant Core::Category::GetRoot::Category
with a single line stacktrace, pointing to the only line within the execute method.
It seems for me, that the namespace-resolution just fails at some point. Category
is a usual model, not a class within my mutation module, so is there any lookup-mechanic not working or something like this? Or did i something really wrong?
PS: i use Rails 3.2.13 with Ruby 2.0, and Mongoid as my ODM if this matters.
Not an issue per-se, but I would like to do something like:
required do
integer :page_id
hash :user_defined_page_data
end
Where user_defined_page_data is a hash provided by the end user.
Thank you,
In our application, we have a scenario that requires our top-level mutation object to call another mutation, and to be able to "bubble up" the errors of the lower-level mutation to the top-level mutation's caller. I'm wondering what the best way is to approach this. My first instinct is to simply do this in the top-level mutation execute method:
outcome = LowerLevel::Update.run(params)
unless outcome.success?
errors.merge(outcome.errors)
end
Should the errors be merged like this? Is there a better approach to "bubbling up"?
I have a bunch of mutations that use model filters, like this one:
class UpdateEmailAddress < Mutations::Command
required do
model :user, new_records: true
string :email
end
def execute
user.email = email
user.save
end
end
I want to be able to test them using double()
s such as what RSpec provides. Unfortunately, they fail the :is_a?
test, and so the mutation never runs.
My current (kludgy) workaround is to do this:
class MockUser
def is_a?(klazz)
(klazz == User) ? true : super
end
attr_accessor :email
def save
true
end
end
describe UpdateEmailAddress do
it { should be_a Mutations::Command }
let(:user) { MockUser.new }
let(:new_address) { '[email protected]' }
let(:valid_input) {{ user: user, email: new_address }}
context 'all input correct' do
subject { described_class.run(valid_input) }
it { should be_success }
it 'sets the user\'s email and saves' do
user.should_receive(:save)
subject
user.email.should eq(new_address)
end
#snip...
end
end
Is there a better way I've missed?
Eg. '123foo' gets converted to 123, due to the use of to_i
. I think that it would be preferable for the integer filter to do something like:
begin
data = Integer(data)
rescue ArgumentError
return [data, :integer]
end
Let me know what you think, I can cut a PR if you want.
Every pull request seems to fail only because the CI service is having issues with RBX. Any chance someone can have this resolved so our PRs can be reviewed on their merits?
The FAQ mentions that the best way to share code between multiple Mutations is to include the common module. In my application, we have 2 API versions (namespaces API::V1 and API::V2). Each controller pushes the params into the respective Mutation's run() method. It is a large set of params, with only a small handful of input differences between the 2 APIs. Ideally, to stay DRY, I would like to share the common set of required/optional inputs, so that both mutation objects do not have to define them more than once.
Each mutation would then be responsible for the processing of specific inputs, and call upon a shared module for the common input processing.
I haven't taken a stab at this yet, but wanted to run the concept by this group to get some opinions on the approach.
Currently, a mutation can either succeed or not. If it does not, it will have a list of errors. The errors can in theory be whatever you want, but the current implementation strongly suggests field-related errors.
I'm working on putting some mutations in place in an existing Rails app. This mostly entails refactoring out some logic from models, but mostly extracting code "trapped" in controllers. Part of this is controller filters - if the mutations are to be used in other contexts, I need to extract those too. Thus I end up with a mutation that really can end a few ways:
In order to treat different classes of errors differently, it would be nice to be able to recognize them on the outside without having to loop through the error list.
Have you put any thought into this? I don't have any solutions, but I'm happy to join in on a discussion, just wanted to see if you'd thought about it before suggesting anything.
Is there a suggested way of handling file uploads?
At the moment, Rails will pass a ActionDispatch::Http::UploadedFile
through as params[:foo]
in a controller. We'd also like the ability to accept a Tempfile
from other business logic we have, and an actual File
from our test suite.
They all behave like aFile
, but with some additional methods and behaviour. They all have .size
and .read
and so on.
This is sort of related to #14, but is it worth adding a filter that accepts things that behave like a File
object?
It would be really nice to be able to specify a default for optional values within the DSL. Here's an example:
class UserCreator < Mutations::Command
required do
string :email
string :name
end
optional do
string :background_color
end
def execute
user =
if background_color_present?
User.create!(inputs)
else
User.create!(inputs.merge({background_color: ['red', 'green', 'blue'].sample}))
end
user
end
end
Would become:
class UserCreator < Mutations::Command
required do
string :email
string :name
end
optional do
string :background_color, default_value: ['red', 'green', 'blue'].sample
end
def execute
User.create!(inputs)
end
end
If this seems like a reasonable addition, I'd be happy to take a stab at implementing it. Thanks!
I have a case where a field can be specified optionally, and should never allow a nil value. I expected to simply declare:
optional do
string :state
end
(Intentionally omitted specifying :nils=>false since that is the default)
However, when I send "state"=>nil, the state input is silently filtered out. Shouldn't this have raised a validation error instead?
That would help in clarifying to the user that this field cannot be unset.
This isn't an issue or a question. This describes how I have integrated Mutations into form views within a Rails 2.3 app. This could be a discussion on how Mutations could evolve to have wider uses within Rails, or it could just be a reference for others looking to implement their own similar solution. This is a bit of an experiment, but comments & feedback are welcome!
This is what my controller actions look like:
def new
@mutation = CreateMutationClass.new
end
def create
@mutation = CreateMutationClass.new(params[:mutation])
outcome = @mutation.run
if outcome.success?
flash[:notice] = "Success"
redirect_to some_path
else
@errors = outcome.errors.message_list
render :action => :new
end
end
def edit
@mutation = UpdateMutationClass.new(MyModel.find(params[:id]))
end
def update
@mutation = UpdateMutationClass.new(MyModel.find(params[:id]), params[:mutation])
outcome = @mutation.run
if outcome.success?
flash[:notice] = "Success"
redirect_to some_path
else
@errors = outcome.errors.message_list
render :action => :edit
end
end
Note that on the mutation class I initially call new
rather than run
; this allows me to bind the Rails forms to the mutation rather than an ActiveRecord model. I am also passing in an ActiveRecord model as the first parameter on the edit
, and update
actions, which assigns attributes to the mutation straight from the model (more on that below).
In my views I then use form_for @mutation, :url => some_path
(the :url
is required here, although I suspect it would be possible to extend the mutation class to allow Rails to deduce the url).
My mutation classes are mostly unchanged, but with a few additions. Here's an example of the CreateMutationClass
.
class CreateMutationClass < Mutations::Command
include MutationExtensions::Base
use :new_record_mocks
use :model_name
required do
# ...
end
optional do
# ...
end
def execute
# ...
end
end
The required
& optional
blocks, and the execute
method, are all standard as per the mutations docs.
And the UpdateMutationClass
:
class UpdateMutationClass < Mutations::Command
include MutationExtensions::Base
use :existing_record_mocks
use :model_name
use :base_model, :my_model
map(:some_attr) { |model| model.method_to_determine_some_attr }
map(:another_attr) { Date.today }
required do
# ...
end
optional do
# ...
end
def execute
# ...
end
end
use :new_record_mocks
and use :existing_record_mocks
each set up the following methods: id
, to_param
, new_record?
and _destroy
. These allow Rails to render the form properly.
use :model_name
simply changes the input field base name, defaulting to 'mutation'
.
use :base_model
allows linking the mutation to an ActiveRecord model. This redefines the new
method to allow the first parameter to be an ActiveRecord model of the expected class. By default the mutation attributes will attempt to get their value by calling a method of the same name on the model, however using a base model also allows using the map
method to handle non-matching attributes. Passing in multiple parameters still works the same way (subsequent hash parameters will override existing attributes).
And here's the MutationExtensions
module:
module MutationExtensions
module Base
def self.included(base)
base.class_eval do
def self.use(*args)
case args.shift
when :model_name
@model_name = ::ActiveSupport::ModelName.new(args.first || 'mutation')
when :base_model
@base_model = args.first
self.send(:extend, BaseModel)
self.send(:required) do
model(*args)
end
when :new_record_mocks
self.send(:include, Mock::NewRecord)
when :existing_record_mocks
self.send(:include, Mock::ExistingRecord)
end
end
end
end
end
# Allows linking the mutation class to a model, so that
# new mutation instances can be instantiated by passing
# in a model instance as the first parameter
module BaseModel
def base_model
@base_model
end
def mappings
@mappings ||= {}.with_indifferent_access
end
def map(attribute, &block)
mappings[attribute] = block
end
def new(*args)
if args.first.class.model_name.underscore.to_sym == @base_model
record = args.shift
inputs_keys = input_filters.required_inputs.keys | input_filters.optional_inputs.keys
attrs = inputs_keys.inject({}) do |hash, key|
if proc = mappings[key]
hash[key] = proc.call(record)
elsif record.respond_to?(key)
hash[key] = record.send(key)
end
hash
end
attrs[@base_model] = record
args.unshift(attrs)
end
super(*args)
end
def association_reflection(association_name, associated_class)
@association_reflections ||= {}
@association_reflections[association_name.to_sym] = associated_class
end
def reflect_on_association(association_name)
OpenStruct.new(:klass => @association_reflections[association_name.to_sym])
end
end
# Mocks new or existing records, so that `form_for` renders ok
module Mock
module AnyRecord
def _destroy; nil; end
def to_param; id ? id.to_s : id; end
end
module NewRecord
include Mock::AnyRecord
def new_record?; true; end
def id; nil; end
end
module ExistingRecord
include Mock::AnyRecord
def new_record?; false; end
def id
if base_model = self.class.base_model
self.send(base_model).try(:id) || 0
else
0
end
end
end
end
end
@cypriss, I'd be interested to hear your thoughts on moving towards wider Rails support so that mutations can more easily apply to UI apps as well as APIs. I'm aware that there are other similar gems out there which integrate with Rails forms, but I've so far enjoyed working with mutations the most :-)
Assuming typical params hash when updating:
{
id: 77,
auction: {
title: 'one two three',
price: 77.9
}
}
Because run()
only accepts hashes, i cannot handle finding record in the controller.
So mutation gets auction id and attributes and is be responsible for finding proper record to update and updating id.
class AuctionUpdate < Mutations::Command
required do
integer :id
hash :auction do
string :title
float :price
end
end
def execute
record = Auction.find(id)
record.attributes = auction
record.save!
# some other logic
record
end
end
Am i missing something? How do you do this?
Is it possible to validate a mutation without running the execute block? Thanks!
hi
im trying to decouple my future rails apps from my business logic and i think this gem could be a bit of help in this.
now im writing a test
describe FindUnreadTicketsForUser do
it "finds unread tickets for a user" do
user = double('user', id: 3)
repo = double('repo')
repo.should_receive(:find).with({is_read: false, assigned_to_user_id: user.id}).and_return(['?'])
unread_tickets = FindUnreadTicketsForUser.run! user: user, tickets_repo: repo
unread_tickets.length.should eq(1)
end
end
and the command
class FindUnreadTicketsForUser < Mutations::Command
required do
model :tickets_repo
model :user
end
def execute
tickets_repo.find....
end
end
this is obviously not finished and maybe my testing approach is wrong. but i dont want to be bound to a specific repo or user class. im just testing the command, not the repo. actually i was not able to have any required 'field' that has a dynamic class. i dont want to say that the field user MUST BE kind of User.
im not sure what im doing here currently, but i know that your gem is missing a DynamicFilter or something like this (or please tell me how to do this ;))
Hi there, i have the following scenario:
i have an mutation.
parameter checks say: everything okay.
i start processing the execute method.
i notice an logical error at the beginning of my business logic.
[ ??? ]
outcome.success? # => should be false!
outcome.errors # => directly add a textmessage?
So, i want to "return" directly from the execute(), setting success (and ideally a meaningful error) correctly.
What is the preferred way to achieve this neat behaviour? Usage of Mutations is within a Rails 3.2 App on MRI 2.0 if this matters.
Changing following line:
https://github.com/cypriss/mutations/blob/master/lib/mutations/date_filter.rb#L20
to:
if data.is_a?(Date) || data.is_a?(Time)
Would support both Date, DateTime and Time objects. I'm dealing with TimeWithZone instances in my rails project and this would come in handy. Up until then, I could use the following:
duck :starts_at, methods: [:acts_like_time?]
I am using the latest version from rubygems but it seems it doesn't include the latest fixes (ie: #86). When do you think it would be possible to have a new version of this gem?
I'm having some issues with mutations and the usage of _present? in conjunction of =
Here is my code :
class Shifts::Create < Mutations::Command
required do
integer :store_id
model :job
end
optional do
integer :reward_points
end
def execute
Rails.logger.info "#{reward_points_present?} #{reward_points}"
if !reward_points_present?
Rails.logger.info "#{reward_points_present?} #{reward_points}"
reward_points = job.base_reward_points
Rails.logger.info "WHAT ?"
Rails.logger.info "#{reward_points_present?} #{reward_points}"
end
Rails.logger.info "#{reward_points_present?} #{reward_points}"
end
end
Running
Shifts::Create.run!(job: job, store_id: store.id)
would legitimately give me back
false
false
WHAT ?
false 50
false 50
But when I run Shifts::Create.run!(job: job, store_id: store.id, reward_points: 12)
I am unexpectedly getting :
true 12
true
I have spent hours trying to understand but I finally think this is not the expected behavior, is it ?
Hello,
I've using mutations for almost three months now and there is something that is kind of a recurring pain and I don't know if I'm doing something wrong here. When there is an exception in a mutation debugging the backtrace is kind of tricky. The line where the exception occurs is in the mutations code instead of the execute where it would be easier for me to debug it:
/app/vendor/bundle/ruby/2.0.0/gems/mutations-0.5.12/lib/mutations/command.rb:84:in `run!'
/app/vendor/bundle/ruby/2.0.0/gems/mutations-0.5.12/lib/mutations/command.rb:36:in `run!'
/app/app/jobs/update_site_google_analytics_job.rb:16:in `block in perform'
/app/vendor/bundle/ruby/2.0.0/gems/exceptional-2.0.33/lib/exceptional.rb:53:in `call'
/app/vendor/bundle/ruby/2.0.0/gems/exceptional-2.0.33/lib/exceptional.rb:53:in `rescue_and_reraise'
Is there a workaround for this?
Thanks in advance for your help :)
It looks like at the moment it's only possible for one error to exist on each parameter, but it seems like there are many situations where there might be two or more errors at one time.
Is it worth moving to a structure where each parameter can have an array of errors?
I need to validate the value of a boolean before performing a mutation command.
I would like to return all the basic validation messages at once. For example if the username or password is missing and the terms and conditions have not been accepted I would like to return both messages at once.
Unfortunately the BooleanFilter
only validates the presence of a boolean value, not that it is either true
or `false.
I was able to check that the value is true using this method:
class SignUpUser < Mutations::Command
required do
string :email
string :password
model :accept_terms_and_conditions, class: TrueClass
end
end
Obviously using the ModelFilter
and expecting it to be an (or more accurately, the) instance of TrueClass
is not an expressive solution to my requirement.
I plan to propose a PR that allows me to express that the boolean should be true,
module Mutations
class SignUpUser < Mutations::Command
required do
string :email
string :password
boolean :accept_terms_and_conditions, expect: true
end
def execute
# do stuff
end
end
end
I have a mutation that updates a stage, and is defined as requiring a user, a stage_id (to identify a stage to update), and a hash that contains the optional fields to update.
required do
model :user
string :stage_id
hash :stage do
optional do
string :name
array :environment
array :soil
array :light
integer :stage_length
end
end
end
When I run the mutation in tests to determine whether an empty mutation returns the right errors: mutation.run({}).errors.message_list
I'm getting these errors:
["User is required", "Stage is required", "Stage is required"]
This seems to be having as effect that when I pass it an id through stage_id
as a string, and a hash through stage
to update, it will say "Stage is not a string"
When I change the name of stage_id
to just id
it recognizes it as a required id. Is this a feature that I just don't understand? (I'm pretty new to Ruby on Rails).
Please add to readme:
How are exceptions handled inside the execute method?
Do they bubble up to the consumer? Or are they catched and your added to the errors?
Is there a way to pass an image/file blob to a mutation?
Thanks!
Imagine the following code:
begin
MutationA.run! params
MutationB.run! params
rescue ValidationException => e
...
end
It would be really fine to have a quick access to the cause of the error, like a mutation name. At the moment, everything we got there is the Outcome
instance, wrapped with ValidationException
.
In fact, the back-reference to the mutation might be useful in many other cases.
As the number of watchers grow and public API gets more precise, it could be fine to start a CHANGELOG so as to let people know when changes will potentially hurt.
When using a ModelFilter
with an OmniAuth::AuthHash
(from the omniauth gem) as the expected class, the Mutation::Command
fails because the auth hash is cast to a HashWithIndifferentAccess
.
You can see some examples in this repo. Here's a short example inline as well:
class OmniAuthAuthentication < Mutations::Command
required do
model :auth_hash, class: OmniAuth::AuthHash
end
def execute
# Never runs because auth_hash fails the type check
# auth_hash is cast to a HashWithIndifferentAccess
end
end
I've started work on a fix that casts all input names to symbols, but it causes a lot of specs to need some tweaks and would not be backwards-compatible. Would such a solution be adequate? It seems like there is not really a need for HashWithIndifferentAccess, since it's overkill for the use that it's being put to.
In order to replace the use of nested attribute, reject_if can be used to remove empty records usually present in one-to-many edit form. Is it possible or are you planning to add this option in array or hash filter?
This gem should support add_error_list
where I can just pass model.errors.full_messages
. Basically add a simple string error message vs forcing to use symbols only.
def execute
if user.valid?
user.save
# Do other stuff - send emails, newsletters
user
else
add_error_list(user.errors.full_messages)
end
end
Right now it seems you have to use add_error with individual symbol etc.
A declarative, efficient, and flexible JavaScript library for building user interfaces.
๐ Vue.js is a progressive, incrementally-adoptable JavaScript framework for building UI on the web.
TypeScript is a superset of JavaScript that compiles to clean JavaScript output.
An Open Source Machine Learning Framework for Everyone
The Web framework for perfectionists with deadlines.
A PHP framework for web artisans
Bring data to life with SVG, Canvas and HTML. ๐๐๐
JavaScript (JS) is a lightweight interpreted programming language with first-class functions.
Some thing interesting about web. New door for the world.
A server is a program made to process requests and deliver data to clients.
Machine learning is a way of modeling and interpreting data that allows a piece of software to respond intelligently.
Some thing interesting about visualization, use data art
Some thing interesting about game, make everyone happy.
We are working to build community through open source technology. NB: members must have two-factor auth.
Open source projects and samples from Microsoft.
Google โค๏ธ Open Source for everyone.
Alibaba Open Source for everyone
Data-Driven Documents codes.
China tencent open source team.