myobie-turbine-core 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.
data/README ADDED
@@ -0,0 +1,6 @@
1
+ This is the core engine of my turbine project: taking text and making sense of it.
2
+
3
+ Look at the specs or at turbine-blog to help understand what this thing does.
4
+
5
+ To run the specs:
6
+ > bacon spec/**/*.rb spec/*.rb
data/Rakefile ADDED
@@ -0,0 +1,13 @@
1
+ begin
2
+ require 'jeweler'
3
+ Jeweler::Tasks.new do |gemspec|
4
+ gemspec.name = "turbine-core"
5
+ gemspec.summary = "TODO"
6
+ gemspec.email = "nathan@myobie.com"
7
+ gemspec.homepage = "http://github.com/myobie/turbine-core"
8
+ gemspec.description = "TODO"
9
+ gemspec.authors = ["Nathan Herald"]
10
+ end
11
+ rescue LoadError
12
+ puts "Jeweler not available. Install it with: sudo gem install jeweler"
13
+ end
data/VERSION.yml ADDED
@@ -0,0 +1,4 @@
1
+ ---
2
+ :major: 0
3
+ :minor: 1
4
+ :patch: 0
data/lib/ext.rb ADDED
@@ -0,0 +1,63 @@
1
+ class String
2
+ def slugify
3
+ self.dup.slugify!
4
+ end
5
+
6
+ def slugify!
7
+ self.gsub!(/[^\x00-\x7F]+/, '') # Remove non-ASCII (e.g. diacritics).
8
+ self.gsub!(/[^a-z0-9\-_\+]+/i, '-') # Turn non-slug chars into the separator.
9
+ self.gsub!(/-{2,}/, '-') # No more than one of the separator in a row.
10
+ self.gsub!(/^-|-$/, '') # Remove leading/trailing separator.
11
+ self.downcase!
12
+ self
13
+ end
14
+
15
+ def make_attr
16
+ self.downcase.to_sym
17
+ end
18
+ end
19
+
20
+ class Symbol
21
+ def html
22
+ (self.to_s + '_html').to_sym
23
+ end
24
+
25
+ def make_attr
26
+ self.to_s.downcase.to_sym
27
+ end
28
+ end
29
+
30
+ class Array
31
+ def make_attrs
32
+ self.collect! { |a| a.to_s.downcase.to_sym }
33
+ end
34
+ end
35
+
36
+ ### class instance vars that inherit down to children
37
+ module ClassLevelInheritableAttributes
38
+
39
+ PRIMITIVES = [NilClass, TrueClass, FalseClass, Fixnum, Float]
40
+
41
+ def cattr_inheritable(*args)
42
+ @cattr_inheritable_attrs ||= [:cattr_inheritable_attrs]
43
+ @cattr_inheritable_attrs += args
44
+ args.each do |arg|
45
+ class_eval %(
46
+ class << self; attr_accessor :#{arg}; end
47
+ )
48
+ end
49
+ @cattr_inheritable_attrs
50
+ end
51
+
52
+ def inherited(subclass)
53
+ @all_children ||= []
54
+ @cattr_inheritable_attrs.each do |inheritable_attribute|
55
+ instance_var = "@#{inheritable_attribute}"
56
+ current_value = instance_variable_get(instance_var)
57
+ current_value = current_value.dup unless PRIMITIVES.include?(current_value.class)
58
+ subclass.instance_variable_set(instance_var, current_value)
59
+ end
60
+ @all_children << subclass
61
+ end
62
+
63
+ end
@@ -0,0 +1,14 @@
1
+ class JsonImporter
2
+
3
+ attr_accessor :result, :type
4
+
5
+ def initialize(type)
6
+ @type = type
7
+ @result = {}
8
+ end
9
+
10
+ def import(json_text)
11
+ @result = JSON.parse(json_text)
12
+ end
13
+
14
+ end
@@ -0,0 +1,113 @@
1
+ class TextImporter
2
+
3
+ attr_accessor :remainder, :result, :type
4
+
5
+ def initialize(type)
6
+ @type = type
7
+ @result = []
8
+ @remainder = ''
9
+ @primary = nil
10
+ @heading = nil
11
+ end
12
+
13
+ def import(text)
14
+ pull_pairs(text)
15
+ eval_primary_field
16
+
17
+ @result
18
+ end
19
+
20
+ def pull_pairs(text)
21
+ last_pair_found = false
22
+
23
+ text.each_line do |line|
24
+
25
+ unless last_pair_found
26
+ possible_pair = detect_pair(line)
27
+ if possible_pair and possible_pair.captures.length == 2
28
+ key = possible_pair.captures[0].downcase.to_sym
29
+ value = possible_pair.captures[1]
30
+ number_of_lines = value.count("\n") + 1
31
+ @result << { key => value.strip }
32
+ next # don't put this in the remainder
33
+ else
34
+ last_pair_found = true
35
+ end#of if
36
+ end
37
+
38
+ @remainder << line # keep this around, it might be useful
39
+ end#of each_line
40
+
41
+ @remainder.strip!
42
+ end#of parse_pairs
43
+
44
+ def detect_pair(line)
45
+ line.strip.match(/(^[A-Za-z0-9_]+):(.+)/)
46
+ end
47
+
48
+ def eval_primary_field
49
+ unless @remainder.blank? || @type.primary_field.blank?
50
+ @primary = @remainder
51
+
52
+ eval_heading_field unless @type.heading_field.blank?
53
+
54
+ save_primary
55
+ save_heading
56
+ end
57
+ end
58
+
59
+ def eval_heading_field
60
+ # check for an <h1> at the beginning of the primary and if it's there pull it out for the heading
61
+ possible_hone = remainder.match(/(.+)\n=+\n*|^# (.+)\w*\n*/)
62
+
63
+ caps = []
64
+ caps = possible_hone.captures.compact unless possible_hone.blank?
65
+
66
+ # if it's there, then parse it out of the remainder
67
+ if !possible_hone.blank? and caps.length == 1
68
+ @heading = caps.first
69
+
70
+ ### Remove heading from text
71
+ @primary = @primary.
72
+ strip.
73
+ gsub(/^#{possible_hone.captures.first}\n=+\n*/, '').
74
+ gsub(/^# #{possible_hone.captures.first}\w*\n*/, '').
75
+ strip
76
+ end#of if
77
+ end#of eval_heading_field
78
+
79
+ def save_primary
80
+ unless @primary.blank?
81
+ # see if a pair already exists for the primary_field
82
+ existing_primary = @result.select { |pair| pair.keys.first == @type.primary_field }
83
+ existing_primary = existing_primary.first
84
+
85
+ # get rid of the primary_field if it's already there
86
+ @result.delete(existing_primary)
87
+
88
+ # if there was any existing, prepend it to remanding
89
+ @primary = existing_primary.to_s + @primary unless existing_primary.blank?
90
+
91
+ # append a new pair for the fields
92
+ @result << { @type.primary_field => @primary } unless @primary.blank?
93
+ end
94
+ end
95
+
96
+ def save_heading
97
+ unless @heading.blank?
98
+ # see if a pair already exists for the primary_field
99
+ existing_heading = @result.select { |pair| pair.keys.first == @type.heading_field }
100
+ existing_heading = existing_heading.first
101
+
102
+ # get rid of the primary_field if it's already there
103
+ @result.delete(existing_heading)
104
+
105
+ # if there was any existing, prepend it to remanding
106
+ @heading = existing_heading.to_s + @heading unless existing_heading.blank?
107
+
108
+ # append a new pair for the fields
109
+ @result << { @type.heading_field => @heading } unless @heading.blank?
110
+ end
111
+ end
112
+
113
+ end
data/lib/post_type.rb ADDED
@@ -0,0 +1,362 @@
1
+ require 'rdiscount' # markdown
2
+ require 'nokogiri'
3
+ require 'uuidtools'
4
+
5
+ class PostType
6
+ extend ClassLevelInheritableAttributes
7
+ include Extlib::Hook
8
+
9
+ DEFAULT_FIELDS = [:published_at, :status, :slug, :trackbacks, :type, :tags]
10
+
11
+ # vars to inherit down
12
+ cattr_inheritable :fields_list, :allowed_fields_list, :required_fields_list, :primary_field, :heading_field,
13
+ :specials_blocks, :defaults_blocks, :only_declared_fields, :always_use_uuid,
14
+ :truncate_slugs, :markdown_fields
15
+
16
+ # defaults for class instance inheritable vars
17
+ @fields_list = []
18
+ @allowed_fields_list = []
19
+ @required_fields_list = []
20
+ @primary_field = nil
21
+ @heading_field = nil
22
+ @specials_blocks = {}
23
+ @defaults_blocks = {}
24
+ @only_declared_fields = true
25
+ @always_use_uuid = false
26
+ @truncate_slugs = true
27
+ @markdown_fields = []
28
+
29
+
30
+ ### cattr_accessor
31
+ @@preferred_order = []
32
+
33
+ def self.preferred_order
34
+ @@preferred_order
35
+ end
36
+ def self.preferred_order=(new_order)
37
+ @@preferred_order = new_order
38
+ end
39
+ ### cattr_accessor
40
+
41
+ attr_accessor :content # where everything is stored
42
+
43
+ ### basic setup methods for types of posts
44
+ def self.fields(*list) # NOTE: this is a replacing function, not addititve like the others
45
+ self.fields_list = list.make_attrs
46
+ self.allowed_fields_list = [DEFAULT_FIELDS, list.make_attrs].flatten.uniq
47
+ end
48
+
49
+ def self.allow(*list)
50
+ self.allowed_fields_list = [self.allowed_fields_list, list.make_attrs].flatten.uniq
51
+ end
52
+
53
+ def self.required(*list)
54
+ self.required_fields_list = [
55
+ self.required_fields_list,
56
+ list.make_attrs.reject { |l| !fields_list.include? l }
57
+ ].flatten.uniq
58
+ end
59
+
60
+ def self.primary(field)
61
+ field = field.make_attr
62
+
63
+ if fields_list.include? field
64
+ self.primary_field = field
65
+ markdown field # primary is a markdown field by default
66
+ end
67
+ end
68
+
69
+ def self.heading(field)
70
+ field = field.make_attr
71
+ self.heading_field = field if fields_list.include? field
72
+ end
73
+
74
+ def self.special(field, &block)
75
+ field = field.make_attr
76
+ self.specials_blocks[field.make_attr] = block if fields_list.include? field
77
+ end
78
+
79
+ def self.default(field, &block)
80
+ field = field.make_attr
81
+ self.defaults_blocks[field.make_attr] = block if fields_list.include? field
82
+ end
83
+
84
+ def self.dynamic(field, &block)
85
+ field = field.make_attr
86
+ self.dynamic_blocks[field] = block
87
+ allow(field)
88
+ end
89
+
90
+ def self.markdown(*list)
91
+ self.markdown_fields = [
92
+ self.markdown_fields,
93
+ list.make_attrs.reject { |l| !fields_list.include? l }
94
+ ].flatten.uniq
95
+
96
+ self.allowed_fields_list = [
97
+ self.allowed_fields_list,
98
+ self.markdown_fields.collect { |m| m.html }
99
+ ].flatten.uniq
100
+ end
101
+
102
+ def set_attr(key, value)
103
+ key = key.make_attr
104
+
105
+ unless value.blank?
106
+ @content[key] = value
107
+
108
+ if self.class.markdown_fields.include?(key)
109
+ markdown = Markdown.new @content[key].strip
110
+ @content[key.html] = markdown.to_html.strip
111
+ else
112
+ @content.delete(key.html)
113
+ end
114
+ else
115
+ @content.delete(key)
116
+ @content.delete(key.html)
117
+ end
118
+ end
119
+
120
+ def set_default(key, value)
121
+ set_attr(key, value) if blank_attr?(key)
122
+ end
123
+
124
+ def blank_attr?(key)
125
+ get_attr(key).blank?
126
+ end
127
+
128
+ alias :get_attr? :blank_attr?
129
+
130
+ def get_attr(key, html = true)
131
+ key = key.make_attr
132
+
133
+ if html && self.class.markdown_fields.include?(key)
134
+ @content[key.html]
135
+ else
136
+ @content[key]
137
+ end
138
+ end
139
+
140
+ def delete_attr(key)
141
+ set_attr(key.make_attr, nil)
142
+ end
143
+
144
+ alias :remove_attr :delete_attr
145
+ alias :del_attr :delete_attr
146
+
147
+ # can be overriden to provide auto detection of type from a block of text
148
+ #
149
+ # Examples:
150
+ # def self.detect?(text)
151
+ # has_keys? text, :title, :body
152
+ # end
153
+ #
154
+ # def self.detect?(text)
155
+ # has_required? text
156
+ # end
157
+ #
158
+ # def self.detect?(text)
159
+ # has_one_or_more? text, :me
160
+ # end
161
+ #
162
+ def self.detect?(text)
163
+ false
164
+ end
165
+
166
+ # useful for detection
167
+ def self.has_keys?(text, *fields)
168
+ needed = fields.make_attrs
169
+ get_pairs_count(text, needed).length == needed.length
170
+ end
171
+
172
+ def self.has_more_than_one?(text, field)
173
+ has_more_than? text, field, 1
174
+ end
175
+
176
+ def self.has_one_or_more?(text, field)
177
+ has_more_than? text, field, 0
178
+ end
179
+
180
+ def self.has_more_than?(text, field, amount)
181
+ get_pairs_count(text, [field]).length > amount
182
+ end
183
+
184
+ def self.get_pairs(text)
185
+ TextImporter.new(self).import(text)
186
+ end
187
+
188
+ def self.get_pairs_count(text, fields)
189
+ pairs = get_pairs(text)
190
+ pairs.reject { |pair| !fields.include?(pair.keys.first) }
191
+ end
192
+
193
+ def self.has_required?(text)
194
+ has_keys? text, *self.required_fields_list
195
+ end
196
+
197
+ # runs through the list of children looking for one that will work
198
+ def self.auto_detect(text)
199
+ list = self.preferred_order.blank? ? @all_children : self.preferred_order
200
+
201
+ list.each { |l| return l.new(text) if l.detect?(text) }
202
+ end
203
+
204
+ def content=(stuff) # !> method redefined; discarding old content=
205
+ @content = { :type => self.class.name.to_s }
206
+ import(stuff)
207
+ eval_specials
208
+ eval_defaults
209
+ parse_tags unless blank_attr?(:tags)
210
+ generate_slug if get_attr?(:slug)
211
+ @content
212
+ end#of content=
213
+
214
+ def valid? # TODO: this doesn't work if there are no required fields and the slug is not unique
215
+ v = true
216
+
217
+ if self.class.required_fields_list.blank?
218
+ v = false unless self.class.required_fields_list.reject { |item| !get_attr(item).blank? }.blank?
219
+ end
220
+
221
+ v = false unless slug_is_unique
222
+
223
+ v
224
+ end
225
+
226
+ def initialize(text = nil)
227
+ if text
228
+ self.content = text
229
+ # sanitize_content_fields
230
+ end
231
+ end
232
+
233
+ def save
234
+ if valid?
235
+ truncate_slug if self.class.truncate_slugs
236
+ fill_default_fields
237
+ send_to_storage
238
+ else
239
+ false
240
+ end
241
+ end
242
+
243
+ # TODO: how to determine the type of stuff? (text, json, yaml, image, video, photo, pdf, generic download file (can lookup type of file for icon if needed))
244
+ def import(stuff, type = :text)
245
+ importer = Kernel.const_get(type.to_s.camel_case+'Importer').new(self.class)
246
+
247
+ # The result sent back by an importer is either:
248
+ # Array:
249
+ # [{ :one => 'stuff' }, { :two => 'stuff' }]
250
+ # Hash:
251
+ # { :one => 'stuff', :two => 'stuff' }
252
+ result = importer.import(stuff)
253
+
254
+ case result
255
+ when Array
256
+ commit_array(result)
257
+ when Hash
258
+ commit_hash(result)
259
+ end
260
+ end
261
+
262
+ def commit_hash(pairs_hash)
263
+ pairs_hash.each do |key, value|
264
+ set_attr(key, value)
265
+ end
266
+ end
267
+
268
+ def commit_array(pairs_array)
269
+ pairs_array.each do |pairs_hash|
270
+ commit_hash(pairs_hash)
271
+ end
272
+ end
273
+
274
+ def eval_defaults
275
+ if valid?
276
+ self.class.defaults_blocks.each do |key, block|
277
+ set_default(key, self.instance_eval(&block))
278
+ end
279
+ end
280
+ end
281
+
282
+ def eval_specials
283
+ self.class.specials_blocks.each do |key, block|
284
+ unless get_attr(key).blank?
285
+ set_attr(key, block.call(get_attr(key)))
286
+ end
287
+ end
288
+ end
289
+
290
+ def parse_tags
291
+ if get_attr(:tags).class == String
292
+ tags_array = get_attr(:tags).split(',').collect { |t| t.strip }
293
+ set_attr(:tags, tags_array)
294
+ end
295
+ end
296
+
297
+ def sanitize_content_fields
298
+ @content.reject! { |key, value| !self.class.allowed_fields_list.include?(key) }
299
+ end
300
+
301
+ def send_to_storage # send to the db or whatever
302
+ false
303
+ end
304
+
305
+ def slug_is_unique # validate uniqueness
306
+ true
307
+ end
308
+
309
+ def fill_default_fields
310
+ set_default(:published_at, Time.now.utc)
311
+ set_default(:status, default_status)
312
+ end
313
+
314
+ def generate_slug # OPTIMIZE: this slug generation is ugly
315
+ result = ''
316
+
317
+ unless self.class.always_use_uuid
318
+ result = get_attr(self.class.heading_field, false).to_s.dup unless self.class.heading_field.blank?
319
+
320
+ if result.blank?
321
+ result = get_attr(self.class.primary_field, false).to_s.dup unless self.class.primary_field.blank?
322
+ end
323
+
324
+ if result.blank?
325
+ self.class.required_fields_list.each do |required_field|
326
+ unless get_attr(required_field).blank?
327
+ result = get_attr(required_field).to_s.dup
328
+ break
329
+ end#of unless
330
+ end#of each
331
+ end#of if
332
+
333
+ result.slugify!
334
+ end#of unless
335
+
336
+ if result.blank?
337
+ result = uuid
338
+ end
339
+
340
+ set_attr(:slug, result)
341
+ end
342
+
343
+ def truncate_slug(letter_count = 50)
344
+ unless get_attr(:slug).blank?
345
+ new_slug = get_attr(:slug).gsub(/^(.{#{letter_count}})(.*)/) { $1.slugify }
346
+ set_attr(:slug, new_slug)
347
+ end
348
+ end
349
+
350
+ def default_status
351
+ :published
352
+ end
353
+
354
+ def post_to_trackbacks
355
+ false
356
+ end
357
+
358
+ def uuid
359
+ UUID.timestamp_create.to_s
360
+ end
361
+
362
+ end
@@ -0,0 +1,10 @@
1
+ class Article < PostType
2
+ fields :title, :body, :category
3
+ required :body
4
+ primary :body
5
+ heading :title
6
+
7
+ def self.detect?(text)
8
+ true # it can always be an article, so make this last in the preferred order
9
+ end
10
+ end
@@ -0,0 +1,10 @@
1
+ class Audio < PostType
2
+ fields :audio_url, :title, :description, :embed
3
+ required :audio_url
4
+ primary :description
5
+ heading :title
6
+
7
+ def self.detect?(text)
8
+ has_required? text
9
+ end
10
+ end
data/lib/types/chat.rb ADDED
@@ -0,0 +1,29 @@
1
+ class Chat < PostType
2
+ fields :transcript
3
+ required :transcript
4
+
5
+ # override commit_pairs to make chat transcripts form the Me: pairs stuff
6
+ def commit_array(pairs_array)
7
+ transcript = []
8
+
9
+ pairs_array.each do |pairs_hash|
10
+
11
+ pairs_hash.each do |key, value|
12
+
13
+ unless self.class.allowed_fields_list.include?(key)
14
+ transcript << { key => value }
15
+ else
16
+ set_attr(key, value)
17
+ end
18
+ end#of each
19
+
20
+ end#of each
21
+
22
+ set_attr(:transcript, transcript)
23
+
24
+ end#of commit_pairs
25
+
26
+ def self.detect?(text)
27
+ has_one_or_more? text, :me
28
+ end
29
+ end#of Chat
data/lib/types/link.rb ADDED
@@ -0,0 +1,26 @@
1
+ class Link < PostType
2
+ fields :url, :title, :description
3
+ required :url
4
+ primary :description
5
+ heading :title
6
+
7
+ special :url do |link_content|
8
+ 'http://' + link_content.gsub(/^http:\/\//, '')
9
+ end
10
+
11
+ default :title do
12
+ result = ''
13
+
14
+ begin
15
+ doc = Nokogiri::HTML(open(get_attr(:url)))
16
+ doc.css('title').each { |t| result = t.content }
17
+ rescue Exception => e
18
+ end
19
+
20
+ result
21
+ end
22
+
23
+ def self.detect?(text)
24
+ has_required? text
25
+ end
26
+ end
@@ -0,0 +1,9 @@
1
+ class Photo < PostType
2
+ fields :photo_url, :caption, :embed
3
+ required :photo_url
4
+ primary :caption
5
+
6
+ def self.detect?(text)
7
+ has_required? text
8
+ end
9
+ end
@@ -0,0 +1,27 @@
1
+ class Quote < PostType
2
+ fields :quote, :source
3
+ required :quote
4
+ primary :quote
5
+
6
+ special :quote do |quote_content|
7
+ unless (quote_content =~ /^<blockquote/).nil?
8
+ doc = Nokogiri::HTML(quote_content)
9
+ doc.css('blockquote').each { |q| quote_content = q.content }
10
+ else
11
+ quote_content = ''
12
+ end
13
+ quote_content.strip
14
+ end
15
+
16
+ def self.detect?(text)
17
+ pairs = get_pairs(text)
18
+
19
+ the_quote = pairs.select { |pair| pair.keys.first == :quote }
20
+ the_quote = the_quote.first || {}
21
+
22
+ markdown = Markdown.new(the_quote[:quote])
23
+ quote_html = markdown.to_html.strip
24
+
25
+ !(text =~ /Quote: (.*)/).nil? || !(quote_html =~ /^<blockquote(.+)<\/blockquote>$/m).nil?
26
+ end
27
+ end
@@ -0,0 +1,14 @@
1
+ class Review < PostType
2
+ fields :rating, :item, :description
3
+ required :rating, :item
4
+ primary :description
5
+ heading :item
6
+
7
+ special :rating do |rating_content|
8
+ rating_content.to_f
9
+ end
10
+
11
+ def self.detect?(text)
12
+ has_required? text
13
+ end
14
+ end
@@ -0,0 +1,10 @@
1
+ class Video < PostType
2
+ fields :video_url, :title, :description, :embed
3
+ required :video_url
4
+ primary :description
5
+ heading :title
6
+
7
+ def self.detect?(text)
8
+ has_required? text
9
+ end
10
+ end
@@ -0,0 +1,174 @@
1
+ require File.join(File.dirname(__FILE__), 'spec_helper.rb')
2
+
3
+ describe PostType do
4
+
5
+ before do
6
+ class Yellow; end
7
+ Object.send(:remove_const, :Yellow)
8
+ end
9
+
10
+ should "have empty content when new" do
11
+ PostType.new.content.should == nil
12
+ end
13
+
14
+ should "have a preferred order array" do
15
+ PostType.preferred_order.should == [Video, Audio, Photo, Chat, Review, Link, Quote, Article]
16
+ end
17
+
18
+ should "set fields" do
19
+ class Yellow < PostType
20
+ fields :one, :two
21
+ end
22
+
23
+ Yellow.fields_list.should == [:one, :two]
24
+ end
25
+
26
+ should "set allowed fields" do
27
+ class Yellow < PostType
28
+ fields :one
29
+ allow :anyone, :noone
30
+ end
31
+
32
+ Yellow.allowed_fields_list.should == [Yellow::DEFAULT_FIELDS, :one, :anyone, :noone].flatten
33
+ end
34
+
35
+ should "set required fields" do
36
+ class Yellow < PostType
37
+ fields :one, :two, :three
38
+ required :one, :two
39
+ end
40
+
41
+ Yellow.required_fields_list.should == [:one, :two]
42
+ end
43
+
44
+ should "set primary field" do
45
+ class Yellow < PostType
46
+ fields :one, :two
47
+ primary :one
48
+ end
49
+
50
+ Yellow.primary_field.should == :one
51
+ end
52
+
53
+ # should use primary field
54
+ # should make primary field markdown automatically
55
+
56
+ should "set heading field" do
57
+ class Yellow < PostType
58
+ fields :one, :two
59
+ heading :one
60
+ end
61
+
62
+ Yellow.heading_field.should == :one
63
+ end
64
+
65
+ should "use heading field" do
66
+ class Yellow < PostType
67
+ fields :one, :two
68
+ primary :one
69
+ heading :two
70
+ end
71
+
72
+ y = Yellow.new "Hello Two
73
+ =========
74
+
75
+ This will be in One."
76
+
77
+ y.get_attr(:one).should == "<p>This will be in One.</p>"
78
+ y.get_attr(:two).should == "Hello Two"
79
+ end
80
+
81
+ should "set special values" do
82
+ class Yellow < PostType
83
+ fields :plus_one, :two
84
+
85
+ special :plus_one do |field_value|
86
+ field_value + 1
87
+ end
88
+ end
89
+
90
+ Yellow.specials_blocks[:plus_one].should.not.be.blank?
91
+ end
92
+
93
+ should "use special values" do
94
+ class Yellow < PostType
95
+ fields :one, :two
96
+
97
+ special :one do |field_value|
98
+ field_value.to_i + 1
99
+ end
100
+ end
101
+
102
+ y = Yellow.new "One: 2\n\nhello"
103
+
104
+ y.get_attr(:one).should == 3
105
+ end
106
+
107
+ should "set default values" do
108
+ class Yellow < PostType
109
+ fields :one, :two
110
+
111
+ default(:one) { 1 }
112
+ end
113
+
114
+ Yellow.defaults_blocks[:one].call.should == 1
115
+ end
116
+
117
+ should "use default values" do
118
+ class Yellow < PostType
119
+ fields :one, :two
120
+
121
+ default :two do
122
+ 2
123
+ end
124
+ end
125
+
126
+ y = Yellow.new 'hello'
127
+
128
+ y.get_attr(:two).should == 2
129
+ end
130
+
131
+ should "use markdown fields" do
132
+ class Yellow < PostType
133
+ fields :one, :two
134
+ markdown :two
135
+ end
136
+
137
+ Yellow.markdown_fields.should == [:two]
138
+ Yellow.allowed_fields_list.should == [Yellow::DEFAULT_FIELDS, :one, :two, :two_html].flatten
139
+ end
140
+
141
+ should "use markdown fields" do
142
+ class Yellow < PostType
143
+ fields :one, :two
144
+ markdown :two
145
+ end
146
+
147
+ y = Yellow.new "Two: *hahaha*"
148
+
149
+ y.get_attr(:two).should == "<p><em>hahaha</em></p>"
150
+ end
151
+
152
+ should "allow setting and getting of an attribute of the content" do
153
+ class Yellow < PostType; end
154
+ y = Yellow.new 'stuff'
155
+ y.set_attr(:first, 'woo hoo')
156
+ y.get_attr(:first).should == 'woo hoo'
157
+ end
158
+
159
+ should "not keep empty attributes" do
160
+ class Yellow < PostType
161
+ fields :one
162
+ primary :one
163
+ end
164
+
165
+ y = Yellow.new 'stuff'
166
+ y.set_attr(:one, '')
167
+ y.get_attr(:one).should.be.nil
168
+ end
169
+
170
+ # should auto_detect ?
171
+ # should save ?
172
+ # should generate slug
173
+
174
+ end
@@ -0,0 +1,25 @@
1
+ require File.join(File.dirname(__FILE__), '../spec_helper.rb')
2
+
3
+ describe Chat do
4
+
5
+ before do
6
+ @good_chat = "You: Hello\nMe: Hello back at ya!\nYou: Wanna go eat\nYou: Somehwere\nMe: Yes!"
7
+ @bad_chat = "Rating: 4\nItem: Dyson\n\nThis should be the description."
8
+
9
+ class Yellow; end
10
+ Object.send(:remove_const, :Yellow)
11
+ end
12
+
13
+ should "detect a well-formed chat" do
14
+ Chat.detect?(@good_chat).should.be.true
15
+ end
16
+
17
+ should "not detect a malformed chat" do
18
+ Chat.detect?(@bad_chat).should.be.false
19
+ end
20
+
21
+ should "autodetect a well-formed chat" do
22
+ PostType.auto_detect(@good_chat).should.be.kind_of Chat
23
+ end
24
+
25
+ end
@@ -0,0 +1,25 @@
1
+ require File.join(File.dirname(__FILE__), '../spec_helper.rb')
2
+
3
+ describe Link do
4
+
5
+ before do
6
+ @good_link = "URL: google.com\n\nThis should be the description."
7
+ @bad_link = "Rating: 4\nItem: Dyson\n\nThis should be the description."
8
+
9
+ class Yellow; end
10
+ Object.send(:remove_const, :Yellow)
11
+ end
12
+
13
+ should "detect a well-formed link" do
14
+ Link.detect?(@good_link).should.be.true
15
+ end
16
+
17
+ should "not detect a malformed link" do
18
+ Link.detect?(@bad_link).should.be.false
19
+ end
20
+
21
+ should "autodetect a well-formed link" do
22
+ PostType.auto_detect(@good_link).should.be.kind_of Link
23
+ end
24
+
25
+ end
@@ -0,0 +1,35 @@
1
+ require File.join(File.dirname(__FILE__), '../spec_helper.rb')
2
+
3
+ describe Quote do
4
+
5
+ before do
6
+ @good_quote = "> This should be a quote."
7
+ @good_quote_with_source = "Source: George Washington\n\n> This is what he said."
8
+ @good_quote_all_pairs = "Quote: Hello\nSource: Me"
9
+ @bad_quote = "Rating: 4\nItem: Dyson\n\nThis should be the description."
10
+
11
+ class Yellow; end
12
+ Object.send(:remove_const, :Yellow)
13
+ end
14
+
15
+ should "detect a well-formed quote through markdown" do
16
+ Quote.detect?(@good_quote).should.be.true
17
+ end
18
+
19
+ should "detect a well-formed quote with source" do
20
+ Quote.detect?(@good_quote_with_source).should.be.true
21
+ end
22
+
23
+ should "detect a well-formed quote if it's all pairs" do
24
+ Quote.detect?(@good_quote_all_pairs).should.be.true
25
+ end
26
+
27
+ should "not detect a malformed quote" do
28
+ Quote.detect?(@bad_quote).should.be.false
29
+ end
30
+
31
+ should "autodetect a well-formed quote" do
32
+ PostType.auto_detect(@good_quote).should.be.kind_of Quote
33
+ end
34
+
35
+ end
@@ -0,0 +1,30 @@
1
+ require File.join(File.dirname(__FILE__), '../spec_helper.rb')
2
+
3
+ describe Review do
4
+
5
+ before do
6
+ @good_review = "Rating: 4\nItem: Dyson\n\nThis should be the description."
7
+ @bad_review = "You: Hello\nMe: Hello back at ya!\nYou: Wanna go eat\nYou: Somehwere\nMe: Yes!"
8
+ @almost_good_review = "Rating: 4\n\nThis should be the description."
9
+
10
+ class Yellow; end
11
+ Object.send(:remove_const, :Yellow)
12
+ end
13
+
14
+ should "detect a well-formed review" do
15
+ Review.detect?(@good_review).should.be.true
16
+ end
17
+
18
+ should "not detect a malformed review" do
19
+ Review.detect?(@bad_review).should.be.false
20
+ end
21
+
22
+ should "not detect an almost good review" do
23
+ Review.detect?(@almost_good_review).should.be.false
24
+ end
25
+
26
+ should "autodetect a well-formed review" do
27
+ PostType.auto_detect(@good_review).should.be.kind_of Review
28
+ end
29
+
30
+ end
data/spec/spec.opts ADDED
File without changes
@@ -0,0 +1,60 @@
1
+ require "rubygems"
2
+ require 'bacon'
3
+ require 'mocha'
4
+
5
+ require File.expand_path(File.join(__FILE__.split('/spec').first, 'turbine-core.rb'))
6
+
7
+ PostType.preferred_order = [Video, Audio, Photo, Chat, Review, Link, Quote, Article]
8
+
9
+ ### run specs with `bacon spec/**/*.rb spec/*.rb`
10
+
11
+
12
+
13
+ # from 1 to 10 how likely, 1 being not very likely and 10 being all the time
14
+ def do_i?(i = 5)
15
+ rand(500) < 50 * i
16
+ end
17
+
18
+
19
+
20
+
21
+ ##
22
+ # Hash additions.
23
+ #
24
+ # From
25
+ # * http://wincent.com/knowledge-base/Fixtures_considered_harmful%3F
26
+ # * Neil Rahilly
27
+
28
+ class Hash
29
+
30
+ ##
31
+ # Filter keys out of a Hash.
32
+ #
33
+ # { :a => 1, :b => 2, :c => 3 }.except(:a)
34
+ # => { :b => 2, :c => 3 }
35
+
36
+ def except(*keys)
37
+ self.reject { |k,v| keys.include?(k || k.to_sym) }
38
+ end
39
+
40
+ ##
41
+ # Override some keys.
42
+ #
43
+ # { :a => 1, :b => 2, :c => 3 }.with(:a => 4)
44
+ # => { :a => 4, :b => 2, :c => 3 }
45
+
46
+ def with(overrides = {})
47
+ self.merge overrides
48
+ end
49
+
50
+ ##
51
+ # Returns a Hash with only the pairs identified by +keys+.
52
+ #
53
+ # { :a => 1, :b => 2, :c => 3 }.only(:a)
54
+ # => { :a => 1 }
55
+
56
+ def only(*keys)
57
+ self.reject { |k,v| !keys.include?(k || k.to_sym) }
58
+ end
59
+
60
+ end
metadata ADDED
@@ -0,0 +1,79 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: myobie-turbine-core
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.1.0
5
+ platform: ruby
6
+ authors:
7
+ - Nathan Herald
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-04-12 00:00:00 -07:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: TODO
17
+ email: nathan@myobie.com
18
+ executables: []
19
+
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README
24
+ files:
25
+ - Rakefile
26
+ - VERSION.yml
27
+ - lib/ext.rb
28
+ - lib/importers/json_importer.rb
29
+ - lib/importers/text_importer.rb
30
+ - lib/post_type.rb
31
+ - lib/types/article.rb
32
+ - lib/types/audio.rb
33
+ - lib/types/chat.rb
34
+ - lib/types/link.rb
35
+ - lib/types/photo.rb
36
+ - lib/types/quote.rb
37
+ - lib/types/review.rb
38
+ - lib/types/video.rb
39
+ - spec/post_type_spec.rb
40
+ - spec/post_types/chat_spec.rb
41
+ - spec/post_types/link_spec.rb
42
+ - spec/post_types/quote_spec.rb
43
+ - spec/post_types/review_spec.rb
44
+ - spec/spec.opts
45
+ - spec/spec_helper.rb
46
+ - README
47
+ has_rdoc: true
48
+ homepage: http://github.com/myobie/turbine-core
49
+ post_install_message:
50
+ rdoc_options:
51
+ - --charset=UTF-8
52
+ require_paths:
53
+ - lib
54
+ required_ruby_version: !ruby/object:Gem::Requirement
55
+ requirements:
56
+ - - ">="
57
+ - !ruby/object:Gem::Version
58
+ version: "0"
59
+ version:
60
+ required_rubygems_version: !ruby/object:Gem::Requirement
61
+ requirements:
62
+ - - ">="
63
+ - !ruby/object:Gem::Version
64
+ version: "0"
65
+ version:
66
+ requirements: []
67
+
68
+ rubyforge_project:
69
+ rubygems_version: 1.2.0
70
+ signing_key:
71
+ specification_version: 2
72
+ summary: TODO
73
+ test_files:
74
+ - spec/post_type_spec.rb
75
+ - spec/post_types/chat_spec.rb
76
+ - spec/post_types/link_spec.rb
77
+ - spec/post_types/quote_spec.rb
78
+ - spec/post_types/review_spec.rb
79
+ - spec/spec_helper.rb