-
-
Save hovsater/6213677 to your computer and use it in GitHub Desktop.
Seeda.definitions do | |
# A simple seed | |
seed { Category.create! name: "Example Category" } | |
# Seeds can be defined in groups | |
define :users do | |
# Seeds can be named for later reference | |
seed(:john) { User.create! name: "John Doe" } | |
seed(:jane) { User.create! name: "Jane Doe" } | |
end | |
# Seeds can have dependencies | |
define :posts, [:users] do |users| | |
seed { users(:john).posts.create! title: "A post" } | |
seed { users(:jane).posts.create! title: "Another post" } | |
end | |
end |
I do understand what you mean. My initial thoughts was similar to yours in regards of wrapping Model.create!
statements into the seed
method. I didn't want to enforce limitations on the use of the ActiveRecord API by only allowing seed to call out to Model.create!
. Consider the following:
seed do
user = User.new name: "John"
user.build_profile age: 18, born: "Sweden"
user.save!
end
This would allow one to create both a user and a profile in the same seed
block, if they feel creating separate seeds for profiles is unnecessary. What do you think about this? I like the less verbose version you provided, but in the same time that enforce limitations. Don't you agree?
This could be rewritten without Seeda just using ORM calls [...]
You're right. What Seeda is trying to provide is a structured alternative. You could accomplish the same functionality with simple ORM calls, but that become cluttered pretty fast. Seeda will allow you to split things out into separate files such as db/seeds/users.rb
and db/seeds/posts.rb
. Could the same be accomplished with simple ORM calls? If so, then I'd have to agree with you that the current implementation is to verbose.
I do like the idea of dynamically creating methods with the name for named seeds. I was thinking about something similar but wanted to keep things simple therefor I decided on injecting the dependencies into the block if they were requested.
One reason to this was the ability to reference unnamed seeds as well. Currently seeds defined in define
statements can be referenced through the define name and the name of seed, if a seed doesn't have a name it get assigned a number (which will increment for each unnamed seed). Seeds that isn't wrapped in a define
statement, end up in a special group called :__unnamed__
.
An example to explain the above:
Seeda.definitions do
seed { "Seed 1" }
define :users do
seed { "Seed 2" }
seed(:john) { "Seed 3" }
end
define :prove_my_point, [:__unnamed__, :users] do |unnamed, users|
unnamed(1) # References "Seed 1"
users(1) # References "Seed 2"
users(:john) # References "Seed 3"
end
end
I definitely like the idea with define
taking a count!
This is me spitting out thoughts 😄
One idea that could increase the value of Seeda would be to apply your idea about context.
How about something like this:
Seeda.definitions do
define :users, User do
# A seed without arguments will execute it's content in the User context.
seed { create! name: "John Doe" }
# A seed with arguments will execute it's content in the User.new context.
seed do |user|
user.name = 'John Doe'
user.save!
end
end
end
I like how this cleans up the multiple calls to User.create!
, which is great if you change the class name to Member
or something else more descriptive or humane. A lot of people have objections to the word "User" as a class name representing people using your app.
This could easily be implemented. Blocks become Procs, which have an arity
attribute, so you could branch on block.arity
being 0 or 1. Run instance_exec
or class_exec
if it's 0 (class_exec
would be best unless you plan on being able to pass in non-Class objects to define
) and yield model_class.new
if it's 1.
I do wonder if {class,instance}_exec
would be too much magic here, though. For example, this line:
seed { create! name: "John Doe" }
When someone first looks at that, they might think create!
was a method on the definition. Maybe seed { model.create! foo: 'bar' }
might be better and the block is simply evaluated in the context of the current object, with model
being the class/object passed into define
.
Don't get me wrong, I love that kind of magic. I used instance_exec
everywhere in Perpetuity at first. But when explaining the gem to other people, I found I was having to justify that magic a lot, which I thought was kind of a UX smell. And it turned out, using yield
in the query DSL was much better anyway (gave it a more Enumerable-like API).
I don't have much to add other than I like the direction you're heading. There was a WONDERFUL baby that got thrown out with the bathwater called FixJour a few years ago. The thing I liked about it was that you could build complicated nested objects by default, E.g. build :user
would return a User
with an associated default Profile
, but if you specified a profile object in the build call, it would seamlessly override the default. This let you do things like spec a user with a nil/missing profile just by calling build :user, profile: nil
, etc.
Anyway, my point is you might want to look at FJ, but if not, please do consider making it easy to override seeds from the test suite UNLESS OF COURSE that's sort of not your point in creating Seeda in the first place. :-)
@jgaskins I feel the same way. I do love the magic that class_exec
and instance_exec
provide, but I don't think it's worth it if people having a hard time understanding what context the methods are executed in. At first thought, I don't think the magic happening here is too confusing, but on the other hand, a new user to gem might think so.
I guess it's a decision that have to be made on whether to use yield
or class_exec
and instance_exec
.
One thought that popped into mind was to convert the name of the definition into singular and then use it like so: seed { user.create! foo: bar }
but I think that is too much trouble of converting the name into singular as well as the confusion with the seed syntax that takes an argument.
@dbrady I will definitely look into FixJour and see if I can get some inspiration!
I like the idea overall, but it seems kind of verbose at the moment. Right now, you're just wrapping ORM calls with boilerplate. This could be rewritten without Seeda just using ORM calls like this:
If you pulled out the
Model.create!
calls, you could probably cut out quite a bit of the verbosity:This is a little less verbose (though still a bit more verbose than without Seeda) and provides an ORM-agnostic way of inserting the data. I'm not saying this is better all around (I don't know your use cases yet), just that it's an attempt to make it better than doing the ORM calls directly. In order for people to use it, it has to be noticeably better.
I'm also noticing that you could set it to persist multiple objects in
define
:Then
define :users
could define a dynamic methodusers
that returns the array of those 100 users.I'm just throwing out ideas here. Seeing the code got me thinking. :-)