diff --git a/src/app/helpers/recommendations_helper.rb b/src/app/helpers/recommendations_helper.rb new file mode 100644 index 00000000..7b456c29 --- /dev/null +++ b/src/app/helpers/recommendations_helper.rb @@ -0,0 +1,66 @@ +module RecommendationsHelper + MAX_RECOMMENDED_FUNCTIONS = 5 + + module RecommendationsPartialPath + def to_partial_path + 'recommendations/function' + end + end + + def more_from_user_function(function) + most_liked = user_most_function function.user, likes_count: :desc + most_commented = user_most_function function.user, comments_count: :desc + most_saved = user_most_function function.user, saves_count: :desc + + recommended_functions = filter_unwanted [most_liked, most_commented, most_saved], function + override_partial_path recommended_functions, RecommendationsPartialPath + end + + def similar_functions(function) + popularity_query = generate_popularity_query + + popular_functions_from_tags = function.tags.map { |tag| tag.functions.order(popularity_query).first } + popular_functions = Function.order(Arel.sql(popularity_query)) + .limit(MAX_RECOMMENDED_FUNCTIONS - popular_functions_from_tags.count) + + recommended_functions = filter_unwanted [*popular_functions_from_tags, *popular_functions], function + override_partial_path recommended_functions, RecommendationsPartialPath + end + + private + + def override_partial_path(functions, partial_path_module) + functions.map { |obj| obj.extend(partial_path_module) } + end + + def generate_popularity_query + likes_wieght = 1 + comments_weight = 0.5 + saves_weight = 0.25 + + "( (likes_count * #{likes_wieght}) + (comments_count * #{comments_weight}) + (saves_count * #{saves_weight}) ) desc" + end + + # Returns the most x function for a certain user + # based on a criteria defined in `parameter`. + # + # @param user [User] + # @param parameter [Hash] + # @returns [Function] + def user_most_function(user, parameter) + user.functions.order(parameter).first + end + + # Filters an array of Functions. It removes an `unwated_function` + # and any `nil` variables in the `functions` array. + # + # @param functions [Array] + # + # @param unwanted_functon [Function] a function to be filtered + # out of the Functions array. + # + # @returns [Array] + def filter_unwanted(functions, unwanted_function) + functions.reject { |func| func.nil? || func == unwanted_function }.uniq + end +end diff --git a/src/app/views/functions/show.html.erb b/src/app/views/functions/show.html.erb index 50832a1e..298f95cf 100644 --- a/src/app/views/functions/show.html.erb +++ b/src/app/views/functions/show.html.erb @@ -1,5 +1,13 @@ <%= content_for :title, sprintf("%s - by: %s", @function.name, @user.name) %> -<%= render 'tags/tags', tags: @function.tags %> -<%= render @function, full: true %> -<%= render @function.comments %> -<%= render 'comments/form', comment: @comment if can?(@comment, :create) %> + +
+
+ <%= render 'tags/tags', tags: @function.tags %> + <%= render @function, full: true %> + <%= render @function.comments %> + <%= render 'comments/form', comment: @comment if can?(@comment, :create) %> +
+
+ <%= render 'recommendations/recommendations', function: @function %> +
+
diff --git a/src/app/views/recommendations/_function.html.erb b/src/app/views/recommendations/_function.html.erb new file mode 100644 index 00000000..8e7b7892 --- /dev/null +++ b/src/app/views/recommendations/_function.html.erb @@ -0,0 +1 @@ +<%= link_to "#{function.user.username}/#{function.name}", [function.user, function], class: 'panel-block' %> diff --git a/src/app/views/recommendations/_recommendation.html.erb b/src/app/views/recommendations/_recommendation.html.erb new file mode 100644 index 00000000..b0055a7f --- /dev/null +++ b/src/app/views/recommendations/_recommendation.html.erb @@ -0,0 +1,10 @@ +<% unless functions.empty? %> + +
+

+ <%= recommendation_title %> +

+ <%= render functions %> +
+ +<% end %> diff --git a/src/app/views/recommendations/_recommendations.html.erb b/src/app/views/recommendations/_recommendations.html.erb new file mode 100644 index 00000000..2b9eac28 --- /dev/null +++ b/src/app/views/recommendations/_recommendations.html.erb @@ -0,0 +1,2 @@ +<%= render 'recommendations/recommendation', recommendation_title: 'Similar Functions', functions: similar_functions(function) %> +<%= render 'recommendations/recommendation', recommendation_title: "More from #{function.user.name}", functions: more_from_user_function(function) %> diff --git a/src/spec/helpers/recommendations_helper_spec.rb b/src/spec/helpers/recommendations_helper_spec.rb new file mode 100644 index 00000000..e79f1570 --- /dev/null +++ b/src/spec/helpers/recommendations_helper_spec.rb @@ -0,0 +1,14 @@ +require 'rails_helper' + +RSpec.describe RecommendationsHelper do + let!(:users) { create_list(:user, 5) } + let(:functions) { users.map { |user| create_list(:function, 5, user: user) }.flatten } + + describe '#more_from_user_function' do + it { expect(helper.more_from_user_function(functions.sample)).to include(a_kind_of(Function)) } + end + + describe '#similar_functions' do + it { expect(helper.similar_functions(functions.sample)).to include(a_kind_of(Function)) } + end +end