rostra 0.0.10 → 0.0.11

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.
data/README.rdoc CHANGED
@@ -18,9 +18,9 @@ Install and run the necessary migrations:
18
18
  rake rostra:install:migrations
19
19
  rake db:migrate
20
20
 
21
- Run the generator to create the configuration file:
21
+ Run the generator to create <tt>config/initializers/rostra.rb</tt> and <tt>app/helpers/application_helper.rb</tt>:
22
22
 
23
- rails generate rostra:install # see `config/initializers/rostra.rb` for details
23
+ rails generate rostra:install
24
24
 
25
25
  Call <tt>rostra</tt> in your user model:
26
26
 
@@ -38,7 +38,9 @@ There's some stuff you'll almost definitely want to do in order to get Rostra wo
38
38
  Also, be sure to read through <tt>config/initializers/rostra.rb</tt> and override configuration options to suit your app. At the very least, you'll want to change:
39
39
 
40
40
  config.deliver_emails_from = 'change_me@example.com'
41
- `
41
+
42
+ Finally, have a look at <tt>app/helpers/rostra/application_helper.rb</tt>, you may need to add/override helper methods.
43
+
42
44
  == Customizing the question and answer models
43
45
  Rostra provides a DSL for adding application specific logic to <tt>Rostra::Question</tt> and <tt>Rostra:Answer</tt>:
44
46
 
@@ -67,6 +69,11 @@ Rostra provides a DSL for adding application specific logic to <tt>Rostra::Quest
67
69
  end
68
70
  end
69
71
 
72
+ == Setting permissions
73
+ Under the hood, Rostra uses <tt>CanCan</tt> to set permissions. By default anyone can read rostra content - even if they are not logged in. A user can contribute content (i.e. ask questions, give answers, and leave comments) as long as they've logged in.
74
+
75
+ If you need to change these defaults, override <tt>can_participate_in_rostra?</tt> in your user model. <tt>CanCan</tt> will use this method to determine who can participate. If you need to specify even more complicated permissions, override <tt>app/models/rostra/ability.rb</tt> and go wild.
76
+
70
77
 
71
78
  == Contributing to Rostra
72
79
  Fork the project, make your changes, and submit a pull request. Please ensure the tests pass:
@@ -47,7 +47,7 @@ $(document).ready(function() {
47
47
  theme_advanced_buttons3 : "",
48
48
  theme_advanced_toolbar_location : "top",
49
49
  theme_advanced_toolbar_align : "left",
50
- plugins: 'paste, autoresize',
50
+ plugins: 'paste',
51
51
  paste_remove_styles: true,
52
52
  paste_remove_spans: true,
53
53
  setup: function (ed) {
@@ -1,68 +1,5 @@
1
1
  module Rostra
2
2
  module ApplicationHelper
3
-
4
- # Loop thru included helper files and include them in the project.
5
- #
6
- Rostra::Config.included_helpers.each do |helper_file|
7
- include helper_file
8
- end
9
-
10
- # Creates a list of tags linking to the index showing only questions with that tag
11
- #
12
- def tag_list(question)
13
- content_tag :div, "Tags: #{question.tags.map { |tag| link_to tag, questions_path(:tag_search => "#{tag}")}.join}".html_safe, class: 'tags'
14
- end
15
-
16
- # Finds the url to the user's avatar following this logic:
17
- #
18
- # 1. Calls the method defined in the <tt>Rostra::Config</tt>
19
- # 2. Uses the users email address to looks for a gravatar
20
- # 3. Renders <tt>app/assets/images/rostra/anonymous_avatar.png</tt>
21
- #
22
- def avatar_url(user)
23
- avatar_method = Rostra::Config.rostra_user_avatar
24
-
25
- if user.respond_to?(avatar_method)
26
- user.send(avatar_method)
27
- else
28
- default_url = "#{main_app.root_url}assets/rostra/anonymous_avatar.png"
29
- gravatar_id = Digest::MD5.hexdigest(user.rostra_user_email.downcase)
30
- "http://gravatar.com/avatar/#{gravatar_id}.png?s=48&d=#{CGI.escape(default_url)}"
31
- end
32
- end
33
-
34
- # Method to build links for ajax-y voting arrows.
35
- #
36
- def link_to_vote(direction, resource)
37
- if user_signed_in? && ( (direction == :up && rostra_user.voted_for?(resource)) || (direction == :down && rostra_user.voted_against?(resource)) )
38
- selected = 'selected'
39
- else
40
- selected = ''
41
- end
42
-
43
- link_to "Vote #{direction.capitalize}", vote_path(resource, direction), method: :put, remote: true, class: "vote #{direction} #{selected}"
44
- end
45
-
46
- # Returns a rostra object's base class name. For example, <tt>@question</tt> is an instance of
47
- # Rostra::Question and so:
48
- #
49
- # class_name(@question) # => 'question'
50
- #
51
- def class_name(resource)
52
- resource.class.name.split('::').last.downcase
53
- end
54
-
55
- private
56
-
57
- def vote_path(resource, direction)
58
- return '#' if !user_signed_in? || can?(:manage, resource)
59
-
60
- if resource.is_a?(Rostra::Question)
61
- vote_question_path(resource, vote_direction: direction)
62
- else
63
- vote_question_answer_path(resource.question, resource, vote_direction: direction)
64
- end
65
- end
66
-
3
+ include Rostra::BaseHelper
67
4
  end
68
5
  end
@@ -0,0 +1,86 @@
1
+ module Rostra
2
+ module BaseHelper
3
+
4
+ # Method used to render to user names (e.g. where it says: "John Does says:"). You can,
5
+ # for example, use this method to turn "John Doe" into a link to his profile page.
6
+ #
7
+ def link_to_profile(user)
8
+ link_to user.rostra_user_name, main_app.user_path(user)
9
+ end
10
+
11
+ # Used to populate both the <tt>title</tt> and <tt>h1</tt> elements for each page.
12
+ #
13
+ def page_title_helper
14
+ case "#{controller_name}##{action_name}"
15
+ when "questions#show" then @question.title
16
+ when "questions#index" then
17
+ if params[:tag_search].present?
18
+ "Recent Questions for tag #{params[:tag_search]}"
19
+ else
20
+ "Recent Questions"
21
+ end
22
+ when "questions#new" then "Post a new question"
23
+ when "questions#edit" then "Editing question"
24
+ when "answers#edit" then "Editing answer"
25
+ else "Recent Questions"
26
+ end
27
+ end
28
+
29
+ # Creates a list of tags linking to the index showing only questions with that tag
30
+ #
31
+ def tag_list(question)
32
+ tags = question.tags.map { |tag| link_to tag, questions_path(:tag_search => "#{tag}")}.join
33
+ content_tag :div, "Tags: #{tags}".html_safe, class: 'tags'
34
+ end
35
+
36
+ # Finds the url to the user's avatar following this logic:
37
+ #
38
+ # 1. Calls <tt>avatar</tt> on the user object
39
+ # 2. Uses the users email address to look for a gravatar
40
+ # 3. Renders <tt>app/assets/images/rostra/anonymous_avatar.png</tt>
41
+ #
42
+ def avatar_url(user)
43
+ if user.respond_to?(:avatar)
44
+ user.avatar
45
+ else
46
+ default_url = "#{main_app.root_url}assets/rostra/anonymous_avatar.png"
47
+ gravatar_id = Digest::MD5.hexdigest(user.rostra_user_email.downcase)
48
+ "http://gravatar.com/avatar/#{gravatar_id}.png?s=48&d=#{CGI.escape(default_url)}"
49
+ end
50
+ end
51
+
52
+ # Method to build links for ajax-y voting arrows.
53
+ #
54
+ def link_to_vote(direction, resource)
55
+ if user_signed_in? && ( (direction == :up && rostra_user.voted_for?(resource)) || (direction == :down && rostra_user.voted_against?(resource)) )
56
+ selected = 'selected'
57
+ else
58
+ selected = ''
59
+ end
60
+
61
+ link_to "Vote #{direction.capitalize}", vote_path(resource, direction), method: :put, remote: true, class: "vote #{direction} #{selected}"
62
+ end
63
+
64
+ # Returns a rostra object's base class name. For example, <tt>@question</tt> is an instance of
65
+ # Rostra::Question and so:
66
+ #
67
+ # class_name(@question) # => 'question'
68
+ #
69
+ def class_name(resource)
70
+ resource.class.name.split('::').last.downcase
71
+ end
72
+
73
+ private
74
+
75
+ def vote_path(resource, direction)
76
+ return '#' if !user_signed_in? || can?(:manage, resource)
77
+
78
+ if resource.is_a?(Rostra::Question)
79
+ vote_question_path(resource, vote_direction: direction)
80
+ else
81
+ vote_question_answer_path(resource.question, resource, vote_direction: direction)
82
+ end
83
+ end
84
+
85
+ end
86
+ end
@@ -7,7 +7,7 @@ module Rostra
7
7
  def initialize(user)
8
8
  @user = user || User.new
9
9
 
10
- if logged_in?
10
+ if @user.can_participate_in_rostra?
11
11
  can :manage, Question, :user => user
12
12
  can :manage, Answer, :user => user
13
13
  can :manage, Comment, :user => user
@@ -22,11 +22,5 @@ module Rostra
22
22
  can :read, :all
23
23
  end
24
24
 
25
- private
26
-
27
- def logged_in?
28
- ! user.new_record?
29
- end
30
-
31
25
  end
32
26
  end
@@ -1,9 +1,9 @@
1
1
  <!DOCTYPE html>
2
2
  <html>
3
3
  <head>
4
- <title>Rostra</title>
5
- <%= stylesheet_link_tag "rostra/application" %>
6
- <%= javascript_include_tag "rostra/application" %>
4
+ <title><%=page_title_helper || 'Rostra' %></title>
5
+ <%= stylesheet_link_tag 'rostra/application' %>
6
+ <%= javascript_include_tag 'rostra/application' %>
7
7
  <%= csrf_meta_tags %>
8
8
  </head>
9
9
  <body id="<%= controller_name %>" class="<%= action_name %>">
@@ -15,21 +15,7 @@
15
15
  <%= content_tag :div, msg, id: "flash_#{name}", class: 'flash' %>
16
16
  <% end %>
17
17
 
18
- <h1><%=
19
- case "#{controller_name}##{action_name}"
20
- when "questions#show" then @question.title
21
- when "questions#index" then
22
- if params[:tag_search].present?
23
- "Recent Questions for tag #{params[:tag_search]}"
24
- else
25
- "Recent Questions"
26
- end
27
- when "questions#new" then "Post a new question"
28
- when "questions#edit" then "Editing question"
29
- when "answers#edit" then "Editing answer"
30
- else "Recent Questions"
31
- end
32
- %></h1>
18
+ <h1><%=page_title_helper %></h1>
33
19
 
34
20
  <%= yield %>
35
21
  </div>
@@ -22,7 +22,6 @@
22
22
  <div class="views_count">
23
23
  <span class="count"><%= question.unique_page_views %></span>
24
24
  <span class="type"><%= question.unique_page_views.abs == 1 ? 'view' : 'views' %></span>
25
-
26
25
  </div>
27
26
  </div>
28
27
 
@@ -30,7 +29,7 @@
30
29
 
31
30
  <h3 class="title"><%= link_to question.title, question %></h3>
32
31
  <cite>
33
- Asked by <%= question.user.rostra_user_name %>
32
+ Asked by <%= link_to_profile(question.user) %>
34
33
  <%= link_to 'edit', edit_question_path(question) if can? :manage, question %>
35
34
  </cite>
36
35
  <div class="text">
@@ -6,7 +6,7 @@
6
6
  <%= render partial: 'rostra/shared/votes', locals: { resource: @question } %>
7
7
  <%= image_tag avatar_url(@question.user), class: 'avatar' %>
8
8
  <cite>
9
- Asked by <%= @question.user.rostra_user_name %>
9
+ Asked by <%= link_to_profile(@question.user) %>
10
10
  <%= link_to 'edit', edit_question_path(@question) if can? :manage, @question %>
11
11
  </cite>
12
12
  <div class="text"><%= simple_format(@question.details) %></div>
@@ -22,7 +22,7 @@
22
22
  <%= render partial: 'rostra/shared/votes', locals: { resource: answer } %>
23
23
  <%= image_tag avatar_url(answer.user), class: 'avatar' %>
24
24
  <cite>
25
- <%= answer.user.rostra_user_name %> says:
25
+ <%= link_to_profile(answer.user) %> says:
26
26
  <%= link_to('edit', edit_question_answer_path(@question, answer)) if can? :manage, answer %>
27
27
  </cite>
28
28
  <div class="text"><%= simple_format(answer.text) %></div>
@@ -34,10 +34,10 @@
34
34
  <div class="comment text">
35
35
  <div class="editable" data-ajax_url="<%= question_comment_path(@question, comment) %>"><%= comment.comment %></div>
36
36
  <div class="timestamp">
37
- <%= comment.user.rostra_user_name %> – <%= time_ago_in_words(comment.updated_at) %> ago
37
+ <%= link_to_profile(comment.user) %> – <%= time_ago_in_words(comment.updated_at) %> ago
38
38
  <% if can? :manage, comment %>
39
39
  <%= link_to('edit', question_comment_path(@question, comment), class: 'edit_comment') %>
40
- <%= link_to('delete', question_comment_path(@question, comment), method: :delete, confirm: 'Are you sure?') if can? :manage, comment %>
40
+ <%= link_to('delete', question_comment_path(@question, comment), method: :delete, confirm: 'Are you sure?') %>
41
41
  <% end %>
42
42
  </div>
43
43
  </div>
@@ -47,9 +47,9 @@
47
47
 
48
48
 
49
49
  <% if can? :manage, Comment %>
50
- <%= link_to("leave comment", '#', class: 'leave_comment') %>
50
+ <%= link_to("comment on this answer", '#', class: 'leave_comment') %>
51
51
  <% else %>
52
- <%= link_to("login leave comment", main_app_login_path) %>
52
+ <%= link_to("login to leave comment", main_app_login_path) %>
53
53
  <% end %>
54
54
 
55
55
 
@@ -5,10 +5,16 @@ module Rostra
5
5
  source_root File.expand_path('../templates', __FILE__)
6
6
  class_option :template_engine
7
7
 
8
+ desc "Generate configuration file where you can set/override Rostra options"
8
9
  def copy_config
9
10
  directory 'config'
10
11
  end
11
12
 
13
+ desc "Generate application helper file where you can set/override Rostra helper methods"
14
+ def copy_application_helper
15
+ directory 'app'
16
+ end
17
+
12
18
  end
13
19
  end
14
20
  end
@@ -0,0 +1,58 @@
1
+ module Rostra
2
+ module ApplicationHelper
3
+ # If you don't need to make any changes, you can delete this file.
4
+ #
5
+ # Include base helper methods required for Rostra views. You probably don't want to remove
6
+ # this line but you can add/override rostra methods in this file. The helpers which you may
7
+ # need to override are listed below. Just un-comment them and make changes.
8
+ #
9
+ include Rostra::BaseHelper
10
+
11
+ # If you want to user helper methods defined elsewhere in your app, you can include them here
12
+ # as well. For example:
13
+ #
14
+ # include MainAppHelper
15
+
16
+ # Creates a list of tags linking to the index showing only questions with that tag
17
+ #
18
+ # def tag_list(question)
19
+ # tags = question.tags.map { |tag| link_to tag, questions_path(:tag_search => "#{tag}")}.join
20
+ # content_tag :div, "Tags: #{tags}".html_safe, class: 'tags'
21
+ # end
22
+
23
+ # Finds the url to the user's avatar following this logic:
24
+ #
25
+ # 1. Calls <tt>avatar</tt> on the user object
26
+ # 2. Uses the users email address to look for a gravatar
27
+ # 3. Renders <tt>app/assets/images/rostra/anonymous_avatar.png</tt>
28
+ #
29
+ # def avatar_url(user)
30
+ # if user.respond_to?(:avatar)
31
+ # user.avatar
32
+ # else
33
+ # default_url = "#{main_app.root_url}assets/rostra/anonymous_avatar.png"
34
+ # gravatar_id = Digest::MD5.hexdigest(user.rostra_user_email.downcase)
35
+ # "http://gravatar.com/avatar/#{gravatar_id}.png?s=48&d=#{CGI.escape(default_url)}"
36
+ # end
37
+ # end
38
+
39
+ # Used to populate both the <tt>title</tt> and <tt>h1</tt> elements for each page.
40
+ #
41
+ # def page_title_helper
42
+ # case "#{controller_name}##{action_name}"
43
+ # when "questions#show" then @question.title
44
+ # when "questions#index" then
45
+ # if params[:tag_search].present?
46
+ # "Recent Questions for tag #{params[:tag_search]}"
47
+ # else
48
+ # "Recent Questions"
49
+ # end
50
+ # when "questions#new" then "Post a new question"
51
+ # when "questions#edit" then "Editing question"
52
+ # when "answers#edit" then "Editing answer"
53
+ # else "Recent Questions"
54
+ # end
55
+ # end
56
+
57
+ end
58
+ end
@@ -1,13 +1,5 @@
1
1
  # Use this setup block to configure all options available in Rostra.
2
2
  Rostra::Config.setup do |config|
3
-
4
- # If you want to access helpers defined in your main app, add them to this array.
5
- # For example, if you'd like to use helpers defined in your <tt>ApplicationHelper</tt>:
6
- #
7
- # config.included_helpers = [ ApplicationHelper ]
8
- #
9
- # config.included_helpers = []
10
-
11
3
  # Helper method used to access current user in the view
12
4
  # config.deliver_emails_from = 'change_me@example.com'
13
5
 
@@ -20,15 +12,6 @@ Rostra::Config.setup do |config|
20
12
  # Method called on the rostra'd model (e.g. User) to access the users email address
21
13
  # config.rostra_user_email = :email
22
14
 
23
- # Method called on the rostra'd model (e.g. User) to access the users avatar. It
24
- # looks for an avatar in this order:
25
- #
26
- # 1. Calls the method defined here
27
- # 2. Uses the users email address to looks for a gravatar
28
- # 3. Renders <tt>app/assets/images/rostra/anonymous_avatar.png</tt>
29
- #
30
- # config.rostra_user_avatar = :avatar
31
-
32
15
  # Route to your login page
33
16
  # config.main_app_login_path = :new_user_session_path
34
17
 
data/lib/rostra/config.rb CHANGED
@@ -1,14 +1,6 @@
1
1
  module Rostra
2
2
  module Config
3
3
 
4
- # If you want to access helpers defined in your main app, add them to this array.
5
- # For example, if you'd like to use helpers defined in your <tt>ApplicationHelper</tt>:
6
- #
7
- # config.included_helpers = [ ApplicationHelper ]
8
- #
9
- mattr_accessor :included_helpers
10
- @@included_helpers = []
11
-
12
4
  # Helper method used to access current user in the view
13
5
  #
14
6
  mattr_accessor :deliver_emails_from
@@ -29,16 +21,6 @@ module Rostra
29
21
  mattr_accessor :rostra_user_email
30
22
  @@rostra_user_email = :email
31
23
 
32
- # Method called on the rostra'd model (e.g. User) to access the users avatar. It looks
33
- # for an avatar in this order:
34
- #
35
- # 1. Calls the method defined here
36
- # 2. Uses the users email address to looks for a gravatar
37
- # 3. Renders <tt>app/assets/images/rostra/anonymous_avatar.png</tt>
38
- #
39
- mattr_accessor :rostra_user_avatar
40
- @@rostra_user_avatar = :avatar
41
-
42
24
  # Route to your login page
43
25
  #
44
26
  mattr_accessor :main_app_login_path
@@ -1,3 +1,3 @@
1
1
  module Rostra
2
- VERSION = "0.0.10"
2
+ VERSION = "0.0.11"
3
3
  end
data/lib/rostra.rb CHANGED
@@ -63,6 +63,22 @@ module Rostra
63
63
  end
64
64
  end
65
65
 
66
+ # By default anyone can read rostra content - even if they are not logged in. This method
67
+ # determines if users can contribute content (i.e. ask questions, give answers, and leave
68
+ # comments). If <tt>can_participate_in_rostra?</tt> returns true, the user can contribute
69
+ # content, otherwise they're restricted to read only.
70
+ #
71
+ # Under the hood, Rostra uses <tt>CanCan</tt> to set permissions and this is the only
72
+ # place where <tt>can_participate_in_rostra?</tt> is actully used. The default is very
73
+ # simple, if the user exists (i.e. they are logged in), then they may participate. But
74
+ # you can override this method in your <tt>User</tt> model to set more complicated
75
+ # conditions participation. If you need to specify even more complicated permissions,
76
+ # override <tt>app/models/rostra/ability.rb</tt> and go wild.
77
+ #
78
+ def can_participate_in_rostra?
79
+ !new_record?
80
+ end
81
+
66
82
  # Rostra will use this method to find a user's full name.
67
83
  #
68
84
  def rostra_user_name
metadata CHANGED
@@ -2,7 +2,7 @@
2
2
  name: rostra
3
3
  version: !ruby/object:Gem::Version
4
4
  prerelease:
5
- version: 0.0.10
5
+ version: 0.0.11
6
6
  platform: ruby
7
7
  authors:
8
8
  - Cory Schires
@@ -10,7 +10,7 @@ autorequire:
10
10
  bindir: bin
11
11
  cert_chain: []
12
12
 
13
- date: 2011-10-20 00:00:00 -05:00
13
+ date: 2011-10-22 00:00:00 -05:00
14
14
  default_executable:
15
15
  dependencies:
16
16
  - !ruby/object:Gem::Dependency
@@ -490,13 +490,13 @@ files:
490
490
  - app/controllers/rostra/comments_controller.rb
491
491
  - app/controllers/rostra/questions_controller.rb
492
492
  - app/helpers/rostra/application_helper.rb
493
+ - app/helpers/rostra/base_helper.rb
493
494
  - app/mailers/rostra/application_mailer.rb
494
495
  - app/models/comment.rb
495
496
  - app/models/rostra/ability.rb
496
497
  - app/models/rostra/answer.rb
497
498
  - app/models/rostra/question.rb
498
499
  - app/models/rostra/question_following.rb
499
- - app/models/rostra/vote.rb
500
500
  - app/models/vote.rb
501
501
  - app/views/layouts/rostra/application.html.erb
502
502
  - app/views/rostra/answers/_form.html.erb
@@ -526,6 +526,7 @@ files:
526
526
  - db/migrate/20111016021105_create_impressions_table.rb
527
527
  - db/migrate/20111019162723_create_rostra_question_followings.rb
528
528
  - lib/generators/rostra/install_generator.rb
529
+ - lib/generators/rostra/templates/app/helpers/rostra/application_helper.rb
529
530
  - lib/generators/rostra/templates/config/initializers/rostra.rb
530
531
  - lib/rostra/config.rb
531
532
  - lib/rostra/email_notifier.rb
@@ -1,19 +0,0 @@
1
- module Rostra
2
- class Vote < ActiveRecord::Base
3
-
4
- scope :for_voter, lambda { |*args| where(["voter_id = ? AND voter_type = ?", args.first.id, args.first.class.name]) }
5
- scope :for_voteable, lambda { |*args| where(["voteable_id = ? AND voteable_type = ?", args.first.id, args.first.class.name]) }
6
- scope :recent, lambda { |*args| where(["created_at > ?", (args.first || 2.weeks.ago)]) }
7
- scope :descending, order("created_at DESC")
8
-
9
- belongs_to :voteable, :polymorphic => true
10
- belongs_to :voter, :polymorphic => true
11
-
12
- attr_accessible :vote, :voter, :voteable
13
-
14
-
15
- # Comment out the line below to allow multiple votes per user.
16
- validates_uniqueness_of :voteable_id, :scope => [:voteable_type, :voter_type, :voter_id]
17
-
18
- end
19
- end