corkboard 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
Files changed (41) hide show
  1. data/MIT-LICENSE +20 -0
  2. data/README.md +44 -0
  3. data/Rakefile +29 -0
  4. data/app/assets/images/corkboard/image.png +0 -0
  5. data/app/assets/javascripts/corkboard.js.erb +45 -0
  6. data/app/assets/javascripts/corkboard/app/board.js +86 -0
  7. data/app/assets/javascripts/corkboard/base.js +9 -0
  8. data/app/assets/javascripts/corkboard/lib/publisher.js +53 -0
  9. data/app/assets/javascripts/corkboard/lib/weighted_randomizer.js +41 -0
  10. data/app/assets/stylesheets/corkboard/application.css +88 -0
  11. data/app/assets/stylesheets/corkboard/responsive.css +93 -0
  12. data/app/controllers/corkboard/application_controller.rb +15 -0
  13. data/app/controllers/corkboard/authorizations_controller.rb +65 -0
  14. data/app/controllers/corkboard/board_controller.rb +10 -0
  15. data/app/controllers/corkboard/posts_controller.rb +72 -0
  16. data/app/helpers/corkboard/application_helper.rb +72 -0
  17. data/app/models/corkboard/authorization.rb +13 -0
  18. data/app/models/corkboard/post.rb +19 -0
  19. data/app/models/corkboard/subscription.rb +34 -0
  20. data/app/views/corkboard/authorizations/index.html.erb +32 -0
  21. data/app/views/corkboard/board/_filters.html.erb +15 -0
  22. data/app/views/corkboard/board/_posts.html.erb +10 -0
  23. data/app/views/corkboard/board/show.html.erb +4 -0
  24. data/app/views/layouts/corkboard/application.html.erb +14 -0
  25. data/config/routes.rb +29 -0
  26. data/db/migrate/01_create_corkboard_authorizations.rb +17 -0
  27. data/lib/corkboard.rb +191 -0
  28. data/lib/corkboard/client.rb +51 -0
  29. data/lib/corkboard/clients/instagram.rb +26 -0
  30. data/lib/corkboard/engine.rb +27 -0
  31. data/lib/corkboard/provider.rb +21 -0
  32. data/lib/corkboard/providers/instagram.rb +29 -0
  33. data/lib/corkboard/publishers/mock.rb +14 -0
  34. data/lib/corkboard/publishers/pusher.rb +31 -0
  35. data/lib/corkboard/service/config.rb +12 -0
  36. data/lib/corkboard/version.rb +3 -0
  37. data/lib/generators/corkboard/install_generator.rb +16 -0
  38. data/lib/generators/corkboard/templates/README +14 -0
  39. data/lib/generators/corkboard/templates/initializer.rb +45 -0
  40. data/lib/tasks/corkboard_tasks.rake +4 -0
  41. metadata +412 -0
@@ -0,0 +1,20 @@
1
+ Copyright 2012 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,44 @@
1
+ # Corkboard
2
+
3
+ TODO: Summary of Corkboard.
4
+
5
+ ## Installation
6
+
7
+ Add this line to your application's Gemfile:
8
+
9
+ gem 'corkboard'
10
+
11
+ And then execute:
12
+
13
+ $ bundle
14
+
15
+ Or install it yourself as:
16
+
17
+ $ gem install corkboard
18
+
19
+ ## Usage
20
+
21
+ TODO: Write usage instructions here
22
+
23
+ ## Developing
24
+
25
+ TODO...
26
+
27
+ ### Jasmine (js) Specs
28
+
29
+ * To run from command line:
30
+
31
+ $ rake spec:javascript
32
+
33
+ * To see them in the browser:
34
+
35
+ $ cd spec/dummy && rails s
36
+ # open browser to http://localhost:3000/jasmine
37
+
38
+ ## Contributing
39
+
40
+ 1. Fork it
41
+ 2. Create your feature branch (`git checkout -b my-new-feature`)
42
+ 3. Commit your changes (`git commit -am 'Added some feature'`)
43
+ 4. Push to the branch (`git push origin my-new-feature`)
44
+ 5. Create new Pull Request
@@ -0,0 +1,29 @@
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
+ exit 1
7
+ end
8
+
9
+ require 'bundler/gem_tasks'
10
+ require 'rspec/core/rake_task'
11
+
12
+ desc 'Default: run specs'
13
+ task :default => [:spec]
14
+
15
+ desc 'Run the full spec suite'
16
+ task :spec => [:'spec:ruby', :'spec:javascript']
17
+
18
+ namespace :spec do
19
+ desc 'Run the RSpec code examples'
20
+ RSpec::Core::RakeTask.new(:ruby) do |t|
21
+ t.pattern = "spec/**/*_spec.rb"
22
+ t.verbose = false
23
+ end
24
+
25
+ desc 'Run the Jasmine code examples'
26
+ task :javascript do
27
+ system 'cd spec/dummy && rake jasminerice:run'
28
+ end
29
+ end
@@ -0,0 +1,45 @@
1
+ //= require modernizr
2
+ //= require jquery
3
+ //= require jquery_ujs
4
+ //= require jquery.masonry
5
+ //= require jquery.masonry-extensions
6
+ //= require pusher
7
+
8
+ //= require corkboard/base
9
+ //= require_tree ./corkboard/lib
10
+ //= require_tree ./corkboard/app
11
+
12
+ (function($, corkboard) {
13
+ // Corkboard properties pulled from the server. Client-side definitions
14
+ // should go in `corkboard/base.js`, to ensure availability and keep this
15
+ // file clean.
16
+ //
17
+ // TODO: consider something like `gon`, rather than making this file `erb`.
18
+ $.extend(corkboard, {
19
+ version : '<%= Corkboard::VERSION %>',
20
+ config : function config(key) {
21
+ return corkboard.config[key] || corkboard.defaults[key];
22
+ }
23
+ });
24
+
25
+ $.extend(corkboard.config, {
26
+ pusher : '<%= Pusher.key %>',
27
+ weights : <%= Corkboard.presentation[:weights].to_json %>
28
+ });
29
+
30
+ // Define jQuery.fn.corkboard
31
+ $.fn.extend({
32
+ // corkboard : $.corkboard.Board
33
+ corkboard : function corkboard() {
34
+ this.each(function() {
35
+ new $.corkboard.Board($(this));
36
+ });
37
+
38
+ return this;
39
+ }
40
+ });
41
+
42
+ $(function() {
43
+ $('article.corkboard').corkboard();
44
+ });
45
+ })(jQuery, jQuery.corkboard);
@@ -0,0 +1,86 @@
1
+ (function($, corkboard) {
2
+ // TODO: allow for and handle multiple matches.
3
+ function Board(board) {
4
+ if(board.length) {
5
+ this.setup(board);
6
+ }
7
+ }
8
+
9
+ // Surfaced as part of the corkboard namespace.
10
+ corkboard.Board = Board;
11
+
12
+ // TODO: consider making selectors configurable.
13
+ // TODO: make the masonry configuration configurable.
14
+ // TODO: add specs (and break up).
15
+ Board.prototype.setup = function setup(board) {
16
+ var selectors = {
17
+ posts : 'ul.corkboard.posts',
18
+ entry : 'li.entry',
19
+ filters : 'nav.corkboard.filters'
20
+ };
21
+ var posts = $(selectors.posts, board);
22
+ var filters = $(selectors.filters);
23
+
24
+ posts.imagesLoaded(function() {
25
+ this.masonry({
26
+ isAnimated : true,
27
+ isFitWidth : true,
28
+ itemSelector : selectors.entry + ':not(.hidden)',
29
+
30
+ columnWidth : function columnWidth(containerWidth) {
31
+ if(containerWidth <= 320) {
32
+ return 160;
33
+ }
34
+ if(containerWidth <= 480) {
35
+ return 182;
36
+ }
37
+ else if(containerWidth <= 768){
38
+ return 140;
39
+ }
40
+ else {
41
+ return 172;
42
+ }
43
+ }
44
+ });
45
+
46
+ // reveal tiles in a pseudo-scattered pattern.
47
+ this.find([selectors.entry, ':nth-child(5n+1)'].join('')).fadeIn(function callback() {
48
+ var next = $(this).next();
49
+
50
+ if( ! next.is(':visible')) {
51
+ next.fadeIn(corkboard.config('fade'), callback);
52
+ };
53
+ });
54
+ });
55
+
56
+ // TODO: generalize filter placement (to other-than-FAB)
57
+ filters.on('click', 'a', function(e) {
58
+ var link = $(this);
59
+ var filters = $('li', selectors.filters);
60
+ var filter = link.parent().toggleClass('active');
61
+ var posts = $(selectors.posts);
62
+ var entries = posts.find(selectors.entry);
63
+ var href = link.attr('href');
64
+ var tag = href.replace('#', '');
65
+
66
+ filters.not(filter).removeClass('active');
67
+
68
+ if(link.hasClass('all') || ( ! filter.hasClass('active'))) {
69
+ $('a.all').parent().addClass('active');
70
+ posts.masonry('filter', entries, '*');
71
+ }
72
+ else {
73
+ posts.masonry('filter', entries, '[data-tags~="' + tag + '"]');
74
+ }
75
+
76
+ return false;
77
+ });
78
+
79
+ new corkboard.Publisher(board, this.updated);
80
+ };
81
+
82
+ Board.prototype.updated = function updated(list, added) {
83
+ list.masonry('reload');
84
+ added.fadeIn(corkboard.config('fade'));
85
+ };
86
+ })(jQuery, jQuery.corkboard);
@@ -0,0 +1,9 @@
1
+ (function($) {
2
+ $.extend({
3
+ corkboard : {
4
+ defaults : {
5
+ fade : 'slow'
6
+ }
7
+ }
8
+ });
9
+ })(jQuery);
@@ -0,0 +1,53 @@
1
+ (function($, corkboard) {
2
+ function Publisher(board, callback) {
3
+ if(board.length) {
4
+ this.board = board;
5
+ this.callback = callback;
6
+ this.subscribe();
7
+ }
8
+ }
9
+
10
+ // Surfaced as part of the corkboard namespace.
11
+ corkboard.Publisher = Publisher;
12
+
13
+ Publisher.prototype.subscribe = function subscribe() {
14
+ var self = this;
15
+ var pusher = new Pusher(corkboard.config.pusher);
16
+ var channel = pusher.subscribe('corkboard');
17
+
18
+ // pusher.bind('pusher:error', function(data) {
19
+ // alert(data);
20
+ // });
21
+
22
+ channel.bind('posts/new', function(data) {
23
+ var eid = data.eid;
24
+
25
+ if(0 === $('[data-eid="' + eid + '"]').length) {
26
+ self.publish(data);
27
+ }
28
+ });
29
+ };
30
+
31
+ // TODO: handle collections (for more efficient updates).
32
+ // TODO: include :type (e.g., 'instagram').
33
+ // TODO: use a (server-shared) template for rendering entries.
34
+ Publisher.prototype.publish = function publish(data) {
35
+ var posts = this.board.find('ul.corkboard.posts');
36
+ var weights = corkboard.config.weights;
37
+ var random = new corkboard.WeightedRandomizer(weights);
38
+ var eid = data.eid;
39
+ var klass = ['entry', random.sample()].join(' ');
40
+ var entry = $('<li class="' + klass + '" data-eid="' + data.eid + '" data-tags="' + data.tags + '"><figure><img src="' + data.image + '"><p class="caption">' + data.caption + '</p></figure></li>');
41
+
42
+ posts.prepend(entry);
43
+
44
+ if(this.callback) {
45
+ this.callback.apply(this, [posts, entry]);
46
+ }
47
+ };
48
+
49
+ // // Uncomment to debug Pusher messages.
50
+ // Pusher.log = function(data) {
51
+ // console.log('\t\t', data);
52
+ // };
53
+ })(jQuery, jQuery.corkboard);
@@ -0,0 +1,41 @@
1
+ (function($, corkboard) {
2
+ // Ported to JS from weighted_randomizer Ruby gem.
3
+ function WeightedRandomizer(setup) {
4
+ this.normalize(setup);
5
+ }
6
+
7
+ // Surfaced as part of the corkboard namespace.
8
+ corkboard.WeightedRandomizer = WeightedRandomizer;
9
+
10
+ WeightedRandomizer.prototype.normalize = function normalize(setup) {
11
+ var dict = {};
12
+ var sum = 0.0;
13
+
14
+ $.each(setup, function(key, weight) {
15
+ sum += weight;
16
+ });
17
+
18
+ $.each(setup, function(key, weight) {
19
+ dict[key] = (weight / sum);
20
+ });
21
+
22
+ this.normalized = dict;
23
+ };
24
+
25
+ WeightedRandomizer.prototype.sample = function sample() {
26
+ var pick = Math.random();
27
+ var result;
28
+
29
+ $.each(this.normalized, function(key, weight) {
30
+ result = key;
31
+
32
+ if(pick <= weight) {
33
+ return false;
34
+ }
35
+
36
+ pick -= weight;
37
+ });
38
+
39
+ return result;
40
+ };
41
+ })(jQuery, jQuery.corkboard);
@@ -0,0 +1,88 @@
1
+ /**
2
+ * Stylesheet: Corkboard
3
+ *= require_self
4
+ * -------------------:--------------------------------------------------------
5
+ */
6
+ article#corkboard {
7
+ position : relative;
8
+ }
9
+
10
+
11
+ /**
12
+ * @section: corkboard filters
13
+ * -------------------:--------------------------------------------------------
14
+ */
15
+ nav.corkboard.filters {
16
+ position : absolute;
17
+ top : 10px;
18
+ right : 10px;
19
+ z-index : 1;
20
+ }
21
+
22
+ nav.corkboard.filters > ul {
23
+ list-style : none;
24
+ margin : 0;
25
+ }
26
+
27
+ nav.corkboard.filters li {
28
+ display : inline-block;
29
+ }
30
+
31
+ nav.corkboard.filters li > a {
32
+ text-decoration : none;
33
+ }
34
+
35
+
36
+ /**
37
+ * @section: corkboard posts
38
+ * -------------------:--------------------------------------------------------
39
+ */
40
+ ul.corkboard.posts {
41
+ font-family : monospace;
42
+ list-style : none;
43
+ overflow : hidden;
44
+ margin : 0 auto;
45
+ padding : 0;
46
+ }
47
+
48
+ ul.corkboard.posts > li {
49
+ display : none;
50
+ float : left;
51
+ overflow : hidden;
52
+ margin : 2px;
53
+ }
54
+
55
+ ul.corkboard.posts > li.s {
56
+ width : 168px;
57
+ height : 168px;
58
+ }
59
+
60
+ ul.corkboard.posts > li.m {
61
+ width : 340px;
62
+ height : 340px;
63
+ }
64
+
65
+ ul.corkboard.posts > li.l {
66
+ width : 512px;
67
+ height : 512px;
68
+ }
69
+
70
+ ul.corkboard.posts figure {
71
+ margin : 0;
72
+ padding : 0;
73
+ width : 100%;
74
+ height : 100%;
75
+ }
76
+
77
+ ul.corkboard.posts img {
78
+ position : absolute;
79
+ top : -5%;
80
+ left : -5%;
81
+ width : 110%;
82
+ height : 110%;
83
+ max-width : none;
84
+ }
85
+
86
+ ul.corkboard.posts p.caption {
87
+ display : none;
88
+ }
@@ -0,0 +1,93 @@
1
+ /**
2
+ * Stylesheet: Corkboard Responsive
3
+ * -------------------:--------------------------------------------------------
4
+ */
5
+
6
+ /**
7
+ * @section: mobile portrait to landscape
8
+ * -------------------:--------------------------------------------------------
9
+ */
10
+ @media only screen and (max-width: 320px) {
11
+ ul.corkboard.posts > li.s,
12
+ ul.corkboard.posts > li.m,
13
+ ul.corkboard.posts > li.l {
14
+ width : 156px;
15
+ height : 156px;
16
+ }
17
+ }
18
+
19
+ /**
20
+ * @section: mobile portrait to landscape
21
+ * -------------------:--------------------------------------------------------
22
+ */
23
+ @media only screen and (min-width: 321px) and (max-width: 480px) {
24
+ ul.corkboard.posts > li.s,
25
+ ul.corkboard.posts > li.m,
26
+ ul.corkboard.posts > li.l {
27
+ width : 178px;
28
+ height : 178px;
29
+ }
30
+ }
31
+
32
+ /**
33
+ * @section: mobile landscape to tablet portrait
34
+ * -------------------:--------------------------------------------------------
35
+ */
36
+ @media only screen and (min-width: 481px) and (max-width: 768px) {
37
+ ul.corkboard.posts > li.s {
38
+ width : 136px;
39
+ height : 136px;
40
+ }
41
+
42
+ ul.corkboard.posts > li.m,
43
+ ul.corkboard.posts > li.l {
44
+ width : 276px;
45
+ height : 276px;
46
+ }
47
+ }
48
+
49
+ /**
50
+ * @section: tablet portrait to standard desktop
51
+ * -------------------:--------------------------------------------------------
52
+ */
53
+ @media only screen and (min-width: 769px) and (max-width: 980px) {
54
+ ul.corkboard.posts > li.s {
55
+ width : 168px;
56
+ height : 168px;
57
+ }
58
+
59
+ ul.corkboard.posts > li.m,
60
+ ul.corkboard.posts > li.l {
61
+ width : 340px;
62
+ height : 340px;
63
+ }
64
+ }
65
+
66
+ /**
67
+ * @section: standard desktop and larger
68
+ * -------------------:--------------------------------------------------------
69
+ */
70
+ @media only screen and (min-width: 981px) {
71
+ ul.corkboard.posts > li.s {
72
+ width : 168px;
73
+ height : 168px;
74
+ }
75
+
76
+ ul.corkboard.posts > li.m {
77
+ width : 340px;
78
+ height : 340px;
79
+ }
80
+
81
+ ul.corkboard.posts > li.l {
82
+ width : 512px;
83
+ height : 512px;
84
+ }
85
+ }
86
+
87
+ /**
88
+ * @section: HD
89
+ * -------------------:--------------------------------------------------------
90
+ */
91
+ @media only screen and (min-width: 1200px) {
92
+
93
+ }