voluntary_ranking 0.0.1

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (114) hide show
  1. checksums.yaml +7 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.rdoc +3 -0
  4. data/Rakefile +29 -0
  5. data/app/assets/javascripts/voluntary_ranking/app.js +11 -0
  6. data/app/assets/javascripts/voluntary_ranking/application.js +4 -0
  7. data/app/assets/javascripts/voluntary_ranking/base.js.coffee +3 -0
  8. data/app/assets/javascripts/voluntary_ranking/components/modal_dialog_component.js.coffee +6 -0
  9. data/app/assets/javascripts/voluntary_ranking/controllers/arguments/index_controller.js.coffee +5 -0
  10. data/app/assets/javascripts/voluntary_ranking/controllers/arguments/new_controller.js.coffee +16 -0
  11. data/app/assets/javascripts/voluntary_ranking/controllers/compare_things/arguments_controller.js.coffee +5 -0
  12. data/app/assets/javascripts/voluntary_ranking/controllers/compare_things_controller.js.coffee +8 -0
  13. data/app/assets/javascripts/voluntary_ranking/controllers/controller.js.coffee +1 -0
  14. data/app/assets/javascripts/voluntary_ranking/controllers/index_controller.js.coffee +5 -0
  15. data/app/assets/javascripts/voluntary_ranking/controllers/profile/rankings_controller.js.coffee +3 -0
  16. data/app/assets/javascripts/voluntary_ranking/controllers/ranking_item_controller.js.coffee +7 -0
  17. data/app/assets/javascripts/voluntary_ranking/controllers/rankings/index_controller.js.coffee +5 -0
  18. data/app/assets/javascripts/voluntary_ranking/controllers/rankings/show_controller.js.coffee +6 -0
  19. data/app/assets/javascripts/voluntary_ranking/controllers/thing_controller.js.coffee +10 -0
  20. data/app/assets/javascripts/voluntary_ranking/controllers/user/rankings/show_controller.js.coffee +92 -0
  21. data/app/assets/javascripts/voluntary_ranking/controllers/user_controller.js.coffee +5 -0
  22. data/app/assets/javascripts/voluntary_ranking/controllers/user_ranking_controller.js.coffee +2 -0
  23. data/app/assets/javascripts/voluntary_ranking/helpers/name_with_apostrophe_helper.js.coffee +5 -0
  24. data/app/assets/javascripts/voluntary_ranking/helpers/stars_html_helper.js.coffee +29 -0
  25. data/app/assets/javascripts/voluntary_ranking/mixins/has_current_user.js.coffee +4 -0
  26. data/app/assets/javascripts/voluntary_ranking/mixins/pagination_controller.js.coffee +51 -0
  27. data/app/assets/javascripts/voluntary_ranking/mixins/ranking_controller.js.coffee +31 -0
  28. data/app/assets/javascripts/voluntary_ranking/mixins/singleton.js.coffee +17 -0
  29. data/app/assets/javascripts/voluntary_ranking/models/argument.js.coffee +7 -0
  30. data/app/assets/javascripts/voluntary_ranking/models/movie.js.coffee +5 -0
  31. data/app/assets/javascripts/voluntary_ranking/models/ranking.js.coffee +5 -0
  32. data/app/assets/javascripts/voluntary_ranking/models/ranking_item.js.coffee +26 -0
  33. data/app/assets/javascripts/voluntary_ranking/models/thing.js.coffee +2 -0
  34. data/app/assets/javascripts/voluntary_ranking/models/user.js.coffee +18 -0
  35. data/app/assets/javascripts/voluntary_ranking/models/user/ranking_item.js.coffee +33 -0
  36. data/app/assets/javascripts/voluntary_ranking/router.js.coffee +30 -0
  37. data/app/assets/javascripts/voluntary_ranking/routes/arguments/index_route.js.coffee +24 -0
  38. data/app/assets/javascripts/voluntary_ranking/routes/compare_things/arguments_route.js.coffee +16 -0
  39. data/app/assets/javascripts/voluntary_ranking/routes/compare_things/compare_things_route.js.coffee +9 -0
  40. data/app/assets/javascripts/voluntary_ranking/routes/profile/rankings_route.js.coffee +29 -0
  41. data/app/assets/javascripts/voluntary_ranking/routes/ranking_item_route.js.coffee +25 -0
  42. data/app/assets/javascripts/voluntary_ranking/routes/rankings/index_route.js.coffee +10 -0
  43. data/app/assets/javascripts/voluntary_ranking/routes/rankings/show_route.js.coffee +19 -0
  44. data/app/assets/javascripts/voluntary_ranking/routes/thing_route.js.coffee +7 -0
  45. data/app/assets/javascripts/voluntary_ranking/routes/user_ranking_route.js.coffee +27 -0
  46. data/app/assets/javascripts/voluntary_ranking/routes/user_route.js.coffee +20 -0
  47. data/app/assets/javascripts/voluntary_ranking/templates/application.handlebars.erb +16 -0
  48. data/app/assets/javascripts/voluntary_ranking/templates/arguments.js.handlebars +1 -0
  49. data/app/assets/javascripts/voluntary_ranking/templates/arguments/index.js.handlebars +27 -0
  50. data/app/assets/javascripts/voluntary_ranking/templates/arguments/new.js.handlebars +17 -0
  51. data/app/assets/javascripts/voluntary_ranking/templates/compare_things.js.handlebars +22 -0
  52. data/app/assets/javascripts/voluntary_ranking/templates/compare_things/arguments.js.handlebars +26 -0
  53. data/app/assets/javascripts/voluntary_ranking/templates/components/modal-dialog.js.handlebars +9 -0
  54. data/app/assets/javascripts/voluntary_ranking/templates/components/rankings-component.handlebars +17 -0
  55. data/app/assets/javascripts/voluntary_ranking/templates/index.js.handlebars +1 -0
  56. data/app/assets/javascripts/voluntary_ranking/templates/navigation.js.handlebars +28 -0
  57. data/app/assets/javascripts/voluntary_ranking/templates/ranking_item.js.handlebars +31 -0
  58. data/app/assets/javascripts/voluntary_ranking/templates/ranking_items/_collection.js.handlebars +84 -0
  59. data/app/assets/javascripts/voluntary_ranking/templates/ranking_items/_form.js.handlebars +30 -0
  60. data/app/assets/javascripts/voluntary_ranking/templates/rankings/_form.js.handlebars +38 -0
  61. data/app/assets/javascripts/voluntary_ranking/templates/rankings/index.js.handlebars +3 -0
  62. data/app/assets/javascripts/voluntary_ranking/templates/rankings/show.js.handlebars +3 -0
  63. data/app/assets/javascripts/voluntary_ranking/templates/shared/_pagination.js.handlebars +23 -0
  64. data/app/assets/javascripts/voluntary_ranking/templates/shared/_stars.js.handlebars +3 -0
  65. data/app/assets/javascripts/voluntary_ranking/templates/thing.js.handlebars +13 -0
  66. data/app/assets/javascripts/voluntary_ranking/templates/thing/index.js.handlebars +0 -0
  67. data/app/assets/javascripts/voluntary_ranking/templates/things/show.arguments.js.handlebars +1 -0
  68. data/app/assets/javascripts/voluntary_ranking/templates/things/show.js.handlebars +1 -0
  69. data/app/assets/javascripts/voluntary_ranking/templates/user.js.handlebars +5 -0
  70. data/app/assets/javascripts/voluntary_ranking/templates/user/rankings/details.js.handlebars +7 -0
  71. data/app/assets/javascripts/voluntary_ranking/views/application/index_view.js.coffee +1 -0
  72. data/app/assets/javascripts/voluntary_ranking/views/arguments/new_view.js.coffee +9 -0
  73. data/app/assets/javascripts/voluntary_ranking/views/index_view.js.coffee +1 -0
  74. data/app/assets/javascripts/voluntary_ranking/views/ranking_items/colllection_view.js.coffee +25 -0
  75. data/app/assets/javascripts/voluntary_ranking/views/shared/pagination.js +4 -0
  76. data/app/assets/stylesheets/voluntary_ranking/application.css +17 -0
  77. data/app/assets/stylesheets/voluntary_ranking/base.css.sass +6 -0
  78. data/app/assets/stylesheets/voluntary_ranking/bootstrap_and_overrides.css.sass +0 -0
  79. data/app/assets/stylesheets/voluntary_ranking/stars.css.sass +31 -0
  80. data/app/assets/stylesheets/voluntary_ranking/twitter-typeahead.css.sass +29 -0
  81. data/app/controllers/api/v1/argument_topics_controller.rb +12 -0
  82. data/app/controllers/api/v1/arguments_controller.rb +32 -0
  83. data/app/controllers/api/v1/ranking_items_controller.rb +43 -0
  84. data/app/controllers/api/v1/rankings_controller.rb +29 -0
  85. data/app/controllers/api/v1/things/arguments_controller.rb +40 -0
  86. data/app/controllers/api/v1/things_controller.rb +26 -0
  87. data/app/controllers/api/v1/user_ranking_items_controller.rb +94 -0
  88. data/app/controllers/api/v1/users_controller.rb +9 -0
  89. data/app/controllers/product/ranking_controller.rb +16 -0
  90. data/app/models/argument.rb +48 -0
  91. data/app/models/argument_topic.rb +7 -0
  92. data/app/models/product/ranking.rb +2 -0
  93. data/app/models/ranking.rb +97 -0
  94. data/app/models/ranking_item.rb +30 -0
  95. data/app/models/user_ranking_item.rb +136 -0
  96. data/app/serializers/argument_serializer.rb +11 -0
  97. data/app/serializers/base_ranking_item_serializer.rb +23 -0
  98. data/app/serializers/ranking_item_serializer.rb +2 -0
  99. data/app/serializers/user_ranking_item_serializer.rb +7 -0
  100. data/app/views/product/ranking/index.html.erb +0 -0
  101. data/config/locales/en.yml +10 -0
  102. data/config/locales/resources/ranking/en.yml +10 -0
  103. data/config/routes.rb +41 -0
  104. data/db/migrate/20130817115303_add_ranking_product.rb +50 -0
  105. data/db/migrate/20140926102943_create_arguments.rb +20 -0
  106. data/lib/concerns/controller/base_ranking_items_controller.rb +10 -0
  107. data/lib/concerns/model/base_ranking_item.rb +29 -0
  108. data/lib/concerns/model/user/ranking.rb +48 -0
  109. data/lib/tasks/voluntary_ranking_tasks.rake +4 -0
  110. data/lib/voluntary_ranking.rb +12 -0
  111. data/lib/voluntary_ranking/ability.rb +11 -0
  112. data/lib/voluntary_ranking/engine.rb +16 -0
  113. data/lib/voluntary_ranking/version.rb +3 -0
  114. metadata +436 -0
@@ -0,0 +1,30 @@
1
+ class RankingItem < ActiveRecord::Base
2
+ include Concerns::Model::BaseRankingItem
3
+
4
+ attr_accessible :ranking_id, :thing, :thing_id, :best, :stars
5
+
6
+ has_many :user_ranking_items, dependent: :destroy
7
+
8
+ validates :thing_id, presence: true, uniqueness: { scope: :ranking_id }
9
+
10
+ acts_as_list scope: :ranking_id
11
+
12
+ def update_position
13
+ other_ranking_item = RankingItem.where(
14
+ 'ranking_id = ? AND stars_sum >= ? AND id <> ?', ranking_id, stars_sum, id
15
+ ).order('position DESC').first
16
+
17
+ if other_ranking_item
18
+ if RankingItem.where(ranking_id: ranking_id).count >= other_ranking_item.position + 1
19
+ #puts "update_position.1.1 (#{thing.name} with #{stars_sum}): #{other_ranking_item.position + 1}"
20
+ insert_at other_ranking_item.position + 1
21
+ else
22
+ #puts "update_position.1.2 (#{thing.name} with #{stars_sum}): #{other_ranking_item.position}"
23
+ insert_at other_ranking_item.position
24
+ end
25
+ else
26
+ #puts "update_position.2 (#{thing.name} with #{stars_sum}): 1"
27
+ insert_at 1
28
+ end
29
+ end
30
+ end
@@ -0,0 +1,136 @@
1
+ class UserRankingItem < ActiveRecord::Base
2
+ include Concerns::Model::BaseRankingItem
3
+
4
+ attr_accessible :ranking, :ranking_id, :thing, :thing_id, :best, :stars
5
+
6
+ belongs_to :user
7
+ belongs_to :ranking_item, counter_cache: true
8
+
9
+ validates :user_id, presence: true
10
+ validates :ranking_item_id, presence: true, uniqueness: { scope: [:user_id] }
11
+ validates :stars, presence: true
12
+ validate :stars_and_best
13
+
14
+ after_validation :copy_validation_errors_from_ranking_item # or before?
15
+ after_create :add_stars_to_ranking_item
16
+ after_update :remove_or_add_stars_to_ranking_item, if: 'stars_changed?'
17
+
18
+ after_destroy :destroy_ranking_item, if: 'UserRankingItem.where(ranking_item_id: ranking_item_id).count == 0'
19
+
20
+ acts_as_list scope: [:user_id, :ranking_id]
21
+
22
+ def self.position_for_user_by_stars(user_id, ranking_id, user_ranking_item_id, stars)
23
+ ranking_items = where(user_id: user_id, ranking_id: ranking_id)
24
+ ranking_items_count = ranking_items.count
25
+ ranking_items = ranking_items.where('id <> ?', user_ranking_item_id) if user_ranking_item_id.present?
26
+
27
+ if stars.to_i >= 3
28
+ ranking_items.order('stars ASC, position DESC').where('stars >= ?', stars).first.try(:position).to_i + 1 || 1
29
+ else
30
+ item = ranking_items.order('stars DESC, position ASC').where('stars <= ?', stars).first
31
+
32
+ if item.present?
33
+ if user_ranking_item_id.present? && item.stars < stars
34
+ item.position - 1
35
+ else
36
+ item.position
37
+ end
38
+ else
39
+ position = ranking_items.order('stars ASC, position DESC').where('stars >= ?', stars).first.try(:position).to_i + 1
40
+
41
+ position -= 1 if user_ranking_item_id.present? && position > ranking_items_count
42
+
43
+ position
44
+ end
45
+ end
46
+ end
47
+
48
+ def move_to_top_of_page(page)
49
+ item_on_top_of_page = user.ranking_items.where(ranking_id: ranking_id).order('position').paginate(page: page, per_page: 10).first
50
+ self.stars = item_on_top_of_page.stars
51
+ self.best = item_on_top_of_page.best
52
+ insert_at(item_on_top_of_page.position)
53
+ end
54
+
55
+ def set_position(value)
56
+ item = UserRankingItem.where(
57
+ 'ranking_id = :ranking_id AND position = :position', ranking_id: ranking_id, position: value
58
+ ).first
59
+
60
+ self.stars = item.stars; self.best = item.best;
61
+
62
+ insert_at(value)
63
+
64
+ reload
65
+ end
66
+
67
+ def update_stars(stars)
68
+ self.stars = stars
69
+ self.best = stars >= 3
70
+ save!
71
+ insert_at UserRankingItem.position_for_user_by_stars(user_id, ranking.id, id, stars)
72
+ end
73
+
74
+ private
75
+
76
+ def copy_validation_errors_from_ranking_item
77
+ return if self.ranking_item.blank? || self.ranking_item.valid?
78
+
79
+ self.ranking_item.errors.each do |field, ranking_item_errors|
80
+ ranking_item_errors = [ranking_item_errors] unless ranking_item_errors.is_a?(Array)
81
+
82
+ ranking_item_errors.each do |error|
83
+ unless attributes.has_key?(field.to_s)
84
+ field = :base
85
+ error = "#{field.to_s.humanize}: #{error}"
86
+ end
87
+
88
+ self.errors[field] << error unless self.errors[field].include? error
89
+ end
90
+ end
91
+ end
92
+
93
+ def stars_and_best
94
+ if self.stars >= 3 && !self.best
95
+ self.errors[:best] << 'Item cannot be worst with more stars than 3.'
96
+ elsif self.stars <= 2 && self.best
97
+ self.errors[:best] << 'Item cannot be best with less stars than 3.'
98
+ end
99
+ end
100
+
101
+ def destroy_ranking_item
102
+ ranking_item.destroy
103
+ end
104
+
105
+ def add_stars_to_ranking_item
106
+ ranking_item.stars_sum += stars
107
+
108
+ if ranking_item.stars_sum == 0
109
+ ranking_item.stars = 0
110
+ else
111
+ ranking_item.stars = (ranking_item.stars_sum / ranking_item.user_ranking_items_count).round
112
+ end
113
+
114
+ ranking_item.save
115
+ ranking_item.update_position
116
+ end
117
+
118
+ def remove_or_add_stars_to_ranking_item
119
+ if stars > stars_was
120
+ #puts "remove_or_add_stars_to_ranking_item.1: #{ranking_item.stars_sum} += (#{stars} - #{stars_was})"
121
+ ranking_item.stars_sum += (stars - stars_was)
122
+ else
123
+ #puts "remove_or_add_stars_to_ranking_item.2: #{ranking_item.stars_sum} -= (#{stars_was} - #{stars})"
124
+ ranking_item.stars_sum -= (stars_was - stars)
125
+ end
126
+
127
+ if ranking_item.stars_sum == 0
128
+ ranking_item.stars = 0
129
+ else
130
+ ranking_item.stars = (ranking_item.stars_sum / ranking_item.user_ranking_items_count).round
131
+ end
132
+
133
+ ranking_item.save
134
+ ranking_item.update_position
135
+ end
136
+ end
@@ -0,0 +1,11 @@
1
+ class ArgumentSerializer < ActiveModel::Serializer
2
+ attributes :id, :topic_id, :topic_name, :thing_id, :thing_name, :value
3
+
4
+ def thing_name
5
+ object.thing.try(:name)
6
+ end
7
+
8
+ def topic_name
9
+ object.topic.try(:name)
10
+ end
11
+ end
@@ -0,0 +1,23 @@
1
+ class BaseRankingItemSerializer < ActiveModel::Serializer
2
+ attributes :id, :position, :thing_id, :thing_name, :best, :stars, :ranking_adjective, :ranking_negative_adjective, :ranking_topic, :ranking_scope
3
+
4
+ def thing_name
5
+ object.thing.name
6
+ end
7
+
8
+ def ranking_adjective
9
+ object.ranking.adjective
10
+ end
11
+
12
+ def ranking_negative_adjective
13
+ object.ranking.negative_adjective
14
+ end
15
+
16
+ def ranking_topic
17
+ object.ranking.topic
18
+ end
19
+
20
+ def ranking_scope
21
+ object.ranking.scope
22
+ end
23
+ end
@@ -0,0 +1,2 @@
1
+ class RankingItemSerializer < BaseRankingItemSerializer
2
+ end
@@ -0,0 +1,7 @@
1
+ class UserRankingItemSerializer < BaseRankingItemSerializer
2
+ attributes :user_name
3
+
4
+ def user_name
5
+ object.user.name
6
+ end
7
+ end
File without changes
@@ -0,0 +1,10 @@
1
+ en:
2
+ activerecord:
3
+ models:
4
+ movie: Movie
5
+
6
+ attributes:
7
+ general:
8
+ name: Name
9
+ parent: Parent
10
+ user_id: User
@@ -0,0 +1,10 @@
1
+ en:
2
+ activerecord:
3
+ errors:
4
+ models:
5
+ ranking:
6
+ attributes:
7
+ base:
8
+ one_ranking_per_topic_scope_and_one_of_the_adjectives: Only one ranking per topic, scope and one of the adjectives allowed!
9
+ name:
10
+ unwanted_special_characters_included: Name must not include slash.
data/config/routes.rb ADDED
@@ -0,0 +1,41 @@
1
+ Rails.application.routes.draw do
2
+ get '/products/ranking' => 'product/ranking#index'
3
+
4
+ namespace :api, defaults: {format: 'json'} do
5
+ scope module: :v1, path: 'v1' do
6
+ # TODO: remove manual routing when get rid of this resource using root UsersController
7
+ resources :argument_topics, only: [] do
8
+ collection do
9
+ get :autocomplete
10
+ end
11
+ end
12
+
13
+ resources :arguments
14
+
15
+ resources :rankings
16
+
17
+ get '/rankings/attributes/:attribute/autocomplete', to: 'rankings#autocomplete_attribute'
18
+
19
+ resources :ranking_items
20
+
21
+ resources :things, only: [:show] do
22
+ collection do
23
+ get :autocomplete
24
+ get :suggest
25
+ end
26
+ end
27
+
28
+ get '/things/:left_thing_name/vs/:right_thing_name/arguments', to: 'things/arguments#comparison'
29
+ get '/things/:thing_name/is_one_of_the/:adjective/:topic/:scope', to: 'ranking_items#show'
30
+
31
+ resources :user_ranking_items do
32
+ member do
33
+ put :move_to_page
34
+ put :move
35
+ end
36
+ end
37
+
38
+ resources :users
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,50 @@
1
+ class AddRankingProduct < ActiveRecord::Migration
2
+ def up
3
+ Product::Ranking.create(name: 'Ranking', text: 'Ranking')
4
+
5
+ create_table :rankings, force: true do |t|
6
+ t.string :adjective # best
7
+ t.string :topic
8
+ t.string :scope
9
+ t.string :negative_adjective # worst
10
+ t.timestamps
11
+ end
12
+
13
+ create_table :ranking_items, force: true do |t|
14
+ t.integer :position
15
+ t.integer :ranking_id
16
+ t.integer :thing_id
17
+ t.boolean :best
18
+ t.integer :user_ranking_items_count, default: 0
19
+ t.integer :stars_sum, default: 0
20
+ t.integer :stars, default: 0
21
+ t.timestamps
22
+ end
23
+
24
+ add_index :ranking_items, [:ranking_id, :thing_id], unique: true
25
+ add_index :ranking_items, [:ranking_id, :stars_sum]
26
+ add_index :ranking_items, [:ranking_id, :position]
27
+
28
+ create_table :user_ranking_items, force: true do |t|
29
+ t.integer :user_id
30
+ t.integer :ranking_item_id
31
+ t.integer :position
32
+ t.boolean :best
33
+ t.integer :stars, default: 0
34
+ t.integer :ranking_id # cache column
35
+ t.integer :thing_id # cache column
36
+ t.timestamps
37
+ end
38
+
39
+ add_index :user_ranking_items, [:user_id, :ranking_id, :thing_id], unique: true
40
+ add_index :user_ranking_items, [:user_id, :ranking_id, :position]
41
+ end
42
+
43
+ def down
44
+ Product::Ranking.first.destroy
45
+
46
+ [:rankings, :ranking_items, :user_ranking_items].each do |table|
47
+ drop_table table
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,20 @@
1
+ class CreateArguments < ActiveRecord::Migration
2
+ def change
3
+ create_table :argument_topics do |t|
4
+ t.string :name
5
+ t.text :text
6
+ t.timestamps
7
+ end
8
+
9
+ add_index :argument_topics, :name, unique: true
10
+
11
+ create_table :arguments do |t|
12
+ t.integer :topic_id
13
+ t.integer :thing_id
14
+ t.string :value
15
+ t.timestamps
16
+ end
17
+
18
+ add_index :arguments, [:topic_id, :thing_id], unique: true
19
+ end
20
+ end
@@ -0,0 +1,10 @@
1
+ module Concerns
2
+ module Controller
3
+ module BaseRankingItemsController
4
+ extend ActiveSupport::Concern
5
+
6
+ #def ranking; @ranking ||= Ranking.find_or_create_by_params(params); end
7
+ def ranking; @ranking ||= Ranking.find_by_params(params) end
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,29 @@
1
+ module Concerns
2
+ module Model
3
+ module BaseRankingItem
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ belongs_to :ranking
8
+ belongs_to :thing
9
+
10
+ validates :ranking_id, presence: true
11
+ validates :thing_id, presence: true
12
+
13
+ #pusherable "#{Rails.env}_channel"
14
+
15
+ def thing=(thing)
16
+ thing.save if thing.new_record?
17
+
18
+ self.thing_id = thing.id
19
+
20
+ if self.respond_to?(:ranking_item_id) && self.ranking.present?
21
+ self.ranking_item ||= self.ranking.items.find_or_create_by_thing_id(thing_id)
22
+ end
23
+
24
+ thing
25
+ end
26
+ end
27
+ end
28
+ end
29
+ end
@@ -0,0 +1,48 @@
1
+ module Concerns
2
+ module Model
3
+ module User
4
+ module Ranking
5
+ module AddThingToUserList
6
+ def << *args
7
+ thing = args.first
8
+ proxy_association.owner.user_list_items.create!(list: thing.class.list, thing: thing)
9
+ self
10
+ end
11
+
12
+ alias_method :create, :<<
13
+ end
14
+
15
+ extend ActiveSupport::Concern
16
+
17
+ included do
18
+ has_many :rankings, through: :ranking_items, dependent: :destroy
19
+ has_many :ranking_items, class_name: 'UserRankingItem', dependent: :destroy
20
+ has_many :things, extend: AddThingToUserList
21
+
22
+ def add_ranking_item(attributes)
23
+ ranking = ::Ranking.find_or_create_by_params(attributes)
24
+ thing = Thing.find_or_create_by(name: attributes[:thing_name])
25
+ ranking_item_attributes = {
26
+ thing_id: thing.id, best: attributes[:best], stars: attributes[:stars]
27
+ }
28
+ ranking_item = ranking.items.where(thing_id: ranking_item_attributes[:thing_id]).first_or_create
29
+
30
+ user_ranking_item = ranking_items.new(
31
+ ranking_item_attributes.merge(ranking_id: ranking.id)
32
+ )
33
+ user_ranking_item.ranking_item_id = ranking_item.id
34
+
35
+ position = UserRankingItem.position_for_user_by_stars id, ranking.id, nil, attributes[:stars]
36
+
37
+ begin
38
+ user_ranking_item.insert_at(position)
39
+ rescue ActiveRecord::RecordInvalid
40
+ end
41
+
42
+ user_ranking_item
43
+ end
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end