diff --git a/Gemfile b/Gemfile index fe0094f..4a47f3d 100644 --- a/Gemfile +++ b/Gemfile @@ -12,7 +12,7 @@ gem 'therubyracer', platforms: :ruby # Use jquery as the JavaScript library gem 'jquery-rails', '~> 4.1.1' gem 'bootstrap-sass', '~> 3.3.6' -# Turbolinks makes following links in your web application faster. Read more: https://github.com/rails/turbolinks +gem 'kaminari', '~> 0.17.0' gem 'turbolinks', '~> 5.0.0' gem 'jbuilder', '~> 2.5' gem 'cocoon', '~> 1.2.9' diff --git a/Gemfile.lock b/Gemfile.lock index ce8cd8c..a9d7abd 100644 --- a/Gemfile.lock +++ b/Gemfile.lock @@ -39,7 +39,7 @@ GEM minitest (~> 5.1) tzinfo (~> 1.1) arel (7.1.2) - autoprefixer-rails (6.4.1.1) + autoprefixer-rails (6.5.0) execjs bcrypt (3.1.11) blankslate (3.1.3) @@ -87,6 +87,9 @@ GEM rails-dom-testing (>= 1, < 3) railties (>= 4.2.0) thor (>= 0.14, < 2.0) + kaminari (0.17.0) + actionpack (>= 3.0.0) + activesupport (>= 3.0.0) libv8 (3.16.14.15) liner (0.2.4) listen (3.1.5) @@ -105,7 +108,7 @@ GEM mime-types-data (~> 3.2015) mime-types-data (3.2016.0521) mini_portile2 (2.1.0) - minitest (5.9.0) + minitest (5.9.1) multi_json (1.12.1) nenv (0.3.0) nio4r (1.2.1) @@ -149,7 +152,7 @@ GEM method_source rake (>= 0.8.7) thor (>= 0.18.1, < 2.0) - rake (11.2.2) + rake (11.3.0) rb-fsevent (0.9.7) rb-inotify (0.9.7) ffi (>= 0.5.0) @@ -235,6 +238,7 @@ DEPENDENCIES guard-rspec jbuilder (~> 2.5) jquery-rails (~> 4.1.1) + kaminari (~> 0.17.0) pg (~> 0.18.4) rails (= 5.0.0) rspec-rails (~> 3.5.0) diff --git a/app/assets/javascripts/ingredients.js b/app/assets/javascripts/ingredients.js index d9165d5..d32c047 100644 --- a/app/assets/javascripts/ingredients.js +++ b/app/assets/javascripts/ingredients.js @@ -14,7 +14,7 @@ window.INGREDIENT_API = {}; },{ name: 'usdaFoods', source: usdaFoodSearchEngine, - limit: 10, + limit: 20, display: function(datum) { return datum.name; } diff --git a/app/assets/stylesheets/application.scss b/app/assets/stylesheets/application.scss index caa005f..dfe4a43 100644 --- a/app/assets/stylesheets/application.scss +++ b/app/assets/stylesheets/application.scss @@ -72,4 +72,32 @@ body { height: $footer_height; background-color: $gray-lighter; border-top: solid 1px $gray-light; +} + +a.sorted { + position: relative; +} + +a.sorted.asc:after { + content: " "; + position: absolute; + margin: 8px 0 0 6px; + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + + border-top: 5px solid black; +} + +a.sorted.desc:after { + content: " "; + position: absolute; + margin: 8px 0 0 6px; + width: 0; + height: 0; + border-left: 5px solid transparent; + border-right: 5px solid transparent; + + border-bottom: 5px solid black; } \ No newline at end of file diff --git a/app/controllers/recipes_controller.rb b/app/controllers/recipes_controller.rb index 117975b..62ceaff 100644 --- a/app/controllers/recipes_controller.rb +++ b/app/controllers/recipes_controller.rb @@ -6,7 +6,8 @@ class RecipesController < ApplicationController # GET /recipes def index - @recipes = Recipe.active + @criteria = ViewModels::RecipeCriteria.new(params[:criteria]) + @recipes = Recipe.for_criteria(@criteria) end # GET /recipes/1 diff --git a/app/decorators/recipe_decorator.rb b/app/decorators/recipe_decorator.rb index 4224500..6b38bba 100644 --- a/app/decorators/recipe_decorator.rb +++ b/app/decorators/recipe_decorator.rb @@ -25,7 +25,7 @@ class RecipeDecorator < BaseDecorator end def average_rating - @average_rating ||= (Log.for_recipe(wrapped).for_user(self.user).where('rating IS NOT NULL').average(:rating) || 0) + rating end end \ No newline at end of file diff --git a/app/helpers/recipes_helper.rb b/app/helpers/recipes_helper.rb index c9ec10e..a9417fc 100644 --- a/app/helpers/recipes_helper.rb +++ b/app/helpers/recipes_helper.rb @@ -32,4 +32,40 @@ module RecipesHelper ].compact.join("\n".html_safe).html_safe end end + + def index_sort_header(text, field, criteria) + uri = URI(request.original_fullpath) + query = Rack::Utils.parse_query(uri.query) + + directions = [:asc, :desc] + + current_field = criteria.sort_column + current_direction = criteria.sort_direction + field_param = 'criteria[sort_column]' + direction_param = 'criteria[sort_direction]' + + if request.get? + is_sorted = current_field == field.to_sym + + if is_sorted && directions.include?(current_direction) + direction = (directions.reject { |d| d == current_direction }).first + else + direction = directions.first + end + + if is_sorted && direction == :asc + link_class = 'sorted desc' + elsif is_sorted && direction == :desc + link_class = 'sorted asc' + else + link_class = 'sorted' + end + + query[field_param.to_s] = field.to_s + query[direction_param.to_s] = direction.to_s + link_to text, "#{uri.path}?#{query.to_query}", class: link_class + else + text + end + end end diff --git a/app/models/log.rb b/app/models/log.rb index 2649f47..938c291 100644 --- a/app/models/log.rb +++ b/app/models/log.rb @@ -8,9 +8,17 @@ class Log < ActiveRecord::Base validates :user_id, presence: true validates :rating, numericality: { only_integer: true, allow_blank: true, greater_than_or_equal_to: 1, less_than_or_equal_to: 5, message: 'must be an integer between 1 and 5, inclusive' } - scope :for_user, ->(user) { where(user: user) } - scope :for_recipe, ->(recipe) { where(source_recipe: recipe) } + scope :for_user, ->(user) { where(user_id: user) } + scope :for_recipe, ->(recipe) { where(source_recipe_id: recipe) } accepts_nested_attributes_for :recipe, update_only: true, allow_destroy: false + after_save :update_rating + + def update_rating + if self.source_recipe + self.source_recipe.update_rating! + end + end + end diff --git a/app/models/recipe.rb b/app/models/recipe.rb index ddabc59..2409903 100644 --- a/app/models/recipe.rb +++ b/app/models/recipe.rb @@ -7,6 +7,7 @@ class Recipe < ActiveRecord::Base scope :undeleted, -> { where('deleted <> ? OR deleted IS NULL', true) } scope :not_log, -> { where('is_log <> ? OR is_log IS NULL', true) } scope :active, -> { undeleted.not_log } + scope :for_criteria, ->(criteria) { active.order(criteria.sort_column => criteria.sort_direction).page(criteria.page).per(criteria.per) } accepts_nested_attributes_for :recipe_ingredients, allow_destroy: true accepts_nested_attributes_for :recipe_steps, allow_destroy: true @@ -59,6 +60,11 @@ class Recipe < ActiveRecord::Base @parsed_yield end + def update_rating! + self.rating = Log.for_recipe(self).for_user(self.user_id).where('rating IS NOT NULL').average(:rating) + save(validate: false) + end + # Creates a copy of this recipe suitable for associating to a log def log_copy(user) copy = Recipe.new diff --git a/app/models/view_models/recipe_criteria.rb b/app/models/view_models/recipe_criteria.rb new file mode 100644 index 0000000..76c98be --- /dev/null +++ b/app/models/view_models/recipe_criteria.rb @@ -0,0 +1,48 @@ +module ViewModels + class RecipeCriteria + + SORT_COLUMNS = :created_at, :name, :rating, :total_time + + attr_writer :sort_column, :sort_direction + attr_writer :page, :per + + def initialize(params = {}) + params ||= {} + ([:sort_column, :sort_direction, :page, :per]).each do |attr| + setter = "#{attr}=" + if params[attr] + self.send(setter, params[attr]) + end + end + end + + def sort_column + @sort_column ||= SORT_COLUMNS.first + @sort_column = @sort_column.to_sym + unless SORT_COLUMNS.include? @sort_column + @sort_column = SORT_COLUMNS.first + end + + @sort_column + end + + def sort_direction + @sort_direction ||= :asc + @sort_direction = @sort_direction.to_sym + unless [:asc, :desc].include? @sort_direction + @sort_direction = :asc + end + + @sort_direction + end + + def page + @page.to_i || 1 + end + + def per + @per.to_i || 50 + end + + end +end \ No newline at end of file diff --git a/app/views/recipes/index.html.erb b/app/views/recipes/index.html.erb index 5f4c6e8..75ffbc8 100644 --- a/app/views/recipes/index.html.erb +++ b/app/views/recipes/index.html.erb @@ -9,15 +9,17 @@
No Recipes
<% else %> + <%= paginate @recipes, :param_name => 'criteria[page]' %> +Name | -Rating | +<%= index_sort_header('Name', :name, @criteria) %> | +<%= index_sort_header('Rating', :rating, @criteria) %> | Yields | -Time | -Created | +<%= index_sort_header('Time', :total_time, @criteria) %> | +<%= index_sort_header('Created', :created_at, @criteria) %> | <% if current_user? %><% end %> @@ -57,6 +59,8 @@ |
---|