dm-is-published 0.0.6 → 1.2.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 (47) hide show
  1. data/Gemfile +22 -0
  2. data/Guardfile +13 -0
  3. data/{LICENSE → LICENSE.txt} +1 -1
  4. data/README.rdoc +37 -5
  5. data/Rakefile +30 -56
  6. data/VERSION +1 -1
  7. data/dm-is-published.gemspec +93 -45
  8. data/docs/apple-touch-icon.png +0 -0
  9. data/docs/classes/DataMapper.html +81 -0
  10. data/docs/classes/DataMapper/Is.html +81 -0
  11. data/docs/classes/DataMapper/Is/Published.html +364 -0
  12. data/docs/classes/DataMapper/Is/Published/ClassMethods.html +237 -0
  13. data/docs/classes/DataMapper/Is/Published/InstanceMethods.html +132 -0
  14. data/docs/classes/DataMapper/Is/Published/ResourceInstanceMethods.html +133 -0
  15. data/docs/created.rid +4 -0
  16. data/docs/css/github.css +129 -0
  17. data/docs/css/main.css +333 -0
  18. data/docs/css/panel.css +384 -0
  19. data/docs/css/reset.css +48 -0
  20. data/docs/favicon.ico +0 -0
  21. data/docs/files/README_rdoc.html +226 -0
  22. data/docs/files/lib/dm-is-published_rb.html +84 -0
  23. data/docs/files/lib/is/published_rb.html +104 -0
  24. data/docs/i/arrows.png +0 -0
  25. data/docs/i/results_bg.png +0 -0
  26. data/docs/i/tree_bg.png +0 -0
  27. data/docs/index.html +13 -0
  28. data/docs/js/highlight.pack.js +1 -0
  29. data/docs/js/jquery-1.3.2.min.js +19 -0
  30. data/docs/js/jquery-effect.js +593 -0
  31. data/docs/js/main.js +24 -0
  32. data/docs/js/navigation.js +142 -0
  33. data/docs/js/search_index.js +1 -0
  34. data/docs/js/searchdoc.js +449 -0
  35. data/docs/js/searcher.js +228 -0
  36. data/docs/panel/index.html +73 -0
  37. data/docs/panel/links.html +12 -0
  38. data/docs/panel/tree.js +1 -0
  39. data/lib/dm-is-published.rb +3 -7
  40. data/lib/{dm-is-published/is → is}/published.rb +26 -10
  41. data/spec/{integration/published_spec.rb → dm-is-published_spec.rb} +48 -11
  42. data/spec/spec_helper.rb +12 -9
  43. metadata +264 -95
  44. data/.gitignore +0 -33
  45. data/History.rdoc +0 -4
  46. data/TODO +0 -0
  47. data/lib/dm-is-published/is/version.rb +0 -7
@@ -0,0 +1,228 @@
1
+ Searcher = function(data) {
2
+ this.data = data;
3
+ this.handlers = [];
4
+ }
5
+
6
+ Searcher.prototype = new function() {
7
+ // search is performed in chunks of 1000 for non-blocking user input
8
+ var CHUNK_SIZE = 1000;
9
+ // do not try to find more than 100 results
10
+ var MAX_RESULTS = 100;
11
+ var huid = 1;
12
+ var suid = 1;
13
+ var runs = 0;
14
+
15
+ this.find = function(query) {
16
+ var queries = splitQuery(query);
17
+ var regexps = buildRegexps(queries);
18
+ var highlighters = buildHilighters(queries);
19
+ var state = { from: 0, pass: 0, limit: MAX_RESULTS, n: suid++};
20
+ var _this = this;
21
+
22
+ this.currentSuid = state.n;
23
+
24
+ if (!query) return;
25
+
26
+ var run = function() {
27
+ // stop current search thread if new search started
28
+ if (state.n != _this.currentSuid) return;
29
+
30
+ var results =
31
+ performSearch(_this.data, regexps, queries, highlighters, state);
32
+ var hasMore = (state.limit > 0 && state.pass < 4);
33
+
34
+ triggerResults.call(_this, results, !hasMore);
35
+ if (hasMore) {
36
+ setTimeout(run, 2);
37
+ }
38
+ runs++;
39
+ };
40
+ runs = 0;
41
+
42
+ // start search thread
43
+ run();
44
+ }
45
+
46
+ /* ----- Events ------ */
47
+ this.ready = function(fn) {
48
+ fn.huid = huid;
49
+ this.handlers.push(fn);
50
+ }
51
+
52
+ /* ----- Utilities ------ */
53
+ function splitQuery(query) {
54
+ return jQuery.grep(query.split(/(\s+|::?|\(\)?)/), function(string) {
55
+ return string.match(/\S/)
56
+ });
57
+ }
58
+
59
+ function buildRegexps(queries) {
60
+ return jQuery.map(queries, function(query) {
61
+ return new RegExp(query.replace(/(.)/g, '([$1])([^$1]*?)'), 'i')
62
+ });
63
+ }
64
+
65
+ function buildHilighters(queries) {
66
+ return jQuery.map(queries, function(query) {
67
+ return jQuery.map(query.split(''), function(l, i) {
68
+ return '\u0001$' + (i*2+1) + '\u0002$' + (i*2+2);
69
+ }).join('');
70
+ });
71
+ }
72
+
73
+ // function longMatchRegexp(index, longIndex, regexps) {
74
+ // for (var i = regexps.length - 1; i >= 0; i--){
75
+ // if (!index.match(regexps[i]) && !longIndex.match(regexps[i])) return false;
76
+ // };
77
+ // return true;
78
+ // }
79
+
80
+
81
+ /* ----- Mathchers ------ */
82
+
83
+ /*
84
+ * This record matches if the index starts with queries[0] and the record
85
+ * matches all of the regexps
86
+ */
87
+ function matchPassBeginning(index, longIndex, queries, regexps) {
88
+ if (index.indexOf(queries[0]) != 0) return false;
89
+ for (var i=1, l = regexps.length; i < l; i++) {
90
+ if (!index.match(regexps[i]) && !longIndex.match(regexps[i]))
91
+ return false;
92
+ };
93
+ return true;
94
+ }
95
+
96
+ /*
97
+ * This record matches if the longIndex starts with queries[0] and the
98
+ * longIndex matches all of the regexps
99
+ */
100
+ function matchPassLongIndex(index, longIndex, queries, regexps) {
101
+ if (longIndex.indexOf(queries[0]) != 0) return false;
102
+ for (var i=1, l = regexps.length; i < l; i++) {
103
+ if (!longIndex.match(regexps[i]))
104
+ return false;
105
+ };
106
+ return true;
107
+ }
108
+
109
+ /*
110
+ * This record matches if the index contains queries[0] and the record
111
+ * matches all of the regexps
112
+ */
113
+ function matchPassContains(index, longIndex, queries, regexps) {
114
+ if (index.indexOf(queries[0]) == -1) return false;
115
+ for (var i=1, l = regexps.length; i < l; i++) {
116
+ if (!index.match(regexps[i]) && !longIndex.match(regexps[i]))
117
+ return false;
118
+ };
119
+ return true;
120
+ }
121
+
122
+ /*
123
+ * This record matches if regexps[0] matches the index and the record
124
+ * matches all of the regexps
125
+ */
126
+ function matchPassRegexp(index, longIndex, queries, regexps) {
127
+ if (!index.match(regexps[0])) return false;
128
+ for (var i=1, l = regexps.length; i < l; i++) {
129
+ if (!index.match(regexps[i]) && !longIndex.match(regexps[i]))
130
+ return false;
131
+ };
132
+ return true;
133
+ }
134
+
135
+
136
+ /* ----- Highlighters ------ */
137
+ function highlightRegexp(info, queries, regexps, highlighters) {
138
+ var result = createResult(info);
139
+ for (var i=0, l = regexps.length; i < l; i++) {
140
+ result.title = result.title.replace(regexps[i], highlighters[i]);
141
+ result.namespace = result.namespace.replace(regexps[i], highlighters[i]);
142
+ };
143
+ return result;
144
+ }
145
+
146
+ function hltSubstring(string, pos, length) {
147
+ return string.substring(0, pos) + '\u0001' + string.substring(pos, pos + length) + '\u0002' + string.substring(pos + length);
148
+ }
149
+
150
+ function highlightQuery(info, queries, regexps, highlighters) {
151
+ var result = createResult(info);
152
+ var pos = 0;
153
+ var lcTitle = result.title.toLowerCase();
154
+
155
+ pos = lcTitle.indexOf(queries[0]);
156
+ if (pos != -1) {
157
+ result.title = hltSubstring(result.title, pos, queries[0].length);
158
+ }
159
+
160
+ result.namespace = result.namespace.replace(regexps[0], highlighters[0]);
161
+ for (var i=1, l = regexps.length; i < l; i++) {
162
+ result.title = result.title.replace(regexps[i], highlighters[i]);
163
+ result.namespace = result.namespace.replace(regexps[i], highlighters[i]);
164
+ };
165
+ return result;
166
+ }
167
+
168
+ function createResult(info) {
169
+ var result = {};
170
+ result.title = info[0];
171
+ result.namespace = info[1];
172
+ result.path = info[2];
173
+ result.params = info[3];
174
+ result.snippet = info[4];
175
+ return result;
176
+ }
177
+
178
+ /* ----- Searching ------ */
179
+ function performSearch(data, regexps, queries, highlighters, state) {
180
+ var searchIndex = data.searchIndex;
181
+ var longSearchIndex = data.longSearchIndex;
182
+ var info = data.info;
183
+ var result = [];
184
+ var i = state.from;
185
+ var l = searchIndex.length;
186
+ var togo = CHUNK_SIZE;
187
+ var matchFunc, hltFunc;
188
+
189
+ while (state.pass < 4 && state.limit > 0 && togo > 0) {
190
+ if (state.pass == 0) {
191
+ matchFunc = matchPassBeginning;
192
+ hltFunc = highlightQuery;
193
+ } else if (state.pass == 1) {
194
+ matchFunc = matchPassLongIndex;
195
+ hltFunc = highlightQuery;
196
+ } else if (state.pass == 2) {
197
+ matchFunc = matchPassContains;
198
+ hltFunc = highlightQuery;
199
+ } else if (state.pass == 3) {
200
+ matchFunc = matchPassRegexp;
201
+ hltFunc = highlightRegexp;
202
+ }
203
+
204
+ for (; togo > 0 && i < l && state.limit > 0; i++, togo--) {
205
+ if (info[i].n == state.n) continue;
206
+ if (matchFunc(searchIndex[i], longSearchIndex[i], queries, regexps)) {
207
+ info[i].n = state.n;
208
+ result.push(hltFunc(info[i], queries, regexps, highlighters));
209
+ state.limit--;
210
+ }
211
+ };
212
+ if (searchIndex.length <= i) {
213
+ state.pass++;
214
+ i = state.from = 0;
215
+ } else {
216
+ state.from = i;
217
+ }
218
+ }
219
+ return result;
220
+ }
221
+
222
+ function triggerResults(results, isLast) {
223
+ jQuery.each(this.handlers, function(i, fn) {
224
+ fn.call(this, results, isLast)
225
+ })
226
+ }
227
+ }
228
+
@@ -0,0 +1,73 @@
1
+ <?xml version="1.0" encoding="UTF-8"?>
2
+ <!DOCTYPE html PUBLIC "-//W3C//DTD XHTML 1.1//EN"
3
+ "http://www.w3.org/TR/xhtml11/DTD/xhtml11.dtd">
4
+
5
+ <html xmlns="http://www.w3.org/1999/xhtml" xml:lang="en">
6
+ <head>
7
+ <title>search index</title>
8
+ <link rel="stylesheet" href="../css/reset.css" type="text/css" media="screen" charset="utf-8" />
9
+ <link rel="stylesheet" href="../css/panel.css" type="text/css" media="screen" charset="utf-8" />
10
+ <script src="../js/search_index.js" type="text/javascript" charset="utf-8"></script>
11
+ <script src="../js/searcher.js" type="text/javascript" charset="utf-8"></script>
12
+ <script src="tree.js" type="text/javascript" charset="utf-8"></script>
13
+ <script src="../js/jquery-1.3.2.min.js" type="text/javascript" charset="utf-8"></script>
14
+ <script src="../js/searchdoc.js" type="text/javascript" charset="utf-8"></script>
15
+ <script type="text/javascript" charset="utf-8">
16
+ function placeholder() {
17
+ if ($('<input type="text">')[0].placeholder !== undefined) return;
18
+
19
+ $('#search-label').click(function() {
20
+ $('#search').focus();
21
+ $('#search-label').hide();
22
+ });
23
+
24
+ $('#search').focus(function() {
25
+ $('#search-label').hide();
26
+ });
27
+ $('#search').blur(function() {
28
+ this.value == '' && $('#search-label').show()
29
+ });
30
+
31
+ $('#search')[0].value == '' && $('#search-label').show();
32
+ }
33
+ $(function() {
34
+ placeholder();
35
+ $('#links').hide();
36
+ var panel = new Searchdoc.Panel($('#panel'), search_data, tree, window.parent.frames[1]);
37
+ $('#search').focus();
38
+
39
+ var s = window.parent.location.search.match(/\?q=([^&]+)/);
40
+ if (s) {
41
+ s = decodeURIComponent(s[1]).replace(/\+/g, ' ');
42
+ if (s.length > 0)
43
+ {
44
+ $('#search').val(s);
45
+ panel.search(s, true);
46
+ }
47
+ }
48
+ })
49
+ </script>
50
+ </head>
51
+ <body>
52
+ <div class="panel panel_tree" id="panel">
53
+ <div class="header">
54
+ <div>
55
+ <label for="search" id="search-label" style="display: none">Search</label>
56
+ <table>
57
+ <tr><td>
58
+ <input type="Search" placeholder="Search" autosave="searchdoc" results="10" id="search" autocomplete="off"/>
59
+ </td></tr>
60
+ </table></div>
61
+ </div>
62
+ <div class="tree">
63
+ <ul>
64
+ </ul>
65
+ </div>
66
+ <div class="result">
67
+ <ul>
68
+ </ul>
69
+ </div>
70
+ </div>
71
+ <a href="links.html" id="links">index</a>
72
+ </body>
73
+ </html>
@@ -0,0 +1,12 @@
1
+ <html>
2
+ <head>File index</head>
3
+ <body>
4
+
5
+ <a href="../files/README_rdoc.html">README.rdoc</a>
6
+
7
+ <a href="../files/lib/dm-is-published_rb.html">lib/dm-is-published.rb</a>
8
+
9
+ <a href="../files/lib/is/published_rb.html">lib/is/published.rb</a>
10
+
11
+ </body>
12
+ </html>
@@ -0,0 +1 @@
1
+ var tree = [["","","files",[["README.rdoc","files/README_rdoc.html","",[]],["","","lib",[["dm-is-published.rb","files/lib/dm-is-published_rb.html","",[]],["","","is",[["published.rb","files/lib/is/published_rb.html","",[]]]]]]]],["DataMapper","classes/DataMapper.html","",[["Is","classes/DataMapper/Is.html","",[["Published","classes/DataMapper/Is/Published.html","",[["ClassMethods","classes/DataMapper/Is/Published/ClassMethods.html","",[]],["InstanceMethods","classes/DataMapper/Is/Published/InstanceMethods.html","",[]],["ResourceInstanceMethods","classes/DataMapper/Is/Published/ResourceInstanceMethods.html","",[]]]]]]]]]
@@ -1,13 +1,9 @@
1
- # Needed to import datamapper and other gems
2
- # require 'rubygems' # read [ http://gist.github.com/54177 ] to understand why this line is commented out
3
- require 'pathname'
4
-
5
1
  # Add all external dependencies for the plugin here
6
- require 'dm-core'
7
- require 'dm-validations'
2
+ require 'data_mapper'
3
+ require 'active_support/core_ext/object/blank' unless ''.respond_to?(:blank?)
8
4
 
9
5
  # Require plugin-files
10
- require Pathname(__FILE__).dirname.expand_path / 'dm-is-published' / 'is' / 'published.rb'
6
+ require_relative './is/published'
11
7
 
12
8
  DataMapper::Model.append_extensions(DataMapper::Is::Published)
13
9
  DataMapper::Model.append_inclusions(DataMapper::Is::Published::ResourceInstanceMethods)
@@ -10,13 +10,13 @@ module DataMapper
10
10
  #
11
11
  #
12
12
  # == Installation
13
- #
14
- # # Add GitHub to your RubyGems sources
15
- # $ gem sources -a http://gems.github.com
16
- #
17
- # $ (sudo)? gem install kematzy-dm-is-published
18
- #
19
- # <b>NB! Depends upon the whole DataMapper suite being installed, and has ONLY been tested with DM 0.10.0 (next branch).</b>
13
+ #
14
+ # $ gem install dm-is-published
15
+ #
16
+ #
17
+ # Or add the gem to your Gemfile, and then run <tt>bundle install</tt>.
18
+ #
19
+ # gem 'dm-is-published', 'CURRENT_VERSION_NUMBER'
20
20
  #
21
21
  #
22
22
  # == Getting Started
@@ -27,7 +27,7 @@ module DataMapper
27
27
  #
28
28
  # Require +dm-is-published+ in your app.
29
29
  #
30
- # require 'dm-core' # must be required first
30
+ # require 'data_mapper' # must be required first
31
31
  # require 'dm-is-published'
32
32
  #
33
33
  # Lets say we have an Article class, and each Article can have a current state,
@@ -103,7 +103,7 @@ module DataMapper
103
103
  #
104
104
  # == Credits
105
105
  #
106
- # Copyright (c) 2008-05-07 [Kematzy at gmail]
106
+ # Copyright (c) 2008 - 2013 [Kematzy at gmail]
107
107
  #
108
108
  # Loosely based on the ActsAsPublishable plugin by [http://fr.ivolo.us/posts/acts-as-publishable]
109
109
  #
@@ -111,7 +111,8 @@ module DataMapper
111
111
  #
112
112
  # Released under the MIT license.
113
113
 
114
- module Published
114
+ module Published
115
+ VERSION = IO.read("#{File.dirname(__FILE__)}/../../VERSION").chomp
115
116
 
116
117
  ##
117
118
  # method that adds a basic published status attribute to your model
@@ -182,6 +183,21 @@ module DataMapper
182
183
  module ClassMethods
183
184
  attr_reader :publish_states, :publish_states_for_validation
184
185
 
186
+ ##
187
+ # Returns a JSON representation of the publish states, where each state
188
+ # is represented as a lowercase key and an uppercase value.
189
+ #
190
+ # ==== Examples
191
+ #
192
+ # Model.publish_states_as_json
193
+ #
194
+ # => { 'live': 'LIVE','draft': 'DRAFT','hidden': 'HIDDEN' }
195
+ #
196
+ # @api public
197
+ def publish_states_as_json
198
+ %Q[{ #{self.publish_states_for_validation.collect{ |state| "'#{state.to_s.downcase}': '#{state.to_s.upcase}'" }.join(',')} }]
199
+ end
200
+
185
201
  ##
186
202
  # Overriding the normal #all method to add some extra sugar.
187
203
  #
@@ -1,10 +1,9 @@
1
- require 'pathname'
2
- require Pathname(__FILE__).dirname.expand_path.parent + 'spec_helper'
1
+ require_relative './spec_helper'
3
2
 
4
3
  if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
5
4
  describe 'DataMapper::Is::Published' do
6
5
 
7
- class DefaultsArticle
6
+ class DefaultsArticle
8
7
  include DataMapper::Resource
9
8
  property :id, Serial
10
9
  property :title, String
@@ -12,7 +11,7 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
12
11
  is :published
13
12
  end
14
13
 
15
- class Article
14
+ class Article
16
15
  include DataMapper::Resource
17
16
  property :id, Serial
18
17
  property :title, String
@@ -20,7 +19,7 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
20
19
  is :published, :live, :draft, :prepared
21
20
  end
22
21
 
23
- class Image
22
+ class Image
24
23
  include DataMapper::Resource
25
24
  property :id, Serial
26
25
  property :title, String
@@ -46,6 +45,13 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
46
45
 
47
46
  end
48
47
 
48
+ describe "VERSION" do
49
+
50
+ it "should have a VERSION constant" do
51
+ DataMapper::Is::Published::VERSION.should match(/\d\.\d+\.\d+/)
52
+ end
53
+
54
+ end #/ VERSION
49
55
 
50
56
  describe "Property :publish_status" do
51
57
 
@@ -103,23 +109,23 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
103
109
  Image.publish_states.should == [:yes, :no]
104
110
  end
105
111
 
106
- class ArticleWithDeclarationAsArray
112
+ class ArticleWithDeclarationAsArray
107
113
  include DataMapper::Resource
108
114
  property :id, Serial
109
115
  is :published, [:a, :b, :c]
110
116
  end
111
117
 
112
- it "should handle the states declared within [ Array ] brackets" do
118
+ it "should handle the states declared within [ Array ] brackets" do
113
119
  ArticleWithDeclarationAsArray.publish_states.should == [:a, :b, :c]
114
120
  end
115
121
 
116
- class ArticleWithDeclarationAsArrayOfStrings
122
+ class ArticleWithDeclarationAsArrayOfStrings
117
123
  include DataMapper::Resource
118
124
  property :id, Serial
119
125
  is :published, %w(a b c)
120
126
  end
121
127
 
122
- it "should handle the states declared as %w(a b c) " do
128
+ it "should handle the states declared as %w(a b c) " do
123
129
  ArticleWithDeclarationAsArrayOfStrings.publish_states.should == [:a, :b, :c]
124
130
  end
125
131
 
@@ -135,6 +141,36 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
135
141
 
136
142
  end #/ #publish_states
137
143
 
144
+ describe "#publish_states_as_json" do
145
+
146
+ it "should return the states as an array of Symbols" do
147
+ DefaultsArticle.publish_states_as_json.should == "{ 'live': 'LIVE','draft': 'DRAFT','hidden': 'HIDDEN' }"
148
+ Article.publish_states_as_json.should == "{ 'live': 'LIVE','draft': 'DRAFT','prepared': 'PREPARED' }"
149
+ Image.publish_states_as_json.should == "{ 'yes': 'YES','no': 'NO' }"
150
+ end
151
+
152
+ class ArticleWithDeclarationAsArray
153
+ include DataMapper::Resource
154
+ property :id, Serial
155
+ is :published, [:a, :b, :c]
156
+ end
157
+
158
+ it "should handle the states declared within [ Array ] brackets" do
159
+ ArticleWithDeclarationAsArray.publish_states_as_json.should == "{ 'a': 'A','b': 'B','c': 'C' }"
160
+ end
161
+
162
+ class ArticleWithDeclarationAsArrayOfStrings
163
+ include DataMapper::Resource
164
+ property :id, Serial
165
+ is :published, %w(a b c)
166
+ end
167
+
168
+ it "should handle the states declared as %w(a b c) " do
169
+ ArticleWithDeclarationAsArrayOfStrings.publish_states_as_json.should == "{ 'a': 'A','b': 'B','c': 'C' }"
170
+ end
171
+
172
+ end #/ #publish_states
173
+
138
174
  describe "#all" do
139
175
 
140
176
  before(:each) do
@@ -143,7 +179,7 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
143
179
  end
144
180
  end
145
181
 
146
- it "should work as expected with Model.all" do
182
+ it "should work as expected with Model.all" do
147
183
  Article.all.size.should == 8
148
184
  end
149
185
 
@@ -309,4 +345,5 @@ if HAS_SQLITE3 || HAS_MYSQL || HAS_POSTGRES
309
345
  end #/ Validations
310
346
 
311
347
  end
312
- end
348
+
349
+ end #/ if