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.
- data/MIT-LICENSE +20 -0
- data/README.md +52 -0
- data/Rakefile +39 -0
- data/app/assets/javascripts/noodnik/application.js +9 -0
- data/app/assets/javascripts/noodnik/nags.erb.coffee +12 -0
- data/app/assets/stylesheets/noodnik/application.css +7 -0
- data/app/assets/stylesheets/noodnik/nags.css +4 -0
- data/app/controllers/noodnik/application_controller.rb +4 -0
- data/app/controllers/noodnik/nags_controller.rb +47 -0
- data/app/helpers/noodnik/application_helper.rb +4 -0
- data/app/helpers/noodnik/nags_helper.rb +51 -0
- data/app/models/noodnik/nag.rb +4 -0
- data/app/views/layouts/application.html.erb +14 -0
- data/app/views/layouts/noodnik/application.html.erb +14 -0
- data/config/routes.rb +6 -0
- data/db/migrate/20110813042447_create_noodnik_nags.rb +11 -0
- data/db/migrate/20110813065655_add_user_id_to_nags.rb +5 -0
- data/db/migrate/20110813072011_add_topic_to_nags.rb +5 -0
- data/lib/noodnik.rb +10 -0
- data/lib/noodnik/engine.rb +5 -0
- data/lib/noodnik/railtie.rb +7 -0
- data/lib/tasks/noodnik_tasks.rake +4 -0
- data/test/dummy/Guardfile +29 -0
- data/test/dummy/Rakefile +7 -0
- data/test/dummy/app/assets/javascripts/application.js +9 -0
- data/test/dummy/app/assets/javascripts/home.js +2 -0
- data/test/dummy/app/assets/stylesheets/application.css +7 -0
- data/test/dummy/app/assets/stylesheets/home.css +4 -0
- data/test/dummy/app/controllers/application_controller.rb +3 -0
- data/test/dummy/app/controllers/home_controller.rb +4 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/helpers/home_helper.rb +2 -0
- data/test/dummy/app/views/home/index.html.erb +10 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/config/application.rb +47 -0
- data/test/dummy/config/boot.rb +10 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +27 -0
- data/test/dummy/config/environments/production.rb +51 -0
- data/test/dummy/config/environments/test.rb +39 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/inflections.rb +10 -0
- data/test/dummy/config/initializers/mime_types.rb +5 -0
- data/test/dummy/config/initializers/noodnik.rb +3 -0
- data/test/dummy/config/initializers/secret_token.rb +7 -0
- data/test/dummy/config/initializers/session_store.rb +8 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +12 -0
- data/test/dummy/config/locales/en.yml +5 -0
- data/test/dummy/config/routes.rb +6 -0
- data/test/dummy/db/development.sqlite3 +0 -0
- data/test/dummy/db/migrate/20110813070108_create_noodnik_nags.rb +11 -0
- data/test/dummy/db/migrate/20110813070109_add_user_id_to_nags.rb +5 -0
- data/test/dummy/db/migrate/20110813072040_add_topic_to_nags.rb +5 -0
- data/test/dummy/db/schema.rb +24 -0
- data/test/dummy/db/test.sqlite3 +0 -0
- data/test/dummy/log/development.log +10612 -0
- data/test/dummy/log/test.log +26879 -0
- data/test/dummy/public/404.html +26 -0
- data/test/dummy/public/422.html +26 -0
- data/test/dummy/public/500.html +26 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/dummy/script/rails +6 -0
- data/test/dummy/spec/controllers/noodnik_nags_controller_spec.rb +121 -0
- data/test/dummy/spec/helpers/nags_helper_spec.rb +130 -0
- data/test/dummy/spec/spec_helper.rb +74 -0
- data/test/dummy/tmp/cache/assets/C7E/8B0/sprockets%2F1a26023e8a9ac4a7477d84b980511817 +0 -0
- data/test/dummy/tmp/cache/assets/C9B/0A0/sprockets%2Fc184c48971885500d230701da1c1b0fb +0 -0
- data/test/dummy/tmp/cache/assets/CC7/610/sprockets%2Fd85d521b5f5052344563eef989889de0 +0 -0
- data/test/dummy/tmp/cache/assets/CDF/870/sprockets%2Fb878faf942403e313a5b103e5d80488e +0 -0
- data/test/dummy/tmp/cache/assets/CF8/F90/sprockets%2F7c5977cba909e2f75909ced513698d03 +0 -0
- data/test/dummy/tmp/cache/assets/D32/A10/sprockets%2F13fe41fee1fe35b49d145bcc06610705 +0 -0
- data/test/dummy/tmp/cache/assets/D54/ED0/sprockets%2F71c9fa01091d432b131da3bb73faf3d4 +0 -0
- data/test/dummy/tmp/cache/assets/D72/AD0/sprockets%2Fc4434fe69351b83a77ebaca291931fee +0 -0
- data/test/dummy/tmp/cache/assets/D7E/3B0/sprockets%2F942cff9f3e4e8a45ae06090e3d883e7b +0 -0
- data/test/dummy/tmp/cache/assets/D84/210/sprockets%2Fabd0103ccec2b428ac62c94e4c40b384 +0 -0
- data/test/dummy/tmp/cache/assets/D98/8B0/sprockets%2Fedbef6e0d0a4742346cf479f2c522eb0 +0 -0
- data/test/dummy/tmp/cache/assets/E04/890/sprockets%2F2f5173deea6c795b8fdde723bb4b63af +0 -0
- data/test/dummy/tmp/cache/assets/E38/C70/sprockets%2F7e7577dbecc6d06993be00b9daabcd9f +0 -0
- data/test/dummy/tmp/pids/server.pid +1 -0
- metadata +184 -0
data/MIT-LICENSE
ADDED
|
@@ -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.
|
data/README.md
ADDED
|
@@ -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
|
data/Rakefile
ADDED
|
@@ -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,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,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
|
data/config/routes.rb
ADDED
data/lib/noodnik.rb
ADDED
|
@@ -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
|
data/test/dummy/Rakefile
ADDED
|
@@ -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,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
|
+
*/
|