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

Associations with default attributes #18

Merged
merged 4 commits into from
Aug 12, 2023
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
25 changes: 23 additions & 2 deletions lib/oaken.rb
Original file line number Diff line number Diff line change
Expand Up @@ -22,10 +22,31 @@ module Stored; end
class Stored::Abstract
def initialize(type)
@type = type
@attributes = {}
end

def with(**attributes)
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Overall pretty happy with how relatively simple it turned out to implement default attributes. It's been on my wishlist since day one! And something that Fixtures aren't that suited for.

if block_given?
previous_attributes, @attributes = @attributes, @attributes.merge(attributes)
yield
else
@attributes = attributes
end
ensure
@attributes = previous_attributes if block_given?
end

def update(id, **attributes)
self.class.define_method(id) { find(id) }

klass = nil
attributes = @attributes.merge(attributes)
attributes.transform_values! do |value|
if !value.respond_to?(:call) then value else
klass ||= Struct.new(:id, *attributes.keys).new(id, *attributes.values)
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Probably some of the most bananas code we've got now. It works! And we can always find something more performant later.

klass.instance_exec(&value)
end
end
end
end

Expand All @@ -35,7 +56,7 @@ def find(id)
end

def update(id, **attributes)
super
attributes = super
objects[id] = @type.new(**attributes)
end

Expand All @@ -50,7 +71,7 @@ def find(id)
end

def update(id, **attributes)
super
attributes = super

if record = @type.find_by(id: id.hash)
record.update!(**attributes)
Expand Down
20 changes: 20 additions & 0 deletions test/oaken_test.rb
Original file line number Diff line number Diff line change
Expand Up @@ -17,6 +17,26 @@ def test_that_it_has_a_version_number

def test_accessing_fixture
assert_equal "Kasper", users.kasper.name
assert_equal "Coworker", users.coworker.name

assert_equal [accounts.business], users.kasper.accounts
assert_equal [accounts.business], users.coworker.accounts
assert_equal [users.kasper, users.coworker], accounts.business.users
end

def test_default_attributes_last_into_test
users.update :homer
assert_equal [accounts.business], users.homer.accounts
end

def test_default_attributes_block
users.with accounts: [accounts.update(:home_co, name: "Yo")] do
users.update :homer
end
assert_equal [accounts.home_co], users.homer.accounts

users.update :homer
assert_equal [accounts.business], users.homer.accounts
end

def test_updating_fixture
Expand Down
6 changes: 5 additions & 1 deletion test/seeds.rb
Original file line number Diff line number Diff line change
@@ -1 +1,5 @@
users.update :kasper, name: "Kasper"
accounts.update :business, name: "Big Business Co."

users.with name: -> { id.to_s.capitalize }, accounts: [accounts.business]
Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

accounts: [accounts.business] here lets us ensure that every user created after here are automatically added to this account. And it's a through association through a Membership class! And we don't have to write the separate Fixture file for those. That's a huge fixture pain gone right there.

Copy link
Owner Author

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Honestly, I'm still kinda stunned that it just worked. But we're just leaning on Active Record's user.accounts = writer method from the has_many :accounts, through: :memberships association.

users.update :kasper
users.update :coworker
23 changes: 23 additions & 0 deletions test/test_helper.rb
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,17 @@
ActiveRecord::Base.logger = Logger.new(STDOUT)

ActiveRecord::Schema.define do
create_table :accounts, force: true do |t|
t.string :name, null: false
t.timestamps
end

create_table :memberships, force: true do |t|
t.integer :account_id, null: false
t.integer :user_id, null: false
t.timestamps
end

create_table :users, force: true do |t|
t.string :name, null: false
t.timestamps
Expand All @@ -21,7 +32,19 @@
end
end

class Account < ActiveRecord::Base
has_many :memberships, dependent: :destroy
has_many :users, through: :memberships, dependent: :destroy
end

class Membership < ActiveRecord::Base
belongs_to :account
belongs_to :user
end

class User < ActiveRecord::Base
has_many :memberships
has_many :accounts, through: :memberships
end

class Comment < ActiveRecord::Base
Expand Down