minimum_viable_product 0.0.8
Sign up to get free protection for your applications and to get access to all the features.
- checksums.yaml +7 -0
- data/MIT-LICENSE +20 -0
- data/README.rdoc +3 -0
- data/Rakefile +37 -0
- data/app/assets/javascripts/minimum_viable_product/application.js.erb +38 -0
- data/app/assets/javascripts/minimum_viable_product/init/controllers.js +23 -0
- data/app/assets/stylesheets/minimum_viable_product/application.scss +23 -0
- data/app/assets/stylesheets/minimum_viable_product/components/buttons.scss +20 -0
- data/app/assets/stylesheets/minimum_viable_product/components/fonts.scss +15 -0
- data/app/assets/stylesheets/minimum_viable_product/components/spacing.scss +13 -0
- data/app/assets/stylesheets/minimum_viable_product/components/typography.scss +40 -0
- data/app/assets/stylesheets/minimum_viable_product/init/_reset.scss +43 -0
- data/app/assets/stylesheets/minimum_viable_product/init/layout.scss +42 -0
- data/app/assets/stylesheets/minimum_viable_product/layout/basic.scss +6 -0
- data/app/assets/stylesheets/minimum_viable_product/layout/carousel.scss +128 -0
- data/app/assets/stylesheets/minimum_viable_product/layout/cover.scss +111 -0
- data/app/assets/stylesheets/minimum_viable_product/pages/sample.scss +14 -0
- data/app/assets/stylesheets/minimum_viable_product/skin/style.scss +8 -0
- data/app/controllers/minimum_viable_product/analytics_controller.rb +14 -0
- data/app/controllers/minimum_viable_product/concerns/analytics_concern.rb +77 -0
- data/app/controllers/minimum_viable_product/concerns/seo_concern.rb +25 -0
- data/app/controllers/minimum_viable_product/concerns/session_concern.rb +40 -0
- data/app/controllers/minimum_viable_product/controller.rb +11 -0
- data/app/controllers/minimum_viable_product/seo_controller.rb +13 -0
- data/app/controllers/minimum_viable_product/styleguide_controller.rb +17 -0
- data/app/helpers/minimum_viable_product/analytics_helper.rb +4 -0
- data/app/helpers/minimum_viable_product/application_helper.rb +15 -0
- data/app/helpers/minimum_viable_product/bootstrap_helper.rb +38 -0
- data/app/helpers/minimum_viable_product/seo_helper.rb +4 -0
- data/app/helpers/minimum_viable_product/styleguide_helper.rb +4 -0
- data/app/models/concerns/slugification.rb +16 -0
- data/app/views/layouts/minimum_viable_product/_header.html.erb +48 -0
- data/app/views/layouts/minimum_viable_product/_instrumentation.html.erb +75 -0
- data/app/views/layouts/minimum_viable_product/_meta.html.erb +37 -0
- data/app/views/layouts/minimum_viable_product/application.html.erb +24 -0
- data/app/views/layouts/minimum_viable_product/basic.html.erb +10 -0
- data/app/views/layouts/minimum_viable_product/carousel.html.erb +13 -0
- data/app/views/layouts/minimum_viable_product/cover.html.erb +13 -0
- data/bin/mvp +68 -0
- data/config/initializers/canonical_host.rb +3 -0
- data/config/initializers/client_side_validations.rb +20 -0
- data/config/initializers/cloudinary.rb +6 -0
- data/config/initializers/geocoder.rb +9 -0
- data/config/initializers/iteration.rb +33 -0
- data/config/initializers/rollbar.rb +59 -0
- data/config/initializers/segment.rb +5 -0
- data/config/initializers/simple_form.rb +165 -0
- data/config/initializers/simple_form_bootstrap.rb +149 -0
- data/config/initializers/spoof_ip.rb +33 -0
- data/config/initializers/ssl.rb +3 -0
- data/config/routes.rb +16 -0
- data/lib/minimum_viable_product/engine.rb +20 -0
- data/lib/minimum_viable_product/ext/nil.rb +5 -0
- data/lib/minimum_viable_product/ext/string.rb +7 -0
- data/lib/minimum_viable_product/version.rb +3 -0
- data/lib/minimum_viable_product.rb +6 -0
- data/lib/tasks/sitemap.rake +42 -0
- data/test/dummy/README.rdoc +28 -0
- data/test/dummy/Rakefile +6 -0
- data/test/dummy/app/assets/javascripts/application.js +13 -0
- data/test/dummy/app/assets/stylesheets/application.css +15 -0
- data/test/dummy/app/controllers/application_controller.rb +5 -0
- data/test/dummy/app/helpers/application_helper.rb +2 -0
- data/test/dummy/app/views/layouts/application.html.erb +14 -0
- data/test/dummy/bin/bundle +3 -0
- data/test/dummy/bin/rails +4 -0
- data/test/dummy/bin/rake +4 -0
- data/test/dummy/bin/setup +29 -0
- data/test/dummy/config/application.rb +26 -0
- data/test/dummy/config/boot.rb +5 -0
- data/test/dummy/config/database.yml +25 -0
- data/test/dummy/config/environment.rb +5 -0
- data/test/dummy/config/environments/development.rb +41 -0
- data/test/dummy/config/environments/production.rb +79 -0
- data/test/dummy/config/environments/test.rb +42 -0
- data/test/dummy/config/initializers/assets.rb +11 -0
- data/test/dummy/config/initializers/backtrace_silencers.rb +7 -0
- data/test/dummy/config/initializers/cookies_serializer.rb +3 -0
- data/test/dummy/config/initializers/filter_parameter_logging.rb +4 -0
- data/test/dummy/config/initializers/inflections.rb +16 -0
- data/test/dummy/config/initializers/mime_types.rb +4 -0
- data/test/dummy/config/initializers/session_store.rb +3 -0
- data/test/dummy/config/initializers/wrap_parameters.rb +14 -0
- data/test/dummy/config/locales/en.yml +23 -0
- data/test/dummy/config/routes.rb +4 -0
- data/test/dummy/config/secrets.yml +22 -0
- data/test/dummy/config.ru +4 -0
- data/test/dummy/public/404.html +67 -0
- data/test/dummy/public/422.html +67 -0
- data/test/dummy/public/500.html +66 -0
- data/test/dummy/public/favicon.ico +0 -0
- data/test/integration/navigation_test.rb +8 -0
- data/test/minimum_viable_product_test.rb +7 -0
- data/test/test_helper.rb +21 -0
- metadata +595 -0
@@ -0,0 +1,75 @@
|
|
1
|
+
<% if ENV['ROLLBAR_CLIENT_ACCESS_TOKEN'] %>
|
2
|
+
<script>
|
3
|
+
var _rollbarConfig = {
|
4
|
+
accessToken: '<%= ENV['ROLLBAR_CLIENT_ACCESS_TOKEN'] %>',
|
5
|
+
captureUncaught: true,
|
6
|
+
captureUnhandledRejections: false,
|
7
|
+
payload: {
|
8
|
+
environment: "production"
|
9
|
+
}
|
10
|
+
};
|
11
|
+
// Rollbar Snippet
|
12
|
+
!function(r){function e(t){if(o[t])return o[t].exports;var n=o[t]={exports:{},id:t,loaded:!1};return r[t].call(n.exports,n,n.exports,e),n.loaded=!0,n.exports}var o={};return e.m=r,e.c=o,e.p="",e(0)}([function(r,e,o){"use strict";var t=o(1).Rollbar,n=o(2);_rollbarConfig.rollbarJsUrl=_rollbarConfig.rollbarJsUrl||"https://d37gvrvc0wt4s1.cloudfront.net/js/v1.9/rollbar.min.js";var a=t.init(window,_rollbarConfig),i=n(a,_rollbarConfig);a.loadFull(window,document,!_rollbarConfig.async,_rollbarConfig,i)},function(r,e){"use strict";function o(r){return function(){try{return r.apply(this,arguments)}catch(e){try{console.error("[Rollbar]: Internal error",e)}catch(o){}}}}function t(r,e,o){window._rollbarWrappedError&&(o[4]||(o[4]=window._rollbarWrappedError),o[5]||(o[5]=window._rollbarWrappedError._rollbarContext),window._rollbarWrappedError=null),r.uncaughtError.apply(r,o),e&&e.apply(window,o)}function n(r){var e=function(){var e=Array.prototype.slice.call(arguments,0);t(r,r._rollbarOldOnError,e)};return e.belongsToShim=!0,e}function a(r){this.shimId=++c,this.notifier=null,this.parentShim=r,this._rollbarOldOnError=null}function i(r){var e=a;return o(function(){if(this.notifier)return this.notifier[r].apply(this.notifier,arguments);var o=this,t="scope"===r;t&&(o=new e(this));var n=Array.prototype.slice.call(arguments,0),a={shim:o,method:r,args:n,ts:new Date};return window._rollbarShimQueue.push(a),t?o:void 0})}function l(r,e){if(e.hasOwnProperty&&e.hasOwnProperty("addEventListener")){var o=e.addEventListener;e.addEventListener=function(e,t,n){o.call(this,e,r.wrap(t),n)};var t=e.removeEventListener;e.removeEventListener=function(r,e,o){t.call(this,r,e&&e._wrapped?e._wrapped:e,o)}}}var c=0;a.init=function(r,e){var t=e.globalAlias||"Rollbar";if("object"==typeof r[t])return r[t];r._rollbarShimQueue=[],r._rollbarWrappedError=null,e=e||{};var i=new a;return o(function(){if(i.configure(e),e.captureUncaught){i._rollbarOldOnError=r.onerror,r.onerror=n(i);var o,a,c="EventTarget,Window,Node,ApplicationCache,AudioTrackList,ChannelMergerNode,CryptoOperation,EventSource,FileReader,HTMLUnknownElement,IDBDatabase,IDBRequest,IDBTransaction,KeyOperation,MediaController,MessagePort,ModalWindow,Notification,SVGElementInstance,Screen,TextTrack,TextTrackCue,TextTrackList,WebSocket,WebSocketWorker,Worker,XMLHttpRequest,XMLHttpRequestEventTarget,XMLHttpRequestUpload".split(",");for(o=0;o<c.length;++o)a=c[o],r[a]&&r[a].prototype&&l(i,r[a].prototype)}return e.captureUnhandledRejections&&(i._unhandledRejectionHandler=function(r){var e=r.reason,o=r.promise,t=r.detail;!e&&t&&(e=t.reason,o=t.promise),i.unhandledRejection(e,o)},r.addEventListener("unhandledrejection",i._unhandledRejectionHandler)),r[t]=i,i})()},a.prototype.loadFull=function(r,e,t,n,a){var i=function(){var e;if(void 0===r._rollbarPayloadQueue){var o,t,n,i;for(e=new Error("rollbar.js did not load");o=r._rollbarShimQueue.shift();)for(n=o.args,i=0;i<n.length;++i)if(t=n[i],"function"==typeof t){t(e);break}}"function"==typeof a&&a(e)},l=!1,c=e.createElement("script"),d=e.getElementsByTagName("script")[0],p=d.parentNode;c.crossOrigin="",c.src=n.rollbarJsUrl,c.async=!t,c.onload=c.onreadystatechange=o(function(){if(!(l||this.readyState&&"loaded"!==this.readyState&&"complete"!==this.readyState)){c.onload=c.onreadystatechange=null;try{p.removeChild(c)}catch(r){}l=!0,i()}}),p.insertBefore(c,d)},a.prototype.wrap=function(r,e){try{var o;if(o="function"==typeof e?e:function(){return e||{}},"function"!=typeof r)return r;if(r._isWrap)return r;if(!r._wrapped){r._wrapped=function(){try{return r.apply(this,arguments)}catch(e){throw e._rollbarContext=o()||{},e._rollbarContext._wrappedSource=r.toString(),window._rollbarWrappedError=e,e}},r._wrapped._isWrap=!0;for(var t in r)r.hasOwnProperty(t)&&(r._wrapped[t]=r[t])}return r._wrapped}catch(n){return r}};for(var d="log,debug,info,warn,warning,error,critical,global,configure,scope,uncaughtError,unhandledRejection".split(","),p=0;p<d.length;++p)a.prototype[d[p]]=i(d[p]);r.exports={Rollbar:a,_rollbarWindowOnError:t}},function(r,e){"use strict";r.exports=function(r,e){return function(o){if(!o&&!window._rollbarInitialized){var t=window.RollbarNotifier,n=e||{},a=n.globalAlias||"Rollbar",i=window.Rollbar.init(n,r);i._processShimQueue(window._rollbarShimQueue||[]),window[a]=i,window._rollbarInitialized=!0,t.processPayloads()}}}}]);
|
13
|
+
// End Rollbar Snippet
|
14
|
+
</script>
|
15
|
+
<% end %>
|
16
|
+
|
17
|
+
<% unless cookies[MVP::AnalyticsConcern::INVISIBLE_SESSION_COOKIE].to_b == true %>
|
18
|
+
<% if ENV['FACEBOOK_TRACKING_ID'] %>
|
19
|
+
<script>
|
20
|
+
!function(f,b,e,v,n,t,s){if(f.fbq)return;n=f.fbq=function(){n.callMethod?
|
21
|
+
n.callMethod.apply(n,arguments):n.queue.push(arguments)};if(!f._fbq)f._fbq=n;
|
22
|
+
n.push=n;n.loaded=!0;n.version='2.0';n.queue=[];t=b.createElement(e);t.async=!0;
|
23
|
+
t.src=v;s=b.getElementsByTagName(e)[0];s.parentNode.insertBefore(t,s)}(window,
|
24
|
+
document,'script','https://connect.facebook.net/en_US/fbevents.js');
|
25
|
+
|
26
|
+
fbq('init', '<%= ENV['FACEBOOK_TRACKING_ID'] %>');
|
27
|
+
fbq('track', "PageView");</script>
|
28
|
+
<noscript><img height="1" width="1" style="display:none"
|
29
|
+
src="https://www.facebook.com/tr?id=<%= ENV['FACEBOOK_TRACKING_ID'] %>&ev=PageView&noscript=1"
|
30
|
+
/></noscript>
|
31
|
+
<!-- End Facebook Pixel Code -->
|
32
|
+
<% end %>
|
33
|
+
|
34
|
+
<% if ENV['SEGMENT_WRITE_KEY'] %>
|
35
|
+
<script type="text/javascript">
|
36
|
+
!function(){var analytics=window.analytics=window.analytics||[];if(!analytics.initialize)if(analytics.invoked)window.console&&console.error&&console.error("Segment snippet included twice.");else{analytics.invoked=!0;analytics.methods=["trackSubmit","trackClick","trackLink","trackForm","pageview","identify","reset","group","track","ready","alias","page","once","off","on"];analytics.factory=function(t){return function(){var e=Array.prototype.slice.call(arguments);e.unshift(t);analytics.push(e);return analytics}};for(var t=0;t<analytics.methods.length;t++){var e=analytics.methods[t];analytics[e]=analytics.factory(e)}analytics.load=function(t){var e=document.createElement("script");e.type="text/javascript";e.async=!0;e.src=("https:"===document.location.protocol?"https://":"http://")+"cdn.segment.com/analytics.js/v1/"+t+"/analytics.min.js";var n=document.getElementsByTagName("script")[0];n.parentNode.insertBefore(e,n)};analytics.SNIPPET_VERSION="3.1.0";
|
37
|
+
analytics.load('<%= ENV['SEGMENT_WRITE_KEY'] %>');
|
38
|
+
<% if current_user %>
|
39
|
+
analytics.identify('<%= analytics_id %>', {
|
40
|
+
email: '<%= current_user.email %>'
|
41
|
+
});
|
42
|
+
<% else %>
|
43
|
+
analytics.identify('<%= analytics_id %>');
|
44
|
+
<% end %>
|
45
|
+
analytics.page('<%= controller.controller_path %>.<%= controller.action_name %>', {
|
46
|
+
iteration: "<%= MVP::Iteration.version %>"
|
47
|
+
});
|
48
|
+
}}();
|
49
|
+
</script>
|
50
|
+
<% end %>
|
51
|
+
|
52
|
+
<% if ENV['GOOGLE_ANALYTICS_ID'] %>
|
53
|
+
<script>
|
54
|
+
(function(i,s,o,g,r,a,m){i['GoogleAnalyticsObject']=r;i[r]=i[r]||function(){
|
55
|
+
(i[r].q=i[r].q||[]).push(arguments)},i[r].l=1*new Date();a=s.createElement(o),
|
56
|
+
m=s.getElementsByTagName(o)[0];a.async=1;a.src=g;m.parentNode.insertBefore(a,m)
|
57
|
+
})(window,document,'script','https://www.google-analytics.com/analytics.js','ga');
|
58
|
+
ga('create', '<%= ENV['GOOGLE_ANALYTICS_ID'] %>', 'auto');
|
59
|
+
ga('send', 'pageview');
|
60
|
+
</script>
|
61
|
+
<% end %>
|
62
|
+
|
63
|
+
<% if ENV['HOTJAR_ID'] %>
|
64
|
+
<script>
|
65
|
+
(function(h,o,t,j,a,r){
|
66
|
+
h.hj=h.hj||function(){(h.hj.q=h.hj.q||[]).push(arguments)};
|
67
|
+
h._hjSettings={hjid:<%= ENV['HOTJAR_ID'] %>,hjsv:5};
|
68
|
+
a=o.getElementsByTagName('head')[0];
|
69
|
+
r=o.createElement('script');r.async=1;
|
70
|
+
r.src=t+h._hjSettings.hjid+j+h._hjSettings.hjsv;
|
71
|
+
a.appendChild(r);
|
72
|
+
})(window,document,'//static.hotjar.com/c/hotjar-','.js?sv=');
|
73
|
+
</script>
|
74
|
+
<% end %>
|
75
|
+
<% end %>
|
@@ -0,0 +1,37 @@
|
|
1
|
+
<meta charset=utf-8>
|
2
|
+
<meta content="IE=edge" http-equiv=X-UA-Compatible>
|
3
|
+
<meta content="width=device-width,initial-scale=1" name=viewport>
|
4
|
+
<% if page.description %>
|
5
|
+
<meta content="<%= page.description %>" name="description">
|
6
|
+
<% end %>
|
7
|
+
<% if page.author %>
|
8
|
+
<meta content="<%= page.author %>" name="author">
|
9
|
+
<% end %>
|
10
|
+
<title><%= page.title %></title>
|
11
|
+
|
12
|
+
<% if page.canonical_url %>
|
13
|
+
<link rel="canonical" href="<%= page.canonical_url %>" />
|
14
|
+
<% end %>
|
15
|
+
|
16
|
+
<% if page.noindex %>
|
17
|
+
<meta name="robots" content="noindex">
|
18
|
+
<% end %>
|
19
|
+
|
20
|
+
<% if page.og_url %>
|
21
|
+
<meta property="og:url" content="<%= page.og_url %>" />
|
22
|
+
<% end %>
|
23
|
+
<% if page.og_type %>
|
24
|
+
<meta property="og:type" content="<%= page.og_type %>" />
|
25
|
+
<% end %>
|
26
|
+
<% if page.og_title || page.title %>
|
27
|
+
<meta property="og:title" content="<%= page.og_title || page.title %>" />
|
28
|
+
<% end %>
|
29
|
+
<% if page.og_description || page.description %>
|
30
|
+
<meta property="og:description" content="<%= page.og_description || page.description %>" />
|
31
|
+
<% end %>
|
32
|
+
<% if page.og_image %>
|
33
|
+
<meta property="og:image" content="<%= page.og_image %>" />
|
34
|
+
<% end %>
|
35
|
+
<% if page.twitter_card %>
|
36
|
+
<meta name="twitter:card" content="<%= page.twitter_card %>" />
|
37
|
+
<% end %>
|
@@ -0,0 +1,24 @@
|
|
1
|
+
<!DOCTYPE HTML>
|
2
|
+
<html lang='en'>
|
3
|
+
<head>
|
4
|
+
<%= render('layouts/minimum_viable_product/meta').gsub(/\n/,'').html_safe %>
|
5
|
+
|
6
|
+
<%= stylesheet_link_tag 'application', media: 'all', 'data-turbolinks-track' => true %>
|
7
|
+
<link href='https://fonts.googleapis.com/css?family=Open+Sans:400,700|Roboto+Slab|Pacifico' rel='stylesheet' type='text/css'>
|
8
|
+
|
9
|
+
<!-- WARNING: Respond.js doesn't work if you view the page via file:// -->
|
10
|
+
<!--[if lt IE 9]> <script src="https://oss.maxcdn.com/html5shiv/3.7.2/html5shiv.min.js"></script> <script src="https://oss.maxcdn.com/respond/1.4.2/respond.min.js"></script> <![endif]-->
|
11
|
+
|
12
|
+
<%= javascript_include_tag 'application', 'data-turbolinks-track' => true %>
|
13
|
+
<%= csrf_meta_tags %>
|
14
|
+
|
15
|
+
<%= render 'layouts/minimum_viable_product/instrumentation' %>
|
16
|
+
</head>
|
17
|
+
<body id="<%= body_id %>" class="<%= body_classes %>" data-controller="<%= controller.controller_path %>" data-action="<%= controller.action_name %>" data-iteration="<%= MVP::Iteration.version %>">
|
18
|
+
<% if content_for? :body %>
|
19
|
+
<%= yield :body %>
|
20
|
+
<% else %>
|
21
|
+
<%= yield %>
|
22
|
+
<% end %>
|
23
|
+
</body>
|
24
|
+
</html>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<% page.classes = "body-carousel" %>
|
2
|
+
|
3
|
+
<%- content_for :body do %>
|
4
|
+
<div class="container">
|
5
|
+
<%= render 'layouts/minimum_viable_product/header' %>
|
6
|
+
</div>
|
7
|
+
<%= yield :carousel %>
|
8
|
+
<div class="container">
|
9
|
+
<%= yield %>
|
10
|
+
</div>
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
<%= render template: "layouts/minimum_viable_product/application" %>
|
@@ -0,0 +1,13 @@
|
|
1
|
+
<% page.classes = "body-cover" %>
|
2
|
+
|
3
|
+
<%- content_for :body do %>
|
4
|
+
<div class="site-wrapper">
|
5
|
+
<div class="site-wrapper-inner">
|
6
|
+
<div class="cover-container">
|
7
|
+
<%= yield %>
|
8
|
+
</div>
|
9
|
+
</div>
|
10
|
+
</div>
|
11
|
+
<% end %>
|
12
|
+
|
13
|
+
<%= render template: "layouts/minimum_viable_product/application" %>
|
data/bin/mvp
ADDED
@@ -0,0 +1,68 @@
|
|
1
|
+
#!/usr/bin/env ruby
|
2
|
+
|
3
|
+
command = ARGV[0]
|
4
|
+
|
5
|
+
error(%%
|
6
|
+
No command specified, please use one of the following
|
7
|
+
new: start a new project
|
8
|
+
update: update mvp inside current project
|
9
|
+
%) unless command
|
10
|
+
|
11
|
+
case command.downcase
|
12
|
+
when 'new'
|
13
|
+
project = if ARGV[1]
|
14
|
+
ARGV[1]
|
15
|
+
else
|
16
|
+
$stdin.reopen(File.open("/dev/tty", "r"))
|
17
|
+
print "Project name? "
|
18
|
+
STDIN.gets.chomp
|
19
|
+
end
|
20
|
+
|
21
|
+
error('Must specify a project name') if project == ""
|
22
|
+
|
23
|
+
slug = slugify(project)
|
24
|
+
|
25
|
+
puts
|
26
|
+
puts "\n== Cloning Base Project =="
|
27
|
+
run "git clone git@github.com:ian/mvp_rails.git #{slug}"
|
28
|
+
|
29
|
+
puts
|
30
|
+
puts "\n== Initializing Project =="
|
31
|
+
files = Dir.glob("#{slug}/**/*").select { |f| File.file?(f) }
|
32
|
+
files.each do |file_name|
|
33
|
+
text = File.read(file_name)
|
34
|
+
new_contents = text.gsub(/__PROJECT_NAME__/, project)
|
35
|
+
.gsub(/__PROJECT_NAME_SLUG__/, slug)
|
36
|
+
|
37
|
+
File.open(file_name, "w") {|file| file.puts new_contents }
|
38
|
+
end
|
39
|
+
run "cd #{slug} && rm -rf .git && git init"
|
40
|
+
|
41
|
+
run "cd #{slug}; bin/setup", "\n== Project Setup"
|
42
|
+
|
43
|
+
when 'update'
|
44
|
+
puts "TBD"
|
45
|
+
end
|
46
|
+
|
47
|
+
BEGIN {
|
48
|
+
def slugify(name)
|
49
|
+
slug = name.gsub(/'/, '').gsub(/[^a-z0-9]+/, '_')
|
50
|
+
slug.chop! if slug[-1] == '-'
|
51
|
+
slug
|
52
|
+
end
|
53
|
+
|
54
|
+
def error(message)
|
55
|
+
puts
|
56
|
+
puts "Error: #{message}"
|
57
|
+
exit 1
|
58
|
+
end
|
59
|
+
|
60
|
+
def run(cmd, message=nil)
|
61
|
+
if message
|
62
|
+
puts
|
63
|
+
puts message
|
64
|
+
end
|
65
|
+
|
66
|
+
system cmd
|
67
|
+
end
|
68
|
+
}
|
@@ -0,0 +1,20 @@
|
|
1
|
+
# ClientSideValidations Initializer
|
2
|
+
|
3
|
+
# Disabled validators. The uniqueness validator is disabled by default for security issues. Enable it on your own responsibility!
|
4
|
+
# ClientSideValidations::Config.disabled_validators = [:uniqueness]
|
5
|
+
|
6
|
+
# Uncomment to validate number format with current I18n locale
|
7
|
+
# ClientSideValidations::Config.number_format_with_locale = true
|
8
|
+
|
9
|
+
# Uncomment the following block if you want each input field to have the validation messages attached.
|
10
|
+
#
|
11
|
+
# Note: client_side_validation requires the error to be encapsulated within
|
12
|
+
# <label for="#{instance.send(:tag_id)}" class="message"></label>
|
13
|
+
#
|
14
|
+
# ActionView::Base.field_error_proc = Proc.new do |html_tag, instance|
|
15
|
+
# unless html_tag =~ /^<label/
|
16
|
+
# %{<div class="field_with_errors">#{html_tag}<label for="#{instance.send(:tag_id)}" class="message">#{instance.error_message.first}</label></div>}.html_safe
|
17
|
+
# else
|
18
|
+
# %{<div class="field_with_errors">#{html_tag}</div>}.html_safe
|
19
|
+
# end
|
20
|
+
# end
|
@@ -0,0 +1,33 @@
|
|
1
|
+
module MinimumViableProduct
|
2
|
+
module Iteration
|
3
|
+
CONFIG_FILE = File.join(Rails.root, '.iteration')
|
4
|
+
|
5
|
+
class << self
|
6
|
+
def write(json)
|
7
|
+
_file = File.open( CONFIG_FILE, "w+" )
|
8
|
+
_file.write(JSON.pretty_generate(json))
|
9
|
+
_file.close
|
10
|
+
|
11
|
+
@config = json
|
12
|
+
end
|
13
|
+
|
14
|
+
def read
|
15
|
+
@config ||= begin
|
16
|
+
JSON.parse( IO.read(CONFIG_FILE), symbolize_names: false )
|
17
|
+
rescue
|
18
|
+
write({'version' => '0.0.1'})
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def bump!(bump)
|
23
|
+
config = read
|
24
|
+
config['version'] = Semantic::Version.new(config['version']).increment!(bump.downcase.to_sym).to_s
|
25
|
+
write(config)
|
26
|
+
end
|
27
|
+
|
28
|
+
def version
|
29
|
+
read["version"]
|
30
|
+
end
|
31
|
+
end
|
32
|
+
end
|
33
|
+
end
|
@@ -0,0 +1,59 @@
|
|
1
|
+
if ENV['ROLLBAR_SERVER_ACCESS_TOKEN']
|
2
|
+
Rollbar.configure do |config|
|
3
|
+
# Without configuration, Rollbar is enabled in all environments.
|
4
|
+
# To disable in specific environments, set config.enabled=false.
|
5
|
+
|
6
|
+
config.access_token = ENV['ROLLBAR_SERVER_ACCESS_TOKEN']
|
7
|
+
|
8
|
+
# Here we'll disable in 'test':
|
9
|
+
if Rails.env.test?
|
10
|
+
config.enabled = false
|
11
|
+
end
|
12
|
+
|
13
|
+
# By default, Rollbar will try to call the `current_user` controller method
|
14
|
+
# to fetch the logged-in user object, and then call that object's `id`,
|
15
|
+
# `username`, and `email` methods to fetch those properties. To customize:
|
16
|
+
# config.person_method = "my_current_user"
|
17
|
+
# config.person_id_method = "my_id"
|
18
|
+
# config.person_username_method = "my_username"
|
19
|
+
# config.person_email_method = "my_email"
|
20
|
+
|
21
|
+
# If you want to attach custom data to all exception and message reports,
|
22
|
+
# provide a lambda like the following. It should return a hash.
|
23
|
+
# config.custom_data_method = lambda { {:some_key => "some_value" } }
|
24
|
+
|
25
|
+
# Add exception class names to the exception_level_filters hash to
|
26
|
+
# change the level that exception is reported at. Note that if an exception
|
27
|
+
# has already been reported and logged the level will need to be changed
|
28
|
+
# via the rollbar interface.
|
29
|
+
# Valid levels: 'critical', 'error', 'warning', 'info', 'debug', 'ignore'
|
30
|
+
# 'ignore' will cause the exception to not be reported at all.
|
31
|
+
# config.exception_level_filters.merge!('MyCriticalException' => 'critical')
|
32
|
+
#
|
33
|
+
# You can also specify a callable, which will be called with the exception instance.
|
34
|
+
# config.exception_level_filters.merge!('MyCriticalException' => lambda { |e| 'critical' })
|
35
|
+
|
36
|
+
# Enable asynchronous reporting (uses girl_friday or Threading if girl_friday
|
37
|
+
# is not installed)
|
38
|
+
# config.use_async = true
|
39
|
+
# Supply your own async handler:
|
40
|
+
# config.async_handler = Proc.new { |payload|
|
41
|
+
# Thread.new { Rollbar.process_from_async_handler(payload) }
|
42
|
+
# }
|
43
|
+
|
44
|
+
# Enable asynchronous reporting (using sucker_punch)
|
45
|
+
# config.use_sucker_punch
|
46
|
+
|
47
|
+
# Enable delayed reporting (using Sidekiq)
|
48
|
+
# config.use_sidekiq
|
49
|
+
# You can supply custom Sidekiq options:
|
50
|
+
# config.use_sidekiq 'queue' => 'default'
|
51
|
+
|
52
|
+
# If you run your staging application instance in production environment then
|
53
|
+
# you'll want to override the environment reported by `Rails.env` with an
|
54
|
+
# environment variable like this: `ROLLBAR_ENV=staging`. This is a recommended
|
55
|
+
# setup for Heroku. See:
|
56
|
+
# https://devcenter.heroku.com/articles/deploying-to-a-custom-rails-environment
|
57
|
+
config.environment = ENV['ROLLBAR_ENV'] || Rails.env
|
58
|
+
end
|
59
|
+
end
|
@@ -0,0 +1,165 @@
|
|
1
|
+
# Use this setup block to configure all options available in SimpleForm.
|
2
|
+
SimpleForm.setup do |config|
|
3
|
+
# Wrappers are used by the form builder to generate a
|
4
|
+
# complete input. You can remove any component from the
|
5
|
+
# wrapper, change the order or even add your own to the
|
6
|
+
# stack. The options given below are used to wrap the
|
7
|
+
# whole input.
|
8
|
+
config.wrappers :default, class: :input,
|
9
|
+
hint_class: :field_with_hint, error_class: :field_with_errors do |b|
|
10
|
+
## Extensions enabled by default
|
11
|
+
# Any of these extensions can be disabled for a
|
12
|
+
# given input by passing: `f.input EXTENSION_NAME => false`.
|
13
|
+
# You can make any of these extensions optional by
|
14
|
+
# renaming `b.use` to `b.optional`.
|
15
|
+
|
16
|
+
# Determines whether to use HTML5 (:email, :url, ...)
|
17
|
+
# and required attributes
|
18
|
+
b.use :html5
|
19
|
+
|
20
|
+
# Calculates placeholders automatically from I18n
|
21
|
+
# You can also pass a string as f.input placeholder: "Placeholder"
|
22
|
+
b.use :placeholder
|
23
|
+
|
24
|
+
## Optional extensions
|
25
|
+
# They are disabled unless you pass `f.input EXTENSION_NAME => true`
|
26
|
+
# to the input. If so, they will retrieve the values from the model
|
27
|
+
# if any exists. If you want to enable any of those
|
28
|
+
# extensions by default, you can change `b.optional` to `b.use`.
|
29
|
+
|
30
|
+
# Calculates maxlength from length validations for string inputs
|
31
|
+
b.optional :maxlength
|
32
|
+
|
33
|
+
# Calculates pattern from format validations for string inputs
|
34
|
+
b.optional :pattern
|
35
|
+
|
36
|
+
# Calculates min and max from length validations for numeric inputs
|
37
|
+
b.optional :min_max
|
38
|
+
|
39
|
+
# Calculates readonly automatically from readonly attributes
|
40
|
+
b.optional :readonly
|
41
|
+
|
42
|
+
## Inputs
|
43
|
+
b.use :label_input
|
44
|
+
b.use :hint, wrap_with: { tag: :span, class: :hint }
|
45
|
+
b.use :error, wrap_with: { tag: :span, class: :error }
|
46
|
+
|
47
|
+
## full_messages_for
|
48
|
+
# If you want to display the full error message for the attribute, you can
|
49
|
+
# use the component :full_error, like:
|
50
|
+
#
|
51
|
+
# b.use :full_error, wrap_with: { tag: :span, class: :error }
|
52
|
+
end
|
53
|
+
|
54
|
+
# The default wrapper to be used by the FormBuilder.
|
55
|
+
config.default_wrapper = :default
|
56
|
+
|
57
|
+
# Define the way to render check boxes / radio buttons with labels.
|
58
|
+
# Defaults to :nested for bootstrap config.
|
59
|
+
# inline: input + label
|
60
|
+
# nested: label > input
|
61
|
+
config.boolean_style = :nested
|
62
|
+
|
63
|
+
# Default class for buttons
|
64
|
+
config.button_class = 'btn'
|
65
|
+
|
66
|
+
# Method used to tidy up errors. Specify any Rails Array method.
|
67
|
+
# :first lists the first message for each field.
|
68
|
+
# Use :to_sentence to list all errors for each field.
|
69
|
+
# config.error_method = :first
|
70
|
+
|
71
|
+
# Default tag used for error notification helper.
|
72
|
+
config.error_notification_tag = :div
|
73
|
+
|
74
|
+
# CSS class to add for error notification helper.
|
75
|
+
config.error_notification_class = 'error_notification'
|
76
|
+
|
77
|
+
# ID to add for error notification helper.
|
78
|
+
# config.error_notification_id = nil
|
79
|
+
|
80
|
+
# Series of attempts to detect a default label method for collection.
|
81
|
+
# config.collection_label_methods = [ :to_label, :name, :title, :to_s ]
|
82
|
+
|
83
|
+
# Series of attempts to detect a default value method for collection.
|
84
|
+
# config.collection_value_methods = [ :id, :to_s ]
|
85
|
+
|
86
|
+
# You can wrap a collection of radio/check boxes in a pre-defined tag, defaulting to none.
|
87
|
+
# config.collection_wrapper_tag = nil
|
88
|
+
|
89
|
+
# You can define the class to use on all collection wrappers. Defaulting to none.
|
90
|
+
# config.collection_wrapper_class = nil
|
91
|
+
|
92
|
+
# You can wrap each item in a collection of radio/check boxes with a tag,
|
93
|
+
# defaulting to :span.
|
94
|
+
# config.item_wrapper_tag = :span
|
95
|
+
|
96
|
+
# You can define a class to use in all item wrappers. Defaulting to none.
|
97
|
+
# config.item_wrapper_class = nil
|
98
|
+
|
99
|
+
# How the label text should be generated altogether with the required text.
|
100
|
+
config.label_text = lambda { |label, required, explicit_label| "#{label}" }
|
101
|
+
|
102
|
+
# You can define the class to use on all labels. Default is nil.
|
103
|
+
# config.label_class = nil
|
104
|
+
|
105
|
+
# You can define the default class to be used on forms. Can be overriden
|
106
|
+
# with `html: { :class }`. Defaulting to none.
|
107
|
+
# config.default_form_class = nil
|
108
|
+
|
109
|
+
# You can define which elements should obtain additional classes
|
110
|
+
# config.generate_additional_classes_for = [:wrapper, :label, :input]
|
111
|
+
|
112
|
+
# Whether attributes are required by default (or not). Default is true.
|
113
|
+
# config.required_by_default = true
|
114
|
+
|
115
|
+
# Tell browsers whether to use the native HTML5 validations (novalidate form option).
|
116
|
+
# These validations are enabled in SimpleForm's internal config but disabled by default
|
117
|
+
# in this configuration, which is recommended due to some quirks from different browsers.
|
118
|
+
# To stop SimpleForm from generating the novalidate option, enabling the HTML5 validations,
|
119
|
+
# change this configuration to true.
|
120
|
+
config.browser_validations = false
|
121
|
+
|
122
|
+
# Collection of methods to detect if a file type was given.
|
123
|
+
# config.file_methods = [ :mounted_as, :file?, :public_filename ]
|
124
|
+
|
125
|
+
# Custom mappings for input types. This should be a hash containing a regexp
|
126
|
+
# to match as key, and the input type that will be used when the field name
|
127
|
+
# matches the regexp as value.
|
128
|
+
# config.input_mappings = { /count/ => :integer }
|
129
|
+
|
130
|
+
# Custom wrappers for input types. This should be a hash containing an input
|
131
|
+
# type as key and the wrapper that will be used for all inputs with specified type.
|
132
|
+
# config.wrapper_mappings = { string: :prepend }
|
133
|
+
|
134
|
+
# Namespaces where SimpleForm should look for custom input classes that
|
135
|
+
# override default inputs.
|
136
|
+
# config.custom_inputs_namespaces << "CustomInputs"
|
137
|
+
|
138
|
+
# Default priority for time_zone inputs.
|
139
|
+
# config.time_zone_priority = nil
|
140
|
+
|
141
|
+
# Default priority for country inputs.
|
142
|
+
# config.country_priority = nil
|
143
|
+
|
144
|
+
# When false, do not use translations for labels.
|
145
|
+
# config.translate_labels = true
|
146
|
+
|
147
|
+
# Automatically discover new inputs in Rails' autoload path.
|
148
|
+
# config.inputs_discovery = true
|
149
|
+
|
150
|
+
# Cache SimpleForm inputs discovery
|
151
|
+
# config.cache_discovery = !Rails.env.development?
|
152
|
+
|
153
|
+
# Default class for inputs
|
154
|
+
# config.input_class = nil
|
155
|
+
|
156
|
+
# Define the default class of the input wrapper of the boolean input.
|
157
|
+
config.boolean_label_class = 'checkbox'
|
158
|
+
|
159
|
+
# Defines if the default input wrapper class should be included in radio
|
160
|
+
# collection wrappers.
|
161
|
+
# config.include_default_input_wrapper_class = true
|
162
|
+
|
163
|
+
# Defines which i18n scope will be used in Simple Form.
|
164
|
+
# config.i18n_scope = 'simple_form'
|
165
|
+
end
|