Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Add ExMachina.with_attrs/1 #33

Closed
paulcsmith opened this issue Oct 8, 2015 · 12 comments
Closed

Add ExMachina.with_attrs/1 #33

paulcsmith opened this issue Oct 8, 2015 · 12 comments

Comments

@paulcsmith
Copy link
Contributor

Recently I had to create a lot of events that all had the same user_id

user_id = 123
create(:event, name: "cancelled", user_id: user_id)
create(:event, name: "started exercise", user_id: user_id)
create(:event, name: "finished exercise", user_id: user_id)

I think it would be nice to have a function that allows you do to something like this

ExMachina.with(user_id: user_id) do
    create(:event, name: "cancelled")
    create(:event, name: "started exercise")
end

I'm not exactly sure how this would work, or if this is the syntax/naming we want, but I think we could come up with something

@jsteiner
Copy link
Contributor

jsteiner commented Oct 8, 2015

Is the user an Ecto model, such that this would be better treated as a has_many? If not, could you just use a function instead?

@paulcsmith
Copy link
Contributor Author

No the user is not an Ecto model. It is just an integer in this case. It is working with an external data source. Calling a function on each create would work, but would probably not be any better than what I'm doing now. I think it would be nice to be able to avoid that repetition if possible. I also think it would work well if user was an Ecto model

user = create(:user)
ExMachina.with(user: user) do
  create(:event, name: "whatever")
  create(:event, name: "foo")
end

Maybe you have another idea though? I'd love to use a regular function and not a macro if possible

@henrik
Copy link

henrik commented Oct 9, 2015

Sketched out one idea of how this could be done without macros, just for fun. I guess this isn't currying in a strict sense, so it might not be a good idea to keep that name.

defmodule ExMachina do
  def example do
    user_event = curry(:create, :event, user: 123)

    # Instead of…
    create(:event, name: "started", user: 123)
    create(:event, name: "finished", user: 123)

    # Try…
    user_event.(name: "started")
    user_event.(name: "finished")
  end

  def curry(action, model, defaults) do
    fn (attrs) ->
      full_attrs = Keyword.merge(defaults, attrs)
      apply(__MODULE__, action, [model, full_attrs])
    end
  end

  def create(model, attrs) do
    IO.puts "create #{model} with #{inspect attrs}"
  end
end

ExMachina.example

@henrik
Copy link

henrik commented Oct 9, 2015

If it's a common use case for a single attribute to vary, one could even do this:

  def example do
    user_event = curry(:create, :event, :name, user: 123)

    user_event.("started")
    user_event.("finished")
  end

  def curry(action, model, attribute_name, defaults) do
    fn (attribute_value) ->
      full_attrs = Keyword.merge(defaults, [{attribute_name, attribute_value}])
      apply(__MODULE__, action, [model, full_attrs])
    end
  end

@henrik
Copy link

henrik commented Oct 9, 2015

One minor issue with this solution is that you can't do user_event.(), should you like to, because
anonymous functions can't have variable numbers of arguments.

Then again, user_event.([]) works fine.

@henrik
Copy link

henrik commented Oct 9, 2015

For the fun of it, here's a slightly evil version abusing Records: https://gist.github.com/henrik/bff879a97f7df44a8830

@paulcsmith
Copy link
Contributor Author

@henrik Those are some interesting ideas. I'm not sure if/which option we will go with but this definitely made me think :) I would like to avoid a macro if possible, but also have a nice clean API.

Another option might be to just allow another parameter in create that gets merged into the passed in atrributes, but honestly I'm not sure I like it much :S

attrs = [user_id: 123]
create(:event, attrs, name: "cancelled")

If you have any other ideas feel free to post here them here :)

@paulcsmith
Copy link
Contributor Author

Something like this might work too, but you'd have to put the . before the parens which is less than ideal :S

w = ExMachina.with(MyApp.Factory, user_id: 123)
w.create.(:event, name: "cancelled)
defmodule ExMachina.with do
  def with(module, default_attrs) do
     default_attrs = Enum.into(%{})
    %{
      create: fn(factroy, attrs) ->
        ExMachina.create(module, Map.merge(default_attrs, attrs))
      end),
      build: fn(factory, attrs) -> ExMachina.build...
    }
  end
end

@henrik
Copy link

henrik commented Oct 13, 2015

On 13 Oct 2015, at 18:41, Paul Smith notifications@github.com wrote:
Another option might be to just allow another parameter in create that gets merged into the passed in atrributes, but honestly I'm not sure I like it much :S

attrs = [user_id: 123]
create(:event, attrs, name: "cancelled")
Oh yeah, I considered that also but forgot to mention it. I think it's got a few things going for it. It's super simple, for one. It's also a bit reminiscent of FactoryGirl traits.

@henrik
Copy link

henrik commented Oct 13, 2015

Another simple one:

with_user = [user: 123]
ExMachina.create(:event, with_user ++ [name: "started"])

Don't like how you need to add in the square brackets, though. And it creates duplicate keys if they're in both lists, which I'm not sure how ExMachina handles.

@paulcsmith
Copy link
Contributor Author

I believe we could do something very similar to what's done with Ecto migrations. See Henrik's example here: https://gist.github.com/henrik/25516815e6680e1c7a82

I believe this would allow us to have the syntax:

MyApp.Factory.with_attrs(user_id: 1) do
  create(:event)
end

The problem with this is that I don't think you could pipe other functions because I don't think they would be available inside this scope. I'd have to try it out

@paulcsmith paulcsmith changed the title Add ExMachina.with/1 Add ExMachina.with_attrs/1 Nov 10, 2015
@paulcsmith
Copy link
Contributor Author

I'm going to close this and revisit it later. Right now it's not a priority right now.

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

3 participants