feedbackable 0.0.4

Sign up to get free protection for your applications and to get access to all the features.
@@ -0,0 +1,4 @@
1
+ *.gem
2
+ .bundle
3
+ Gemfile.lock
4
+ pkg/*
data/Gemfile ADDED
@@ -0,0 +1,4 @@
1
+ source "http://rubygems.org"
2
+
3
+ # Specify your gem's dependencies in feedbackable.gemspec
4
+ gemspec
@@ -0,0 +1,29 @@
1
+ Installation:
2
+
3
+ 1) Add gem to your Gemfile, as:
4
+ gem 'feedbackable'
5
+ then run
6
+ bundle install
7
+
8
+ 2) Your email settings must be preconfigured before using feedbackable gem
9
+
10
+ 3) Run generators:
11
+ rails g feedbackable:install
12
+ rake db:migrate
13
+ rails g feedbackable:views
14
+
15
+ 4) Add admin_email configuration parameter to your application.rb:
16
+ config.feedbackable.admin_email = 'your@admin_email.com'
17
+ 5) Add to your application.js
18
+ //= require jqmodal
19
+ //= require init
20
+ To your application.css
21
+ *= require jqmodal
22
+ 6) Add to your model:
23
+ has_feedback
24
+ And to your view:
25
+ <a href="#" class="jqModal">Add feedback</a>
26
+ <div class="jqmWindow" id="dialog">
27
+ <a href="#" class="jqmClose">X</a>
28
+ <%= render :partial => 'feedbacks/form', :locals => { :feedbackable => @your_model_instance } %>
29
+ </div>
@@ -0,0 +1 @@
1
+ require "bundler/gem_tasks"
@@ -0,0 +1,24 @@
1
+ class FeedbacksController < ApplicationController
2
+
3
+ def create
4
+ pf = params[:feedback]
5
+ @feedback = Feedback.new(
6
+ :subject => pf[:subject],
7
+ :feed_text => pf[:feed_text],
8
+ :contact_email => pf[:contact_email]
9
+ )
10
+ @feedback.feedbackable_type = pf[:feedbackable_type]
11
+ @feedback.feedbackable_id = pf[:feedbackable_id]
12
+
13
+ respond_to do |format|
14
+ if @feedback.save
15
+ format.js
16
+ # Need some load mask in the future
17
+ UserMailer.feedback_send(pf[:subject], pf[:feed_text], pf[:contact_email]).deliver
18
+ else
19
+ format.js { render :partial => 'errors' }
20
+ end
21
+ end
22
+ end
23
+
24
+ end
File without changes
@@ -0,0 +1,8 @@
1
+ class UserMailer < ActionMailer::Base
2
+ def feedback_send(subject, body, from)
3
+ @subject = subject
4
+ @body = body
5
+ @from = from
6
+ mail(:to => Feedbackable::config.admin_email, :from => from, :subject => "You've got new feedback!")
7
+ end
8
+ end
@@ -0,0 +1,10 @@
1
+ class Feedback < ActiveRecord::Base
2
+
3
+ belongs_to :feedbackable, :polymorphic => true
4
+
5
+ attr_accessible :subject, :feed_text, :contact_email
6
+ validates_presence_of :subject, :feed_text, :feedbackable, :presence => true
7
+ validates :contact_email, :presence => true,
8
+ :length => {:minimum => 3, :maximum => 254},
9
+ :format => {:with => /^([^@\s]+)@((?:[-a-z0-9]+\.)+[a-z]{2,})$/i}
10
+ end
@@ -0,0 +1 @@
1
+ $("#new_feedback-notice").html('<div><%= escape_javascript(@feedback.errors.map {|k,v| "#{k.to_s.titleize} #{v}"}.join('<br/>')).html_safe %></div>');
@@ -0,0 +1,26 @@
1
+ <%= form_for Feedback.new, :remote => true do |f| %>
2
+ <div id='new_feedback-notice'></div>
3
+
4
+ <div class="field">
5
+ <%= f.label :subject %>:<br />
6
+ <%= f.text_field :subject %>
7
+ </div>
8
+
9
+ <div class="field">
10
+ <%= f.label :contact_email %>:<br />
11
+ <%= f.text_field :contact_email %>
12
+ </div>
13
+
14
+ <div class="field">
15
+ <%= f.label :feed_text %>:<br />
16
+ <%= f.text_area:feed_text %>
17
+ </div>
18
+
19
+ <%= f.hidden_field :feedbackable_type, :value => feedbackable.class.name %><br />
20
+ <%= f.hidden_field :feedbackable_id , :value => feedbackable.id %><br />
21
+
22
+ <div class="actions">
23
+ <%= f.submit "Send feedback" %>
24
+ </div>
25
+
26
+ <% end %>
@@ -0,0 +1,3 @@
1
+ $("#new_feedback")[0].reset();
2
+ $('#dialog').jqmHide();
3
+ alert("Thanks, your feedback has been sent");
@@ -0,0 +1,8 @@
1
+ <h3>Hi!</h3>
2
+ <p><i>New feedback:</i></p>
3
+ <b>From:</b>
4
+ <%= @from %><br/>
5
+ <b>Subject:</b>
6
+ <%= @subject %><br/>
7
+ <b>Feedback text:</b>
8
+ <%= @body %>
@@ -0,0 +1,5 @@
1
+ Rails.application.routes.draw do
2
+
3
+ resources :feedbacks
4
+
5
+ end
@@ -0,0 +1,23 @@
1
+ # -*- encoding: utf-8 -*-
2
+ $:.push File.expand_path("../lib", __FILE__)
3
+ require "feedbackable/version"
4
+
5
+ Gem::Specification.new do |s|
6
+ s.name = "feedbackable"
7
+ s.version = Feedbackable::VERSION
8
+ s.authors = ["Marat Galiev"]
9
+ s.email = ["kazanlug@gmail.com"]
10
+ s.homepage = "http://www.smartapps.ru"
11
+ s.summary = %q{Add feedback to your models}
12
+ s.description = %q{Adding feedback action to model}
13
+
14
+ s.rubyforge_project = "feedbackable"
15
+
16
+ s.files = `git ls-files`.split("\n")
17
+ s.test_files = `git ls-files -- {test,spec,features}/*`.split("\n")
18
+ s.executables = `git ls-files -- bin/*`.split("\n").map{ |f| File.basename(f) }
19
+ s.require_paths = ["lib"]
20
+
21
+ # specify any dependencies here; for example:
22
+ s.add_development_dependency "rspec"
23
+ end
@@ -0,0 +1,26 @@
1
+ require "feedbackable/version"
2
+ require "feedbackable/railtie"
3
+ require "feedbackable/engine"
4
+ require "feedbackable/active_record_extension"
5
+
6
+ module Feedbackable
7
+
8
+ def self.configure(&block)
9
+ yield @config ||= Feedbackable::Configuration.new
10
+ end
11
+
12
+ def self.config
13
+ @config
14
+ end
15
+
16
+ class Configuration #:nodoc:
17
+ include ActiveSupport::Configurable
18
+ config_accessor :admin_email
19
+ def param_name
20
+ config.param_name.respond_to?(:call) ? config.param_name.call() : config.param_name
21
+ end
22
+ end
23
+
24
+ ActiveRecord::Base.extend(Feedbackable::ActiveRecordExtension)
25
+
26
+ end
@@ -0,0 +1,7 @@
1
+ module Feedbackable
2
+ module ActiveRecordExtension
3
+ def has_feedback(options = {})
4
+ # some options must be here, like feedbacks table name etc
5
+ end
6
+ end
7
+ end
@@ -0,0 +1,4 @@
1
+ module Feedbackable
2
+ class Engine < ::Rails::Engine
3
+ end
4
+ end
@@ -0,0 +1,14 @@
1
+ require 'rails'
2
+
3
+ module Feedbackable
4
+ class Railtie < ::Rails::Railtie
5
+ config.feedbackable = ActiveSupport::OrderedOptions.new
6
+
7
+ initializer "feedbackable.configure" do |app|
8
+ Feedbackable.configure do |config|
9
+ config.admin_email = app.config.feedbackable[:admin_email] || 'admin@example.com'
10
+ end
11
+
12
+ end
13
+ end
14
+ end
@@ -0,0 +1,3 @@
1
+ module Feedbackable
2
+ VERSION = "0.0.4"
3
+ end
@@ -0,0 +1,42 @@
1
+ require 'rails/generators'
2
+ require 'rails/generators/migration'
3
+
4
+ module Feedbackable
5
+ module Generators
6
+ class InstallGenerator < Rails::Generators::Base
7
+ include Rails::Generators::Migration
8
+
9
+ source_root File.expand_path("../templates", __FILE__)
10
+
11
+ desc "Generates migration for Feebacker"
12
+
13
+ def self.orm
14
+ Rails::Generators.options[:rails][:orm]
15
+ end
16
+
17
+ def self.orm_has_migration?
18
+ [:active_record].include? orm
19
+ end
20
+
21
+ def self.next_migration_number(dirname)
22
+ if ActiveRecord::Base.timestamped_migrations
23
+ migration_number = Time.now.utc.strftime("%Y%m%d%H%M%S").to_i
24
+ migration_number += 1
25
+ migration_number.to_s
26
+ else
27
+ "%.3d" % (current_migration_number(dirname) + 1)
28
+ end
29
+ end
30
+
31
+ def copy_migration
32
+ migration_template 'install.rb', 'db/migrate/install_feedbackable.rb'
33
+ end
34
+
35
+ def add_assets
36
+ ad = File.expand_path("../../../../vendor/assets", __FILE__)
37
+ directory ad, "vendor/assets"
38
+ end
39
+
40
+ end
41
+ end
42
+ end
@@ -0,0 +1,18 @@
1
+ class InstallFeedbackable < ActiveRecord::Migration
2
+ def self.up
3
+ create_table :feedbacks do |t|
4
+ t.string :subject, :limit => 20
5
+ t.text :feed_text
6
+ t.references :feedbackable, :polymorphic => true
7
+ t.string :contact_email
8
+ t.timestamps
9
+ end
10
+
11
+ add_index :feedbacks, :feedbackable_type
12
+ add_index :feedbacks, :feedbackable_id
13
+ end
14
+
15
+ def self.down
16
+ drop_table :feedbacks
17
+ end
18
+ end
@@ -0,0 +1,17 @@
1
+ require 'rails/generators'
2
+
3
+ module Feedbackable
4
+ module Generators
5
+ class ViewsGenerator < Rails::Generators::Base
6
+ source_root File.expand_path('../../../../app/views', __FILE__)
7
+
8
+ def copy_or_fetch
9
+ filename_pattern = File.join self.class.source_root, "*"
10
+ Dir.glob(filename_pattern).map {|f| File.basename f}.each do |f|
11
+ directory f.to_s, "app/views/#{f}"
12
+ end
13
+ end
14
+
15
+ end
16
+ end
17
+ end
@@ -0,0 +1 @@
1
+ require 'feedbackable'
@@ -0,0 +1,38 @@
1
+ require 'active_record'
2
+ require 'action_controller/railtie'
3
+
4
+ # database
5
+ ActiveRecord::Base.configurations = {'test' => {:adapter => 'mysql2', :database => 'marat_development', :username => "root", :password => "777777"}}
6
+ # ActiveRecord::Base.configurations = {'test' => {:adapter => 'sqlite3', :database => ':memory:'}}
7
+ ActiveRecord::Base.establish_connection('test')
8
+
9
+ # config
10
+ app = Class.new(Rails::Application)
11
+ app.config.secret_token = "3b7cd727ee24e8444053437c36cc66c4"
12
+ app.config.session_store :cookie_store, :key => "_myapp_session"
13
+ app.config.active_support.deprecation = :log
14
+ app.initialize!
15
+
16
+ # models
17
+ class Order < ActiveRecord::Base
18
+ has_feedback
19
+ end
20
+
21
+ # routes
22
+ app.routes.draw do
23
+ resources :feedbacks
24
+ end
25
+
26
+ #migrations
27
+ ActiveRecord::Base.silence do
28
+ ActiveRecord::Migration.verbose = false
29
+ ActiveRecord::Schema.define :version => 0 do
30
+ create_table "feedbacks", :force => true do |t|
31
+ t.string :subject, :limit => 20
32
+ t.text :feed_text
33
+ t.references :feedbackable, :polymorphic => true
34
+ t.string :contact_email
35
+ t.timestamps
36
+ end
37
+ end
38
+ end
@@ -0,0 +1,6 @@
1
+ # Simulate a gem providing a subclass of ActiveRecord::Base before the Railtie is loaded.
2
+
3
+ require 'active_record'
4
+
5
+ class GemDefinedModel < ActiveRecord::Base
6
+ end
@@ -0,0 +1,21 @@
1
+ require 'spec_helper'
2
+
3
+ describe Feedback do
4
+
5
+ it "should create valid feedback object" do
6
+ order = Order.new
7
+ order.save
8
+ feedback = Feedback.new
9
+ feedback.subject = "Test subject"
10
+ feedback.feed_text = "Cool order!"
11
+ feedback.contact_email = 'from@test.com'
12
+ feedback.feedbackable = order
13
+ feedback.save.should be true
14
+ end
15
+
16
+ it "should not create feedback without data" do
17
+ feedback = Feedback.new
18
+ feedback.save.should be false
19
+ end
20
+
21
+ end
@@ -0,0 +1,10 @@
1
+ $LOAD_PATH.unshift(File.join(File.dirname(__FILE__), '..', 'lib'))
2
+ $LOAD_PATH.unshift(File.dirname(__FILE__))
3
+
4
+
5
+ require 'rails'
6
+ require 'active_record'
7
+ require 'feedbackable'
8
+ require File.dirname(__FILE__) << '/../app/models/feedback'
9
+
10
+ require 'fake_app'
@@ -0,0 +1,6 @@
1
+ $().ready(function() {
2
+ $('#dialog').jqm();
3
+ $('.jqmClose').on('click',function() {
4
+ $("#new_feedback-notice").html('');
5
+ });
6
+ });
@@ -0,0 +1,69 @@
1
+ /*
2
+ * jqModal - Minimalist Modaling with jQuery
3
+ * (http://dev.iceburg.net/jquery/jqModal/)
4
+ *
5
+ * Copyright (c) 2007,2008 Brice Burgess <bhb@iceburg.net>
6
+ * Dual licensed under the MIT and GPL licenses:
7
+ * http://www.opensource.org/licenses/mit-license.php
8
+ * http://www.gnu.org/licenses/gpl.html
9
+ *
10
+ * $Version: 03/01/2009 +r14
11
+ */
12
+ (function($) {
13
+ $.fn.jqm=function(o){
14
+ var p={
15
+ overlay: 50,
16
+ overlayClass: 'jqmOverlay',
17
+ closeClass: 'jqmClose',
18
+ trigger: '.jqModal',
19
+ ajax: F,
20
+ ajaxText: '',
21
+ target: F,
22
+ modal: F,
23
+ toTop: F,
24
+ onShow: F,
25
+ onHide: F,
26
+ onLoad: F
27
+ };
28
+ return this.each(function(){if(this._jqm)return H[this._jqm].c=$.extend({},H[this._jqm].c,o);s++;this._jqm=s;
29
+ H[s]={c:$.extend(p,$.jqm.params,o),a:F,w:$(this).addClass('jqmID'+s),s:s};
30
+ if(p.trigger)$(this).jqmAddTrigger(p.trigger);
31
+ });};
32
+
33
+ $.fn.jqmAddClose=function(e){return hs(this,e,'jqmHide');};
34
+ $.fn.jqmAddTrigger=function(e){return hs(this,e,'jqmShow');};
35
+ $.fn.jqmShow=function(t){return this.each(function(){t=t||window.event;$.jqm.open(this._jqm,t);});};
36
+ $.fn.jqmHide=function(t){return this.each(function(){t=t||window.event;$.jqm.close(this._jqm,t)});};
37
+
38
+ $.jqm = {
39
+ hash:{},
40
+ open:function(s,t){var h=H[s],c=h.c,cc='.'+c.closeClass,z=(parseInt(h.w.css('z-index'))),z=(z>0)?z:3000,o=$('<div></div>').css({height:'100%',width:'100%',position:'fixed',left:0,top:0,'z-index':z-1,opacity:c.overlay/100});if(h.a)return F;h.t=t;h.a=true;h.w.css('z-index',z);
41
+ if(c.modal) {if(!A[0])L('bind');A.push(s);}
42
+ else if(c.overlay > 0)h.w.jqmAddClose(o);
43
+ else o=F;
44
+
45
+ h.o=(o)?o.addClass(c.overlayClass).prependTo('body'):F;
46
+ if(ie6){$('html,body').css({height:'100%',width:'100%'});if(o){o=o.css({position:'absolute'})[0];for(var y in {Top:1,Left:1})o.style.setExpression(y.toLowerCase(),"(_=(document.documentElement.scroll"+y+" || document.body.scroll"+y+"))+'px'");}}
47
+
48
+ if(c.ajax) {var r=c.target||h.w,u=c.ajax,r=(typeof r == 'string')?$(r,h.w):$(r),u=(u.substr(0,1) == '@')?$(t).attr(u.substring(1)):u;
49
+ r.html(c.ajaxText).load(u,function(){if(c.onLoad)c.onLoad.call(this,h);if(cc)h.w.jqmAddClose($(cc,h.w));e(h);});}
50
+ else if(cc)h.w.jqmAddClose($(cc,h.w));
51
+
52
+ if(c.toTop&&h.o)h.w.before('<span id="jqmP'+h.w[0]._jqm+'"></span>').insertAfter(h.o);
53
+ (c.onShow)?c.onShow(h):h.w.show();e(h);return F;
54
+ },
55
+ close:function(s){var h=H[s];if(!h.a)return F;h.a=F;
56
+ if(A[0]){A.pop();if(!A[0])L('unbind');}
57
+ if(h.c.toTop&&h.o)$('#jqmP'+h.w[0]._jqm).after(h.w).remove();
58
+ if(h.c.onHide)h.c.onHide(h);else{h.w.hide();if(h.o)h.o.remove();} return F;
59
+ },
60
+ params:{}};
61
+ var s=0,H=$.jqm.hash,A=[],ie6=$.browser.msie&&($.browser.version == "6.0"),F=false,
62
+ i=$('<iframe src="javascript:false;document.write(\'\');" class="jqm"></iframe>').css({opacity:0}),
63
+ e=function(h){if(ie6)if(h.o)h.o.html('<p style="width:100%;height:100%"/>').prepend(i);else if(!$('iframe.jqm',h.w)[0])h.w.prepend(i); f(h);},
64
+ f=function(h){try{$(':input:visible',h.w)[0].focus();}catch(_){}},
65
+ L=function(t){$()[t]("keypress",m)[t]("keydown",m)[t]("mousedown",m);},
66
+ m=function(e){var h=H[A[A.length-1]],r=(!$(e.target).parents('.jqmID'+h.s)[0]);if(r)f(h);return !r;},
67
+ hs=function(w,t,c){return w.each(function(){var s=this._jqm;$(t).each(function() {
68
+ if(!this[c]){this[c]=[];$(this).click(function(){for(var i in {jqmShow:1,jqmHide:1})for(var s in this[i])if(H[this[i][s]])H[this[i][s]].w[i](this);return F;});}this[c].push(s);});});};
69
+ })(jQuery);
@@ -0,0 +1,37 @@
1
+ /* jqModal base Styling courtesy of;
2
+ Brice Burgess <bhb@iceburg.net> */
3
+
4
+ /* The Window's CSS z-index value is respected (takes priority). If none is supplied,
5
+ the Window's z-index value will be set to 3000 by default (via jqModal.js). */
6
+
7
+ .jqmWindow {
8
+ display: none;
9
+
10
+ position: fixed;
11
+ top: 17%;
12
+ left: 50%;
13
+
14
+ margin-left: -300px;
15
+ width: 600px;
16
+
17
+ background-color: #EEE;
18
+ color: #333;
19
+ border: 1px solid black;
20
+ padding: 12px;
21
+ }
22
+
23
+ .jqmOverlay { background-color: #000; }
24
+
25
+ /* Background iframe styling for IE6. Prevents ActiveX bleed-through (<select> form elements, etc.) */
26
+ * iframe.jqm {position:absolute;top:0;left:0;z-index:-1;
27
+ width: expression(this.parentNode.offsetWidth+'px');
28
+ height: expression(this.parentNode.offsetHeight+'px');
29
+ }
30
+
31
+ /* Fixed posistioning emulation for IE6
32
+ Star selector used to hide definition from browsers other than IE6
33
+ For valid CSS, use a conditional include instead */
34
+ * html .jqmWindow {
35
+ position: absolute;
36
+ top: expression((document.documentElement.scrollTop || document.body.scrollTop) + Math.round(17 * (document.documentElement.offsetHeight || document.body.clientHeight) / 100) + 'px');
37
+ }
metadata ADDED
@@ -0,0 +1,86 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: feedbackable
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.0.4
5
+ prerelease:
6
+ platform: ruby
7
+ authors:
8
+ - Marat Galiev
9
+ autorequire:
10
+ bindir: bin
11
+ cert_chain: []
12
+ date: 2012-03-19 00:00:00.000000000 Z
13
+ dependencies:
14
+ - !ruby/object:Gem::Dependency
15
+ name: rspec
16
+ requirement: &80727290 !ruby/object:Gem::Requirement
17
+ none: false
18
+ requirements:
19
+ - - ! '>='
20
+ - !ruby/object:Gem::Version
21
+ version: '0'
22
+ type: :development
23
+ prerelease: false
24
+ version_requirements: *80727290
25
+ description: Adding feedback action to model
26
+ email:
27
+ - kazanlug@gmail.com
28
+ executables: []
29
+ extensions: []
30
+ extra_rdoc_files: []
31
+ files:
32
+ - .gitignore
33
+ - Gemfile
34
+ - README.md
35
+ - Rakefile
36
+ - app/controllers/feedbacks_controller.rb
37
+ - app/mailers/.gitkeep
38
+ - app/mailers/user_mailer.rb
39
+ - app/models/feedback.rb
40
+ - app/views/feedbacks/_errors.js.erb
41
+ - app/views/feedbacks/_form.html.erb
42
+ - app/views/feedbacks/create.js.erb
43
+ - app/views/user_mailer/feedback_send.html.erb
44
+ - config/routes.rb
45
+ - feedbackable.gemspec
46
+ - lib/feedbackable.rb
47
+ - lib/feedbackable/active_record_extension.rb
48
+ - lib/feedbackable/engine.rb
49
+ - lib/feedbackable/railtie.rb
50
+ - lib/feedbackable/version.rb
51
+ - lib/generators/feedbackable/install_generator.rb
52
+ - lib/generators/feedbackable/templates/install.rb
53
+ - lib/generators/feedbackable/views_generator.rb
54
+ - lib/init.rb
55
+ - spec/fake_app.rb
56
+ - spec/fake_gem.rb
57
+ - spec/feedbackable/feedback_test.rb
58
+ - spec/spec_helper.rb
59
+ - vendor/assets/javascripts/init.js
60
+ - vendor/assets/javascripts/jqmodal.js
61
+ - vendor/assets/stylesheets/jqmodal.css
62
+ homepage: http://www.smartapps.ru
63
+ licenses: []
64
+ post_install_message:
65
+ rdoc_options: []
66
+ require_paths:
67
+ - lib
68
+ required_ruby_version: !ruby/object:Gem::Requirement
69
+ none: false
70
+ requirements:
71
+ - - ! '>='
72
+ - !ruby/object:Gem::Version
73
+ version: '0'
74
+ required_rubygems_version: !ruby/object:Gem::Requirement
75
+ none: false
76
+ requirements:
77
+ - - ! '>='
78
+ - !ruby/object:Gem::Version
79
+ version: '0'
80
+ requirements: []
81
+ rubyforge_project: feedbackable
82
+ rubygems_version: 1.8.15
83
+ signing_key:
84
+ specification_version: 3
85
+ summary: Add feedback to your models
86
+ test_files: []