myobie-turbine-core 0.1.0

Sign up to get free protection for your applications and to get access to all the features.
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