corkboard 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 (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
+ }