voluntary_ranking 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
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,29 @@
1
+ .tt-dropdown-menu, .gist
2
+ text-align: left
3
+
4
+ .tt-query
5
+ box-shadow: 0 1px 1px rgba(0, 0, 0, 0.075) inset
6
+
7
+ .tt-hint
8
+ color: #999
9
+
10
+ .tt-dropdown-menu
11
+ background-color: #fff
12
+ border: 1px solid rgba(0, 0, 0, 0.2)
13
+ border-radius: 8px
14
+ box-shadow: 0 5px 10px rgba(0, 0, 0, 0.2)
15
+ margin-top: 12px
16
+ padding: 8px 0
17
+ width: 422px
18
+
19
+ .tt-suggestion
20
+ font-size: 18px
21
+ line-height: 24px
22
+ padding: 3px 20px
23
+
24
+ .tt-suggestion.tt-cursor
25
+ background-color: #0097cf
26
+ color: #fff
27
+
28
+ .tt-suggestion p
29
+ margin: 0
@@ -0,0 +1,12 @@
1
+ class Api::V1::ArgumentTopicsController < ActionController::Base
2
+ include Voluntary::V1::BaseController
3
+
4
+ respond_to :json
5
+
6
+ def autocomplete
7
+ render json: (
8
+ ArgumentTopic.order(:name).where("name LIKE ?", "%#{params[:term]}%").
9
+ map{|t| { id: t.id, value: t.name }}
10
+ ), root: false
11
+ end
12
+ end
@@ -0,0 +1,32 @@
1
+ class Api::V1::ArgumentsController < ActionController::Base
2
+ include Voluntary::V1::BaseController
3
+
4
+ respond_to :json
5
+
6
+ def index
7
+ options = {}
8
+
9
+ arguments = Argument
10
+ arguments = Argument.where(thing_id: params[:thing_id]) if params[:thing_id].present?
11
+ options[:json] = arguments.paginate(page: params[:page], per_page: 10)
12
+
13
+ options[:meta] = {
14
+ pagination: {
15
+ total_pages: options[:json].total_pages, current_page: options[:json].current_page,
16
+ previous_page: options[:json].previous_page, next_page: options[:json].next_page
17
+ }
18
+ }
19
+
20
+ respond_with do |format|
21
+ format.json { render options }
22
+ end
23
+ end
24
+
25
+ def create
26
+ raise CanCan::AccessDenied if current_user.blank?
27
+
28
+ respond_to do |format|
29
+ format.json { render json: Argument.create_with_topic(params[:argument]) }
30
+ end
31
+ end
32
+ end
@@ -0,0 +1,43 @@
1
+ class Api::V1::RankingItemsController < ActionController::Base
2
+ include Voluntary::V1::BaseController
3
+ include Concerns::Controller::BaseRankingItemsController
4
+
5
+ respond_to :json
6
+
7
+ def index
8
+ options = {}
9
+
10
+ if ranking.present?
11
+ options[:json] = ranking.items.order('position').includes(:thing).paginate(page: params[:page], per_page: 10)
12
+
13
+ options[:meta] = {
14
+ pagination: {
15
+ total_pages: options[:json].total_pages, current_page: options[:json].current_page,
16
+ previous_page: options[:json].previous_page, next_page: options[:json].next_page
17
+ }
18
+ }
19
+ else
20
+ options[:json] = []
21
+ options[:meta] = { pagination: { total_pages: 0, current_page: 0, previous_page: 0, next_page: 0 } }
22
+ end
23
+
24
+ respond_with do |format|
25
+ format.json { render options }
26
+ end
27
+ end
28
+
29
+ def show
30
+ ranking = Ranking.where(
31
+ '(LOWER(adjective) = :adjective OR LOWER(negative_adjective) = :adjective) AND LOWER(topic) = :topic AND LOWER(scope) = :scope',
32
+ adjective: params[:adjective].downcase, topic: params[:topic].downcase, scope: params[:scope].downcase
33
+ ).first
34
+ thing = Thing.where('LOWER(name) = ?', params[:thing_name].downcase).first
35
+ ranking_item = RankingItem.where(ranking_id: ranking.try(:id), thing_id: thing.try(:id)).first
36
+
37
+ raise ActiveRecord::RecordNotFound if ranking_item.blank?
38
+
39
+ respond_with do |format|
40
+ format.json { render json: ranking_item }
41
+ end
42
+ end
43
+ end
@@ -0,0 +1,29 @@
1
+ class Api::V1::RankingsController < ActionController::Base
2
+ include Voluntary::V1::BaseController
3
+
4
+ respond_to :json
5
+
6
+ def index
7
+ options = {}
8
+
9
+ rankings = Ranking
10
+ rankings = Ranking.for_user(params[:user_name]) if params[:user_name].present?
11
+ rankings = Ranking.for_thing(params[:thing_id]) if params[:thing_id].present?
12
+ options[:json] = rankings.paginate page: params[:page], per_page: 1, count: { group: 'rankings.id' }
13
+
14
+ options[:meta] = {
15
+ pagination: {
16
+ total_pages: options[:json].total_pages, current_page: options[:json].current_page,
17
+ previous_page: options[:json].previous_page, next_page: options[:json].next_page
18
+ }
19
+ }
20
+
21
+ respond_with do |format|
22
+ format.json { render options }
23
+ end
24
+ end
25
+
26
+ def autocomplete_attribute
27
+ render json: Ranking.autocomplete_attribute(params[:attribute], params[:term]), root: false
28
+ end
29
+ end
@@ -0,0 +1,40 @@
1
+ class Api::V1::Things::ArgumentsController < ActionController::Base
2
+ include Voluntary::V1::BaseController
3
+
4
+ respond_to :json
5
+
6
+ def comparison
7
+ options = {}
8
+
9
+ arguments = Argument.compare_two_things(params[:side], params[:left_thing_name], params[:right_thing_name]).
10
+ paginate(page: params[:page], per_page: 10)
11
+
12
+ options[:json] = arguments.map do |argument_comparison|
13
+ hash = {
14
+ left: { id: argument_comparison.id, value: argument_comparison.value },
15
+ right: { id: argument_comparison.right_id, value: argument_comparison.right_value },
16
+ topic: { id: argument_comparison.topic_id, name: argument_comparison.topic_name }
17
+ }
18
+
19
+ if params[:side] == 'right'
20
+ left = hash[:left].clone
21
+ right = hash[:right].clone
22
+ hash[:left] = right
23
+ hash[:right] = left
24
+ end
25
+
26
+ hash
27
+ end
28
+
29
+ options[:meta] = {
30
+ pagination: {
31
+ total_pages: arguments.total_pages, current_page: arguments.current_page,
32
+ previous_page: arguments.previous_page, next_page: arguments.next_page
33
+ }
34
+ }
35
+
36
+ respond_with do |format|
37
+ format.json { render options }
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,26 @@
1
+ class Api::V1::ThingsController < ActionController::Base
2
+ include Voluntary::V1::BaseController
3
+
4
+ respond_to :json
5
+
6
+ def show
7
+ @thing = Thing.where('LOWER(name) = ?', params[:id].downcase).first
8
+
9
+ raise ActiveRecord::RecordNotFound if @thing.blank?
10
+
11
+ respond_with do |format|
12
+ format.json { render json: @thing, root: true }
13
+ end
14
+ end
15
+
16
+ def autocomplete
17
+ render json: (
18
+ Thing.order(:name).where("name LIKE ?", "%#{params[:term]}%").
19
+ map{|t| { id: t.id, value: t.name }}
20
+ ), root: false
21
+ end
22
+
23
+ def suggest
24
+ render json: Thing.suggest(params[:term]), root: false
25
+ end
26
+ end
@@ -0,0 +1,94 @@
1
+ class Api::V1::UserRankingItemsController < ActionController::Base
2
+ include Voluntary::V1::BaseController
3
+ include Concerns::Controller::BaseRankingItemsController
4
+
5
+ respond_to :json
6
+
7
+ def index
8
+ options = {}
9
+
10
+ if ranking.present?
11
+ options[:json] = if params[:user_id].present? || params[:user_name].present?
12
+ user.ranking_items
13
+ else
14
+ UserRankingItem
15
+ end
16
+
17
+ if params[:thing_name].present?
18
+ options[:json] = options[:json].where(
19
+ thing_id: Thing.where('LOWER(name) = ?', params[:thing_name].downcase).first.id
20
+ )
21
+ end
22
+
23
+ options[:json] = options[:json].order('position').where(ranking_id: ranking.id).includes(:thing)
24
+ options[:json] = options[:json].paginate(page: params[:page], per_page: 10)
25
+
26
+ options[:meta] = {
27
+ pagination: {
28
+ total_pages: options[:json].total_pages, current_page: options[:json].current_page,
29
+ previous_page: options[:json].previous_page, next_page: options[:json].next_page
30
+ }
31
+ }
32
+ else
33
+ options[:json] = []
34
+ options[:meta] = { pagination: { total_pages: 0, current_page: 0, previous_page: 0, next_page: 0 } }
35
+ end
36
+
37
+ respond_with do |format|
38
+ format.json { render options }
39
+ end
40
+ end
41
+
42
+ def create
43
+ raise CanCan::AccessDenied if current_user.blank?
44
+
45
+ respond_to do |format|
46
+ format.json { render json: current_user.add_ranking_item(params[:user_ranking_item]) }
47
+ end
48
+ end
49
+
50
+ def update
51
+ user_ranking_item = current_user.ranking_items.find(params[:id])
52
+ user_ranking_item.update_stars params[:user_ranking_item][:stars]
53
+ respond_with user_ranking_item
54
+ end
55
+
56
+ def move
57
+ user_ranking_item = UserRankingItem.find(params[:id])
58
+
59
+ raise CanCan::AccessDenied unless can? :update, user_ranking_item
60
+
61
+ user_ranking_item.set_position(params[:position].to_i)
62
+ respond_with user_ranking_item
63
+ end
64
+
65
+ def move_to_page
66
+ user_ranking_item = UserRankingItem.find(params[:id])
67
+
68
+ raise CanCan::AccessDenied unless can? :update, user_ranking_item
69
+
70
+ user_ranking_item.move_to_top_of_page(params[:page])
71
+
72
+ respond_to do |format|
73
+ format.json { render json: user_ranking_item }
74
+ end
75
+ end
76
+
77
+ def destroy
78
+ current_user.ranking_items.find(params[:id]).destroy!
79
+
80
+ render nothing: true
81
+ end
82
+
83
+ private
84
+
85
+ def user
86
+ if @user
87
+ @user
88
+ elsif params[:user_id].present?
89
+ @user = User.find(params[:user_id])
90
+ elsif params[:user_name].present?
91
+ @user = User.where('LOWER(name) = ?', params[:user_name].downcase).first
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,9 @@
1
+ class Api::V1::UsersController < ActionController::Base
2
+ include Voluntary::V1::BaseController
3
+
4
+ respond_to :json
5
+
6
+ def show
7
+ respond_with User.find(params[:id])
8
+ end
9
+ end
@@ -0,0 +1,16 @@
1
+ class Product::RankingController < Voluntary::EmberJs::ApplicationController
2
+ layout Proc.new { |controller| controller.request.xhr? || request.format.try('json?') ? false : 'voluntary/ember_js' }
3
+
4
+ def index
5
+ end
6
+
7
+ protected
8
+
9
+ def voluntary_ember_js_stylesheets
10
+ ['voluntary_ranking/application']
11
+ end
12
+
13
+ def voluntary_ember_js_javascripts
14
+ ['voluntary_ranking/application']
15
+ end
16
+ end
@@ -0,0 +1,48 @@
1
+ class Argument < ActiveRecord::Base
2
+ belongs_to :topic, class_name: 'ArgumentTopic'
3
+ belongs_to :thing
4
+
5
+ scope :compare_two_things, ->(side, left_thing_name, right_thing_name) do
6
+ left_thing_name, right_thing_name = right_thing_name, left_thing_name if side == 'right'
7
+
8
+ left_thing = Thing.where('LOWER(name) = ?', left_thing_name.downcase).first
9
+ right_thing = Thing.where('LOWER(name) = ?', right_thing_name.downcase).first
10
+
11
+ scope = joins(:topic).select('arguments.id, arguments.value, arguments.topic_id, argument_topics.name AS topic_name, arguments2.id AS right_id, arguments2.value AS right_value').
12
+ joins("#{side == 'both' ? 'INNER' : 'LEFT'} JOIN arguments arguments2 ON arguments2.thing_id = #{sanitize(right_thing.id)} AND arguments2.topic_id = arguments.topic_id")
13
+ scope = scope.where('arguments2.id IS NULL') unless side == 'both'
14
+ scope.where('arguments.thing_id = ?', left_thing.id)
15
+ end
16
+
17
+ validates :topic_id, presence: true
18
+ validates :thing_id, presence: true, uniqueness: { scope: :topic_id }
19
+ validates :value, presence: true
20
+
21
+ attr_accessible :topic_id, :thing_id, :value
22
+
23
+ def self.create_with_topic(attributes)
24
+ topic = ArgumentTopic.where('LOWER(name) = ?', attributes[:topic_name].to_s.strip.downcase).first
25
+
26
+ unless topic
27
+ topic = ArgumentTopic.create(name: attributes[:topic_name].to_s.strip)
28
+ end
29
+
30
+ if topic.valid?
31
+ thing_id = if attributes[:thing_name].present?
32
+ Thing.where('LOWER(name) = ?', attributes[:thing_name].downcase).first.id
33
+ else
34
+ Thing.where(id: attributes[:thing_id]).first.try(:id)
35
+ end
36
+
37
+ argument = Argument.create(topic_id: topic.id, thing_id: thing_id, value: attributes[:value])
38
+
39
+ if argument.valid?
40
+ argument
41
+ else
42
+ { errors: argument.errors.to_hash }
43
+ end
44
+ else
45
+ { errors: { topic: topic.errors.to_hash } }
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,7 @@
1
+ class ArgumentTopic < ActiveRecord::Base
2
+ has_many :arguments
3
+
4
+ validates :name, presence: true, uniqueness: { case_sensitive: false }
5
+
6
+ attr_accessible :name, :text
7
+ end
@@ -0,0 +1,2 @@
1
+ class Product::Ranking < ::Product
2
+ end
@@ -0,0 +1,97 @@
1
+ class Ranking < ActiveRecord::Base
2
+ attr_accessible :adjective, :topic, :scope, :negative_adjective
3
+
4
+ has_many :items, class_name: 'RankingItem', dependent: :destroy
5
+ has_many :user_items, class_name: 'UserRankingItem', dependent: :destroy
6
+
7
+ scope :for_user, ->(user_name) do
8
+ all.select('DISTINCT(rankings.id), rankings.*').joins(:user_items).
9
+ where('user_ranking_items.user_id = ?', User.where('LOWER(name) = ?', user_name.downcase).first.id)
10
+ end
11
+
12
+ scope :for_thing, ->(thing_id) do
13
+ all.select('DISTINCT(rankings.id), rankings.*').joins(:items).where('ranking_items.thing_id = ?', thing_id)
14
+ end
15
+
16
+ validates :adjective, presence: true
17
+ validates :topic, presence: true, uniqueness: { scope: [:adjective, :scope], case_sensitive: false }
18
+ validates :scope, presence: true
19
+ validate :one_ranking_per_topic_scope_and_one_of_the_adjectives
20
+
21
+ #pusherable "#{Rails.env}_channel"
22
+
23
+ def self.find_by_params(params)
24
+ scope = if params[:negative_adjective].present?
25
+ where(
26
+ 'LOWER(adjective) = ? AND LOWER(negative_adjective) = ?',
27
+ params[:adjective].downcase, params[:negative_adjective].downcase
28
+ )
29
+ else
30
+ where(
31
+ '(LOWER(adjective) = :adjective OR LOWER(negative_adjective) = :adjective)',
32
+ adjective: params[:adjective].downcase
33
+ )
34
+ end
35
+
36
+ scope.where(
37
+ 'LOWER(topic) = ? AND LOWER(scope) = ?', params[:topic].downcase, params[:scope].downcase
38
+ ).first
39
+ end
40
+
41
+ def self.find_or_create_by_params(params)
42
+ attributes = (params[:user_ranking_item] || params[:ranking_item] || params).clone
43
+ attributes.symbolize_keys! unless params.is_a?(ActiveSupport::HashWithIndifferentAccess)
44
+
45
+ if attributes[:ranking_id].present? then Ranking.find(attributes[:ranking_id])
46
+ elsif attributes[:adjective].present?
47
+ rankings = Ranking
48
+ ranking = Ranking.new
49
+
50
+ attributes.each do |param, value|
51
+ if ranking.respond_to?(param) && value.present?
52
+ rankings = rankings.where("LOWER(#{param}) = ?", value.downcase)
53
+ else
54
+ attributes.delete(param)
55
+ end
56
+ end
57
+
58
+ rankings.first || Ranking.create(attributes)
59
+ end
60
+ end
61
+
62
+ def self.autocomplete_attribute(attribute, term)
63
+ unless ['adjective', 'negative_adjective', 'topic', 'scope'].include? attribute
64
+ raise ActiveRecord::RecordNotFound
65
+ end
66
+
67
+ Ranking.select("DISTINCT(#{attribute})").order(attribute).limit(10).
68
+ where("#{attribute} LIKE ?", "%#{term.strip}%").map(&attribute.to_sym).map{|v| { value: v }}
69
+ end
70
+
71
+ private
72
+
73
+ def one_ranking_per_topic_scope_and_one_of_the_adjectives
74
+ if Ranking.where(
75
+ '(' +
76
+ 'LOWER(adjective) = :adjective OR LOWER(negative_adjective) = :adjective OR ' +
77
+ 'LOWER(adjective) = :negative_adjective OR LOWER(negative_adjective) = :negative_adjective' +
78
+ ') AND ' +
79
+ 'LOWER(topic) = :topic AND LOWER(scope) = :scope',
80
+ adjective: adjective.downcase, negative_adjective: negative_adjective.downcase,
81
+ topic: topic.downcase, scope: scope.downcase
82
+ ).any?
83
+ errors[:base] << I18n.t(
84
+ 'activerecord.errors.models.ranking.attributes.base.' +
85
+ 'one_ranking_per_topic_scope_and_one_of_the_adjectives'
86
+ )
87
+ end
88
+ end
89
+
90
+ def special_characters_excluded
91
+ if adjective.match(/\//) || negative_adjective.match(/\//) || topic.match(/\//) || scope.match(/\//)
92
+ errors[:name] << I18n.t(
93
+ 'activerecord.errors.models.ranking.attributes.name.unwanted_special_characters_included'
94
+ )
95
+ end
96
+ end
97
+ end