diff --git a/Dockerfile b/Dockerfile index 918a649..04dce4f 100644 --- a/Dockerfile +++ b/Dockerfile @@ -23,6 +23,7 @@ ENV PASSENGER_APP_ENV docker RUN mkdir -p /home/app/parsley/ COPY Gemfile /home/app/parsley/ COPY Gemfile.lock /home/app/parsley/ +RUN gem install bundler RUN cd /home/app/parsley/ && bundle install --deployment # Copy the app into the image diff --git a/app/controllers/recipes_controller.rb b/app/controllers/recipes_controller.rb index a7a24e7..af6fe81 100644 --- a/app/controllers/recipes_controller.rb +++ b/app/controllers/recipes_controller.rb @@ -37,12 +37,15 @@ class RecipesController < ApplicationController end end + @recipe = RecipeDecorator.decorate(@recipe, view_context) + end # GET /recipes/1 def scale @scale = params[:factor] @recipe.scale(@scale, true) + @recipe = RecipeDecorator.decorate(@recipe, view_context) render :show end @@ -84,7 +87,7 @@ class RecipesController < ApplicationController ensure_owner(@recipe) do @recipe.deleted = true - if @recipe.save + if @recipe.save(validate: false) redirect_to recipes_url, notice: 'Recipe was successfully destroyed.' else redirect_to recipes_url, error: 'Recipe could not be destroyed.' diff --git a/app/decorators/base_decorator.rb b/app/decorators/base_decorator.rb new file mode 100644 index 0000000..bbed4ff --- /dev/null +++ b/app/decorators/base_decorator.rb @@ -0,0 +1,50 @@ +require 'delegate' + +# Minimal Decorator Base Class +# +# Implements a SimpleDelegator, provides access to the view_context through the `h` method, and provides +# a factory method to construct Delegators or collections of Delegators +# +# In a controller, the view_context can be retrieved by calling a method of the same name +# In a view or view helper, the context is `self` +# +# See also ApplicationHelper#decorate +class BaseDecorator < SimpleDelegator + + class << self + # Decorates a single object or a collection of objects. If the given objects are `BaseDecorators`, they will be + # unwrapped first + def decorate(obj, view_context) + decorated = Array.wrap(obj).map do |o| + case o + when nil + nil + when BaseDecorator + new(o.__getobj__, view_context) + else + new(o, view_context) + end + end + obj.respond_to?(:each) ? decorated : decorated.first + end + end + + def initialize(base, view_context) + super(base) + @view_context = view_context + end + + def h + @view_context + end + + # For some reason, view_context.html_escape is marked as private. To provide access to the same functionality, + # define it here + def html_escape(*args) + ERB::Util.html_escape(*args) + end + + def wrapped + __getobj__ + end +end \ No newline at end of file diff --git a/app/decorators/nutrition_data_decorator.rb b/app/decorators/nutrition_data_decorator.rb new file mode 100644 index 0000000..d43a935 --- /dev/null +++ b/app/decorators/nutrition_data_decorator.rb @@ -0,0 +1,19 @@ +class NutritionDataDecorator < BaseDecorator + + [:protein, :lipids, :carbohydrates, :kcal, :fiber, :sugar].each do |m| + + def format_number(n) + '%.1f' % n + end + + define_method m do + format_number(super()) + end + + define_method "#{m}_per" do |per| + format_number(wrapped.send(m) / per) + end + + end + +end \ No newline at end of file diff --git a/app/decorators/recipe_decorator.rb b/app/decorators/recipe_decorator.rb new file mode 100644 index 0000000..9a65715 --- /dev/null +++ b/app/decorators/recipe_decorator.rb @@ -0,0 +1,17 @@ +class RecipeDecorator < BaseDecorator + + def source_markup + uri = begin + URI.parse(self.source) + rescue URI::InvalidURIError + nil + end + + if uri.is_a? URI::HTTP + h.link_to(uri.host, uri.to_s) + else + h.content_tag('span', self.source) + end + end + +end \ No newline at end of file diff --git a/app/helpers/application_helper.rb b/app/helpers/application_helper.rb index 1238a1a..fe2e5a1 100644 --- a/app/helpers/application_helper.rb +++ b/app/helpers/application_helper.rb @@ -1,5 +1,18 @@ module ApplicationHelper + # Given a model or collection of models, returns them with the given decorator. If a block is passed, yields the + # decorated objects as well + # + # Useful in this context: + # <% decorate(@model_obj, ModelObjDecorator) do |model_obj| %> + # <%= model_obj.decorator_method %> + # <% end %> + def decorate(obj, decorator_class) + decorated = decorator_class.decorate(obj, self) + yield(decorated) if block_given? + decorated + end + def timestamp(time) time ? time.strftime('%D %R') : '' end diff --git a/app/views/ingredients/_form.html.erb b/app/views/ingredients/_form.html.erb index 2458bf7..2061cee 100644 --- a/app/views/ingredients/_form.html.erb +++ b/app/views/ingredients/_form.html.erb @@ -58,7 +58,7 @@
Source
<%= @recipe.source %>
+Source
<%= @recipe.source_markup %>
Item | + <% if @recipe.parsed_yield %> +<%= @recipe.parsed_yield.label %> | + <% end %> +Total | +|||
---|---|---|---|---|---|
Item | +Calories | <% if @recipe.parsed_yield %> -<%= @recipe.parsed_yield.label %> | +<%= nutrition_data.kcal_per(@recipe.parsed_yield.number) %> | <% end %> -Total | +<%= nutrition_data.kcal %> |
Calories | - <% if @recipe.parsed_yield %> -<%= @recipe.nutrition_data.kcal / @recipe.parsed_yield.number %> | - <% end %> -<%= @recipe.nutrition_data.kcal %> | -|||
Grams Protein | - <% if @recipe.parsed_yield %> -<%= @recipe.nutrition_data.protein / @recipe.parsed_yield.number %> | - <% end %> -<%= @recipe.nutrition_data.protein %> | -|||
Grams Fat | - <% if @recipe.parsed_yield %> -<%= @recipe.nutrition_data.lipids / @recipe.parsed_yield.number %> | - <% end %> -<%= @recipe.nutrition_data.lipids %> | -|||
Grams Carbohydrates | - <% if @recipe.parsed_yield %> -<%= @recipe.nutrition_data.carbohydrates / @recipe.parsed_yield.number %> | - <% end %> -<%= @recipe.nutrition_data.carbohydrates %> | -|||
Grams Sugar | - <% if @recipe.parsed_yield %> -<%= @recipe.nutrition_data.sugar / @recipe.parsed_yield.number %> | - <% end %> -<%= @recipe.nutrition_data.sugar %> | -|||
Grams Fiber | - <% if @recipe.parsed_yield %> -<%= @recipe.nutrition_data.fiber / @recipe.parsed_yield.number %> | - <% end %> -<%= @recipe.nutrition_data.fiber %> | -