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

#extends should raise error if no template found #192

Closed
wulftone opened this issue Mar 19, 2012 · 36 comments
Closed

#extends should raise error if no template found #192

wulftone opened this issue Mar 19, 2012 · 36 comments

Comments

@wulftone
Copy link

extends should raise error if no template found... it would be nice if it interrupted execution and told us what was wrong. A typo or whatever could be hard to track down otherwise.

@nesquena
Copy link
Owner

Good point, perhaps this can be a configuration option.

@radar
Copy link
Contributor

radar commented Mar 26, 2012

I believe I am encountering this issue at the moment.

I have a "products/index" template and a "products/show" template which both contain the same attributes.

When I attempt to use the show template in the index template to replace the attributes line with extends "products/show" or extends "show" or extends "spree/api/v1/products/show", it does not add the attributes and returns nothing.

Raising an exception would be excellent, and telling us where Rabl is looking for these templates would be even better.

@nesquena
Copy link
Owner

Yep, fair points. Since spree is open-source, I will take a look and try to figure out why it isn't working. If you are curious though, the template path is resolved like this: https://github.com/nesquena/rabl/blob/master/lib/rabl/partials.rb#L40

@radar
Copy link
Contributor

radar commented Mar 26, 2012

The tests inside the spec directory (particularly spec/controllers/api/v1/products_controller_spec) should aid in the debugging.

Thanks so much for taking a look, Nathan!

On Monday, 26 March 2012 at 1:30 PM, Nathan Esquenazi wrote:

Yep, fair points. Since spree is open-source, I will take a look and try to figure out why it isn't working. If you are curious though, the template path is resolved like this: https://github.com/nesquena/rabl/blob/master/lib/rabl/partials.rb#L40


Reply to this email directly or view it on GitHub:
#192 (comment)

@nesquena
Copy link
Owner

Thanks, will dig into this sometime today or tomorrow and report back here.

@radar
Copy link
Contributor

radar commented Mar 26, 2012

I've dug into this problem this afternoon and it looks like it's happening because @_scope is an ActionView::Base object rather than... well, I'm not sure what you expect it to be. But the object that @_scope is doesn't respond to find_template and so no template is even attempted to be found.

Hope that helps!

@radar
Copy link
Contributor

radar commented Mar 26, 2012

Should @_scope here be @_scope.lookup_context, perhaps?

@nesquena
Copy link
Owner

@radar Yes that code path was a result of me integrating a patch for Rails template lookup. I am actually not very familiar with the correct way to leverage Rails for finding me a template. If you notice though, the second code path (in the else) is the way I used to go about it.

Let me ask it this way. Is there a simple way to tweak this such that Rails 3 and Rails 2 would support the usage of find_template. Right now we have:

if defined?(@_scope) && @_scope.respond_to?(:find_template)
  # use Rails's own template resolution mechanism (partials and no partial)
  lookup_proc = lambda { |partial| @_scope.find_template(file, [], partial) }
  template = lookup_proc.call(false) rescue lookup_proc.call(true)
  file_path = File.join(Rails.root.to_s, template.inspect) if template
else # fallback to manual
  root_path = Rails.root
  view_path = options[:view_path] || File.join(root_path, "app/views/")
  file_path = Dir[File.join(view_path, file + ".{*.,}rabl")].first
end

It looks like you are suggesting:

if defined?(@_scope) && @_scope.respond_to?(:lookup_context)
  # use Rails's own template resolution mechanism (partials and no partial)
  lookup_proc = lambda { |partial| @_scope.lookup_context.find_template(file, [], partial) }
  template = lookup_proc.call(false) rescue lookup_proc.call(true)
  file_path = File.join(Rails.root.to_s, template.inspect) if template
else # fallback to manual
  root_path = Rails.root
  view_path = options[:view_path] || File.join(root_path, "app/views/")
  file_path = Dir[File.join(view_path, file + ".{*.,}rabl")].first
end

Would that work in just Rails 3+ I am assuming? Is there an equivalent in Rails 2.3? Looks like http://apidock.com/rails/ActionView/PathSet/find_template suggests something like:

@_scope.view_paths.find_template(...) 

could work in Rails 2.3?

Considering you wrote "Rails 3 in action", I am going to assume you have a pretty solid handle on all this :) Would love your guidance on making the template resolution for Rails 2 and 3 as robust as possible

@nesquena
Copy link
Owner

Also keep in mind I have Rails 2 and Rails 3 full integration testing that I run with each rabl release including full testing of the use of extends and partial. So in at least the most trivial case, I am assuming the 'fallback' case must be working. However, perhaps the first code path (find_template) doesn't work or not with the later versions of Rails.

@nesquena
Copy link
Owner

Also, sorry to keep posting but I just tested the current code path in Rails 3 and at least there the existing find_template as I have it now actually works exactly as expected, returning the correct template. I removed the other code path and tested with find_template and as expected Rails 3 still returned the correct path.

@nesquena
Copy link
Owner

@radar OK, quick update. Added Rails 3.2 template integration testing, and tweaked template resolution here: f1fbe39 to use lookup_context. Also I raise now if no template is found.

Tested this in Sinatra, Padrino, Rails 2+3+3.2 integration tests and everything seemed to pass. Can you try rabl git master on your project and let me know if it works any better?

@nesquena
Copy link
Owner

@wulftone can you try rabl from git and verify that it raises as expected now?

@nesquena
Copy link
Owner

@radar Had a bit more time. Forked spree and fixed all the tests: nesquena/spree@4057b25 . Also confirmed that raising works now. All released into 0.6.3. Closing. Fun side note, you need to use render_views declaration at the test class level in RSpec to allow views to properly render.

@radar
Copy link
Contributor

radar commented Mar 27, 2012

The new partial finding code looks a lot cleaner! Nice work.

@nesquena
Copy link
Owner

Indeed much cleaner and supports Rails 2, 3, and 3.2. Was definitely worth digging into. Thanks for bringing this up.

@wulftone
Copy link
Author

Hmm it still fails silently on a call to extends with a non-existent template:

# user.rabl
extends 'non-existent-template'

# render code
Rabl::Engine.new(File.read('user.rabl')).render(nil,{object: User.new}) # => "{}"

No error raised!

@nesquena
Copy link
Owner

I am confused, why wouldn't

raise "Cannot find rabl template '#{file}' within registered views!" unless File.exist?(file_path.to_s)
force non-existent templates to error? That path is executed for every child and extends call. Why would it be ignored?

@wulftone
Copy link
Author

Ah yes, because I'm a moron! I did the above test in a console that was already running. : \

Sorry, when I first looked at your solution I was like "Yes!" and then I fooled myself into over analyzing why it failed in my test without first checking the obvious.

It works as advertised. : P

Thank you!

@nesquena
Copy link
Owner

Ha OK thanks for following up, I appreciate it. Glad to hear everything is working as expected now. Let me know if you run into any other issues.

@wulftone
Copy link
Author

Maybe I spoke too soon... It's been causing some of my integration specs to fail... Here's some console tests I did while changing the Gemfile to require rabl 0.6.2 (which works) and 0.6.5 (which fails)... complete with bundle updates and restarting the console! : P

With rabl 0.6.5:

$ rails c
require rails: 0.000000 (0.000271)
require execjs: 0.070000 (0.066629)
require pg: 0.040000 (0.031867)
require devise: 0.450000 (0.456183)
require cancan: 2.080000 (2.100743)
require easy_roles: 0.000000 (0.001713)
require eco: 0.030000 (0.034807)
require paperclip: 0.290000 (0.294825)
require aws-sdk: 0.190000 (0.179455)
require chronic: 0.150000 (0.163188)
require validates_timeliness: 0.080000 (0.080775)
require rabl: 0.090000 (0.093904)
require jquery-rails: 0.000000 (0.003568)
require miniskirt: 0.010000 (0.007731)
require sass-rails: 0.060000 (0.056728)
require coffee-rails: 0.100000 (0.101894)
require uglifier: 0.000000 (0.002151)
require awesome_print: 0.110000 (0.108399)
require thin-rails: 0.130000 (0.127519)
require rspec-rails: 0.000000 (0.000998)
require faker: 0.110000 (0.110679)
Loading development environment (Rails 3.2.0)
1.9.3p125 :001 >     Rabl::Engine.new(File.read(Rails.root + 'app/views/homefront/_yc.json.rabl')).render(nil,{object: User.find(1)})
  User Load (9.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 1]]
RuntimeError: Cannot find rabl template 'homefront/users/user_base' within registered view paths!
  from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/rabl-0.6.5/lib/rabl/partials.rb:42:in `block in fetch_source'
  from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/rabl-0.6.5/lib/rabl.rb:48:in `source_cache'
  from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/rabl-0.6.5/lib/rabl/partials.rb:33:in `fetch_source'
  from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/rabl-0.6.5/lib/rabl/partials.rb:12:in `partial'
  from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/rabl-0.6.5/lib/rabl/builder.rb:111:in `extends'
  from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/rabl-0.6.5/lib/rabl/builder.rb:33:in `block in compile_hash'
  from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/rabl-0.6.5/lib/rabl/builder.rb:32:in `each'
  from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/rabl-0.6.5/lib/rabl/builder.rb:32:in `compile_hash'
  from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/rabl-0.6.5/lib/rabl/builder.rb:20:in `block in build'
  from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/rabl-0.6.5/lib/rabl/builder.rb:140:in `cache_results'
  from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/rabl-0.6.5/lib/rabl/builder.rb:19:in `build'
  from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/rabl-0.6.5/lib/rabl/engine.rb:39:in `to_hash'
  from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/rabl-0.6.5/lib/rabl/engine.rb:50:in `to_json'
  from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/rabl-0.6.5/lib/rabl/engine.rb:27:in `block in render'
  from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/rabl-0.6.5/lib/rabl/engine.rb:245:in `cache_results'
  from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/rabl-0.6.5/lib/rabl/engine.rb:27:in `render'
  from (irb):1
  from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/railties-3.2.0/lib/rails/commands/console.rb:47:in `start'
  from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/railties-3.2.0/lib/rails/commands/console.rb:8:in `start'
  from /home/trevor/.rvm/gems/ruby-1.9.3-p125/gems/railties-3.2.0/lib/rails/commands.rb:41:in `<top (required)>'
  from script/rails:6:in `require'
  from script/rails:6:in `<main>'1.9.3p125 :002 > 
1.9.3p125 :003 >   exit

And after a Gemfile change to rabl 0.6.2 and bundle update:

$ rails c
require rails: 0.000000 (0.000273)
require execjs: 0.060000 (0.059494)
require pg: 0.040000 (0.032845)
require devise: 0.430000 (0.448843)
require cancan: 2.070000 (2.080844)
require easy_roles: 0.000000 (0.001761)
require eco: 0.030000 (0.036375)
require paperclip: 0.290000 (0.292989)
require aws-sdk: 0.180000 (0.186761)
require chronic: 0.170000 (0.162254)
require validates_timeliness: 0.080000 (0.079831)
require rabl: 0.080000 (0.093442) # => also notice rabl 0.6.2 loaded faster... but whatever.. : )
require jquery-rails: 0.010000 (0.003656)
require miniskirt: 0.050000 (0.049985)
require sass-rails: 0.010000 (0.015635)
require coffee-rails: 0.100000 (0.105430)
require uglifier: 0.010000 (0.002153)
require awesome_print: 0.090000 (0.093807)
require thin-rails: 0.110000 (0.117729)
require rspec-rails: 0.000000 (0.000999)
require faker: 0.100000 (0.101527)
Loading development environment (Rails 3.2.0)
1.9.3p125 :001 >     Rabl::Engine.new(File.read(Rails.root + 'app/views/homefront/_yc.json.rabl')).render(nil,{object: User.find(1)})
  User Load (9.0ms)  SELECT "users".* FROM "users" WHERE "users"."id" = $1 LIMIT 1  [["id", 1]]
  User Load (1.2ms)  SELECT "users".* FROM "users" INNER JOIN "relationships" ON "users"."id" = "relationships"."user_b_id" WHERE "relationships"."user_a_id" = 1
  YcFoulInstance Load (0.8ms)  SELECT "yc_foul_instances".* FROM "yc_foul_instances" WHERE "yc_foul_instances"."assignee_id" = 4
  YcFoulInstance Load (0.5ms)  SELECT "yc_foul_instances".* FROM "yc_foul_instances" WHERE "yc_foul_instances"."assignee_id" = 3
  YcFoulInstance Load (0.5ms)  SELECT "yc_foul_instances".* FROM "yc_foul_instances" WHERE "yc_foul_instances"."assignee_id" = 2
  Relationship Load (0.4ms)  SELECT "relationships".* FROM "relationships" WHERE "relationships"."user_b_id" = 1
"{\"id\":1,\"first_name\"}" # etc... cut out for brevity, but it works
1.9.3p125 :002 > 

Here's my rabl template:

# _yc.json.rabl
object @user
extends 'homefront/users/user_base'
child :dependents => :dependents do
  extends 'yc/users/dependents'
end
child :relationships_as_dependent => :relationships_as_dependent do
  extends 'relationships/relationship'
end

In 0.6.5 it raises the error as expected in the console, but shows up as this cryptic error in my capybara/webkit integration tests:

 Failure/Error: visit yellowcard_path
 Capybara::Driver::Webkit::WebkitInvalidResponseError:
   Unable to load URL: http://127.0.0.1:37592/yellowcard
 # ./spec/integration/yc/yc_spec.rb:21:in `block (3 levels) in <top (required)>'

It fails in both test and dev environments on 0.6.5, and works fine in 0.6.2.

@radar
Copy link
Contributor

radar commented Mar 29, 2012

fwiw, I still cannot use extends within a child block either and I've worked around it by doing this: https://github.com/spree/spree/blob/api/api/app/views/spree/api/v1/line_items/show.rabl#L3-5

@wulftone
Copy link
Author

Hmm. I haven't had a problem using extends within a child block. It was the user_base that was failing first, as the raised error specified. I'll see if I can set up a new rails 3.2 app with the same problem.

@radar
Copy link
Contributor

radar commented Mar 29, 2012

Ah, I was being stupid. extends "variants/variant" != extends "spree/api/v1/variants/variant".

@nesquena
Copy link
Owner

@radar Does that mean everything is working ok then?

@radar
Copy link
Contributor

radar commented Mar 29, 2012

If you're still keen to investigate something, I suspect I may not be able to use extends "spree/api/v1/variants/variant" in the line_items/show template still. It's not too important as it's just got the one line in it, but would be nice to know why that particular one is failing. Perhaps related to my PR #198?

@nesquena
Copy link
Owner

Really? In my original pull request https://github.com/nesquena/spree/blob/a698a3ffc4f0f3f93351cc177229cc8a2f1e314d/api/app/views/spree/api/v1/line_items/show.rabl#L4 I had changed it to using extends and the tests seemed to pass without error. This was with 0.6.5. I can take another look though today.

Out of curiosity if you change it to extends "spree/api/v1/variants/_variant" does it work?

@radar
Copy link
Contributor

radar commented Mar 29, 2012

Yeah, was getting a non-"rails can't find this template" error for the template this morning, indicating that it's falling right to the end of that method again.

@nesquena
Copy link
Owner

Cool I'll take a look. This Rails template resolution logic is starting to get out of hand how many edge cases there seem to be haha.

@radar
Copy link
Contributor

radar commented Mar 29, 2012

Did you take the lunar calendar into consideration when building the template resolution? ;)

@nesquena
Copy link
Owner

Ha, it's getting to that point. If I can fix these last issues here and we can work through #202 then I'm hoping at that point resolution will be fairly reliable.

@nesquena
Copy link
Owner

@radar OK, I think your issue is you don't have render_views in your rspec controller test for line_items. In order for tests to pass you need to have render_views in your controller test if your templates want to use extends. It has to do with the extreme view stubbing that rspec does by default.

Check spree/spree#1337 that makes the tests pass

@nesquena
Copy link
Owner

@wulftone Can you try from master? I think I fixed it in a pretty easy way. (when there's no template, fall back to manual lookup)

@wulftone
Copy link
Author

I tried switching the extensions like so:

# style 1
extends 'homefront/users/user_base' # => Fails with:
  ActionView::Template::Error (Missing partial homefront/users/user_base with {:locale=>[:en], :formats=>[:html], :handlers=>[:erb, :builder, :coffee, :rabl]}

# style 2
extends 'homefront/users/user_base.json.rabl' # => Works, but has deprecation warning:
  DEPRECATION WARNING: Passing a template handler in the template name is deprecated. You can simply remove the handler name or pass render :handlers => [:rabl] instead. (called from block in fetch_rails_source at /home/trevor/.rvm/gems/ruby-1.9.3-p125/bundler/gems/rabl-8e149e41dcc8/lib/rabl/partials.rb:64)

# style 3
extends 'homefront/users/user_base.json' # => Works, however, child elements do *not* need the `.json` extension to work.  Only root-level `extends` statements need `.json`

So, using style 3, my rabl file looks like this:

object @user
extends 'homefront/users/user_base.json'
child :dependents => :dependents do
  extends 'yc/users/dependents'
end
child :relationships_as_dependent => :relationships_as_dependent do
  extends 'relationships/relationship'
end

In 0.6.2, style 1 works. Rails, why do you make things so difficult sometimes? : P

@nesquena
Copy link
Owner

OK, we have a patch here: #205 (comment) that may address this. I am going to integrate it and ask if you can test it again. Sometimes the complexity of this Rails template handling does start to get frustrating though haha.

@wulftone
Copy link
Author

Ok I think this is working now... although it still raises the error you added to partials.rb in the rails console (using the method of calling the template I posted above), it seems to work on the development and test servers.

Thanks a ton for doing this, I know it wasn't the most painless process ever... : )

@nesquena
Copy link
Owner

Awesome, thanks for testing again. Let me know if you run into any other issues.

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