pluto-models 1.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.
@@ -0,0 +1,81 @@
1
+ module Pluto
2
+
3
+ class Formatter
4
+
5
+ include LogUtils::Logging
6
+
7
+ include Models
8
+ include ManifestHelper
9
+
10
+ include TextUtils::DateHelper # e.g. lets us use time_ago_in_words
11
+ include TextUtils::HypertextHelper # e.g. lets us use link_to, strip_tags, sanitize, textify, etc.
12
+
13
+ def initialize( opts, config )
14
+ @opts = opts
15
+ @config = config
16
+ end
17
+
18
+ attr_reader :opts, :config, :site
19
+
20
+
21
+ def run( arg )
22
+ ### remove - always use make( site_key )
23
+ ## fix: change arg to planet_key or just key or similar
24
+ # todo: rename run to some less generic - merge/build/etc. ??
25
+
26
+ site_key = arg
27
+ site_key = site_key.downcase.gsub('.ini','').gsub('.yml','') # remove .ini|.yml extension if present
28
+
29
+ manifest_name = opts.manifest
30
+ output_path = opts.output_path
31
+
32
+ make_for(site_key, manifest_name, output_path )
33
+ end
34
+
35
+
36
+ def make_for( site_key, manifest_name, output_path )
37
+
38
+ ## fix: remove reference to opts
39
+ ## - e.g. now still used for auto-installer
40
+
41
+ manifest_name = manifest_name.downcase.gsub('.txt', '' ) # remove .txt if present
42
+
43
+ logger.debug "manifest=#{manifest_name}"
44
+
45
+ # check for matching manifests
46
+ manifests = installed_template_manifests.select { |m| m[0] == manifest_name+'.txt' }
47
+
48
+ if manifests.empty?
49
+
50
+ ### try - autodownload
51
+ puts "*** template pack '#{manifest_name}' not found; trying auto-install..."
52
+
53
+ Installer.new( opts ).install( manifest_name )
54
+
55
+ ### try again
56
+
57
+ # check for matching manifests
58
+ manifests = installed_template_manifests.select { |m| m[0] == manifest_name+'.txt' }
59
+
60
+ if manifests.empty?
61
+ puts "*** error: unknown template pack '#{manifest_name}'; use pluto ls to list installed template packs"
62
+ exit 2
63
+ end
64
+ end
65
+
66
+ manifestsrc = manifests[0][1]
67
+ pakpath = output_path
68
+
69
+ @site = Site.find_by_key( site_key )
70
+ if @site.nil?
71
+ puts "*** warn: no site with key '#{site_key}' found; using untitled site record"
72
+ @site = Site.new
73
+ @site.title = 'Planet Untitled'
74
+ end
75
+
76
+ Pakman::Templater.new.merge_pak( manifestsrc, pakpath, binding, site_key )
77
+ end
78
+
79
+ end # class Formatter
80
+
81
+ end # module Pluto
@@ -0,0 +1,58 @@
1
+ module Pluto
2
+
3
+ class Installer
4
+
5
+ ### fix: remove opts, use config (wrapped!!)
6
+
7
+ include LogUtils::Logging
8
+
9
+ def initialize( opts )
10
+ @opts = opts
11
+ end
12
+
13
+ attr_reader :opts
14
+
15
+
16
+ def install( shortcut_or_source )
17
+
18
+ logger.debug "fetch >#{shortcut_or_source}<"
19
+
20
+ ## check for builtin shortcut (assume no / or \)
21
+ if shortcut_or_source.index( '/' ).nil? && shortcut_or_source.index( '\\' ).nil?
22
+ shortcut = shortcut_or_source
23
+ sources = opts.map_fetch_shortcut( shortcut )
24
+
25
+ if sources.empty?
26
+ puts "** Error: No mapping found for shortcut '#{shortcut}'."
27
+ return
28
+ end
29
+ puts " Mapping fetch shortcut '#{shortcut}' to: #{sources.join(',')}"
30
+ else
31
+ sources = [shortcut_or_source] # pass arg through unmapped
32
+ end
33
+
34
+ sources.each do |source|
35
+ install_template( source )
36
+ end
37
+
38
+ end # method run
39
+
40
+
41
+ def install_template( src )
42
+ # src = 'http://github.com/geraldb/slideshow/raw/d98e5b02b87ee66485431b1bee8fb6378297bfe4/code/templates/fullerscreen.txt'
43
+ # src = 'http://github.com/geraldb/sandbox/raw/13d4fec0908fbfcc456b74dfe2f88621614b5244/s5blank/s5blank.txt'
44
+ uri = URI.parse( src )
45
+ logger.debug "scheme: #{uri.scheme}, host: #{uri.host}, port: #{uri.port}, path: #{uri.path}"
46
+
47
+ pakname = File.basename( uri.path ).downcase.gsub('.txt','')
48
+ pakpath = File.expand_path( "#{opts.config_path}/#{pakname}" )
49
+
50
+ logger.debug "packname >#{pakname}<"
51
+ logger.debug "pakpath >#{pakpath}<"
52
+
53
+ Pakman::Fetcher.new.fetch_pak( src, pakpath )
54
+ end
55
+
56
+ end # class Installer
57
+
58
+ end # module Pluto
@@ -0,0 +1,42 @@
1
+ module Pluto
2
+
3
+ class Lister
4
+
5
+ include LogUtils::Logging
6
+
7
+ include ManifestHelper
8
+
9
+ def initialize( opts )
10
+ @opts = opts
11
+ end
12
+
13
+ attr_reader :opts
14
+
15
+ def list
16
+ home = Env.home
17
+ ## replace home w/ ~ (to make out more readable (shorter))
18
+ ## e.g. use gsub( home, '~' )
19
+
20
+ puts ''
21
+ puts 'Installed template packs in search path'
22
+
23
+ installed_template_manifest_patterns.each_with_index do |pattern,i|
24
+ puts " [#{i+1}] #{pattern.gsub(home,'~')}"
25
+ end
26
+ puts ' include:'
27
+
28
+ manifests = installed_template_manifests
29
+ if manifests.empty?
30
+ puts " -- none --"
31
+ else
32
+ manifests.each do |manifest|
33
+ pakname = manifest[0].gsub('.txt','')
34
+ manifestpath = manifest[1].gsub(home,'~')
35
+ puts "%16s (%s)" % [pakname,manifestpath]
36
+ end
37
+ end
38
+ end # method list
39
+
40
+ end # class Lister
41
+
42
+ end # module Pluto
@@ -0,0 +1,51 @@
1
+ module Pluto
2
+
3
+ module ManifestHelper
4
+
5
+ ## shared methods for handling manifest lookups
6
+ ##
7
+ # note: required attribs (in host class) include:
8
+ # - opts.config_path
9
+
10
+ def installed_template_manifest_patterns
11
+
12
+ # 1) search . # that is, working/current dir
13
+ # 2) search <config_dir>
14
+ # 3) search <gem>/templates
15
+
16
+ ###
17
+ # Note
18
+ # -- for now - no longer ship w/ builtin template packs
19
+ # - download on demand if needed
20
+
21
+ builtin_patterns = [
22
+ ## "#{Pluto.root}/templates/*.txt"
23
+ ]
24
+ config_patterns = [
25
+ ## "#{File.expand_path(opts.config_path)}/*.txt",
26
+ "#{File.expand_path(opts.config_path)}/*/*.txt"
27
+ ]
28
+ current_patterns = [
29
+ ## "*.txt",
30
+ "*/*.txt",
31
+ "node_modules/*/*.txt", # note: add support for npm installs - use/make slideshow required in name? for namespace in the future???
32
+ ]
33
+
34
+ patterns = []
35
+ patterns += current_patterns
36
+ patterns += config_patterns
37
+ patterns += builtin_patterns
38
+ end
39
+
40
+ def installed_template_manifests
41
+ excludes = [
42
+ "Manifest.txt",
43
+ "*/Manifest.txt"
44
+ ]
45
+
46
+ Pakman::Finder.new.find_manifests( installed_template_manifest_patterns, excludes )
47
+ end
48
+
49
+
50
+ end # module Manifest
51
+ end # module Slideshow
@@ -0,0 +1,130 @@
1
+ # encoding: utf-8
2
+
3
+ # core and stdlibs
4
+
5
+ require 'yaml'
6
+ require 'json'
7
+ require 'uri'
8
+ require 'pp'
9
+ require 'fileutils'
10
+ require 'logger'
11
+ require 'date'
12
+ require 'digest/md5'
13
+
14
+
15
+ # 3rd party ruby gems/libs
16
+
17
+ require 'active_record' ## todo: add sqlite3? etc.
18
+
19
+ require 'logutils'
20
+ require 'props' # manage settings/env
21
+ require 'fetcher' # fetch (download) files
22
+ require 'pakman' # template pack manager
23
+ require 'feedutils'
24
+ require 'textutils'
25
+
26
+ require 'activityutils'
27
+
28
+ require 'props/activerecord'
29
+ require 'logutils/activerecord'
30
+
31
+
32
+ # our own code
33
+
34
+ require 'pluto/version' # note: let version always get first
35
+ require 'pluto/schema'
36
+ require 'pluto/activerecord'
37
+
38
+ require 'pluto/models/activity'
39
+ require 'pluto/models/feed'
40
+ require 'pluto/models/item'
41
+ require 'pluto/models/site'
42
+ require 'pluto/models/subscription'
43
+ require 'pluto/models/utils'
44
+
45
+ require 'pluto/manifest_helpers'
46
+ require 'pluto/connecter'
47
+
48
+ require 'pluto/installer'
49
+ require 'pluto/fetcher'
50
+ require 'pluto/refresher'
51
+ require 'pluto/subscriber'
52
+ require 'pluto/updater'
53
+ require 'pluto/lister'
54
+ require 'pluto/formatter'
55
+
56
+
57
+ module Pluto
58
+
59
+ def self.connect!( config=nil ) # convenience shortcut
60
+ Connecter.new.connect!( config )
61
+ end
62
+
63
+
64
+ # todo: add alias update_site( config ) ??
65
+ def self.update_subscriptions( config )
66
+ Subscriber.new.update_subscriptions( config )
67
+ end
68
+
69
+ def self.update_feeds
70
+ Refresher.new.update_feeds
71
+ end
72
+
73
+ def self.update_sites
74
+ Refresher.new.update_sites
75
+ end
76
+
77
+ def self.load_tasks
78
+ # load all builtin Rake tasks (from tasks/*rake)
79
+ load 'pluto/tasks/env.rake'
80
+ load 'pluto/tasks/setup.rake'
81
+ load 'pluto/tasks/stats.rake'
82
+ load 'pluto/tasks/update.rake'
83
+ end
84
+
85
+ end # module Pluto
86
+
87
+
88
+
89
+ ######
90
+ # todo - move to ext/array.rb or similar
91
+
92
+ class Array
93
+
94
+ ## todo: check if there's already a builtin method for this
95
+ #
96
+ # note:
97
+ # in rails ary.in_groups(3) results in
98
+ # top-to-bottom, left-to-right.
99
+ # and not left-to-right first and than top-to-bottom.
100
+ #
101
+ # rename to in_groups_vertical(3) ???
102
+
103
+ def in_columns( cols ) # alias for convenience for chunks - needed? why? why not?
104
+ chunks( cols )
105
+ end
106
+
107
+ def chunks( number_of_chunks )
108
+ ## NB: use chunks - columns might be in use by ActiveRecord!
109
+ ###
110
+ # e.g.
111
+ # [1,2,3,4,5,6,7,8,9,10].columns(3)
112
+ # becomes:
113
+ # [[1,4,7,10],
114
+ # [2,5,8],
115
+ # [3,6,9]]
116
+
117
+ ## check/todo: make a copy of the array first??
118
+ # for now reference to original items get added to columns
119
+ chunks = (1..number_of_chunks).collect { [] }
120
+ each_with_index do |item,index|
121
+ chunks[ index % number_of_chunks ] << item
122
+ end
123
+ chunks
124
+ end
125
+
126
+ end
127
+
128
+
129
+ # say hello
130
+ puts Pluto.banner if $DEBUG || (defined?($RUBYLIBS_DEBUG) && $RUBYLIBS_DEBUG)
@@ -0,0 +1,8 @@
1
+ module Pluto
2
+ module Models
3
+
4
+ # add shortcut/alias
5
+ Activity = ActivityDb::Models::Activity
6
+
7
+ end # module Models
8
+ end # module Pluto
@@ -0,0 +1,127 @@
1
+ module Pluto
2
+ module Models
3
+
4
+ class Feed < ActiveRecord::Base
5
+ self.table_name = 'feeds'
6
+
7
+ include Pluto::ActiveRecordMethods # e.g. read_attribute_w_fallbacks
8
+
9
+ has_many :items
10
+ has_many :subscriptions
11
+ has_many :sites, :through => :subscriptions
12
+
13
+
14
+ def self.latest
15
+ # note: order by first non-null datetime field
16
+ # coalesce - supported by sqlite (yes), postgres (yes)
17
+
18
+ # note: if not published, touched or built use hardcoded 1971-01-01 for now
19
+ ## order( "coalesce(published,touched,built,'1971-01-01') desc" )
20
+ order( "coalesce(feeds.last_published,'1971-01-01') desc" )
21
+ end
22
+
23
+ ##################################
24
+ # attribute reader aliases
25
+ #
26
+ # todo: check if we can use alias_method :name, :title - works for non-existing/on-demand-generated method too??
27
+
28
+ def name() title; end # alias for title
29
+ def description() summary; end # alias for summary -- also add descr shortcut??
30
+ def link() url; end # alias for url
31
+ def feed() feed_url; end # alias for feed_url
32
+
33
+ def url?() read_attribute(:url).present?; end
34
+ def title?() read_attribute(:title).present?; end
35
+ def title2?() read_attribute(:title2).present?; end
36
+ def feed_url?() read_attribute(:feed_url).present?; end
37
+
38
+ def url() read_attribute_w_fallbacks( :url, :auto_url ); end
39
+ def title() read_attribute_w_fallbacks( :title, :auto_title ); end
40
+ def title2() read_attribute_w_fallbacks( :title2, :auto_title2 ); end
41
+ def feed_url() read_attribute_w_fallbacks( :feed_url, :auto_feed_url ); end
42
+
43
+
44
+ def published?() read_attribute(:published).present?; end
45
+ def touched?() read_attribute(:touched).present?; end
46
+
47
+
48
+ def published
49
+ ## todo/fix: use a new name - do NOT squeeze convenience lookup into existing
50
+ # db backed attribute
51
+
52
+ read_attribute_w_fallbacks(
53
+ :published,
54
+ :touched, # try touched (aka updated (ATOM))
55
+ :built # try build (aka lastBuildDate (RSS))
56
+ )
57
+ end
58
+
59
+
60
+ def debug=(value) @debug = value; end
61
+ def debug?() @debug || false; end
62
+
63
+ def save_from_struct!( data )
64
+
65
+ update_from_struct!( data )
66
+
67
+ data.items.each do |item|
68
+
69
+ item_rec = Item.find_by_guid( item.guid )
70
+ if item_rec.nil?
71
+ item_rec = Item.new
72
+ puts "** NEW | #{item.title}"
73
+ else
74
+ ## todo: check if any attribs changed
75
+ puts "UPDATE | #{item.title}"
76
+ end
77
+
78
+ item_rec.debug = debug? ? true : false # pass along debug flag
79
+ item_rec.update_from_struct!( self, item )
80
+
81
+ end # each item
82
+ end
83
+
84
+
85
+ def update_from_struct!( data )
86
+
87
+ ## todo: move to FeedUtils::Feed ??? why? why not??
88
+ if data.generator
89
+ generator_full = ''
90
+ generator_full << data.generator
91
+ generator_full << " @version=#{data.generator_version}" if data.generator_version
92
+ generator_full << " @uri=#{data.generator_uri}" if data.generator_uri
93
+ else
94
+ generator_full = nil
95
+ end
96
+
97
+ feed_attribs = {
98
+ format: data.format,
99
+ published: data.published,
100
+ touched: data.updated,
101
+ built: data.built,
102
+ summary: data.summary,
103
+ ### todo/fix: add/use
104
+ # auto_title: ???,
105
+ # auto_url: ???,
106
+ # auto_feed_url: ???,
107
+ auto_title2: data.title2,
108
+ generator: generator_full
109
+ }
110
+
111
+ if debug?
112
+ ## puts "*** dump feed_attribs:"
113
+ ## pp feed_attribs
114
+ puts "*** dump feed_attribs w/ class types:"
115
+ feed_attribs.each do |key,value|
116
+ puts " #{key}: >#{value}< : #{value.class.name}"
117
+ end
118
+ end
119
+
120
+ update_attributes!( feed_attribs )
121
+ end
122
+
123
+ end # class Feed
124
+
125
+
126
+ end # module Models
127
+ end # module Pluto