broker 0.0.9

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,117 @@
1
+ #--#####################################################################
2
+ # Copyright (c) 2009-2012 Gareth Lewis and Intuit, Inc.
3
+ #
4
+ # All rights reserved. This program and the accompanying materials
5
+ # are made available under the terms of the Eclipse Public License v1.0
6
+ # which accompanies this distribution, and is available at
7
+ # http://www.opensource.org/licenses/eclipse-1.0.php
8
+ #
9
+ # Contributors:
10
+ # Gareth Lewis - Initial contribution.
11
+ # Intuit Partner Platform.
12
+ #++#####################################################################
13
+
14
+ module QuickBase
15
+
16
+ # Miscellaneous static helper methods
17
+ class Misc
18
+
19
+ def Misc.ruby19?
20
+ RUBY_VERSION >= "1.9"
21
+ end
22
+
23
+ def Misc.createBase32ConversionHash
24
+ base32Symbols = {}
25
+ decimal = 0
26
+ ("a".."z").each{|letter|
27
+ unless letter == "l" or letter == "o"
28
+ base32Symbols[decimal.to_s]=letter
29
+ decimal += 1
30
+ end
31
+ }
32
+ (2..9).each{|number|
33
+ base32Symbols[decimal.to_s]=number.to_s
34
+ decimal += 1
35
+ }
36
+ base32Symbols
37
+ end
38
+
39
+ def Misc.decimalToBase32(decimalNumber)
40
+ @base32Symbols ||= Misc.createBase32ConversionHash
41
+ base32Num = ""
42
+ decimalNumber = decimalNumber.to_i
43
+ if decimalNumber < 32
44
+ base32Num = @base32Symbols[decimalNumber.to_s]
45
+ else
46
+ power = 10
47
+ power -= 1 while (decimalNumber/(32**power)) < 1
48
+ while decimalNumber > 0
49
+ n = (decimalNumber/(32**power))
50
+ base32Num << @base32Symbols[n.to_s] if @base32Symbols[n.to_s]
51
+ decimalNumber = (decimalNumber-((32**power)*n))
52
+ power -= 1
53
+ end
54
+ end
55
+ base32Num
56
+ end
57
+
58
+ def Misc.isBase32Number?(string)
59
+ ret = true
60
+ if string
61
+ @base32Symbols ||= Misc.createBase32ConversionHash
62
+ base32SymbolsValues = @base32Symbols.values
63
+ stringCopy = string.to_s
64
+ stringCopy.split(//).each{|char|
65
+ if !base32SymbolsValues.include?(char)
66
+ ret = false
67
+ break
68
+ end
69
+ }
70
+ else
71
+ ret = false
72
+ end
73
+ ret
74
+ end
75
+
76
+ def Misc.isDbidString?(string)
77
+ Misc.isBase32Number?(string)
78
+ end
79
+
80
+ def Misc.time_in_milliseconds(time = nil)
81
+ ret = 0
82
+ time ||= Time.now
83
+ if time.is_a?(Time)
84
+ ret = (time.to_f * 1000).to_i
85
+ elsif time.is_a?(DateTime)
86
+ t = Time.mktime(time.year,time.month,time.day,time.hour,time.min,time.sec,0)
87
+ ret = (t.to_f * 1000).to_i
88
+ elsif time.is_a?(Date)
89
+ t = Time.mktime(time.year,time.month,time.day,0,0,0,0)
90
+ ret = (t.to_f * 1000).to_i
91
+ end
92
+ ret
93
+ end
94
+
95
+ def Misc.listUserToArray(listUser)
96
+ listUser.split(/;/)
97
+ end
98
+
99
+ def Misc.arrayToListUser(array)
100
+ array.join(";")
101
+ end
102
+
103
+ def Misc.save_file(filename, contents, mode="wb")
104
+ File.open(filename, mode){|f|
105
+ f.write(contents)
106
+ f.flush
107
+ }
108
+ rescue StandardError => error
109
+ new_filename = filename.gsub(/\W/,"_")
110
+ File.open(new_filename, mode){|f|
111
+ f.write(contents)
112
+ f.flush
113
+ }
114
+ end
115
+
116
+ end
117
+ end
@@ -0,0 +1 @@
1
+ require_relative 'QuickBaseClient'
@@ -0,0 +1,16 @@
1
+ require 'broker/session'
2
+
3
+ module Broker
4
+ class Export < Broker::Session
5
+
6
+ def initialize(opt={})
7
+ super(opt)
8
+ end
9
+
10
+ def query
11
+ res = client.dbid_bji2vn2ck.qid_22.records.map { |r| r.to_s }
12
+ sign_out && res
13
+ end
14
+
15
+ end
16
+ end
@@ -0,0 +1,11 @@
1
+ require 'broker'
2
+ require 'broker/client/quickbase_client'
3
+
4
+ module Broker
5
+ class Import
6
+
7
+
8
+
9
+
10
+ end
11
+ end
@@ -0,0 +1,66 @@
1
+ require 'broker/utility'
2
+
3
+ module Broker
4
+ class Launcher
5
+
6
+ def initialize
7
+ @poller = Broker::Poller.new
8
+ @finished = false
9
+ end
10
+
11
+ def run
12
+ puts "launcher is running"
13
+ @poller.start
14
+ end
15
+
16
+ def stop
17
+ @finished = true
18
+ @poller.terminate
19
+ puts "launcher stopped"
20
+ end
21
+
22
+ end
23
+
24
+ class Poller
25
+ include Utility
26
+
27
+ def initialize
28
+ @finished = false
29
+ @wait_time = Broker.poll_interval
30
+ @folder = Broker.queue
31
+ end
32
+
33
+ def terminate
34
+ @finished = true
35
+ if @thread
36
+ t = @thread
37
+ @thread = nil
38
+ wait 1
39
+ t.value
40
+ puts "Polling thread terminated"
41
+ end
42
+ end
43
+
44
+ def start
45
+ @thread ||= safe_thread("poller") do
46
+ pause_first
47
+
48
+ while !@finished
49
+ puts "Checking for new files in #{@folder}"
50
+ wait
51
+ end
52
+ end
53
+ end
54
+
55
+ private
56
+
57
+ def pause_first
58
+ sleep 5
59
+ end
60
+
61
+ def wait(dur=nil)
62
+ sleep dur || @wait_time
63
+ end
64
+ end
65
+
66
+ end
@@ -0,0 +1,34 @@
1
+ require 'broker'
2
+ require 'broker/client/quickbase_client'
3
+
4
+ module Broker
5
+ class Session
6
+
7
+ attr_reader :client, :app
8
+
9
+ def initialize(opt={})
10
+ @app = opt[:app]
11
+
12
+ credentials = {
13
+ "username" => Broker.secrets['USER'],
14
+ "password" => Broker.secrets['PASSWORD'],
15
+ "appname" => @app,
16
+ "org" => Broker.secrets['ORG'],
17
+ "apptoken" => opt[:token] || Broker.tables[@app]['token']
18
+ }
19
+
20
+ @client = QuickBase::Client.init(credentials)
21
+ end
22
+
23
+ def get_field_names(table)
24
+ table &&= table.to_s
25
+ db = Broker.tables[@app]['tables'][table]
26
+ db && @client.getFieldNames(db, "", true)
27
+ end
28
+
29
+ def sign_out
30
+ @client.signOut
31
+ end
32
+
33
+ end
34
+ end
@@ -0,0 +1,21 @@
1
+ # Use this hook to configure Broker
2
+
3
+ Broker.setup do |config|
4
+
5
+ # Store private quickbase credentials for authenticating via the api
6
+ config.secrets_path = 'config/secrets.yml'
7
+
8
+ # Store a table mapping for all quickbase apps you want to connect with
9
+ config.tables_path = 'config/quickbase_tables.yml'
10
+
11
+ # Directory where your import files will be queued for import
12
+ config.queue = 'broker_queue'
13
+
14
+ # Uncomment to change the default file type to use for importing
15
+ # [:csv, :tab]
16
+ #config.file_ext = :csv
17
+
18
+ # Sets polling wait time before checking for new import files
19
+ config.poll_interval = 10
20
+
21
+ end
@@ -0,0 +1,22 @@
1
+ # Add Quickbase Table Configuration here
2
+ #
3
+ # Give each app an easy to remember/type name for broker to use
4
+ # Tables should also be given a simple short name
5
+ #
6
+ # This config file will be used to generate the sync queue and to connect to the corresponding apps in quickbase
7
+ # Don't check this into version control, keep this private
8
+ #
9
+ # EXAMPLES
10
+ #
11
+ tracker:
12
+ name: Job Tracker
13
+ token: your_apps_token_for_tracker
14
+ tables:
15
+ main: table_dbid
16
+ people: table_dbid
17
+ billings:
18
+ name: Financial Billings
19
+ token: your_apps_token_for_billings
20
+ tables:
21
+ projects: table_dbid
22
+ pace: table_dbid
@@ -0,0 +1,10 @@
1
+ # Place your quickbase credentials here
2
+ # Do not check into version control, keep your secrets safe
3
+ #
4
+ # ORG -> if you are using a custom subdomain such as mycompany.quickbase.com,
5
+ # you will set ORG: mycompany, otherwise www is the default
6
+ #
7
+ #
8
+ ORG: www
9
+ USERNAME: billy_the_kid@user.com
10
+ PASSWORD: mickeymouse
@@ -0,0 +1,17 @@
1
+ module Broker
2
+ module Utility
3
+
4
+ def watcher(last_words)
5
+ yield
6
+ rescue Exception => ex
7
+ raise ex
8
+ end
9
+
10
+ def safe_thread(name, &block)
11
+ Thread.new do
12
+ watcher(name, &block)
13
+ end
14
+ end
15
+
16
+ end
17
+ end
@@ -0,0 +1,3 @@
1
+ module Broker
2
+ VERSION = "0.0.9"
3
+ end
data/lib/broker/web.rb ADDED
@@ -0,0 +1,33 @@
1
+ require 'erb'
2
+ require 'sinatra/base'
3
+
4
+ require 'broker/web_helpers'
5
+ require 'broker/web_routes'
6
+
7
+
8
+ module Broker
9
+ class Web < Sinatra::Base
10
+
11
+ set :root, File.expand_path(File.dirname(__FILE__) + "/../../web")
12
+ set :public_folder, Proc.new { "#{root}/assets" }
13
+ set :views, Proc.new { "#{root}/views" }
14
+
15
+ helpers WebHelpers
16
+
17
+ register Broker::WebRoutes
18
+
19
+ DEFAULT_TABS = {
20
+ "Dashboard" => '',
21
+ "Imports" => 'imports',
22
+ "Exports" => 'exports',
23
+ "API" => 'api'
24
+ }
25
+
26
+ class << self
27
+ def default_tabs
28
+ DEFAULT_TABS
29
+ end
30
+ end
31
+
32
+ end
33
+ end
@@ -0,0 +1,12 @@
1
+ require 'uri'
2
+
3
+ module Broker
4
+ module WebHelpers
5
+ def root_path
6
+ "#{env['SCRIPT_NAME']}/"
7
+ end
8
+ def current_path
9
+ @current_path ||= request.path_info.gsub(/^\//,'')
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,34 @@
1
+ require 'broker/import'
2
+ require 'broker/export'
3
+
4
+ module Broker
5
+ module WebRoutes
6
+ def self.registered(app)
7
+ app.get "/" do
8
+ erb :index
9
+ end
10
+
11
+ app.get '/imports' do
12
+ erb :imports
13
+ end
14
+
15
+ app.get '/exports' do
16
+ db = params['table']
17
+ app = params['app']
18
+ @fields = []
19
+ @header = "Exports"
20
+ if db && app
21
+ qb = Broker::Export.new(:app => app)
22
+ @fields = qb.get_field_names(db)
23
+ @header = "App: #{app.downcase} | Table: #{db.capitalize}"
24
+ qb.sign_out
25
+ end
26
+ erb :exports
27
+ end
28
+
29
+ app.get '/api' do
30
+ erb :api
31
+ end
32
+ end
33
+ end
34
+ end
data/lib/broker.rb ADDED
@@ -0,0 +1,115 @@
1
+ require 'broker/version'
2
+ require 'yaml'
3
+
4
+ module Broker
5
+ NAME = "Broker: Quickbase Data Intake"
6
+
7
+ DEFAULTS = {
8
+ secrets_path: 'config/secrets.yml',
9
+ tables_path: 'config/quickbase_tables.yml',
10
+ initializer: 'config/initializers/broker.rb',
11
+ poll_interval: 300,
12
+ queue: 'broker_queue',
13
+ file_ext: :csv
14
+ }
15
+
16
+ @@secrets_path = @@secrets_path ||= DEFAULTS[:secrets_path]
17
+ @@tables_path = @@tables_path ||= DEFAULTS[:tables_path]
18
+ @@initializer = @@initializer ||= DEFAULTS[:initializer]
19
+ @@poll_interval = @poll_interval ||= DEFAULTS[:poll_interval]
20
+ @@queue = @@queue ||= DEFAULTS[:queue]
21
+ @@file_ext = @@file_ext ||= DEFAULTS[:file_ext]
22
+
23
+ @@secrets = YAML.load_file(@@secrets_path) rescue {}
24
+ @@tables = YAML.load_file(@@tables_path) rescue {}
25
+
26
+ def self.lookup_tbid(opt={})
27
+ tables[opt[:app]]['tables'][opt[:table]]
28
+ end
29
+
30
+ def self.path
31
+ Dir.pwd
32
+ end
33
+
34
+ def self.setup
35
+ yield self unless launched?
36
+ end
37
+
38
+ def self.launched?
39
+ defined?(Broker::Launcher)
40
+ end
41
+
42
+ def self.config_files
43
+ [secrets_path, tables_path, initializer]
44
+ end
45
+
46
+ # def self.options
47
+ # @options ||= DEFAULTS.dup
48
+ # end
49
+ #
50
+ # def self.options=(opt)
51
+ # @options = opt
52
+ # end
53
+
54
+ def self.poll_interval
55
+ @@poll_interval
56
+ end
57
+
58
+ def self.poll_interval=(val)
59
+ @@poll_interval = val
60
+ end
61
+
62
+ def self.queue
63
+ @@queue
64
+ end
65
+
66
+ def self.queue=(val)
67
+ @@queue = val
68
+ end
69
+
70
+ def file_ext
71
+ @@file_ext
72
+ end
73
+
74
+ def file_ext=(val)
75
+ @@file_ext = val if [:csv, :tab].include? val
76
+ end
77
+
78
+ def self.secrets
79
+ @@secrets
80
+ end
81
+
82
+ def self.secrets_path
83
+ @@secrets_path
84
+ end
85
+
86
+ def self.secrets_path=(val)
87
+ @@secrets_path = val
88
+ @@secrets = YAML.load_file(@@secrets_path) rescue {}
89
+ end
90
+
91
+ def self.tables
92
+ @@tables
93
+ end
94
+
95
+ def self.tables_path
96
+ @@tables_path
97
+ end
98
+
99
+ def self.tables_path=(val)
100
+ @@tables_path = val
101
+ @@tables = YAML.load_file(@@tables_path) rescue {}
102
+ end
103
+
104
+ def self.initializer
105
+ @@initializer
106
+ end
107
+
108
+ def self.initializer=(val)
109
+ @@initializer = val
110
+ end
111
+
112
+ end
113
+
114
+ require 'broker/application'
115
+
File without changes