noodnik 0.1.0

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 (82) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.md +52 -0
  3. data/Rakefile +39 -0
  4. data/app/assets/javascripts/noodnik/application.js +9 -0
  5. data/app/assets/javascripts/noodnik/nags.erb.coffee +12 -0
  6. data/app/assets/stylesheets/noodnik/application.css +7 -0
  7. data/app/assets/stylesheets/noodnik/nags.css +4 -0
  8. data/app/controllers/noodnik/application_controller.rb +4 -0
  9. data/app/controllers/noodnik/nags_controller.rb +47 -0
  10. data/app/helpers/noodnik/application_helper.rb +4 -0
  11. data/app/helpers/noodnik/nags_helper.rb +51 -0
  12. data/app/models/noodnik/nag.rb +4 -0
  13. data/app/views/layouts/application.html.erb +14 -0
  14. data/app/views/layouts/noodnik/application.html.erb +14 -0
  15. data/config/routes.rb +6 -0
  16. data/db/migrate/20110813042447_create_noodnik_nags.rb +11 -0
  17. data/db/migrate/20110813065655_add_user_id_to_nags.rb +5 -0
  18. data/db/migrate/20110813072011_add_topic_to_nags.rb +5 -0
  19. data/lib/noodnik.rb +10 -0
  20. data/lib/noodnik/engine.rb +5 -0
  21. data/lib/noodnik/railtie.rb +7 -0
  22. data/lib/tasks/noodnik_tasks.rake +4 -0
  23. data/test/dummy/Guardfile +29 -0
  24. data/test/dummy/Rakefile +7 -0
  25. data/test/dummy/app/assets/javascripts/application.js +9 -0
  26. data/test/dummy/app/assets/javascripts/home.js +2 -0
  27. data/test/dummy/app/assets/stylesheets/application.css +7 -0
  28. data/test/dummy/app/assets/stylesheets/home.css +4 -0
  29. data/test/dummy/app/controllers/application_controller.rb +3 -0
  30. data/test/dummy/app/controllers/home_controller.rb +4 -0
  31. data/test/dummy/app/helpers/application_helper.rb +2 -0
  32. data/test/dummy/app/helpers/home_helper.rb +2 -0
  33. data/test/dummy/app/views/home/index.html.erb +10 -0
  34. data/test/dummy/app/views/layouts/application.html.erb +14 -0
  35. data/test/dummy/config.ru +4 -0
  36. data/test/dummy/config/application.rb +47 -0
  37. data/test/dummy/config/boot.rb +10 -0
  38. data/test/dummy/config/database.yml +25 -0
  39. data/test/dummy/config/environment.rb +5 -0
  40. data/test/dummy/config/environments/development.rb +27 -0
  41. data/test/dummy/config/environments/production.rb +51 -0
  42. data/test/dummy/config/environments/test.rb +39 -0
  43. data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
  44. data/test/dummy/config/initializers/inflections.rb +10 -0
  45. data/test/dummy/config/initializers/mime_types.rb +5 -0
  46. data/test/dummy/config/initializers/noodnik.rb +3 -0
  47. data/test/dummy/config/initializers/secret_token.rb +7 -0
  48. data/test/dummy/config/initializers/session_store.rb +8 -0
  49. data/test/dummy/config/initializers/wrap_parameters.rb +12 -0
  50. data/test/dummy/config/locales/en.yml +5 -0
  51. data/test/dummy/config/routes.rb +6 -0
  52. data/test/dummy/db/development.sqlite3 +0 -0
  53. data/test/dummy/db/migrate/20110813070108_create_noodnik_nags.rb +11 -0
  54. data/test/dummy/db/migrate/20110813070109_add_user_id_to_nags.rb +5 -0
  55. data/test/dummy/db/migrate/20110813072040_add_topic_to_nags.rb +5 -0
  56. data/test/dummy/db/schema.rb +24 -0
  57. data/test/dummy/db/test.sqlite3 +0 -0
  58. data/test/dummy/log/development.log +10612 -0
  59. data/test/dummy/log/test.log +26879 -0
  60. data/test/dummy/public/404.html +26 -0
  61. data/test/dummy/public/422.html +26 -0
  62. data/test/dummy/public/500.html +26 -0
  63. data/test/dummy/public/favicon.ico +0 -0
  64. data/test/dummy/script/rails +6 -0
  65. data/test/dummy/spec/controllers/noodnik_nags_controller_spec.rb +121 -0
  66. data/test/dummy/spec/helpers/nags_helper_spec.rb +130 -0
  67. data/test/dummy/spec/spec_helper.rb +74 -0
  68. data/test/dummy/tmp/cache/assets/C7E/8B0/sprockets%2F1a26023e8a9ac4a7477d84b980511817 +0 -0
  69. data/test/dummy/tmp/cache/assets/C9B/0A0/sprockets%2Fc184c48971885500d230701da1c1b0fb +0 -0
  70. data/test/dummy/tmp/cache/assets/CC7/610/sprockets%2Fd85d521b5f5052344563eef989889de0 +0 -0
  71. data/test/dummy/tmp/cache/assets/CDF/870/sprockets%2Fb878faf942403e313a5b103e5d80488e +0 -0
  72. data/test/dummy/tmp/cache/assets/CF8/F90/sprockets%2F7c5977cba909e2f75909ced513698d03 +0 -0
  73. data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
  74. data/test/dummy/tmp/cache/assets/D54/ED0/sprockets%2F71c9fa01091d432b131da3bb73faf3d4 +0 -0
  75. data/test/dummy/tmp/cache/assets/D72/AD0/sprockets%2Fc4434fe69351b83a77ebaca291931fee +0 -0
  76. data/test/dummy/tmp/cache/assets/D7E/3B0/sprockets%2F942cff9f3e4e8a45ae06090e3d883e7b +0 -0
  77. data/test/dummy/tmp/cache/assets/D84/210/sprockets%2Fabd0103ccec2b428ac62c94e4c40b384 +0 -0
  78. data/test/dummy/tmp/cache/assets/D98/8B0/sprockets%2Fedbef6e0d0a4742346cf479f2c522eb0 +0 -0
  79. data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
  80. data/test/dummy/tmp/cache/assets/E38/C70/sprockets%2F7e7577dbecc6d06993be00b9daabcd9f +0 -0
  81. data/test/dummy/tmp/pids/server.pid +1 -0
  82. metadata +184 -0
@@ -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,52 @@
1
+ #noodnik
2
+
3
+ This gem is a simple solution that allows you to remind your users to do things such as update their profiles, complete surveys or try a new feature. You can easily add links for postponing these reminders.
4
+
5
+ ## Installation
6
+
7
+ ## Examples
8
+
9
+ ### A Quick Nagging Example
10
+
11
+ <%= nag_user_to :update_profile do %>
12
+ You still need to <%= link_to 'update your profile', update_profile_path %>!
13
+ <% end %>
14
+
15
+ This will render the following HTML:
16
+
17
+ <div class="noodnik-nag">
18
+ You still need to <a href="/profiles/update" class="noodnik-complete" data-noodnik-complete-path="/noodnik/complete?topic=update_profile">update your profile</a>!
19
+ </div>
20
+
21
+ - Clicking on "**update your profile**" will stop the message from appearing the next time the user views the page.
22
+
23
+ ### Postponing Nags
24
+
25
+ Inside ```nag_user_to``` blocks, you can use the ```postpone_for``` helper to create links for temporarily postponing nags:
26
+
27
+ <%= nag_user_to :complete_survey do %>
28
+ Don't forget to <%= link_to 'complete our survey', complete_survey_path %>
29
+ (<%= postpone_for 5.days %>)
30
+ <% end %>
31
+
32
+ Will output something that looks like:
33
+
34
+ Don't forget to [complete our survey] [1] ([Remind me in 5 days] [1])
35
+
36
+ - Clicking on "**complete our survey**" will take the user to ```complete_survey_path```, but will also mark this nag as *complete* (which means that this entire div block won't be displayed ever again in the future)
37
+ - Click on "**Remind me in 5 days**" will hide the div block and prevent it from being displayed in the next 5 days. The user will stay in the same page and won't be redirected anywhere.
38
+
39
+ ### Manually Completing Nags
40
+
41
+ Any ```link_to``` under a ```nag_user_to``` block will automatically mark the nag as *complete* when clicked. You might choose to mark nags as complete at a different time (for example, only after the user actually finishes a survey).
42
+ To achieve this, you need to specify ```complete: false``` for links inside ```nag_user_to``` blocks.
43
+
44
+ <%= nag_user_to :complete_survey do %>
45
+ <%= link_to 'complete our survey', complete_survey_path, complete: false %>
46
+ <% end %>
47
+
48
+ Then, you can mark this as complete by calling:
49
+
50
+ TODO
51
+
52
+ [1]: http://www.example.com/complete_survey
@@ -0,0 +1,39 @@
1
+ #!/usr/bin/env rake
2
+ begin
3
+ require 'bundler/setup'
4
+ rescue LoadError
5
+ puts 'You must `gem install bundler` and `bundle install` to run rake tasks'
6
+ end
7
+ begin
8
+ require 'rdoc/task'
9
+ rescue LoadError
10
+ require 'rdoc/rdoc'
11
+ require 'rake/rdoctask'
12
+ RDoc::Task = Rake::RDocTask
13
+ end
14
+
15
+ RDoc::Task.new(:rdoc) do |rdoc|
16
+ rdoc.rdoc_dir = 'rdoc'
17
+ rdoc.title = 'Noodnik'
18
+ rdoc.options << '--line-numbers'
19
+ rdoc.rdoc_files.include('README.rdoc')
20
+ rdoc.rdoc_files.include('lib/**/*.rb')
21
+ end
22
+
23
+ APP_RAKEFILE = File.expand_path("../test/dummy/Rakefile", __FILE__)
24
+ load 'rails/tasks/engine.rake'
25
+
26
+
27
+ Bundler::GemHelper.install_tasks
28
+
29
+ require 'rake/testtask'
30
+
31
+ Rake::TestTask.new(:test) do |t|
32
+ t.libs << 'lib'
33
+ t.libs << 'test'
34
+ t.pattern = 'test/**/*_test.rb'
35
+ t.verbose = false
36
+ end
37
+
38
+
39
+ task :default => :test
@@ -0,0 +1,9 @@
1
+ // This is a manifest file that'll be compiled into including all the files listed below.
2
+ // Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
3
+ // be included in the compiled file accessible from http://example.com/assets/application.js
4
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
5
+ // the compiled file.
6
+ //
7
+ //= require jquery
8
+ //= require jquery_ujs
9
+ //= require_tree .
@@ -0,0 +1,12 @@
1
+ $ ->
2
+ $('a.noodnik-postpone').click (event) ->
3
+ event.preventDefault()
4
+ url = $(this).attr 'href'
5
+ $box = $(this).parent()
6
+ $box = $(this).parentsUntil('div.noodnik-nag').parent() unless $box.hasClass 'noodnik-nag'
7
+ $.get url, ->
8
+ $box.hide()
9
+
10
+ $('a.noodnik-complete').click (event) ->
11
+ url = $(this).data 'noodnik-complete-path'
12
+ $.get url
@@ -0,0 +1,7 @@
1
+ /*
2
+ * This is a manifest file that'll automatically include all the stylesheets available in this directory
3
+ * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
4
+ * the top of the compiled file, but it's generally better to create a new file per style scope.
5
+ *= require_self
6
+ *= require_tree .
7
+ */
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,4 @@
1
+ module Noodnik
2
+ class ApplicationController < ActionController::Base
3
+ end
4
+ end
@@ -0,0 +1,47 @@
1
+ module Noodnik
2
+ class NagsController < ApplicationController
3
+ def postpone
4
+ next_nag = params[:period].to_i.from_now
5
+ topic = params[:topic]
6
+
7
+ if user_id.present?
8
+ nag = find_or_create_nag(topic)
9
+ nag.next_nag = next_nag
10
+ nag.save
11
+ else
12
+ cookies[topic] = next_nag.to_s
13
+ end
14
+
15
+ render :nothing => true
16
+ end
17
+
18
+ def complete
19
+ topic = params[:topic]
20
+
21
+ if user_id.present?
22
+ nag = find_or_create_nag(topic)
23
+ nag.completed = true
24
+ nag.save
25
+ else
26
+ cookies.delete topic
27
+ end
28
+
29
+ render :nothing => true
30
+ end
31
+
32
+ private
33
+
34
+ def user_id
35
+ Noodnik.current_user_id.call
36
+ end
37
+
38
+ def find_or_create_nag(topic)
39
+ attr = { user_id: user_id, topic: topic }
40
+ if Nag.exists? attr
41
+ Nag.find :first, conditions: attr
42
+ else
43
+ Nag.new attr
44
+ end
45
+ end
46
+ end
47
+ end
@@ -0,0 +1,4 @@
1
+ module Noodnik
2
+ module ApplicationHelper
3
+ end
4
+ end
@@ -0,0 +1,51 @@
1
+ module Noodnik
2
+ module NagsHelper
3
+ def nag_user_to(topic, &block)
4
+ nag = find_nag(topic)
5
+ return if nag && (nag.next_nag > Time.now || nag.completed?)
6
+
7
+ begin
8
+ class_eval('alias :original_link_to :link_to')
9
+ class_eval('alias :link_to :my_custom_link_to')
10
+ Context.new.local_eval do
11
+ @noodnik_topic = topic
12
+ content_tag :div, class: 'noodnik-nag' do
13
+ capture(&block)
14
+ end
15
+ end
16
+ ensure
17
+ @noodnik_topic = nil
18
+ class_eval('alias :link_to :original_link_to')
19
+ end
20
+ end
21
+
22
+ private
23
+
24
+ def find_nag(topic)
25
+ attr = { user_id: Noodnik.current_user_id.call, topic: topic }
26
+ Nag.find :first, conditions: attr
27
+ end
28
+
29
+ def my_custom_link_to(*args)
30
+ has_options = args.last.is_a? Hash
31
+ html_options = has_options ? args.last : {}
32
+ html_options["data-noodnik-complete-path"] = noodnik.routes.url_helpers.complete_path(topic: @noodnik_topic)
33
+
34
+ if html_options.include? :class
35
+ html_options[:class] += " noodnik-complete"
36
+ else
37
+ html_options[:class] = "noodnik-complete"
38
+ end
39
+
40
+ args << html_options unless has_options
41
+ original_link_to *args
42
+ end
43
+
44
+ class Context
45
+ include Rails.application.routes.mounted_helpers
46
+ def postpone_for(period)
47
+ original_link_to "Remind me in #{period.inspect}", noodnik.routes.url_helpers.postpone_path(period: period, topic: @noodnik_topic), class: 'noodnik-postpone'
48
+ end
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,4 @@
1
+ module Noodnik
2
+ class Nag < ActiveRecord::Base
3
+ end
4
+ end
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Noodnik</title>
5
+ <%= stylesheet_link_tag "noodnik/application" %>
6
+ <%= javascript_include_tag "noodnik/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,14 @@
1
+ <!DOCTYPE html>
2
+ <html>
3
+ <head>
4
+ <title>Noodnik</title>
5
+ <%= stylesheet_link_tag "noodnik/application" %>
6
+ <%= javascript_include_tag "noodnik/application" %>
7
+ <%= csrf_meta_tags %>
8
+ </head>
9
+ <body>
10
+
11
+ <%= yield %>
12
+
13
+ </body>
14
+ </html>
@@ -0,0 +1,6 @@
1
+ Noodnik::Engine.routes.draw do
2
+ get "postpone" => "nags#postpone"
3
+
4
+ get "complete" => "nags#complete"
5
+
6
+ end
@@ -0,0 +1,11 @@
1
+ class CreateNoodnikNags < ActiveRecord::Migration
2
+ def change
3
+ create_table :noodnik_nags do |t|
4
+ t.integer :id
5
+ t.datetime :next_nag
6
+ t.boolean :completed
7
+
8
+ t.timestamps
9
+ end
10
+ end
11
+ end
@@ -0,0 +1,5 @@
1
+ class AddUserIdToNags < ActiveRecord::Migration
2
+ def change
3
+ add_column :noodnik_nags, :user_id, :integer
4
+ end
5
+ end
@@ -0,0 +1,5 @@
1
+ class AddTopicToNags < ActiveRecord::Migration
2
+ def change
3
+ add_column :noodnik_nags, :topic, :string
4
+ end
5
+ end
@@ -0,0 +1,10 @@
1
+ require "noodnik/engine"
2
+ require "noodnik/railtie"
3
+
4
+ module Noodnik
5
+ mattr_accessor :current_user_id
6
+
7
+ def self.setup
8
+ yield self
9
+ end
10
+ end
@@ -0,0 +1,5 @@
1
+ module Noodnik
2
+ class Engine < Rails::Engine
3
+ isolate_namespace Noodnik
4
+ end
5
+ end
@@ -0,0 +1,7 @@
1
+ module Noodnik
2
+ class Railtie < Rails::Railtie
3
+ initializer "noodnik.view_helpers" do
4
+ ActionView::Base.send :include, NagsHelper
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ # desc "Explaining what the task does"
2
+ # task :noodnik do
3
+ # # Task goes here
4
+ # end
@@ -0,0 +1,29 @@
1
+ # A sample Guardfile
2
+ # More info at https://github.com/guard/guard#readme
3
+
4
+ guard 'rspec', :version => 2 do
5
+ watch(%r{^engine.+[^swp]$}) { "spec/" }
6
+ watch(%r{^spec/.+_spec\.rb$})
7
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
8
+ watch('spec/spec_helper.rb') { "spec/" }
9
+
10
+ # Rails example
11
+ watch(%r{^spec/.+_spec\.rb$})
12
+ watch(%r{^app/(.+)\.rb$}) { |m| "spec/#{m[1]}_spec.rb" }
13
+ watch(%r{^lib/(.+)\.rb$}) { |m| "spec/lib/#{m[1]}_spec.rb" }
14
+ watch(%r{^app/controllers/(.+)_(controller)\.rb$}) { |m| ["spec/routing/#{m[1]}_routing_spec.rb", "spec/#{m[2]}s/#{m[1]}_#{m[2]}_spec.rb", "spec/acceptance/#{m[1]}_spec.rb"] }
15
+ watch(%r{^spec/support/(.+)\.rb$}) { "spec/" }
16
+ watch('spec/spec_helper.rb') { "spec/" }
17
+ watch('config/routes.rb') { "spec/routing" }
18
+ watch('app/controllers/application_controller.rb') { "spec/controllers" }
19
+ # Capybara request specs
20
+ watch(%r{^app/views/(.+)/.*\.(erb|haml)$}) { |m| "spec/requests/#{m[1]}_spec.rb" }
21
+ end
22
+
23
+ guard 'spork', :cucumber_env => { 'RAILS_ENV' => 'test' }, :rspec_env => { 'RAILS_ENV' => 'test' } do
24
+ watch('config/application.rb')
25
+ watch('config/environment.rb')
26
+ watch(%r{^config/environments/.+\.rb$})
27
+ watch(%r{^config/initializers/.+\.rb$})
28
+ watch('spec/spec_helper.rb')
29
+ end
@@ -0,0 +1,7 @@
1
+ #!/usr/bin/env rake
2
+ # Add your own tasks in files placed in lib/tasks ending in .rake,
3
+ # for example lib/tasks/capistrano.rake, and they will automatically be available to Rake.
4
+
5
+ require File.expand_path('../config/application', __FILE__)
6
+
7
+ Dummy::Application.load_tasks
@@ -0,0 +1,9 @@
1
+ // This is a manifest file that'll be compiled into including all the files listed below.
2
+ // Add new JavaScript/Coffee code in separate files in this directory and they'll automatically
3
+ // be included in the compiled file accessible from http://example.com/assets/application.js
4
+ // It's not advisable to add code directly here, but if you do, it'll appear at the bottom of the
5
+ // the compiled file.
6
+ //
7
+ //= require jquery
8
+ //= require jquery_ujs
9
+ //= require_tree .
@@ -0,0 +1,2 @@
1
+ // Place all the behaviors and hooks related to the matching controller here.
2
+ // All this logic will automatically be available in application.js.
@@ -0,0 +1,7 @@
1
+ /*
2
+ * This is a manifest file that'll automatically include all the stylesheets available in this directory
3
+ * and any sub-directories. You're free to add application-wide styles to this file and they'll appear at
4
+ * the top of the compiled file, but it's generally better to create a new file per style scope.
5
+ *= require_self
6
+ *= require_tree .
7
+ */
@@ -0,0 +1,4 @@
1
+ /*
2
+ Place all the styles related to the matching controller here.
3
+ They will automatically be included in application.css.
4
+ */
@@ -0,0 +1,3 @@
1
+ class ApplicationController < ActionController::Base
2
+ protect_from_forgery
3
+ end