opinio-bootstrap 0.6.0.2

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 (34) hide show
  1. data/Gemfile +31 -0
  2. data/MIT-LICENSE +20 -0
  3. data/README.md +197 -0
  4. data/Rakefile +28 -0
  5. data/app/controllers/opinio/comments_controller.rb +47 -0
  6. data/app/views/opinio/comments/_comment.html.erb +20 -0
  7. data/app/views/opinio/comments/_comments.html.erb +12 -0
  8. data/app/views/opinio/comments/_new.html.erb +19 -0
  9. data/app/views/opinio/comments/create.js.erb +16 -0
  10. data/app/views/opinio/comments/destroy.js.erb +3 -0
  11. data/app/views/opinio/comments/index.html.erb +7 -0
  12. data/app/views/opinio/comments/reply.js.erb +6 -0
  13. data/config/locales/opinio.en.yml +16 -0
  14. data/lib/generators/opinio/install/install_generator.rb +38 -0
  15. data/lib/generators/opinio/install/templates/initializers/opinio.erb +28 -0
  16. data/lib/generators/opinio/install/templates/migrations/create_model.rb +13 -0
  17. data/lib/generators/opinio/install/templates/models/model.rb +3 -0
  18. data/lib/generators/opinio/views/views_generator.rb +27 -0
  19. data/lib/opinio.rb +66 -0
  20. data/lib/opinio/controllers/extensions.rb +39 -0
  21. data/lib/opinio/controllers/helpers.rb +22 -0
  22. data/lib/opinio/controllers/internal_helpers.rb +37 -0
  23. data/lib/opinio/controllers/replies.rb +23 -0
  24. data/lib/opinio/opinio_model.rb +77 -0
  25. data/lib/opinio/opinio_model/validations.rb +50 -0
  26. data/lib/opinio/opinio_subjectum.rb +24 -0
  27. data/lib/opinio/orm/active_record.rb +19 -0
  28. data/lib/opinio/rails.rb +8 -0
  29. data/lib/opinio/rails/routes.rb +20 -0
  30. data/lib/opinio/railtie.rb +22 -0
  31. data/lib/opinio/schema.rb +21 -0
  32. data/lib/opinio/shared_examples.rb +89 -0
  33. data/lib/opinio/version.rb +5 -0
  34. metadata +145 -0
data/Gemfile ADDED
@@ -0,0 +1,31 @@
1
+ source "http://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ gem "kaminari"
6
+ gem "rails", "~> 3.0.0"
7
+ gem "sqlite3"
8
+ gem "jquery-rails"
9
+
10
+ group :development do
11
+ platforms :mri_19 do
12
+ gem "debugger"
13
+ end
14
+ gem 'guard-rspec'
15
+ if RUBY_PLATFORM =~ /darwin/i
16
+ gem 'rb-fsevent'
17
+ gem 'growl'
18
+ end
19
+ end
20
+
21
+ group :test do
22
+ gem "cucumber"
23
+ gem "cucumber-rails"
24
+ gem "capybara"
25
+ gem "launchy"
26
+ gem "database_cleaner"
27
+ gem "rspec-rails", :git => "https://github.com/rspec/rspec-rails.git", :tag => "v2.12.2"
28
+ end
29
+ # To use debugger (ruby-debug for Ruby 1.8.7+, ruby-debug19 for Ruby 1.9.2+)
30
+ # gem 'ruby-debug'
31
+ # gem 'ruby-debug19'
@@ -0,0 +1,20 @@
1
+ Copyright 2011 YOURNAME
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining
4
+ a copy of this software and associated documentation files (the
5
+ "Software"), to deal in the Software without restriction, including
6
+ without limitation the rights to use, copy, modify, merge, publish,
7
+ distribute, sublicense, and/or sell copies of the Software, and to
8
+ permit persons to whom the Software is furnished to do so, subject to
9
+ the following conditions:
10
+
11
+ The above copyright notice and this permission notice shall be
12
+ included in all copies or substantial portions of the Software.
13
+
14
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND,
15
+ EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF
16
+ MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND
17
+ NONINFRINGEMENT. IN NO EVENT SHALL THE AUTHORS OR COPYRIGHT HOLDERS BE
18
+ LIABLE FOR ANY CLAIM, DAMAGES OR OTHER LIABILITY, WHETHER IN AN ACTION
19
+ OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, OUT OF OR IN CONNECTION
20
+ WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE SOFTWARE.
@@ -0,0 +1,197 @@
1
+ # Opinio-Bootstrap #
2
+
3
+ ## Description ##
4
+
5
+ This is a fork of Luiz Pereira's great [Opinio](https://github.com/Draiken/opinio) gem for Rails 3+.
6
+
7
+ It intends to simply utilize [Twitter Bootstrap](http://twitter.github.com/bootstrap/) styling in views out-of-the-box.
8
+
9
+ In particular, it utilizes the media-object component.
10
+
11
+ Currently it requires an image_url(:thumb) method (the default for [Carrierwave](https://github.com/jnicklas/carrierwave)) for your User object; If you are set up with Bootstrap and are storing an image on the User, styling will automatically include the User's avatar along with a link to the user, along with the comment and associated links.
12
+
13
+ So you need a valid call to
14
+
15
+ @user.image_url(:thumb).to_s
16
+
17
+ for the avatar image (media object).
18
+
19
+ To do: make dynamic the method by which User's image is called; provide a default stock placeholder image for those not utilizing an image mounting system for their User object (e.g. Carrierwave/Paperclip).
20
+
21
+ Make sure Bootstrap is in your Rails *Gemfile*:
22
+
23
+ gem "bootstrap-sass", ">= 2.2.2.0"
24
+
25
+ The remainder of this page is [Opinio](https://github.com/Draiken/opinio)'s original README.
26
+
27
+ ## Original ##
28
+
29
+ **IMPORTANT** Version 0.6 might break some behaviour from 0.5 and lower versions, please refer to the [changelog](https://github.com/Draiken/opinio/blob/master/CHANGELOG.rdoc)
30
+
31
+ ## Description ##
32
+
33
+ Opinio is an engine used to add comments behaviour to your application.
34
+ The engine is designed to work only with **Rails 3**
35
+
36
+ ## Intallation ##
37
+
38
+ Simply add the following line to your *Gemfile*:
39
+
40
+ gem "opinio"
41
+
42
+ and run:
43
+
44
+ bundle
45
+
46
+ ## Usage ##
47
+
48
+ Opinio provides generators to facilitate it's usage.
49
+ The most common way to quickly get Opinio working is:
50
+
51
+ rails g opinio:install comment
52
+
53
+ This will generate the `Comment` model, migration and also generate the opinio initializer for customization of the engine.
54
+ A `opinio_model` will be added on the `routes.rb`. This method adds the default urls for the model that will act as the comment
55
+ in your app.
56
+
57
+ In order to add the comments functionality to a model, you use the `opinio_subjectum` method
58
+
59
+ class Post < ActiveRecord::Base
60
+ opinio_subjectum
61
+ end
62
+
63
+ On the `routes.rb` you should simply add an `opinio` for your commentable resource
64
+
65
+ resources :posts do
66
+ opinio
67
+ end
68
+
69
+ To render the comments in your views, there is a helper to display the resource's comments easily:
70
+
71
+ <%= comments_for @post %>
72
+
73
+ This will render the comments and a form for new comment submissions. Alternatively you can render just the comments or just the form like this:
74
+
75
+ <%= render_comments @post %>
76
+ <%= render_comments_form @post %>
77
+
78
+ If you need to render the comments with a specific page or limit
79
+ you can use Kaminari's configurations like `paginates_per 10` on your comment model
80
+ or you can customize it for specific views on the helpers
81
+
82
+ <%= render_comments @post, :page => params[:page], :limit => 5 %>
83
+
84
+ This options can also be used on the `comments_for` helper.
85
+
86
+ ## Customization ##
87
+
88
+ ### Views ###
89
+
90
+ Of course you will want to customize how the comments are displayed or any other customization to the view. To generate the view files on your application, run:
91
+
92
+ rails g opinio:views
93
+
94
+ And you can customize all the views used by the engine.
95
+
96
+ ### Behaviour ###
97
+
98
+ #### Opinio Model ####
99
+
100
+ You can customize the opinio model to suit your needs. The basic customization is the owner class name. It defaults to `User` but you can change that in the initializer or in the model by passing the `:owner_class_name => "MyOwnerClass"` option to the `opinio_model` method call.
101
+
102
+ Another customization you can do is set the `counter_cache` to true in the commentable model. You can use the `:counter_cache` option for that.
103
+
104
+ The other two customizations are only made through the initializer, and they are the `accept_replies` which defaults to true and `strip_html_tags_on_save` which also defaults to `true`.
105
+
106
+ Validations on the opinio model are very basic, just ensuring it has a body, a commentable and an owner, if you want any other kind of validation, like the minimum size of a comment, you can use the regular AR validations as you wish.
107
+
108
+ Remember that if you use titles, you need to add that to your comments table, since the generator doesn't add it by default.
109
+
110
+ #### Opinio Subjectum ####
111
+
112
+ To change how the models that actually have the comments, you can customize them with any option you would use to a regular `has_many` relationship in ActiveRecord.
113
+
114
+ The default options are these:
115
+
116
+ has_many :comments,
117
+ :class_name => Opinio.model_name,
118
+ :as => :commentable,
119
+ :order => "created_at #{Opinio.sort_order}"),
120
+ :dependent => :destroy
121
+
122
+ The `sort_order` and `model_name` are both setup in the initializer. Here you can do things like, let's say you have moderation in your comments, you can only show the approved comments:
123
+
124
+ opinio_subjectum :conditions => ["approved = ?", true]
125
+
126
+ Remember you can override any of these options (except `as`) by passing them to the `opinio_subjectum` method.
127
+
128
+ #### Pretty Urls ####
129
+
130
+ Often times you will want the engine to show the index of comments for a specific item
131
+ without having to pass the `:commentable_type` or `:commentable_id` parameters.
132
+
133
+ In order to do that, opinio provides a method to `ActionController::Base`:
134
+
135
+ opinio_identifier do |params|
136
+ next Review.find(params[:review_id]) if params[:review_id]
137
+ next Product.find(params[:product_id]) if params[:product_id]
138
+ end
139
+
140
+ Note: you use next instead of return because it is a proc that will be executed later on, and you cannot return on procs
141
+
142
+ Basically on this method you receive the `params` variable and you tell the engine, who owns
143
+ the comments from that page.
144
+ This allows you to use routes like:
145
+
146
+ /products/1/comments
147
+ /products/1/reviews/1/comments
148
+
149
+ Without passing those 2 parameters.
150
+ I suggest you put this method on the `ApplicationController`
151
+
152
+ #### Customize destroy conditions ####
153
+
154
+ By default, noone can destroy a comment in the engine. You have to tell the engine who can do it.
155
+ To setup a custom destroy condition use the methods provided by opinio
156
+ in our controllers. For instance, if our opinio model is called 'comment'
157
+ it could be written like this:
158
+
159
+ comment_destroy_conditions do |comment|
160
+ comment.owner == current_user
161
+ end
162
+
163
+ This would make users only be able to remove their own comments.
164
+ Another example would be using the `CanCan`:
165
+
166
+ comment_destroy_conditions do |comment|
167
+ authorize :destroy, comment
168
+ end
169
+
170
+ You get the picture, you're inside your controller's methods on that block
171
+ so you can call anything your normal controllers call on actions.
172
+
173
+ ### Testing ###
174
+
175
+ Opinio provides a few shared examples for testing of your model with rspec
176
+ On your opinio model test case you can require opinio's shared examples and use them
177
+
178
+ require 'opinio/shared_examples'
179
+
180
+ describe Comment do
181
+ it_should_behave_like :opinio
182
+ end
183
+
184
+ describe Post do
185
+ it_should_behave_like :opinio_subjectum
186
+ end
187
+
188
+ ## Contribution ##
189
+
190
+ If you want to help in any way with **Opinio** please message me or fork the project, make the changes and send me a pull request.
191
+ For issues please use the github [issues tracker](https://github.com/Draiken/opinio/issues)
192
+
193
+ ### TODO ###
194
+
195
+ * Refactor the `comments_for` helper
196
+ * Extract documentation to wiki
197
+ * Add mongoid support
@@ -0,0 +1,28 @@
1
+ # encoding: UTF-8
2
+ require 'rubygems'
3
+ begin
4
+ require 'bundler/setup'
5
+ rescue LoadError
6
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
7
+ end
8
+
9
+ require 'rake'
10
+ require 'rdoc/task'
11
+
12
+ require 'cucumber'
13
+ require 'cucumber/rake/task'
14
+
15
+ require 'rspec/core'
16
+ require 'rspec/core/rake_task'
17
+
18
+ RSpec::Core::RakeTask.new(:spec)
19
+
20
+ task :default => :spec
21
+
22
+ Rake::RDocTask.new(:rdoc) do |rdoc|
23
+ rdoc.rdoc_dir = 'rdoc'
24
+ rdoc.title = 'Opinio'
25
+ rdoc.options << '--line-numbers' << '--inline-source'
26
+ rdoc.rdoc_files.include('README.rdoc')
27
+ rdoc.rdoc_files.include('lib/**/*.rb')
28
+ end
@@ -0,0 +1,47 @@
1
+ class Opinio::CommentsController < ApplicationController
2
+ include Opinio::Controllers::InternalHelpers
3
+ include Opinio::Controllers::Replies if Opinio.accept_replies
4
+
5
+ def index
6
+ @comments = resource.comments.page(params[:page])
7
+ end
8
+
9
+ def create
10
+ @comment = resource.comments.build(params[:comment])
11
+ @comment.owner = send(Opinio.current_user_method)
12
+ if @comment.save
13
+ flash_area = :notice
14
+ message = t('opinio.messages.comment_sent')
15
+ else
16
+ flash_area = :error
17
+ message = t('opinio.messages.comment_sending_error')
18
+ end
19
+
20
+ respond_to do |format|
21
+ format.js
22
+ format.html do
23
+ set_flash(flash_area, message)
24
+ redirect_to(opinio_after_create_path(resource))
25
+ end
26
+ end
27
+ end
28
+
29
+ def destroy
30
+ @comment = Opinio.model_name.constantize.find(params[:id])
31
+
32
+ if can_destroy_opinio?(@comment)
33
+ @comment.destroy
34
+ set_flash(:notice, t('opinio.messages.comment_destroyed'))
35
+ else
36
+ #flash[:error] = I18n.translate('opinio.comment.not_permitted', :default => "Not permitted")
37
+ logger.warn "user #{send(Opinio.current_user_method)} tried to remove a comment from another user #{@comment.owner.id}"
38
+ render :text => "unauthorized", :status => 401 and return
39
+ end
40
+
41
+ respond_to do |format|
42
+ format.js
43
+ format.html { redirect_to( opinio_after_destroy_path(@comment) ) }
44
+ end
45
+ end
46
+
47
+ end
@@ -0,0 +1,20 @@
1
+ <% reply = defined?(reply) ? reply : false %>
2
+ <div id="comment_<%= comment.id %>" class="media">
3
+ <a class="pull-left" href="<%= user_path(comment.owner) %>">
4
+ <img class="media-object" src="<%= comment.owner.image_url(:thumb).to_s %>" />
5
+ <span><%= comment.owner.name.titlecase %></span>
6
+ </a>
7
+ <div class="media-body">
8
+ <%= simple_format(comment.body) %>
9
+ <% if comment.owner == send(Opinio.current_user_method) %>
10
+ <%= link_to t('opinio.actions.delete'), comment_path(comment), :method => :delete, :remote => true %>
11
+ <% end %>
12
+ <%# this enables only 1 level of replies %>
13
+ <% if Opinio.accept_replies && !reply && !(comment.owner == current_user) %>
14
+ <span><%= link_to t('opinio.actions.reply'), reply_comment_path(comment), :remote => true %></span>
15
+ <% end %>
16
+ <div id="comment_<%= comment.id %>_replies" class="replies media">
17
+ <%= render :partial => "opinio/comments/comment", :collection => comment.comments, :locals => {:reply => true} %>
18
+ </div>
19
+ </div>
20
+ </div>
@@ -0,0 +1,12 @@
1
+ <div>
2
+ <%= paginate comments %>
3
+ </div>
4
+ <% unless comments.empty? %>
5
+ <%= render :partial => 'opinio/comments/comment', :collection => comments %>
6
+ <% else %>
7
+ <h3><%= t('opinio.messages.no_comments_found') %></h3>
8
+ <% end %>
9
+
10
+ <div>
11
+ <%= paginate comments %>
12
+ </div>
@@ -0,0 +1,19 @@
1
+ <div id="new_comment">
2
+ <% if send(Opinio.current_user_method) %>
3
+ <h3 class="addcomment"><%= t('opinio.messages.add_comment') %></h3>
4
+ <%= form_for Comment.new, :remote => false do |f| %>
5
+ <p>
6
+ <%= f.text_area :body, :placeholder => "Enter comment...", rows:"10" %>
7
+ </p>
8
+ <%= hidden_field_tag :commentable_id, commentable.id %>
9
+ <%= hidden_field_tag :commentable_type, commentable.class.base_class.name.to_s %>
10
+ <%= hidden_field_tag :original_controller, params[:controller] %>
11
+ <%= hidden_field_tag :original_id, params[:id] %>
12
+ <%= f.submit t('opinio.actions.add') %>
13
+ <% end %>
14
+ <% else %>
15
+ <p>
16
+ <%= t('opinio.messages.must_be_logged_in_to_comment') %>
17
+ </p>
18
+ <% end %>
19
+ </div>
@@ -0,0 +1,16 @@
1
+ $('#no_comments').hide();
2
+ <% if @comment.valid? %>
3
+ <% if @reply %>
4
+ if($('#comment_<%= @comment.commentable_id %> ul').length == 0)
5
+ $('#comment_<%= @comment.commentable_id %>').append('<ul id="comment_<%= @comment.commentable_id %>_replies" class="replies"></ul>');
6
+ $('#comment_<%= @comment.commentable_id %>_replies').append("<%= escape_javascript( render @comment, :locals => {:reply => @reply} ) %>");
7
+ $('#commentable_id').val('<%= @comment.commentable.commentable_id %>');
8
+ $('#commentable_type').val('<%= @comment.commentable.commentable_type %>');
9
+ <% else %>
10
+ $('#comments').<%= Opinio.sort_order == 'ASC' ? 'append' : 'prepend'%>("<%= escape_javascript( render @comment, :locals => {:reply => @reply} ) %>");
11
+ <% end %>
12
+ $('textarea#comment_body').val('');
13
+ <% else %>
14
+ $('#comments').prepend("<%= escape_javascript(flash[:notice]) %>");
15
+ $('#comments').prepend("<%= escape_javascript(flash[:error]) %>");
16
+ <% end %>
@@ -0,0 +1,3 @@
1
+ jQuery('#comment_<%= @comment.id %>').remove();
2
+ jQuery('#comments').prepend("<%= escape_javascript(flash[:notice]) %>");
3
+ jQuery('#comments').prepend("<%= escape_javascript(flash[:error]) %>");
@@ -0,0 +1,7 @@
1
+ <h1><%= t('opinio.messages.comments') %></h1>
2
+
3
+ <%= paginate @comments %>
4
+
5
+ <%= render :partial => "opinio/comments/comments", :locals => { :comments => @comments } %>
6
+
7
+ <%= paginate @comments %>
@@ -0,0 +1,6 @@
1
+ <% @comment = Comment.find(@commentable_id) %>
2
+ $("#commentable_id").val('<%= @commentable_id %>');
3
+ $("#commentable_type").val('<%= @commentable_type %>');
4
+ $("#new_comment textarea").val('');
5
+ $("#new_comment h3.addcomment").text('Replying to <%= @comment.owner.name.titlecase %>');
6
+ $("#new_comment textarea").focus();
@@ -0,0 +1,16 @@
1
+ en:
2
+ opinio:
3
+ actions:
4
+ delete: 'Delete'
5
+ reply: 'Reply'
6
+ add: 'Add comment'
7
+ messages:
8
+ no_comments_found: 'No comments found'
9
+ must_be_logged_in_to_comment: 'Must be logged in to comment.'
10
+ add_comment: 'Add comment'
11
+ comments: 'Comments'
12
+ comment_sent: 'Comment sent successfully.'
13
+ comment_sending_error: 'Error while sending the comment.'
14
+ comment_destroyed: 'Comment removed successfully.'
15
+ comment_interval: 'You must wait %{time} seconds to comment again.'
16
+ cannot_be_comment_of_comment: "Cannot reply another comment's reply"
@@ -0,0 +1,38 @@
1
+ module Opinio
2
+ module Generators
3
+ class InstallGenerator < Rails::Generators::NamedBase
4
+ include Rails::Generators::Migration
5
+
6
+ source_root File.expand_path('../templates', __FILE__)
7
+
8
+
9
+ def generate_model
10
+ template "models/model.rb", "app/models/#{file_name}.rb"
11
+ end
12
+
13
+ def generate_migration
14
+ migration_template "migrations/create_model.rb", "db/migrate/create_#{table_name}", {:assigns => {:name => name, :table_name => table_name}}
15
+ end
16
+
17
+ def generate_initializer
18
+ template "initializers/opinio.erb", "config/initializers/opinio.rb"
19
+ end
20
+
21
+ def generate_route
22
+ route "opinio_model"
23
+ end
24
+
25
+ def add_dependency_gem
26
+ gem "kaminari"
27
+ end
28
+
29
+ def self.next_migration_number(dirname)
30
+ if ActiveRecord::Base.timestamped_migrations
31
+ Time.now.utc.strftime("%Y%m%d%H%M%S")
32
+ else
33
+ "%.3d" % (current_migration_number(dirname) + 1)
34
+ end
35
+ end
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,28 @@
1
+ # Opinio Configuration
2
+
3
+ Opinio.setup do |config|
4
+
5
+ # Use this to change the class that calls +opinio+ method
6
+ config.model_name = "<%= class_name %>"
7
+
8
+ # This is the owner class of the comment (opinio model)
9
+ # config.owner_class_name = "User"
10
+
11
+ # Change this if you do not want to allow replies on your comments
12
+ # config.accept_replies = true
13
+
14
+ # Here you can change the method called to check who is the current user
15
+ # config.current_user_method = :current_user
16
+
17
+ # Strip html tags on save comment
18
+ config.strip_html_tags_on_save = true
19
+
20
+ # Comments sort order by created_at (DESC or ASC)
21
+ # Note: you can override that easily within each opinio subjectum
22
+ config.sort_order = 'DESC'
23
+
24
+ # Wether or not the default opinio controller should set the flash
25
+ # when creating/removing comments
26
+ config.set_flash = true
27
+
28
+ end
@@ -0,0 +1,13 @@
1
+ class Create<%= table_name.camelize %> < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :<%= table_name %> do |t|
4
+ t.opinio
5
+
6
+ t.timestamps
7
+ end
8
+ end
9
+
10
+ def self.down
11
+ drop_table :<%= table_name %>
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ class <%= class_name %> < ActiveRecord::Base
2
+ opinio
3
+ end
@@ -0,0 +1,27 @@
1
+ module Opinio
2
+ module Generators
3
+ class ViewsGenerator < Rails::Generators::Base
4
+
5
+ source_root File.expand_path('../../../../../app/views/opinio/comments', __FILE__)
6
+
7
+ class_option :haml, :desc => 'Generate HAML views instead of ERB.', :type => :boolean
8
+
9
+ def copy_views
10
+ html_names = ["_comment", "_comments", "_new", "index"]
11
+ js_names = ["create", "destroy", "reply"]
12
+ html_names.each do |name|
13
+ copy_file "#{name}.html.#{view_language}", "app/views/opinio/comments/#{name}.html.#{view_language}"
14
+ end
15
+ js_names.each do |name|
16
+ copy_file "#{name}.js.erb", "app/views/opinio/comments/#{name}.js.erb"
17
+ end
18
+ end
19
+
20
+ def view_language
21
+ options.haml? ? 'haml' : 'erb'
22
+ end
23
+
24
+
25
+ end
26
+ end
27
+ end
@@ -0,0 +1,66 @@
1
+ module Opinio
2
+ require 'opinio/schema'
3
+
4
+ module Controllers
5
+ require 'opinio/controllers/helpers'
6
+ require 'opinio/controllers/internal_helpers'
7
+ require 'opinio/controllers/replies'
8
+ end
9
+
10
+ require 'opinio/railtie'
11
+ require 'opinio/rails'
12
+ require 'opinio/orm/active_record'
13
+
14
+ mattr_accessor :model_name
15
+ @@model_name = "Comment"
16
+
17
+ mattr_accessor :owner_class_name
18
+ @@owner_class_name = "User"
19
+
20
+ mattr_accessor :use_title
21
+ @@use_title = false
22
+
23
+ mattr_accessor :accept_replies
24
+ @@accept_replies = false
25
+
26
+ mattr_accessor :custom_identifiers
27
+ @@custom_identifiers = Array.new
28
+
29
+ mattr_accessor :interval_between_comments
30
+ @@interval_between_comments = false
31
+
32
+ mattr_accessor :destroy_conditions
33
+ @@destroy_conditions = Proc.new { false }
34
+
35
+ mattr_accessor :current_user_method
36
+ @@current_user_method = :current_user
37
+
38
+ mattr_accessor :strip_html_tags_on_save
39
+ @@strip_html_tags_on_save = true
40
+
41
+ mattr_accessor :sort_order
42
+ @@sort_order = 'DESC'
43
+
44
+ mattr_accessor :set_flash
45
+ @@set_flash = true
46
+
47
+ def self.setup
48
+ yield self
49
+ end
50
+
51
+ def self.opinio_identifier(block)
52
+ @@custom_identifiers << block
53
+ end
54
+
55
+ def self.set_destroy_conditions(&block)
56
+ @@destroy_conditions = block
57
+ end
58
+
59
+ def self.check_custom_identifiers(params)
60
+ self.custom_identifiers.each do |identifier|
61
+ identified = identifier.call(params)
62
+ return identified unless identified.nil?
63
+ end
64
+ nil
65
+ end
66
+ end
@@ -0,0 +1,39 @@
1
+ module Opinio
2
+ module Controllers
3
+ module Extensions
4
+
5
+ def self.included(base)
6
+ base.extend ClassMethods
7
+ base.send :include, InstanceMethods
8
+
9
+ base.class_eval do
10
+ (class << self; self; end).instance_eval do
11
+ define_method "#{Opinio.model_name.underscore}_destroy_conditions" do |&block|
12
+ Opinio.set_destroy_conditions( &block )
13
+ end
14
+ end
15
+ end
16
+ end
17
+
18
+ module ClassMethods
19
+ def opinio_identifier(&block)
20
+ Opinio.opinio_identifier(block)
21
+ end
22
+ end
23
+
24
+ module InstanceMethods
25
+ def can_destroy_opinio?(opinio)
26
+ self.instance_exec(opinio, &Opinio.destroy_conditions)
27
+ end
28
+
29
+ def opinio_after_create_path(resource)
30
+ resource.is_a?(Opinio.model_name.constantize) ? resource.commentable : resource
31
+ end
32
+
33
+ def opinio_after_destroy_path(comment)
34
+ comment.commentable
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
@@ -0,0 +1,22 @@
1
+ module Opinio
2
+ module Controllers
3
+ module Helpers
4
+
5
+ def comments_for(object, options = {})
6
+ render_comments(object, options) +
7
+ ( render_comments_form(object, options) unless options[:no_new] ).to_s
8
+ end
9
+
10
+ def render_comments(object, options = {})
11
+ limit = options.delete(:limit) || Opinio.model_name.constantize.default_per_page
12
+ page = options.delete(:page) || 1
13
+ render( :partial => "opinio/comments/comments", :locals => {:comments => object.comments.page(page).limit(limit), :commentable => object, :options => options} )
14
+ end
15
+
16
+ def render_comments_form(object, options = {})
17
+ render( :partial => "opinio/comments/new", :locals => {:commentable => object, :options => options} )
18
+ end
19
+ end
20
+
21
+ end
22
+ end
@@ -0,0 +1,37 @@
1
+ module Opinio
2
+ module Controllers
3
+ module InternalHelpers
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ helper_method :resource, :resource_name
8
+ end
9
+
10
+ def resource
11
+ @resource ||= custom_resource_identifier(params) || resource_by_params
12
+ end
13
+
14
+ def resource_by_params
15
+ if params[:commentable_type]
16
+ params[:commentable_type].constantize.find(params[:commentable_id])
17
+ elsif params[:comment]
18
+ params[:comment][:commentable_type].constantize.find(params[:comment][:commentable_id])
19
+ else
20
+ raise "Unable to determine comments holder"
21
+ end
22
+ end
23
+
24
+ def custom_resource_identifier(params)
25
+ Opinio.check_custom_identifiers(params)
26
+ end
27
+
28
+ def resource_name
29
+ Opinio.model_name
30
+ end
31
+
32
+ def set_flash(name, message)
33
+ flash[name] = message if Opinio.set_flash
34
+ end
35
+ end
36
+ end
37
+ end
@@ -0,0 +1,23 @@
1
+ module Opinio
2
+ module Controllers
3
+ module Replies
4
+ extend ActiveSupport::Concern
5
+
6
+ included do
7
+ before_filter :check_reply, :only => [:create]
8
+ end
9
+
10
+ def reply
11
+ @commentable_type = Opinio.model_name
12
+ @commentable_id = params[:id]
13
+ @commentable = Opinio.model_name.constantize.find(params[:id])
14
+ end
15
+
16
+ private
17
+
18
+ def check_reply
19
+ @reply = params[:commentable_type] == Opinio.model_name
20
+ end
21
+ end
22
+ end
23
+ end
@@ -0,0 +1,77 @@
1
+ require 'opinio/opinio_model/validations'
2
+
3
+ module Opinio
4
+ module OpinioModel
5
+
6
+ def self.included(base)
7
+ base.extend ClassMethods
8
+ end
9
+
10
+ module ClassMethods
11
+
12
+ # Adds the Opinio functionallity to the model
13
+ # You can pass a hash of options to customize the Opinio model
14
+ def opinio(*args)
15
+ return if self.included_modules.include?(Opinio::OpinioModel::Validations)
16
+ options = args.extract_options!
17
+
18
+ if Opinio.use_title
19
+ attr_accessible :title
20
+ end
21
+ attr_accessible :body
22
+
23
+ belongs_to :commentable, :polymorphic => true, :counter_cache => options.fetch(:counter_cache, false)
24
+ belongs_to :owner, :class_name => options.fetch(:owner_class_name, Opinio.owner_class_name)
25
+
26
+ scope :owned_by, lambda {|owner| where('owner_id = ?', owner.id) }
27
+
28
+ send :include, Opinio::OpinioModel::Validations
29
+
30
+ if Opinio.accept_replies
31
+ send :include, RepliesSupport
32
+ end
33
+
34
+ if Opinio.strip_html_tags_on_save
35
+ send :include, Sanitizing
36
+ end
37
+
38
+ end
39
+ end
40
+
41
+ module Sanitizing
42
+ def self.included(base)
43
+ base.class_eval do
44
+ send :include, ActionView::Helpers::SanitizeHelper
45
+ before_save :strip_html_tags
46
+ end
47
+ end
48
+
49
+ private
50
+
51
+ def strip_html_tags
52
+ self.body = strip_tags(self.body)
53
+ end
54
+ end
55
+
56
+ module RepliesSupport
57
+ def self.included(base)
58
+ base.class_eval do
59
+ validate :cannot_be_comment_of_a_comments_comment
60
+ opinio_subjectum :order => 'created_at ASC'
61
+ end
62
+ end
63
+
64
+ private
65
+
66
+ # Validates that you cannot comment on a comment's comment
67
+ def cannot_be_comment_of_a_comments_comment
68
+ if new_record? && self.commentable_type == Opinio.model_name
69
+ if commentable.commentable_type == Opinio.model_name
70
+ errors.add :base, I18n.t('opinio.messages.cannot_be_comment_of_comment')
71
+ end
72
+ end
73
+ end
74
+
75
+ end
76
+ end
77
+ end
@@ -0,0 +1,50 @@
1
+ module Opinio
2
+ module OpinioModel
3
+ module Validations
4
+
5
+ extend ActiveSupport::Concern
6
+
7
+ included do
8
+
9
+ #TODO: refactor this
10
+ if Opinio.interval_between_comments
11
+ include Opinio::OpinioModel::Validations::IntervalMethods
12
+ validate :validate_last_comment_time, :if => :new_record?
13
+ cattr_accessor :comments_interval
14
+ self.comments_interval = options.reverse_merge(:time => Opinio.interval_between_comments)[:time]
15
+ end
16
+
17
+ validates :body, :presence => true
18
+ validates :commentable, :presence => true
19
+ validates :owner, :presence => true, :associated => true
20
+
21
+ end
22
+
23
+
24
+ module IntervalMethods
25
+
26
+ private
27
+
28
+ # Checks the time of the last comment
29
+ # made by the same owner
30
+ def validate_last_comment_time
31
+ last_comment = Comment.owned_by(self.owner).order('created_at DESC').last
32
+ if last_comment
33
+ if (Time.now - last_comment.created_at).round >= self.comments_interval
34
+ true
35
+ else
36
+ errors.add(:created_at,
37
+ I18n.translate('opinio.messages.comment_interval',
38
+ :time => self.comments_interval))
39
+ false
40
+ end
41
+ else
42
+ true
43
+ end
44
+ end
45
+
46
+ end # IntervalMethods
47
+
48
+ end # Validations
49
+ end # OpinioModel
50
+ end # Opinio
@@ -0,0 +1,24 @@
1
+ module Opinio
2
+ module OpinioSubjectum
3
+
4
+ def self.included(base)
5
+ base.extend(ClassMethods)
6
+ end
7
+
8
+ module ClassMethods
9
+ def opinio_subjectum(*args)
10
+ options = args.extract_options!
11
+ options.delete(:as)
12
+
13
+ default_options = { :class_name => Opinio.model_name,
14
+ :as => :commentable,
15
+ :order => "created_at #{Opinio.sort_order}",
16
+ :dependent => :destroy }
17
+
18
+ has_many :comments, default_options.merge(options)
19
+
20
+ end
21
+ end
22
+
23
+ end
24
+ end
@@ -0,0 +1,19 @@
1
+ require 'active_record/connection_adapters/abstract/schema_definitions'
2
+
3
+ module Opinio
4
+ module Orm
5
+ module ActiveRecord
6
+ module Schema
7
+ include Opinio::Schema
8
+ # Tell how to apply schema methods.
9
+ def apply_opinio_schema(name, type, options={})
10
+ column name, type.to_s.downcase.to_sym, options
11
+ end
12
+ end
13
+ end
14
+ end
15
+ end
16
+
17
+ ActiveRecord::ConnectionAdapters::Table.send :include, Opinio::Orm::ActiveRecord::Schema
18
+ ActiveRecord::ConnectionAdapters::TableDefinition.send :include, Opinio::Orm::ActiveRecord::Schema
19
+
@@ -0,0 +1,8 @@
1
+ require File.join(File.dirname(__FILE__), 'rails', 'routes')
2
+
3
+ module Opinio
4
+ class Engine < ::Rails::Engine
5
+
6
+ end
7
+ end
8
+
@@ -0,0 +1,20 @@
1
+ module ActionDispatch::Routing
2
+ class Mapper
3
+ def opinio(*args)
4
+ options = args.extract_options!.symbolize_keys
5
+ route_name = options[:path_name] || Opinio.model_name.pluralize.downcase
6
+ options[:controller] ||= 'opinio/comments'
7
+
8
+ get "#{ route_name }(/:page)" => "#{options[:controller].to_s}#index", :as => :comments
9
+ end
10
+
11
+ def opinio_model(*args)
12
+ options = args.extract_options!
13
+ options[:controller] ||= 'opinio/comments'
14
+ resources :comments, options do
15
+ get 'reply', :on => :member if Opinio.accept_replies
16
+ end
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,22 @@
1
+ require 'rails'
2
+
3
+ module Opinio
4
+ class Railtie < ::Rails::Railtie #:nodoc:
5
+ initializer 'opinio' do |app|
6
+ ActiveSupport.on_load(:active_record) do
7
+ require File.join(File.dirname(__FILE__), 'opinio_model')
8
+ require File.join(File.dirname(__FILE__), 'opinio_subjectum')
9
+ ::ActiveRecord::Base.send :include, Opinio::OpinioModel
10
+ ::ActiveRecord::Base.send :include, Opinio::OpinioSubjectum
11
+ end
12
+ ActiveSupport.on_load(:action_view) do
13
+ require File.join(File.dirname(__FILE__), 'controllers', 'helpers')
14
+ ::ActionView::Base.send :include, Opinio::Controllers::Helpers
15
+ end
16
+ ActiveSupport.on_load(:action_controller) do
17
+ require File.join(File.dirname(__FILE__), 'controllers', 'extensions')
18
+ ::ActionController::Base.send :include, Opinio::Controllers::Extensions
19
+ end
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,21 @@
1
+ module Opinio
2
+ module Schema
3
+
4
+ def opinio(options = {})
5
+ null = options[:null] || false
6
+ default = options.key?(:default) ? options[:default] : ("" if null == false)
7
+
8
+ apply_opinio_schema :owner_id, :integer, :null => false
9
+ apply_opinio_schema :commentable_id, :integer, :null => false
10
+ apply_opinio_schema :commentable_type, :string, :null => false
11
+ apply_opinio_schema :title, :string, :default => default, :null => null if options[:title]
12
+ apply_opinio_schema :body, :text, :null => false
13
+ end
14
+
15
+ # Overwrite with specific modification to create your own schema.
16
+ def apply_opinio_schema(name, type, options={})
17
+ raise NotImplementedError
18
+ end
19
+
20
+ end
21
+ end
@@ -0,0 +1,89 @@
1
+ shared_examples_for :opinio do
2
+
3
+ let(:comment) { Comment.new }
4
+ let(:owner) { User.create }
5
+
6
+ before(:all) do
7
+ @post = Post.new(:title => "My first post", :body => "Damn I really suck at writing")
8
+ @post.save
9
+ end
10
+
11
+ it "should have the correct attributes" do
12
+ should respond_to(:owner)
13
+ should respond_to(:body)
14
+ should respond_to(:commentable)
15
+ end
16
+
17
+
18
+ it "should not allow comments of comments" do
19
+ Comment.class_eval do
20
+ include Opinio::OpinioModel::RepliesSupport
21
+ end
22
+
23
+ c = Comment.new(:body => "The Comment !")
24
+ c.commentable = @post
25
+ c.owner = owner
26
+ c.save.should == true
27
+
28
+ c2 = Comment.new(:body => "The Comment !")
29
+ c2.owner = owner
30
+ c2.commentable = c
31
+ c2.save.should == true
32
+
33
+ c3 = c2.dup
34
+ c3.commentable = c2
35
+ c3.save.should == false
36
+
37
+ c3.errors[:base].count.should == 1
38
+ end
39
+
40
+ it "should validate presence of body and commentable" do
41
+ comment.should be_invalid
42
+ comment.errors[:body].should be_present
43
+ comment.errors[:commentable].should be_present
44
+ end
45
+
46
+ it "and it should insist on having an owner" do
47
+ c = Comment.new(:body => "The Comment !")
48
+ c.commentable = @post
49
+ c.save.should == false
50
+ end
51
+
52
+ context "when strip_html_tags_on_save is true" do
53
+ it "should strip html tags" do
54
+ comment = create_valid_comment('<h1>Chuck will save us!</h1>')
55
+ comment.body.should == 'Chuck will save us!'
56
+ end
57
+ end
58
+
59
+ context "when strip_html_tags_on_save is false" do
60
+
61
+ # we have to create a different class
62
+ # because opinio adds the strip tags callbacks
63
+ # only once when opinio is called on the class
64
+ class NoSanitizerComment < ActiveRecord::Base
65
+ self.table_name = :comments
66
+ end
67
+
68
+ before do
69
+ Opinio.stub(:strip_html_tags_on_save).and_return(false)
70
+ NoSanitizerComment.class_eval do
71
+ opinio
72
+ end
73
+ end
74
+ it "should not strip html tags" do
75
+ comment = create_valid_comment('<h1>Chuck will save us!</h1>', NoSanitizerComment)
76
+ comment.body.should == '<h1>Chuck will save us!</h1>'
77
+ end
78
+ end
79
+
80
+ end
81
+
82
+
83
+ shared_examples_for :opinio_subjectum do
84
+
85
+ it "and should add opinio_subjectum functionallity" do
86
+ should respond_to(:comments)
87
+ end
88
+
89
+ end
@@ -0,0 +1,5 @@
1
+ module Opinio
2
+ class Version
3
+ VERSION = '0.6.0.2'
4
+ end
5
+ end
metadata ADDED
@@ -0,0 +1,145 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: opinio-bootstrap
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.6.0.2
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Luiz Felipe Garcia Pereira
9
+ - Richard Carey
10
+ autorequire:
11
+ bindir: bin
12
+ cert_chain: []
13
+ date: 2013-02-10 00:00:00.000000000 Z
14
+ dependencies:
15
+ - !ruby/object:Gem::Dependency
16
+ name: rails
17
+ requirement: !ruby/object:Gem::Requirement
18
+ none: false
19
+ requirements:
20
+ - - ~>
21
+ - !ruby/object:Gem::Version
22
+ version: '3'
23
+ type: :runtime
24
+ prerelease: false
25
+ version_requirements: !ruby/object:Gem::Requirement
26
+ none: false
27
+ requirements:
28
+ - - ~>
29
+ - !ruby/object:Gem::Version
30
+ version: '3'
31
+ - !ruby/object:Gem::Dependency
32
+ name: kaminari
33
+ requirement: !ruby/object:Gem::Requirement
34
+ none: false
35
+ requirements:
36
+ - - ! '>='
37
+ - !ruby/object:Gem::Version
38
+ version: '0'
39
+ type: :runtime
40
+ prerelease: false
41
+ version_requirements: !ruby/object:Gem::Requirement
42
+ none: false
43
+ requirements:
44
+ - - ! '>='
45
+ - !ruby/object:Gem::Version
46
+ version: '0'
47
+ - !ruby/object:Gem::Dependency
48
+ name: jquery-rails
49
+ requirement: !ruby/object:Gem::Requirement
50
+ none: false
51
+ requirements:
52
+ - - ! '>='
53
+ - !ruby/object:Gem::Version
54
+ version: '0'
55
+ type: :runtime
56
+ prerelease: false
57
+ version_requirements: !ruby/object:Gem::Requirement
58
+ none: false
59
+ requirements:
60
+ - - ! '>='
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ - !ruby/object:Gem::Dependency
64
+ name: bootstrap-sass
65
+ requirement: !ruby/object:Gem::Requirement
66
+ none: false
67
+ requirements:
68
+ - - ! '>='
69
+ - !ruby/object:Gem::Version
70
+ version: 2.2.2.0
71
+ type: :runtime
72
+ prerelease: false
73
+ version_requirements: !ruby/object:Gem::Requirement
74
+ none: false
75
+ requirements:
76
+ - - ! '>='
77
+ - !ruby/object:Gem::Version
78
+ version: 2.2.2.0
79
+ description: Opinio is an engine used to add comments functionallity to rails 3 applications;
80
+ this version is adapted to Twitter Bootstrap.
81
+ email:
82
+ - luiz.felipe.gp@gmail.com
83
+ - rc@rcdmcg.com
84
+ executables: []
85
+ extensions: []
86
+ extra_rdoc_files: []
87
+ files:
88
+ - app/controllers/opinio/comments_controller.rb
89
+ - app/views/opinio/comments/_comment.html.erb
90
+ - app/views/opinio/comments/_comments.html.erb
91
+ - app/views/opinio/comments/_new.html.erb
92
+ - app/views/opinio/comments/create.js.erb
93
+ - app/views/opinio/comments/destroy.js.erb
94
+ - app/views/opinio/comments/index.html.erb
95
+ - app/views/opinio/comments/reply.js.erb
96
+ - lib/generators/opinio/install/install_generator.rb
97
+ - lib/generators/opinio/install/templates/initializers/opinio.erb
98
+ - lib/generators/opinio/install/templates/migrations/create_model.rb
99
+ - lib/generators/opinio/install/templates/models/model.rb
100
+ - lib/generators/opinio/views/views_generator.rb
101
+ - lib/opinio/controllers/extensions.rb
102
+ - lib/opinio/controllers/helpers.rb
103
+ - lib/opinio/controllers/internal_helpers.rb
104
+ - lib/opinio/controllers/replies.rb
105
+ - lib/opinio/opinio_model/validations.rb
106
+ - lib/opinio/opinio_model.rb
107
+ - lib/opinio/opinio_subjectum.rb
108
+ - lib/opinio/orm/active_record.rb
109
+ - lib/opinio/rails/routes.rb
110
+ - lib/opinio/rails.rb
111
+ - lib/opinio/railtie.rb
112
+ - lib/opinio/schema.rb
113
+ - lib/opinio/shared_examples.rb
114
+ - lib/opinio/version.rb
115
+ - lib/opinio.rb
116
+ - config/locales/opinio.en.yml
117
+ - MIT-LICENSE
118
+ - Rakefile
119
+ - Gemfile
120
+ - README.md
121
+ homepage: https://github.com/rceee/opinio-bootstrap
122
+ licenses: []
123
+ post_install_message:
124
+ rdoc_options: []
125
+ require_paths:
126
+ - lib
127
+ required_ruby_version: !ruby/object:Gem::Requirement
128
+ none: false
129
+ requirements:
130
+ - - ! '>='
131
+ - !ruby/object:Gem::Version
132
+ version: '0'
133
+ required_rubygems_version: !ruby/object:Gem::Requirement
134
+ none: false
135
+ requirements:
136
+ - - ! '>='
137
+ - !ruby/object:Gem::Version
138
+ version: '0'
139
+ requirements: []
140
+ rubyforge_project:
141
+ rubygems_version: 1.8.24
142
+ signing_key:
143
+ specification_version: 3
144
+ summary: A rails 3 engine for comments, with Twitter Bootstrap styling.
145
+ test_files: []