rostra 0.0.1

Sign up to get free protection for your applications and to get access to all the features.
Files changed (54) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.rdoc +30 -0
  3. data/Rakefile +27 -0
  4. data/app/assets/images/rostra/vote_arrows.png +0 -0
  5. data/app/assets/javascripts/rostra/application.js +28 -0
  6. data/app/assets/javascripts/rostra/jquery.jeditable.js +543 -0
  7. data/app/assets/stylesheets/rostra/application.css +177 -0
  8. data/app/assets/stylesheets/rostra/buttons.css +43 -0
  9. data/app/assets/stylesheets/rostra/flash_messages.css +13 -0
  10. data/app/assets/stylesheets/rostra/forms.css +40 -0
  11. data/app/assets/stylesheets/rostra/questions.css +4 -0
  12. data/app/assets/stylesheets/rostra/reset.css +13 -0
  13. data/app/assets/stylesheets/rostra/typography.css +7 -0
  14. data/app/controllers/rostra/answers_controller.rb +47 -0
  15. data/app/controllers/rostra/application_controller.rb +31 -0
  16. data/app/controllers/rostra/questions_controller.rb +57 -0
  17. data/app/helpers/rostra/application_helper.rb +4 -0
  18. data/app/helpers/rostra/questions_helper.rb +4 -0
  19. data/app/models/rostra/ability.rb +27 -0
  20. data/app/models/rostra/answer.rb +10 -0
  21. data/app/models/rostra/question.rb +15 -0
  22. data/app/models/rostra/vote.rb +19 -0
  23. data/app/models/vote.rb +17 -0
  24. data/app/views/layouts/rostra/application.html.erb +23 -0
  25. data/app/views/rostra/answers/_form.html.erb +8 -0
  26. data/app/views/rostra/answers/edit.html.erb +7 -0
  27. data/app/views/rostra/answers/vote.js.erb +1 -0
  28. data/app/views/rostra/questions/_form.html.erb +7 -0
  29. data/app/views/rostra/questions/edit.html.erb +7 -0
  30. data/app/views/rostra/questions/index.html.erb +45 -0
  31. data/app/views/rostra/questions/new.html.erb +7 -0
  32. data/app/views/rostra/questions/show.html.erb +47 -0
  33. data/app/views/rostra/questions/vote.js.erb +1 -0
  34. data/app/views/rostra/shared/_answers_votes.html.erb +27 -0
  35. data/app/views/rostra/shared/_sidebar.html.erb +8 -0
  36. data/app/views/rostra/shared/_votes.html.erb +27 -0
  37. data/config/cucumber.yml +8 -0
  38. data/config/initializers/client_side_validations.rb +14 -0
  39. data/config/initializers/simple_form.rb +93 -0
  40. data/config/locales/simple_form.en.yml +24 -0
  41. data/config/routes.rb +10 -0
  42. data/db/migrate/20110918170142_create_rostra_questions.rb +10 -0
  43. data/db/migrate/20111003233228_acts_as_taggable_on_migration.rb +28 -0
  44. data/db/migrate/20111005012613_add_user_id_to_questions.rb +5 -0
  45. data/db/migrate/20111007205730_create_rostra_answers.rb +11 -0
  46. data/db/migrate/20111008224511_thumbs_up_migration.rb +25 -0
  47. data/lib/rostra/engine.rb +13 -0
  48. data/lib/rostra/version.rb +3 -0
  49. data/lib/rostra.rb +11 -0
  50. data/lib/tasks/cucumber.rake +65 -0
  51. data/lib/tasks/rake_routes.rake +40 -0
  52. data/lib/tasks/rostra_tasks.rake +4 -0
  53. data/lib/templates/erb/scaffold/_form.html.erb +13 -0
  54. metadata +273 -0
@@ -0,0 +1,47 @@
1
+ <h1><%= @question.title %></h1>
2
+
3
+ <%= render 'rostra/shared/sidebar' %>
4
+
5
+ <div id="page_content">
6
+
7
+ <div class="question">
8
+ <%= render 'rostra/shared/votes' %>
9
+ <h3 class="title"><%= @question.title %></h3>
10
+
11
+ <cite>
12
+ Asked by <%= @question.user.name %>
13
+ <%= link_to 'edit', edit_question_path(@question) if can? :manage, @question %>
14
+ </cite>
15
+ <div class="text"><%= simple_format(@question.details) %></div>
16
+ <div class="timestamp"><%= time_ago_in_words(@question.updated_at) %> ago</div>
17
+ <div class="tags">
18
+ Tags:
19
+ <% @question.tags.each do |tag| %>
20
+ <%= link_to tag, "" %>
21
+ <% end %>
22
+ </div>
23
+ </div>
24
+
25
+ <% unless @answers.empty? %>
26
+ <h3 id="answer_count"><%= pluralize(@answers.size, 'Answer') %></h3>
27
+ <div id="answers">
28
+ <% @answers.each do |answer| %>
29
+ <div class="answer" id="<%= dom_id(answer) %>">
30
+ <%= render partial: 'rostra/shared/answers_votes', locals: {answer: answer} %>
31
+ <cite>
32
+ <%= answer.user.name %> says:
33
+ <%= link_to('edit', edit_question_answer_path(@question, answer)) if can? :manage, answer %>
34
+ </cite>
35
+ <div class="text"><%= simple_format(answer.text) %></div>
36
+ <div class="timestamp"><%= time_ago_in_words(answer.updated_at) %> ago</div>
37
+ </div>
38
+ <% end %>
39
+ </div>
40
+ <% end %>
41
+
42
+ <% if user_signed_in? %>
43
+ <%= render 'rostra/answers/form' %>
44
+ <% else %>
45
+ <%= link_to "Login to answer this question", main_app.new_user_session_path, class: 'button' %>
46
+ <% end %>
47
+ </div><!-- page_content -->
@@ -0,0 +1 @@
1
+ $('.vote_wrapper').replaceWith('<%= escape_javascript(render 'rostra/shared/votes') %>');
@@ -0,0 +1,27 @@
1
+ <div class="answers_vote_wrapper">
2
+
3
+ <% if user_signed_in? %>
4
+
5
+ <% up_selected = current_user.voted_for?(answer) ? 'selected' : "" %>
6
+ <%= link_to('Vote Answer Up', vote_question_answer_path(@question, answer, vote_type: 'up'), method: :put, remote: true, class: "vote up #{up_selected}") %>
7
+
8
+ <div class="count"><%= answer.plusminus %></div>
9
+
10
+ <% down_selected = current_user.voted_against?(answer) ? 'selected' : "" %>
11
+ <%= link_to('Vote Answer Down', vote_question_answer_path(@question, answer, vote_type: 'down'), method: :put, remote: true, class: "vote down #{down_selected}") %>
12
+
13
+ <% else %>
14
+
15
+ <%= link_to('Vote Answer Up', '#', class: "answers vote up login_flash") %>
16
+ <div class="count"><%= answer.plusminus %></div>
17
+ <%= link_to('Vote Answer Down', '#', class: "vote down login_flash") %>
18
+
19
+ <% end %>
20
+
21
+ </div>
22
+
23
+ <% unless user_signed_in? %>
24
+ <div class="hidden flash" id="flash_notice">
25
+ You must <%= link_to 'login', main_app.new_user_session_path %> or <%= link_to 'signup', main_app.new_user_registration_path %> in order to vote
26
+ </div>
27
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <div id="sidebar">
2
+ <% if user_signed_in? %>
3
+ <%= link_to 'Ask a question', new_question_path, class: "button" %>
4
+ <% else %>
5
+ <%= link_to 'Login to ask a question', main_app.new_user_session_path, class: "button" %>
6
+ <% end %>
7
+ </div><!--sidebar-->
8
+
@@ -0,0 +1,27 @@
1
+ <div class="vote_wrapper">
2
+
3
+ <% if user_signed_in? %>
4
+
5
+ <% up_selected = current_user.voted_for?(@question) ? 'selected' : "" %>
6
+ <%= link_to('Vote Question Up', vote_question_path(@question, vote_type: 'up'), method: :put, remote: true, class: "vote up #{up_selected}") %>
7
+
8
+ <div class="count"><%= @question.plusminus %></div>
9
+
10
+ <% down_selected = current_user.voted_against?(@question) ? 'selected' : "" %>
11
+ <%= link_to('Vote Question Down', vote_question_path(@question, vote_type: 'down'), method: :put, remote: true, class: "vote down #{down_selected}") %>
12
+
13
+ <% else %>
14
+
15
+ <%= link_to('Vote Question Up', '#', class: "vote up login_flash") %>
16
+ <div class="count"><%= @question.plusminus %></div>
17
+ <%= link_to('Vote Question Down', '#', class: "vote down login_flash") %>
18
+
19
+ <% end %>
20
+
21
+ </div>
22
+
23
+ <% unless user_signed_in? %>
24
+ <div class="hidden flash" id="flash_notice">
25
+ You must <%= link_to 'login', main_app.new_user_session_path %> or <%= link_to 'signup', main_app.new_user_registration_path %> in order to vote
26
+ </div>
27
+ <% end %>
@@ -0,0 +1,8 @@
1
+ <%
2
+ rerun = File.file?('rerun.txt') ? IO.read('rerun.txt') : ""
3
+ rerun_opts = rerun.to_s.strip.empty? ? "--format #{ENV['CUCUMBER_FORMAT'] || 'progress'} features" : "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} #{rerun}"
4
+ std_opts = "--format #{ENV['CUCUMBER_FORMAT'] || 'pretty'} --strict --tags ~@wip"
5
+ %>
6
+ default: <%= std_opts %> features
7
+ wip: --tags @wip:3 --wip features
8
+ rerun: <%= rerun_opts %> --format rerun --out rerun.txt --strict --tags ~@wip
@@ -0,0 +1,14 @@
1
+ # ClientSideValidations Initializer
2
+
3
+ require 'client_side_validations/simple_form' if defined?(::SimpleForm)
4
+ require 'client_side_validations/formtastic' if defined?(::Formtastic)
5
+
6
+ # Uncomment the following block if you want each input field to have the validation messages attached.
7
+ # ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
8
+ # unless html_tag =~ /^<label/
9
+ # %{<div class="field_with_errors">#{html_tag}<label for="#{instance.send(:tag_id)}" class="message">#{instance.error_message.first}</label></div>}.html_safe
10
+ # else
11
+ # %{<div class="field_with_errors">#{html_tag}</div>}.html_safe
12
+ # end
13
+ # end
14
+
@@ -0,0 +1,93 @@
1
+ # Use this setup block to configure all options available in SimpleForm.
2
+ SimpleForm.setup do |config|
3
+ # Components used by the form builder to generate a complete input. You can remove
4
+ # any of them, change the order, or even add your own components to the stack.
5
+ # config.components = [ :placeholder, :label_input, :hint, :error ]
6
+
7
+ # Default tag used on hints.
8
+ # config.hint_tag = :span
9
+
10
+ # CSS class to add to all hint tags.
11
+ # config.hint_class = :hint
12
+
13
+ # CSS class used on errors.
14
+ # config.error_class = :error
15
+
16
+ # Default tag used on errors.
17
+ # config.error_tag = :span
18
+
19
+ # Method used to tidy up errors.
20
+ # config.error_method = :first
21
+
22
+ # Default tag used for error notification helper.
23
+ # config.error_notification_tag = :p
24
+
25
+ # CSS class to add for error notification helper.
26
+ # config.error_notification_class = :error_notification
27
+
28
+ # ID to add for error notification helper.
29
+ # config.error_notification_id = nil
30
+
31
+ # You can wrap all inputs in a pre-defined tag.
32
+ # config.wrapper_tag = :div
33
+
34
+ # CSS class to add to all wrapper tags.
35
+ config.wrapper_class = :field
36
+
37
+ # CSS class to add to the wrapper if the field has errors.
38
+ # config.wrapper_error_class = :field_with_errors
39
+
40
+ # You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
41
+ # config.collection_wrapper_tag = nil
42
+
43
+ # You can wrap each item in a collection of radio/check boxes with a tag, defaulting to span.
44
+ # config.item_wrapper_tag = :span
45
+
46
+ # Series of attempts to detect a default label method for collection.
47
+ # config.collection_label_methods = [ :to_label, :name, :title, :to_s ]
48
+
49
+ # Series of attempts to detect a default value method for collection.
50
+ # config.collection_value_methods = [ :id, :to_s ]
51
+
52
+ # How the label text should be generated altogether with the required text.
53
+ config.label_text = lambda { |label, required| "#{label} #{required}" }
54
+
55
+ # You can define the class to use on all labels. Default is nil.
56
+ # config.label_class = nil
57
+
58
+ # You can define the class to use on all forms. Default is simple_form.
59
+ # config.form_class = :simple_form
60
+
61
+ # Whether attributes are required by default (or not). Default is true.
62
+ # config.required_by_default = true
63
+
64
+ # Tell browsers whether to use default HTML5 validations (novalidate option).
65
+ # Default is enabled.
66
+ config.browser_validations = false
67
+
68
+ # Determines whether HTML5 types (:email, :url, :search, :tel) and attributes
69
+ # (e.g. required) are used or not. True by default.
70
+ # Having this on in non-HTML5 compliant sites can cause odd behavior in
71
+ # HTML5-aware browsers such as Chrome.
72
+ # config.html5 = true
73
+
74
+ # Custom mappings for input types. This should be a hash containing a regexp
75
+ # to match as key, and the input type that will be used when the field name
76
+ # matches the regexp as value.
77
+ # config.input_mappings = { /count/ => :integer }
78
+
79
+ # Collection of methods to detect if a file type was given.
80
+ # config.file_methods = [ :mounted_as, :file?, :public_filename ]
81
+
82
+ # Default priority for time_zone inputs.
83
+ # config.time_zone_priority = nil
84
+
85
+ # Default priority for country inputs.
86
+ # config.country_priority = nil
87
+
88
+ # Default size for text inputs.
89
+ # config.default_input_size = 50
90
+
91
+ # When false, do not use translations for labels, hints or placeholders.
92
+ # config.translate = true
93
+ end
@@ -0,0 +1,24 @@
1
+ en:
2
+ simple_form:
3
+ "yes": 'Yes'
4
+ "no": 'No'
5
+ required:
6
+ text: 'required'
7
+ mark: '*'
8
+ # You can uncomment the line below if you need to overwrite the whole required html.
9
+ # When using html, text and mark won't be used.
10
+ # html: '<abbr title="required">*</abbr>'
11
+ error_notification:
12
+ default_message: "Some errors were found, please take a look:"
13
+ # Labels and hints examples
14
+ # labels:
15
+ # password: 'Password'
16
+ # user:
17
+ # new:
18
+ # email: 'E-mail para efetuar o sign in.'
19
+ # edit:
20
+ # email: 'E-mail.'
21
+ # hints:
22
+ # username: 'User name to sign in.'
23
+ # password: 'No special characters, please.'
24
+
data/config/routes.rb ADDED
@@ -0,0 +1,10 @@
1
+ Rostra::Engine.routes.draw do
2
+ resources :questions do
3
+ put :vote, :on => :member
4
+ resources :answers do
5
+ put :vote, :on => :member
6
+ end
7
+ end
8
+
9
+ root :to => "questions#index"
10
+ end
@@ -0,0 +1,10 @@
1
+ class CreateRostraQuestions < ActiveRecord::Migration
2
+ def change
3
+ create_table :rostra_questions do |t|
4
+ t.string :title
5
+ t.text :details
6
+
7
+ t.timestamps
8
+ end
9
+ end
10
+ end
@@ -0,0 +1,28 @@
1
+ class ActsAsTaggableOnMigration < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :tags do |t|
4
+ t.string :name
5
+ end
6
+
7
+ create_table :taggings do |t|
8
+ t.references :tag
9
+
10
+ # You should make sure that the column created is
11
+ # long enough to store the required class names.
12
+ t.references :taggable, :polymorphic => true
13
+ t.references :tagger, :polymorphic => true
14
+
15
+ t.string :context
16
+
17
+ t.datetime :created_at
18
+ end
19
+
20
+ add_index :taggings, :tag_id
21
+ add_index :taggings, [:taggable_id, :taggable_type, :context]
22
+ end
23
+
24
+ def self.down
25
+ drop_table :taggings
26
+ drop_table :tags
27
+ end
28
+ end
@@ -0,0 +1,5 @@
1
+ class AddUserIdToQuestions < ActiveRecord::Migration
2
+ def change
3
+ add_column :rostra_questions, :user_id, :integer
4
+ end
5
+ end
@@ -0,0 +1,11 @@
1
+ class CreateRostraAnswers < ActiveRecord::Migration
2
+ def change
3
+ create_table :rostra_answers do |t|
4
+ t.integer :user_id
5
+ t.integer :question_id
6
+ t.text :text
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,25 @@
1
+ class ThumbsUpMigration < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :votes, :force => true do |t|
4
+
5
+ t.boolean :vote, :default => false
6
+ t.references :voteable, :polymorphic => true, :null => false
7
+ t.references :voter, :polymorphic => true
8
+ t.timestamps
9
+
10
+ end
11
+
12
+ add_index :votes, [:voter_id, :voter_type]
13
+ add_index :votes, [:voteable_id, :voteable_type]
14
+
15
+
16
+ # Comment out the line below to allow multiple votes per voter on a single entity.
17
+ add_index :votes, [:voter_id, :voter_type, :voteable_id, :voteable_type], :unique => true, :name => 'fk_one_vote_per_user_per_entity'
18
+
19
+ end
20
+
21
+ def self.down
22
+ drop_table :votes
23
+ end
24
+
25
+ end
@@ -0,0 +1,13 @@
1
+ module Rostra
2
+ class Engine < Rails::Engine
3
+ require 'acts-as-taggable-on'
4
+ require 'cancan'
5
+ require 'simple_form'
6
+ require 'jquery-rails'
7
+ require 'client_side_validations'
8
+ require 'thumbs_up'
9
+ require 'grb'
10
+
11
+ isolate_namespace Rostra
12
+ end
13
+ end
@@ -0,0 +1,3 @@
1
+ module Rostra
2
+ VERSION = "0.0.1"
3
+ end
data/lib/rostra.rb ADDED
@@ -0,0 +1,11 @@
1
+ require "rostra/engine"
2
+
3
+ module Rostra
4
+
5
+ # def self.included(base)
6
+ # def base.rostra
7
+ # base.has_many :questions
8
+ # end
9
+ # end
10
+
11
+ end
@@ -0,0 +1,65 @@
1
+ # IMPORTANT: This file is generated by cucumber-rails - edit at your own peril.
2
+ # It is recommended to regenerate this file in the future when you upgrade to a
3
+ # newer version of cucumber-rails. Consider adding your own code to a new file
4
+ # instead of editing this one. Cucumber will automatically load all features/**/*.rb
5
+ # files.
6
+
7
+
8
+ unless ARGV.any? {|a| a =~ /^gems/} # Don't load anything when running the gems:* tasks
9
+
10
+ vendored_cucumber_bin = Dir["#{Rails.root}/vendor/{gems,plugins}/cucumber*/bin/cucumber"].first
11
+ $LOAD_PATH.unshift(File.dirname(vendored_cucumber_bin) + '/../lib') unless vendored_cucumber_bin.nil?
12
+
13
+ begin
14
+ require 'cucumber/rake/task'
15
+
16
+ namespace :cucumber do
17
+ Cucumber::Rake::Task.new({:ok => 'db:test:prepare'}, 'Run features that should pass') do |t|
18
+ t.binary = vendored_cucumber_bin # If nil, the gem's binary is used.
19
+ t.fork = true # You may get faster startup if you set this to false
20
+ t.profile = 'default'
21
+ end
22
+
23
+ Cucumber::Rake::Task.new({:wip => 'db:test:prepare'}, 'Run features that are being worked on') do |t|
24
+ t.binary = vendored_cucumber_bin
25
+ t.fork = true # You may get faster startup if you set this to false
26
+ t.profile = 'wip'
27
+ end
28
+
29
+ Cucumber::Rake::Task.new({:rerun => 'db:test:prepare'}, 'Record failing features and run only them if any exist') do |t|
30
+ t.binary = vendored_cucumber_bin
31
+ t.fork = true # You may get faster startup if you set this to false
32
+ t.profile = 'rerun'
33
+ end
34
+
35
+ desc 'Run all features'
36
+ task :all => [:ok, :wip]
37
+
38
+ task :statsetup do
39
+ require 'rails/code_statistics'
40
+ ::STATS_DIRECTORIES << %w(Cucumber\ features features) if File.exist?('features')
41
+ ::CodeStatistics::TEST_TYPES << "Cucumber features" if File.exist?('features')
42
+ end
43
+ end
44
+ desc 'Alias for cucumber:ok'
45
+ task :cucumber => 'cucumber:ok'
46
+
47
+ task :default => :cucumber
48
+
49
+ task :features => :cucumber do
50
+ STDERR.puts "*** The 'features' task is deprecated. See rake -T cucumber ***"
51
+ end
52
+
53
+ # In case we don't have ActiveRecord, append a no-op task that we can depend upon.
54
+ task 'db:test:prepare' do
55
+ end
56
+
57
+ task :stats => 'cucumber:statsetup'
58
+ rescue LoadError
59
+ desc 'cucumber rake task not available (cucumber not installed)'
60
+ task :cucumber do
61
+ abort 'Cucumber rake task is not available. Be sure to install cucumber as a gem or plugin'
62
+ end
63
+ end
64
+
65
+ end
@@ -0,0 +1,40 @@
1
+ # Essentially copy rake routes task from core rails in order to have this during development
2
+ # See: http://stackoverflow.com/questions/7431687/listing-rake-routes-for-a-mountable-rails-3-1-engine
3
+ #
4
+ desc 'Print out all defined routes in match order, with names. Target specific controller with CONTROLLER=x.'
5
+ task :routes => :environment do
6
+ Rails.application.reload_routes!
7
+ all_routes = Rostra::Engine.routes.routes
8
+
9
+ if ENV['CONTROLLER']
10
+ all_routes = all_routes.select{ |route| route.defaults[:controller] == ENV['CONTROLLER'] }
11
+ end
12
+
13
+ routes = all_routes.collect do |route|
14
+
15
+ reqs = route.requirements.dup
16
+ rack_app = route.app unless route.app.class.name.to_s =~ /^ActionDispatch::Routing/
17
+
18
+ endpoint = rack_app ? rack_app.inspect : "#{reqs[:controller]}##{reqs[:action]}"
19
+ constraints = reqs.except(:controller, :action)
20
+
21
+ reqs = endpoint == '#' ? '' : endpoint
22
+
23
+ unless constraints.empty?
24
+ reqs = reqs.empty? ? constraints.inspect : "#{reqs} #{constraints.inspect}"
25
+ end
26
+
27
+ {:name => route.name.to_s, :verb => route.verb.to_s, :path => route.path, :reqs => reqs}
28
+ end
29
+
30
+ # Skip the route if it's internal info route
31
+ routes.reject! { |r| r[:path] =~ %r{/rails/info/properties|^/assets} }
32
+
33
+ name_width = routes.map{ |r| r[:name].length }.max
34
+ verb_width = routes.map{ |r| r[:verb].length }.max
35
+ path_width = routes.map{ |r| r[:path].length }.max
36
+
37
+ routes.each do |r|
38
+ puts "#{r[:name].rjust(name_width)} #{r[:verb].ljust(verb_width)} #{r[:path].ljust(path_width)} #{r[:reqs]}"
39
+ end
40
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :rostra do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,13 @@
1
+ <%%= simple_form_for(@<%= singular_table_name %>) do |f| %>
2
+ <%%= f.error_notification %>
3
+
4
+ <div class="inputs">
5
+ <%- attributes.each do |attribute| -%>
6
+ <%%= f.<%= attribute.reference? ? :association : :input %> :<%= attribute.name %> %>
7
+ <%- end -%>
8
+ </div>
9
+
10
+ <div class="actions">
11
+ <%%= f.button :submit %>
12
+ </div>
13
+ <%% end %>