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

Introduce a context for rendering fixtures ERB. #13022

Merged
merged 1 commit into from Dec 3, 2013

Conversation

Projects
None yet
3 participants
@pwnall
Contributor

pwnall commented Nov 24, 2013

I ran into this issue while trying to use binary data in my fixtures. The following guides motivated me to write this patch.

http://realityforge.org/code/rails/2006/04/06/loading-binary-data-into-rails-fixtures.html
http://www.tamingthemindmonkey.com/2011/08/18/rails-binary-data-in-fixtures
These define a new method right in the fixture file. I'm sure other people do this every once in a while too. With the current code, method definitions leak to other fixtures via the main object. This introduces subtle test inder-dependencies. Methods won't leak after this patch. (proven by new test case)

http://stackoverflow.com/questions/12644057/how-to-use-binary-data-in-rails-fixtures
This is a nice approach that's amenable to being packaged into a gem, except there is no clean place for adding this sort of functionality.

I look forward to your feedback.

@senny

This comment has been minimized.

Show comment
Hide comment
@senny

senny Nov 26, 2013

Member

Could this break applications that rely on the fact that currently methods are leaked?

Member

senny commented Nov 26, 2013

Could this break applications that rely on the fact that currently methods are leaked?

@pwnall

This comment has been minimized.

Show comment
Hide comment
@pwnall

pwnall Nov 26, 2013

Contributor

It depends on how you define "break".

This only impacts fixture code, so it won't break the folks who don't have tests at all.

Assuming you have tests, and you run them when you upgrade Rails, this will turn flaky tests into definite failures.

I think the fixtures load order is currently decided by this call to Dir.[]. That method has no guarantee for the order in which it returns directory entries. One would hope it will return sorted entries, but in my tests this was not the case. StackOverflow agrees with me.

For some restricted definition of "break", this is a breaking change. I am willing to write the documentation needed for devs who run into this to understand what's happening. At the same time, I consider that the change turns ticking bombs into problems that are immediately visible.

For context, imagine this exploding all of a sudden in a continuous integration / continuous deployment environment. 💣 😢

Contributor

pwnall commented Nov 26, 2013

It depends on how you define "break".

This only impacts fixture code, so it won't break the folks who don't have tests at all.

Assuming you have tests, and you run them when you upgrade Rails, this will turn flaky tests into definite failures.

I think the fixtures load order is currently decided by this call to Dir.[]. That method has no guarantee for the order in which it returns directory entries. One would hope it will return sorted entries, but in my tests this was not the case. StackOverflow agrees with me.

For some restricted definition of "break", this is a breaking change. I am willing to write the documentation needed for devs who run into this to understand what's happening. At the same time, I consider that the change turns ticking bombs into problems that are immediately visible.

For context, imagine this exploding all of a sudden in a continuous integration / continuous deployment environment. 💣 😢

@senny

This comment has been minimized.

Show comment
Hide comment
@senny

senny Nov 27, 2013

Member

of course by breaking I meant the tests and not production code. I don't see a reasonable way to issue depreciation warnings only in these cases so we need to compensate with docs.

/cc @rafaelfranca

Member

senny commented Nov 27, 2013

of course by breaking I meant the tests and not production code. I don't see a reasonable way to issue depreciation warnings only in these cases so we need to compensate with docs.

/cc @rafaelfranca

@pwnall

This comment has been minimized.

Show comment
Hide comment
@pwnall

pwnall Nov 27, 2013

Contributor

@senny I figured you already knew the answer, and were asking for documentation purposes, or to have the answer spelled out in the PR log. Sorry, I misunderstood and my answer was weird :/

I didn't see a 4_1_release_notes.md in rails/guides/source. What is a good place to document the change?

Contributor

pwnall commented Nov 27, 2013

@senny I figured you already knew the answer, and were asking for documentation purposes, or to have the answer spelled out in the PR log. Sorry, I misunderstood and my answer was weird :/

I didn't see a 4_1_release_notes.md in rails/guides/source. What is a good place to document the change?

@senny

This comment has been minimized.

Show comment
Hide comment
@senny

senny Nov 28, 2013

Member

@pwnall sorry for being vague. It's good to reflect these thoughts in the PR so we can later link to it.

I'll prepare the release notes and the upgrading guide today. I'll let you know when things are ready.
In the meantime you could polish the rdocs. To make sure people find render_context we should add a section to the main fixture documentation.

Member

senny commented Nov 28, 2013

@pwnall sorry for being vague. It's good to reflect these thoughts in the PR so we can later link to it.

I'll prepare the release notes and the upgrading guide today. I'll let you know when things are ready.
In the meantime you could polish the rdocs. To make sure people find render_context we should add a section to the main fixture documentation.

@pwnall

This comment has been minimized.

Show comment
Hide comment
@pwnall

pwnall Nov 30, 2013

Contributor

@senny I added a Changelog entry and a paragraph in the fixtures documentation. Can I do anything else to help document this?

Contributor

pwnall commented Nov 30, 2013

@senny I added a Changelog entry and a paragraph in the fixtures documentation. Can I do anything else to help document this?

@senny

This comment has been minimized.

Show comment
Hide comment
@senny

senny Nov 30, 2013

Member

You can add a section to the upgrading ruby on rails guide

I pushed a first draft of the 4.1 release notes. You can add an entry in the notable changes section and even link to the upgrading guide.

Member

senny commented Nov 30, 2013

You can add a section to the upgrading ruby on rails guide

I pushed a first draft of the 4.1 release notes. You can add an entry in the notable changes section and even link to the upgrading guide.

@senny

View changes

Show outdated Hide outdated activerecord/CHANGELOG.md
@senny

View changes

Show outdated Hide outdated activerecord/CHANGELOG.md
@senny

View changes

Show outdated Hide outdated activerecord/lib/active_record/fixture_set/render_context.rb
@pwnall

This comment has been minimized.

Show comment
Hide comment
@pwnall

pwnall Dec 2, 2013

Contributor

@senny Thank you! I added notes in the upgrade guide and 4.1 release notes. I look forward to your feedback!

Contributor

pwnall commented Dec 2, 2013

@senny Thank you! I added notes in the upgrade guide and 4.1 release notes. I look forward to your feedback!

@senny

View changes

Show outdated Hide outdated activerecord/CHANGELOG.md
@senny

View changes

Show outdated Hide outdated activerecord/test/cases/fixture_set/file_test.rb
@senny

This comment has been minimized.

Show comment
Hide comment
@senny

senny Dec 2, 2013

Member

I added some minor comments. This is looking good.

Member

senny commented Dec 2, 2013

I added some minor comments. This is looking good.

@rafaelfranca

View changes

Show outdated Hide outdated activerecord/lib/active_record/fixture_set/render_context.rb
@senny

This comment has been minimized.

Show comment
Hide comment
@senny

senny Dec 2, 2013

Member

I discussed this change with @rafaelfranca. We came to the conclusion that we should:

  1. use an anonymous class as the context. This will get rid of the lookup hack and the render_context.rb file.
class Fixture
  def self.context
    @context ||= Class.new do
      def get_binding
        binding()
      end
    end
  end
end
  1. We should expose a simple API for third party code to provide helpers for that context. ActiveRecord::FixtureSet.context will return the anonymous context class. Third party code can add helpers like so: ActiveRecord::FixtureSet.context.send :include, Paperclip::FixtureHelpers
  2. The docs should only talk about ActiveRecord::FixtureSet.context
  3. We still create a subclass of the context to isolate each file.
Member

senny commented Dec 2, 2013

I discussed this change with @rafaelfranca. We came to the conclusion that we should:

  1. use an anonymous class as the context. This will get rid of the lookup hack and the render_context.rb file.
class Fixture
  def self.context
    @context ||= Class.new do
      def get_binding
        binding()
      end
    end
  end
end
  1. We should expose a simple API for third party code to provide helpers for that context. ActiveRecord::FixtureSet.context will return the anonymous context class. Third party code can add helpers like so: ActiveRecord::FixtureSet.context.send :include, Paperclip::FixtureHelpers
  2. The docs should only talk about ActiveRecord::FixtureSet.context
  3. We still create a subclass of the context to isolate each file.
@rafaelfranca

This comment has been minimized.

Show comment
Hide comment
@rafaelfranca
Member

rafaelfranca commented Dec 2, 2013

👍

@pwnall

This comment has been minimized.

Show comment
Hide comment
@pwnall

pwnall Dec 2, 2013

Contributor

This doesn't quite work :(

I tried adding this to fixtures.rb

class ActiveRecord::FixtureSet
  def self.context
    @context ||= Class.new do
      def get_binding
        binding()
      end
    end
  end
end

One problem is that FixtureSet is now in the lookup scope of the binding, so using File.read in a helper will actually break, because File will get resolved as ActiveRecord::FixtureSet::File.

The other problem is that unless I define get_binding on the class is that is used as a context, test_independent_render_contexts fails, implying that methods are actually getting defined on the shared superclass.

Thoughts?

Contributor

pwnall commented Dec 2, 2013

This doesn't quite work :(

I tried adding this to fixtures.rb

class ActiveRecord::FixtureSet
  def self.context
    @context ||= Class.new do
      def get_binding
        binding()
      end
    end
  end
end

One problem is that FixtureSet is now in the lookup scope of the binding, so using File.read in a helper will actually break, because File will get resolved as ActiveRecord::FixtureSet::File.

The other problem is that unless I define get_binding on the class is that is used as a context, test_independent_render_contexts fails, implying that methods are actually getting defined on the shared superclass.

Thoughts?

@senny

This comment has been minimized.

Show comment
Hide comment
@senny

senny Dec 2, 2013

Member

We can define get_binding in the created subclass.

Member

senny commented Dec 2, 2013

We can define get_binding in the created subclass.

@rafaelfranca

This comment has been minimized.

Show comment
Hide comment
@rafaelfranca

rafaelfranca Dec 2, 2013

Member

About the File problem it is more about class clash than lookup scope. I have the feeling that ActiveRecord::FixtureSet::File should not be called File

Member

rafaelfranca commented Dec 2, 2013

About the File problem it is more about class clash than lookup scope. I have the feeling that ActiveRecord::FixtureSet::File should not be called File

@pwnall

This comment has been minimized.

Show comment
Hide comment
@pwnall

pwnall Dec 2, 2013

Contributor

@rafaelfranca In the end, we'll have a namespace where we have to pay a lot of attention when we add new names. I think that ActiveRecord::FixtureSet is less suitable than a namespace that is specifically designed for this purpose, like ActiveRecord::FixtureSet::RenderContext.

@senny I think that is correct. I also think it has to be defined in a "clean" namespace, because that namespace will be in lookup scope.

Contributor

pwnall commented Dec 2, 2013

@rafaelfranca In the end, we'll have a namespace where we have to pay a lot of attention when we add new names. I think that ActiveRecord::FixtureSet is less suitable than a namespace that is specifically designed for this purpose, like ActiveRecord::FixtureSet::RenderContext.

@senny I think that is correct. I also think it has to be defined in a "clean" namespace, because that namespace will be in lookup scope.

@rafaelfranca

This comment has been minimized.

Show comment
Hide comment
@rafaelfranca

rafaelfranca Dec 2, 2013

Member

I'm fine with a clean namespace but I don't want to see that module hack. It is ugly and hard to understand.

So if there is a way to make a clean namespace without it 👍

Member

rafaelfranca commented Dec 2, 2013

I'm fine with a clean namespace but I don't want to see that module hack. It is ugly and hard to understand.

So if there is a way to make a clean namespace without it 👍

@pwnall

This comment has been minimized.

Show comment
Hide comment
@pwnall

pwnall Dec 2, 2013

Contributor

@rafaelfranca What do you think about this version?

ActiveRecord::FixtureSet::RenderContext does not show up in the hierarchy of the ERB evaluation context, and it's just a private namespace that is an implementation detail.

Contributor

pwnall commented Dec 2, 2013

@rafaelfranca What do you think about this version?

ActiveRecord::FixtureSet::RenderContext does not show up in the hierarchy of the ERB evaluation context, and it's just a private namespace that is an implementation detail.

@rafaelfranca

This comment has been minimized.

Show comment
Hide comment
@rafaelfranca

rafaelfranca Dec 2, 2013

Member

Very good ❤️

Member

rafaelfranca commented Dec 2, 2013

Very good ❤️

@pwnall

This comment has been minimized.

Show comment
Hide comment
@pwnall

pwnall Dec 2, 2013

Contributor

@rafaelfranca The other alternative I have is along the lines of

ActiveRecord::FixtureSet::File.define_singleton_method(:render_context) do
  Class.new ActiveRecord::FixtureSet.context do
    def get_binding
      binding()
    end
  end
end
Contributor

pwnall commented Dec 2, 2013

@rafaelfranca The other alternative I have is along the lines of

ActiveRecord::FixtureSet::File.define_singleton_method(:render_context) do
  Class.new ActiveRecord::FixtureSet.context do
    def get_binding
      binding()
    end
  end
end
@rafaelfranca

View changes

Show outdated Hide outdated activerecord/lib/active_record/fixtures.rb
@rafaelfranca

View changes

Show outdated Hide outdated activerecord/lib/active_record/fixtures.rb
@rafaelfranca

View changes

Show outdated Hide outdated guides/source/4_1_release_notes.md
@rafaelfranca

View changes

Show outdated Hide outdated guides/source/upgrading_ruby_on_rails.md
@pwnall

This comment has been minimized.

Show comment
Hide comment
@pwnall

pwnall Dec 2, 2013

Contributor

@rafaelfranca Thank you! I'll go through the docs and update everything.

Contributor

pwnall commented Dec 2, 2013

@rafaelfranca Thank you! I'll go through the docs and update everything.

@pwnall

This comment has been minimized.

Show comment
Hide comment
@pwnall

pwnall Dec 2, 2013

Contributor

@rafaelfranca I updated the docs and commit message.

Contributor

pwnall commented Dec 2, 2013

@rafaelfranca I updated the docs and commit message.

Introduce a context for rendering fixtures ERB.
Fixture files are passed through an ERB renderer before being read as
YAML. The rendering is currently done in the context of the main object,
so method definitons leak into other fixtures, and there is no clean
place to define fixture helpers.

After this commit, the ERB renderer will use a new subclass of
ActiveRecord::FixtureSet.context_class each time a fixture is rendered.
@pwnall

This comment has been minimized.

Show comment
Hide comment
@pwnall

pwnall Dec 3, 2013

Contributor

@senny Sorry, and thank you for catching that! I used git grep to make sure I didn't miss any other occurrence.

Contributor

pwnall commented Dec 3, 2013

@senny Sorry, and thank you for catching that! I used git grep to make sure I didn't miss any other occurrence.

@senny

This comment has been minimized.

Show comment
Hide comment
@senny

senny Dec 3, 2013

Member

@pwnall 👍 I think we are ready to go. Thank you very much for all the experimentation and updates, the speedy replies and the contribution. ❤️

Member

senny commented Dec 3, 2013

@pwnall 👍 I think we are ready to go. Thank you very much for all the experimentation and updates, the speedy replies and the contribution. ❤️

senny added a commit that referenced this pull request Dec 3, 2013

Merge pull request #13022 from pwnall/fixture_context
Introduce a context for rendering fixtures ERB.

@senny senny merged commit b6f189e into rails:master Dec 3, 2013

@pwnall

This comment has been minimized.

Show comment
Hide comment
@pwnall

pwnall Dec 3, 2013

Contributor

@senny Thank you and @rafaelfranca for the patience and guidance!

Contributor

pwnall commented Dec 3, 2013

@senny Thank you and @rafaelfranca for the patience and guidance!

@rafaelfranca

This comment has been minimized.

Show comment
Hide comment
@rafaelfranca

rafaelfranca Dec 5, 2013

Member

Great we could make this in time to 4.1, thank you so much for the work

Member

rafaelfranca commented Dec 5, 2013

Great we could make this in time to 4.1, thank you so much for the work

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment