merit 0.2.5 → 0.3.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (86) hide show
  1. data/Gemfile +2 -0
  2. data/Gemfile.lock +103 -0
  3. data/README.markdown +17 -2
  4. data/app/models/badge.rb +27 -19
  5. data/app/models/merit_action.rb +5 -3
  6. data/app/models/sash.rb +5 -14
  7. data/lib/generators/active_record/merit_generator.rb +14 -14
  8. data/lib/generators/{merit → active_record}/templates/create_badges_sashes.rb +0 -0
  9. data/lib/generators/{merit → active_record}/templates/create_merit_actions.rb +0 -0
  10. data/lib/generators/{merit → active_record}/templates/create_sashes.rb +0 -0
  11. data/lib/generators/merit/install_generator.rb +3 -20
  12. data/lib/generators/merit/merit_generator.rb +12 -0
  13. data/lib/generators/merit/templates/merit.rb +10 -1
  14. data/lib/merit.rb +11 -2
  15. data/lib/merit/controller_extensions.rb +2 -2
  16. data/lib/merit/model_additions.rb +18 -17
  17. data/lib/merit/models/active_record/badges_sash.rb +12 -0
  18. data/lib/merit/models/active_record/merit_action.rb +2 -0
  19. data/lib/merit/models/active_record/sash.rb +12 -0
  20. data/lib/merit/models/mongo_mapper/merit_action.rb +12 -0
  21. data/lib/merit/models/mongo_mapper/sash.rb +15 -0
  22. data/lib/merit/rule.rb +2 -2
  23. data/lib/merit/rules_badge.rb +1 -1
  24. data/merit.gemspec +16 -0
  25. data/test/dummy/Rakefile +7 -0
  26. data/test/dummy/app/controllers/application_controller.rb +7 -0
  27. data/test/dummy/app/controllers/comments_controller.rb +90 -0
  28. data/test/dummy/app/controllers/registrations_controller.rb +15 -0
  29. data/test/dummy/app/controllers/users_controller.rb +67 -0
  30. data/test/dummy/app/helpers/application_helper.rb +2 -0
  31. data/test/dummy/app/models/comment.rb +4 -0
  32. data/test/dummy/app/models/merit_badge_rules.rb +41 -0
  33. data/test/dummy/app/models/merit_point_rules.rb +18 -0
  34. data/test/dummy/app/models/merit_rank_rules.rb +25 -0
  35. data/test/dummy/app/models/user.rb +9 -0
  36. data/test/dummy/app/views/comments/_form.html.erb +29 -0
  37. data/test/dummy/app/views/comments/edit.html.erb +6 -0
  38. data/test/dummy/app/views/comments/index.html.erb +35 -0
  39. data/test/dummy/app/views/comments/new.html.erb +5 -0
  40. data/test/dummy/app/views/comments/show.html.erb +23 -0
  41. data/test/dummy/app/views/layouts/application.html.haml +18 -0
  42. data/test/dummy/app/views/users/_form.html.erb +22 -0
  43. data/test/dummy/app/views/users/edit.html.erb +6 -0
  44. data/test/dummy/app/views/users/index.html.erb +26 -0
  45. data/test/dummy/app/views/users/new.html.erb +5 -0
  46. data/test/dummy/app/views/users/show.html.erb +18 -0
  47. data/test/dummy/config.ru +4 -0
  48. data/test/dummy/config/application.rb +24 -0
  49. data/test/dummy/config/boot.rb +10 -0
  50. data/test/dummy/config/database.yml +22 -0
  51. data/test/dummy/config/environment.rb +5 -0
  52. data/test/dummy/config/environments/development.rb +26 -0
  53. data/test/dummy/config/environments/production.rb +49 -0
  54. data/test/dummy/config/environments/test.rb +35 -0
  55. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  56. data/test/dummy/config/initializers/badges_data.rb +40 -0
  57. data/test/dummy/config/initializers/inflections.rb +10 -0
  58. data/test/dummy/config/initializers/mime_types.rb +5 -0
  59. data/test/dummy/config/initializers/secret_token.rb +7 -0
  60. data/test/dummy/config/initializers/session_store.rb +8 -0
  61. data/test/dummy/config/locales/en.yml +5 -0
  62. data/test/dummy/config/routes.rb +9 -0
  63. data/test/dummy/db/migrate/20110421191249_create_users.rb +12 -0
  64. data/test/dummy/db/migrate/20110421191250_create_comments.rb +16 -0
  65. data/test/dummy/db/schema.rb +61 -0
  66. data/test/dummy/db/seeds.rb +17 -0
  67. data/test/dummy/public/404.html +26 -0
  68. data/test/dummy/public/422.html +26 -0
  69. data/test/dummy/public/500.html +26 -0
  70. data/test/dummy/public/favicon.ico +0 -0
  71. data/test/dummy/public/javascripts/application.js +2 -0
  72. data/test/dummy/public/javascripts/controls.js +965 -0
  73. data/test/dummy/public/javascripts/dragdrop.js +974 -0
  74. data/test/dummy/public/javascripts/effects.js +1123 -0
  75. data/test/dummy/public/javascripts/prototype.js +6001 -0
  76. data/test/dummy/public/javascripts/rails.js +191 -0
  77. data/test/dummy/public/stylesheets/.gitkeep +0 -0
  78. data/test/dummy/public/stylesheets/scaffold.css +56 -0
  79. data/test/dummy/script/rails +6 -0
  80. data/test/integration/navigation_test.rb +132 -0
  81. data/test/merit_unit_test.rb +22 -0
  82. data/test/support/integration_case.rb +5 -0
  83. data/test/test_helper.rb +22 -0
  84. metadata +132 -13
  85. data/app/models/badges_sash.rb +0 -14
  86. data/lib/generators/merit/templates/create_badges.rb +0 -19
data/Gemfile CHANGED
@@ -1,5 +1,7 @@
1
1
  source "http://rubygems.org"
2
2
 
3
+ gem 'ambry'
4
+
3
5
  gem "rails", "~> 3.0.10"
4
6
  gem "sqlite3"
5
7
  gem "haml"
@@ -0,0 +1,103 @@
1
+ GEM
2
+ remote: http://rubygems.org/
3
+ specs:
4
+ abstract (1.0.0)
5
+ actionmailer (3.0.11)
6
+ actionpack (= 3.0.11)
7
+ mail (~> 2.2.19)
8
+ actionpack (3.0.11)
9
+ activemodel (= 3.0.11)
10
+ activesupport (= 3.0.11)
11
+ builder (~> 2.1.2)
12
+ erubis (~> 2.6.6)
13
+ i18n (~> 0.5.0)
14
+ rack (~> 1.2.1)
15
+ rack-mount (~> 0.6.14)
16
+ rack-test (~> 0.5.7)
17
+ tzinfo (~> 0.3.23)
18
+ activemodel (3.0.11)
19
+ activesupport (= 3.0.11)
20
+ builder (~> 2.1.2)
21
+ i18n (~> 0.5.0)
22
+ activerecord (3.0.11)
23
+ activemodel (= 3.0.11)
24
+ activesupport (= 3.0.11)
25
+ arel (~> 2.0.10)
26
+ tzinfo (~> 0.3.23)
27
+ activeresource (3.0.11)
28
+ activemodel (= 3.0.11)
29
+ activesupport (= 3.0.11)
30
+ activesupport (3.0.11)
31
+ ambry (0.2.4)
32
+ arel (2.0.10)
33
+ builder (2.1.2)
34
+ capybara (1.1.2)
35
+ mime-types (>= 1.16)
36
+ nokogiri (>= 1.3.3)
37
+ rack (>= 1.0.0)
38
+ rack-test (>= 0.5.4)
39
+ selenium-webdriver (~> 2.0)
40
+ xpath (~> 0.1.4)
41
+ childprocess (0.3.1)
42
+ ffi (~> 1.0.6)
43
+ erubis (2.6.6)
44
+ abstract (>= 1.0.0)
45
+ ffi (1.0.11)
46
+ haml (3.1.4)
47
+ i18n (0.5.0)
48
+ json (1.6.5)
49
+ mail (2.2.19)
50
+ activesupport (>= 2.3.6)
51
+ i18n (>= 0.4.0)
52
+ mime-types (~> 1.16)
53
+ treetop (~> 1.4.8)
54
+ mime-types (1.17.2)
55
+ multi_json (1.0.4)
56
+ nokogiri (1.5.0)
57
+ polyglot (0.3.3)
58
+ rack (1.2.5)
59
+ rack-mount (0.6.14)
60
+ rack (>= 1.0.0)
61
+ rack-test (0.5.7)
62
+ rack (>= 1.0)
63
+ rails (3.0.11)
64
+ actionmailer (= 3.0.11)
65
+ actionpack (= 3.0.11)
66
+ activerecord (= 3.0.11)
67
+ activeresource (= 3.0.11)
68
+ activesupport (= 3.0.11)
69
+ bundler (~> 1.0)
70
+ railties (= 3.0.11)
71
+ railties (3.0.11)
72
+ actionpack (= 3.0.11)
73
+ activesupport (= 3.0.11)
74
+ rake (>= 0.8.7)
75
+ rdoc (~> 3.4)
76
+ thor (~> 0.14.4)
77
+ rake (0.9.3.beta.1)
78
+ rdoc (3.12)
79
+ json (~> 1.4)
80
+ rubyzip (0.9.6.1)
81
+ selenium-webdriver (2.19.0)
82
+ childprocess (>= 0.2.5)
83
+ ffi (~> 1.0.9)
84
+ multi_json (~> 1.0.4)
85
+ rubyzip
86
+ sqlite3 (1.3.5)
87
+ thor (0.14.6)
88
+ treetop (1.4.10)
89
+ polyglot
90
+ polyglot (>= 0.3.1)
91
+ tzinfo (0.3.31)
92
+ xpath (0.1.4)
93
+ nokogiri (~> 1.3)
94
+
95
+ PLATFORMS
96
+ ruby
97
+
98
+ DEPENDENCIES
99
+ ambry
100
+ capybara
101
+ haml
102
+ rails (~> 3.0.10)
103
+ sqlite3
@@ -5,7 +5,7 @@
5
5
 
6
6
  # Installation
7
7
 
8
- 1. Add 'merit' to your Gemfile
8
+ 1. Add <tt>gem 'merit'</tt> to your Gemfile
9
9
  2. Run <tt>rails g merit:install</tt>
10
10
  3. Run <tt>rails g merit MODEL_NAME</tt>
11
11
  4. Run <tt>rake db:migrate</tt>
@@ -115,6 +115,21 @@ You may also add badges/rank "by hand" from controller actions:
115
115
 
116
116
  ---
117
117
 
118
+ # MongoMapper support (experimental)
119
+
120
+ You may use <tt>merit</tt> with <tt>mongo_mapper</tt> ORM by calling
121
+ <tt>rails g merit MODEL_NAME --orm mongo_mapper</tt> in the third step of the
122
+ installation, and configuring <tt>config.orm = :mongo_mapper</tt> in
123
+ <tt>config/initializers/merit.rb</tt>.
124
+
125
+ ---
126
+
127
+ # Upgrade to 0.3.0
128
+
129
+ Badges data is now stored in <tt>config/initializers/merit.rb</tt> using
130
+ <tt>ambry</tt> syntax (not in the DB anymore, as that table needed to be in
131
+ sync in all development environments).
132
+
118
133
  # Upgrade to 0.2.0
119
134
 
120
135
  Added <tt>had_errors</tt> boolean attribute to <tt>merit_actions</tt> table.
@@ -138,9 +153,9 @@ To run tests follow:
138
153
 
139
154
  # To-do list
140
155
 
156
+ * Use ActiveSupport's Array#wrap where I build arrays by hand
141
157
  * Ranking should not be badges, so .badges doesn't return them (2-stars
142
158
  shouldn't be badge).
143
- * <tt>grep -r 'FIXME\|TODO' .</tt>
144
159
  * :value parameter (for star voting for example) should be configurable
145
160
  (depends on params[:value] on the controller).
146
161
  * Make fixtures for integration testing (now creating objects on test file!).
@@ -1,39 +1,47 @@
1
- class Badge < ActiveRecord::Base
2
- has_many :badges_sashes
3
- has_many :sashes, :through => :badges_sashes
1
+ require 'ambry/active_model'
2
+
3
+ class Badge
4
+ extend Ambry::Model
5
+ extend Ambry::ActiveModel
6
+
7
+ field :id, :name, :level, :image, :description
8
+
9
+ validates_presence_of :id, :name
10
+ validates_uniqueness_of :id
11
+
12
+ filters do
13
+ def find_by_id(ids)
14
+ ids = ids.kind_of?(Array) ? ids : [ids]
15
+ find{|b| ids.include? b.id }
16
+ end
17
+ def by_name(name)
18
+ find{|b| b.name == name.to_s }
19
+ end
20
+ def by_level(level)
21
+ find{|b| b.level.to_s == level.to_s }
22
+ end
23
+ end
4
24
 
5
25
  # Grant badge to sash
6
26
  def grant_to(object_or_sash)
7
27
  object_or_sash.create_sash_if_none unless object_or_sash.kind_of?(Sash)
8
28
  sash = object_or_sash.respond_to?(:sash) ? object_or_sash.sash : object_or_sash
9
- unless sash.badges.include? self
10
- sash.badges << self
11
- sash.save
12
- end
29
+ sash.add_badge(id) unless sash.badge_ids.include?(id)
13
30
  end
14
31
 
15
32
  # Take out badge from sash
16
33
  def delete_from(object_or_sash)
17
34
  object_or_sash.create_sash_if_none unless object_or_sash.kind_of?(Sash)
18
35
  sash = object_or_sash.respond_to?(:sash) ? object_or_sash.sash : object_or_sash
19
- if sash.badges.include? self
20
- sash.badges -= [self]
21
- sash.save
22
- end
36
+ sash.rm_badge(id) if sash.badge_ids.include?(id)
23
37
  end
24
38
 
25
39
  # Give rank to sash if it's greater. Delete lower ranks it may have.
26
40
  def grant_rank_to(sash)
27
41
  # Grant to sash if had lower rank. Do nothing if has same or greater rank.
28
42
  if sash.has_lower_rank_than(self)
29
- sash.badges -= Badge.where(:name => name) # Clean up old ranks
30
- sash.badges << self
31
- sash.save
43
+ Badge.by_name(name).keys.each{|id| sash.rm_badge(id) } # Clean up old ranks
44
+ sash.add_badge(id)
32
45
  end
33
46
  end
34
-
35
- def self.latest(limit = nil)
36
- scope = order('created_at DESC')
37
- limit.present? ? scope.limit(limit) : scope
38
- end
39
47
  end
@@ -1,6 +1,8 @@
1
- class MeritAction < ActiveRecord::Base
1
+ require "merit/models/#{Merit.orm}/merit_action"
2
+
3
+ class MeritAction
2
4
  # Check rules defined for a merit_action
3
- def check_badge_rules(defined_rules)
5
+ def check_rules(defined_rules)
4
6
  action_name = "#{target_model}\##{action_method}"
5
7
 
6
8
  unless had_errors
@@ -25,7 +27,7 @@ class MeritAction < ActiveRecord::Base
25
27
  begin
26
28
  target = target_object.send(to)
27
29
  rescue
28
- Rails.logger.warn "[merit] No target_object found on check_badge_rules."
30
+ Rails.logger.warn "[merit] No target_object found on check_rules."
29
31
  return
30
32
  end
31
33
  end
@@ -1,19 +1,10 @@
1
- class Sash < ActiveRecord::Base
2
- has_many :badges_sashes
3
- has_many :badges, :through => :badges_sashes
4
-
5
- # Latest badges granted by Merit
6
- def self.latest_badges(limit = 10)
7
- select('DISTINCT sashes.id, sashes.*').joins(:badges_sashes).order('badges_sashes.created_at DESC').limit(limit)
8
- end
1
+ require "merit/models/#{Merit.orm}/sash"
9
2
 
3
+ class Sash
10
4
  # Decides if sash has lower rank than a given badge
11
5
  def has_lower_rank_than(badge)
12
- levels(badge.name).all_lower_than badge.level
13
- end
14
-
15
- # Collect Sash levels given a badge name
16
- def levels(badge_name)
17
- badges.where(:name => badge_name).collect(&:level)
6
+ badges = Badge.find_by_id(badge_ids)
7
+ levels = badges.by_name(badge.name).collect(&:level)
8
+ levels.all_lower_than badge.level
18
9
  end
19
10
  end
@@ -3,25 +3,25 @@ require 'rails/generators/active_record'
3
3
  module ActiveRecord
4
4
  module Generators
5
5
  class MeritGenerator < ActiveRecord::Generators::Base
6
- argument :attributes, :type => :array, :default => [], :banner => "field:type field:type"
6
+ include Rails::Generators::Migration
7
+ source_root File.expand_path('../templates', __FILE__)
8
+ desc "add active_record merit migrations"
7
9
 
8
- source_root File.expand_path("../templates", __FILE__)
9
-
10
- def model_exists?
11
- File.exists?(File.join(destination_root, model_path))
12
- end
13
-
14
- def model_path
15
- @model_path ||= File.join("app", "models", "#{file_path}.rb")
10
+ def self.next_migration_number(path)
11
+ unless @prev_migration_nr
12
+ @prev_migration_nr = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
13
+ else
14
+ @prev_migration_nr += 1
15
+ end
16
+ @prev_migration_nr.to_s
16
17
  end
17
18
 
18
- def copy_merit_migration
19
+ def copy_migrations_and_model
20
+ migration_template 'create_merit_actions.rb', 'db/migrate/create_merit_actions.rb'
21
+ migration_template 'create_sashes.rb', 'db/migrate/create_sashes.rb'
22
+ migration_template 'create_badges_sashes.rb', 'db/migrate/create_badges_sashes.rb'
19
23
  migration_template "add_fields_to_model.rb", "db/migrate/add_fields_to_#{table_name}"
20
24
  end
21
-
22
- def inject_merit_content
23
- inject_into_class(model_path, class_name, " has_merit\n\n") if model_exists?
24
- end
25
25
  end
26
26
  end
27
27
  end
@@ -1,30 +1,13 @@
1
- require 'rails/generators/migration'
2
-
3
1
  module Merit
4
2
  module Generators
5
- class InstallGenerator < ::Rails::Generators::Base
6
- include Rails::Generators::Migration
3
+ class InstallGenerator < Rails::Generators::Base
7
4
  source_root File.expand_path('../templates', __FILE__)
8
- desc "add the migrations"
9
-
10
- def self.next_migration_number(path)
11
- unless @prev_migration_nr
12
- @prev_migration_nr = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
13
- else
14
- @prev_migration_nr += 1
15
- end
16
- @prev_migration_nr.to_s
17
- end
18
-
5
+ desc "Copy config and rules files"
19
6
  def copy_migrations_and_model
20
- migration_template 'create_merit_actions.rb', 'db/migrate/create_merit_actions.rb'
21
- migration_template 'create_badges.rb', 'db/migrate/create_badges.rb'
22
- migration_template 'create_sashes.rb', 'db/migrate/create_sashes.rb'
23
- migration_template 'create_badges_sashes.rb', 'db/migrate/create_badges_sashes.rb'
7
+ template 'merit.rb', 'config/initializers/merit.rb'
24
8
  template 'merit_badge_rules.rb', 'app/models/merit_badge_rules.rb'
25
9
  template 'merit_point_rules.rb', 'app/models/merit_point_rules.rb'
26
10
  template 'merit_rank_rules.rb', 'app/models/merit_rank_rules.rb'
27
- template 'merit.rb', 'config/initializers/merit.rb'
28
11
  end
29
12
  end
30
13
  end
@@ -3,6 +3,18 @@ module Merit
3
3
  class MeritGenerator < Rails::Generators::NamedBase
4
4
  source_root File.expand_path("../templates", __FILE__)
5
5
  hook_for :orm
6
+
7
+ def model_exists?
8
+ File.exists?(File.join(destination_root, model_path))
9
+ end
10
+
11
+ def model_path
12
+ @model_path ||= File.join("app", "models", "#{file_path}.rb")
13
+ end
14
+
15
+ def inject_merit_content
16
+ inject_into_class(model_path, class_name, " has_merit\n\n") if model_exists?
17
+ end
6
18
  end
7
19
  end
8
20
  end
@@ -2,4 +2,13 @@
2
2
  Merit.setup do |config|
3
3
  # Check rules on each request or in background
4
4
  # config.checks_on_each_request = true
5
- end
5
+
6
+ # Define ORM. Could be :active_record (default) and :mongo_mapper
7
+ # config.orm = :active_record
8
+ end
9
+
10
+ # Create application badges (uses https://github.com/norman/ambry)
11
+ # Badge.create!({
12
+ # :id => 1,
13
+ # :name => 'just-registered'
14
+ # })
@@ -11,6 +11,10 @@ module Merit
11
11
  mattr_accessor :checks_on_each_request
12
12
  @@checks_on_each_request = true
13
13
 
14
+ # Define ORM
15
+ mattr_accessor :orm
16
+ @@orm = :active_record
17
+
14
18
  # Load configuration from initializer
15
19
  def self.setup
16
20
  yield self
@@ -18,9 +22,14 @@ module Merit
18
22
 
19
23
  class Engine < Rails::Engine
20
24
  initializer 'merit.controller' do |app|
25
+ # Merit.orm now set
26
+ if Merit.orm == :active_record
27
+ require "merit/models/#{Merit.orm}/badges_sash"
28
+ end
29
+
21
30
  ActiveSupport.on_load(:action_controller) do
22
- include Merit::ControllerExtensions
31
+ include Merit::ControllerExtensions
23
32
  end
24
33
  end
25
34
  end
26
- end
35
+ end
@@ -16,7 +16,7 @@ module Merit
16
16
  target_id = target_object.try(:id)
17
17
  end
18
18
 
19
- # TODO: value relies on params[:value] on the controller, should be configurable
19
+ # TODO: value should be configurable (now it's params[:value] set in the controller)
20
20
  value = params[:value]
21
21
  MeritAction.create(
22
22
  :user_id => current_user.try(:id),
@@ -29,7 +29,7 @@ module Merit
29
29
 
30
30
  # Check rules in after_filter?
31
31
  if Merit.checks_on_each_request
32
- ::MeritBadgeRules.new.check_new_actions
32
+ badge_rules.check_new_actions
33
33
  end
34
34
  end
35
35
  end
@@ -1,30 +1,31 @@
1
1
  module Merit
2
- def self.included(base)
3
- base.send :extend, ClassMethods
4
- end
2
+ extend ActiveSupport::Concern
5
3
 
6
4
  module ClassMethods
7
5
  def has_merit(options = {})
8
6
  belongs_to :sash
9
- send :include, InstanceMethods
7
+
8
+ if Merit.orm == :mongo_mapper
9
+ plugin Merit
10
+ key :sash_id, String
11
+ key :points, Integer, :default => 0
12
+ end
10
13
  end
11
14
  end
12
15
 
13
- module InstanceMethods
14
- # Return it's sash badges
15
- def badges
16
- create_sash_if_none
17
- sash.badges
18
- end
16
+ def badges
17
+ create_sash_if_none
18
+ Badge.find_by_id(sash.badge_ids).to_a
19
+ end
19
20
 
20
- # Create sash if doesn't have
21
- def create_sash_if_none
22
- if sash.nil?
23
- self.sash = Sash.new
24
- self.save(:validate => false)
25
- end
21
+ # Create sash if doesn't have
22
+ def create_sash_if_none
23
+ if sash.nil?
24
+ self.sash = Sash.new
25
+ self.save(:validate => false)
26
26
  end
27
27
  end
28
28
  end
29
29
 
30
- ActiveRecord::Base.send :include, Merit
30
+ ActiveRecord::Base.send :include, Merit if Object.const_defined?('ActiveRecord')
31
+ MongoMapper::Document.plugin Merit if Object.const_defined?('MongoMapper')