basilik 0.0.1

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,141 @@
1
+ require 'basilik/faults'
2
+
3
+ module Basilik
4
+ module Action
5
+ include Basilik::Faults
6
+
7
+ def read
8
+ location = json_url
9
+ resp = Typhoeus.get( location, gen_opts( params: {format: :export} ) )
10
+ res = handle_response( resp, location )
11
+ res.is_a?(Map) ? Snapshot.new( res ).to_map : res.is_a?(String) ? res.to_val : res
12
+ end
13
+
14
+ def set( data )
15
+ location = json_url
16
+ resp = Typhoeus.put( location, gen_opts( body: data.to_json ) )
17
+ handle_response( resp, location )
18
+ end
19
+
20
+ def update( data )
21
+ location = json_url
22
+ resp = Typhoeus.patch( location, gen_opts( body: data.to_json ) )
23
+ handle_response( resp, location )
24
+ end
25
+
26
+ def inc( field )
27
+ map = read
28
+ current_val = map[field]
29
+ raise NonNumericFieldError, "The field #{field} does not have a numeric value" if current_val and !current_val.is_a? Numeric
30
+ if current_val
31
+ new_val = map[field] + 1
32
+ else
33
+ new_val = 0
34
+ end
35
+ update( field => new_val )
36
+ end
37
+
38
+ def dec( field )
39
+ map = read
40
+ current_val = map[field]
41
+ raise NonNumericFieldError, "The field #{field} does not have a numeric value" if current_val and !current_val.is_a? Numeric
42
+ if current_val
43
+ new_val = map[field] - 1
44
+ else
45
+ new_val = 0
46
+ end
47
+ update( field => new_val )
48
+ end
49
+
50
+ def remove
51
+ location = json_url
52
+ resp = Typhoeus.delete( location, gen_opts )
53
+ unless resp.success?
54
+ raise InvalidRequestError, "<#{resp.return_code}> Unable to perform request #{location}"
55
+ end
56
+ end
57
+
58
+ def push( data=nil )
59
+ location = json_url
60
+ opts = {}
61
+ if data
62
+ opts[:body] = data.to_json
63
+ else
64
+ opts[:body] = ''.to_json
65
+ end
66
+ resp = Typhoeus.post( location, gen_opts( opts ) )
67
+ res = handle_response( resp, location )
68
+ Basilik::Load.new( uri.to_s + '/' + res.name )
69
+ end
70
+
71
+ def set_priority( priority )
72
+ location = json_url( true )
73
+ resp = Typhoeus.put( location, gen_opts( body: priority.to_json ) )
74
+ unless resp.success? or resp.body
75
+ raise InvalidRequestError, "<#{resp.return_code}> Unable to perform request #{location}"
76
+ end
77
+ end
78
+
79
+ def get_priority
80
+ location = json_url( true )
81
+ resp = Typhoeus.get( location, gen_opts( params: { format: :export} ) )
82
+ res = handle_response( resp, location )
83
+ res.to_i
84
+ rescue NoDataError
85
+ nil
86
+ end
87
+
88
+ def get_rules
89
+ location = rules_url
90
+ resp = Typhoeus.get( location, gen_opts )
91
+ handle_response( resp, location )
92
+ end
93
+
94
+ def set_rules( rules )
95
+ location = rules_url
96
+ resp = Typhoeus.put( location, gen_opts( body: {:rules => rules}.to_json ) )
97
+ handle_response( resp, location )
98
+ end
99
+
100
+
101
+ private
102
+
103
+ def gen_opts( opts={} )
104
+ if @auth_token
105
+ opts[:params] ||= {}
106
+ opts[:params][:auth] = @auth_token
107
+ end
108
+ opts
109
+ end
110
+
111
+ def handle_response( resp, location )
112
+ if resp.response_code == 403
113
+ raise PermissionDeniedError, "No permission for #{location}"
114
+ end
115
+ unless resp.success?
116
+ raise InvalidRequestError, "<#{resp.return_code}> Unable to perform request #{location}"
117
+ end
118
+ if resp.body.empty? or resp.body == "null"
119
+ raise NoDataError, "No data found at location #{location}"
120
+ end
121
+
122
+ results = JSON.parse( resp.body ) rescue nil
123
+ results ? (results.is_a?(Hash) ? Map( results ) : results ) : resp.body.gsub( /\"/, '' )
124
+ end
125
+
126
+ def json_url( priority=nil )
127
+ if @url =~ /\.json$/
128
+ loc = @url
129
+ elsif root?
130
+ loc = @url + "/.json"
131
+ else
132
+ loc = @url + ".json"
133
+ end
134
+ priority ? loc.gsub( /\.json$/, "/.priority/.json" ) : loc
135
+ end
136
+
137
+ def rules_url
138
+ @uri.merge( '.settings/rules.json' ).to_s
139
+ end
140
+ end
141
+ end
@@ -0,0 +1 @@
1
+ Basilik.require_all_libs_relative_to File.expand_path( File.join( %w(basilik core_ext), Basilik::LIBPATH ) )
@@ -0,0 +1,23 @@
1
+ require 'values'
2
+
3
+ class Map
4
+ Event = Value.new( :event_type, :ref )
5
+ def diff(other)
6
+ res = dup.
7
+ delete_if { |k, v| other[k] == v }.
8
+ merge!(other.dup.delete_if { |k, v| has_key?(k) })
9
+ events = []
10
+ res.each_pair do |k,v|
11
+ if self[k]
12
+ if other[k]
13
+ events << Event.new( Basilik::Load.evt_value, k )
14
+ else
15
+ events << Event.new( Basilik::Load.evt_child_removed, k )
16
+ end
17
+ else
18
+ events << Event.new( Basilik::Load.evt_child_added, k )
19
+ end
20
+ end
21
+ events
22
+ end
23
+ end
@@ -0,0 +1,15 @@
1
+ class String
2
+ def to_val
3
+ if self =~ /\A[-+]?\d+$/
4
+ Integer( self )
5
+ elsif self =~ /\A[-+]?\d+\.\d+$/
6
+ Float( self )
7
+ elsif self =~ /false/
8
+ false
9
+ elsif self =~ /true/
10
+ true
11
+ else
12
+ self
13
+ end
14
+ end
15
+ end
@@ -0,0 +1,9 @@
1
+ module Basilik
2
+ module Faults
3
+ class NoDataError < RuntimeError; end
4
+ class InvalidRequestError < RuntimeError; end
5
+ class InvalidJSONError < RuntimeError; end
6
+ class PermissionDeniedError < RuntimeError; end
7
+ class NonNumericFieldError < RuntimeError; end
8
+ end
9
+ end
@@ -0,0 +1,22 @@
1
+ require 'basilik/ref'
2
+
3
+ module Basilik
4
+ class Load
5
+ include Basilik::Ref
6
+ include Basilik::Action
7
+
8
+ attr_reader :url, :uri
9
+
10
+ def self.evt_value; :value; end
11
+ def self.evt_child_added; :add_child; end
12
+ def self.evt_child_changed; :mod_child; end
13
+ def self.evt_child_removed; :rm_child; end
14
+ def self.evt_child_moved; :mv_child; end
15
+
16
+ def initialize( url, auth_token=nil )
17
+ @url = url
18
+ @uri = URI.parse( @url )
19
+ @auth_token = auth_token
20
+ end
21
+ end
22
+ end
@@ -0,0 +1,64 @@
1
+ require 'uri'
2
+
3
+ module Basilik
4
+ module Ref
5
+ def name
6
+ parse_path[-1]
7
+ end
8
+
9
+ def val
10
+ read
11
+ end
12
+
13
+ def root
14
+ return self if root?
15
+ Basilik::Load.new( @uri.scheme + '://' + @uri.host )
16
+ end
17
+
18
+ def root?
19
+ @uri.path.empty? or @uri.path == "/"
20
+ end
21
+
22
+ def parent
23
+ return nil if root?
24
+ path = parse_path[0..-2].join( "/" )
25
+ Basilik::Load.new( root.uri.merge(path).to_s )
26
+ end
27
+
28
+ def child( child_path )
29
+ Basilik::Load.new( "#{uri.to_s}/#{child_path}" )
30
+ end
31
+
32
+ def child?( child_path )
33
+ child( child_path ).read
34
+ true
35
+ rescue
36
+ false
37
+ end
38
+
39
+ def children?
40
+ data = read
41
+ data.is_a? Map and !data.empty?
42
+ end
43
+
44
+ def num_children
45
+ data = read
46
+ if data.is_a? Map and !data.empty?
47
+ data.count
48
+ else
49
+ 0
50
+ end
51
+ end
52
+
53
+ def to_s
54
+ @url
55
+ end
56
+
57
+
58
+ private
59
+
60
+ def parse_path
61
+ @uri.path.split( "/" )
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,99 @@
1
+ module Basilik
2
+ class Snapshot
3
+
4
+ class Node
5
+ attr_accessor :priority, :name, :parent, :children, :value
6
+
7
+ def initialize( name )
8
+ @name = name
9
+ @priority = nil
10
+ @children = []
11
+ @value = nil
12
+ @parent = nil
13
+ end
14
+
15
+ def add_child( child )
16
+ child.parent = self
17
+ children << child
18
+ end
19
+
20
+ def to_map
21
+ map = Map.new
22
+ order( map )
23
+ map
24
+ end
25
+
26
+ def dump
27
+ puts "#{' '*level}#{name} [#{priority.inspect}] --#{value.inspect}"
28
+ children.sort_by do |c|
29
+ if c.priority and c.priority.is_a? Integer
30
+ "b_#{c.priority}_#{c.name}"
31
+ elsif c.priority
32
+ "z_#{c.priority}_#{c.name}"
33
+ else
34
+ "a_#{c.name}"
35
+ end
36
+ end.each do |c|
37
+ c.dump
38
+ end
39
+ end
40
+
41
+ private
42
+
43
+ def order( map )
44
+ map[name] = children.empty? ? value : Map.new
45
+ children.sort_by do |c|
46
+ if c.priority and c.priority.is_a? Numeric
47
+ "b_#{'%05.2f' % c.priority}_#{c.name}"
48
+ elsif c.priority
49
+ "z_#{c.priority}_#{c.name}"
50
+ else
51
+ "a_#{c.name}"
52
+ end
53
+ end.each do |c|
54
+ c.send( :order, map[name] )
55
+ end
56
+ end
57
+
58
+ def level
59
+ level = 0
60
+ node = self
61
+ while node.parent do
62
+ level +=1
63
+ node = node.parent
64
+ end
65
+ level
66
+ end
67
+ end
68
+
69
+ def initialize( map )
70
+ @map = map
71
+ @root = Node.new( 'root' )
72
+ build( @map, @root )
73
+ end
74
+
75
+ def to_map
76
+ @root.to_map.root
77
+ end
78
+
79
+ def dump
80
+ @root.dump
81
+ end
82
+
83
+ private
84
+
85
+ def build( map, root )
86
+ map.each_pair do |k,v|
87
+ node = Node.new( k )
88
+ if v.is_a? Hash
89
+ priority = v.delete('.priority')
90
+ node.priority = priority if priority
91
+ build( v, node )
92
+ else
93
+ node.value = v.is_a?(String) ? v.to_val : v
94
+ end
95
+ root.add_child( node )
96
+ end
97
+ end
98
+ end
99
+ end
@@ -0,0 +1,3 @@
1
+ module Basilik
2
+ VERSION = "0.0.1"
3
+ end
data/lib/basilik.rb ADDED
@@ -0,0 +1,17 @@
1
+ require 'json'
2
+ require 'typhoeus'
3
+ require 'map'
4
+
5
+ module Basilik
6
+ LIBPATH = ::File.expand_path(::File.dirname(__FILE__)) + ::File::SEPARATOR
7
+ PATH = ::File.dirname(LIBPATH) + ::File::SEPARATOR
8
+
9
+ def self.require_all_libs_relative_to( fname, dir = nil )
10
+ dir ||= ::File.basename(fname, '.*')
11
+ search_me = ::File.expand_path(
12
+ ::File.join(::File.dirname(fname), dir, '**', '*.rb'))
13
+ Dir.glob(search_me).sort.each { |rb| require rb }
14
+ end
15
+ end
16
+
17
+ Basilik.require_all_libs_relative_to File.expand_path( "basilik", Basilik::LIBPATH )
Binary file
@@ -0,0 +1,45 @@
1
+ require 'spec_helper'
2
+
3
+ describe Map do
4
+ describe '#diff' do
5
+ describe 'single vals' do
6
+ it "tracks value changes correctly" do
7
+ prev = Map(:a,1)
8
+ curr = Map(:a,2)
9
+ evts = prev.diff( curr )
10
+ evts.should have(1).item
11
+ evts.first.event_type.should == Basilik::Load.evt_value
12
+ evts.first.ref.should == 'a'
13
+ end
14
+
15
+ it "tracks adds correctly" do
16
+ prev = Map.new
17
+ curr = Map(:a,2)
18
+ evts = prev.diff( curr )
19
+ evts.should have(1).item
20
+ evts.first.event_type.should == Basilik::Load.evt_child_added
21
+ evts.first.ref.should == 'a'
22
+ end
23
+
24
+ it "tracks deletes correctly" do
25
+ prev = Map(:a,2)
26
+ curr = Map.new
27
+ evts = prev.diff( curr )
28
+ evts.should have(1).item
29
+ evts.first.event_type.should == Basilik::Load.evt_child_removed
30
+ evts.first.ref.should == 'a'
31
+ end
32
+ end
33
+
34
+ describe 'multi vals' do
35
+ it 'tracks multi value changes correctly' do
36
+ prev = Map(:a,1)
37
+ curr = Map({a:{a_1:10, a_2:20}})
38
+ evts = prev.diff( curr )
39
+ evts.should have(1).item
40
+ evts.first.event_type.should == Basilik::Load.evt_value
41
+ evts.first.ref.should == 'a'
42
+ end
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,26 @@
1
+ require 'spec_helper'
2
+
3
+ describe String do
4
+ describe '#to_val' do
5
+ it "keep strings unchanged" do
6
+ "fred".to_val.should == "fred"
7
+ end
8
+
9
+ it "converts an integer correctly" do
10
+ "10".to_val.should == 10
11
+ end
12
+
13
+ it "converts an float correctly" do
14
+ "10.5".to_val.should == 10.5
15
+ end
16
+
17
+ it "converts a boolean correctly" do
18
+ "true".to_val.should == true
19
+ "false".to_val.should == false
20
+ end
21
+
22
+ it 'converts null correctly' do
23
+ 'null'.to_val.should == "null"
24
+ end
25
+ end
26
+ end
@@ -0,0 +1,109 @@
1
+ require 'spec_helper'
2
+
3
+ describe Basilik::Ref do
4
+ before :all do
5
+ @ref = Basilik::Load.new( ENV['fb_url'] )
6
+ end
7
+
8
+ describe '#child?' do
9
+ it "retrieves complex values correctly" do
10
+ @ref.set( a:{b:{c:1, d:"hello"}} )
11
+ @ref.child?( :a ).should == true
12
+ @ref.child?( 'a/b' ).should == true
13
+ @ref.child?( :z ).should == false
14
+ end
15
+ end
16
+
17
+ describe '#children?' do
18
+ it "retrieves complex values correctly" do
19
+ @ref.set( a:{b:{c:1, d:"hello"}} )
20
+ @ref.child(:a).children?.should == true
21
+ @ref.child('a/b').children?.should == true
22
+ @ref.child( 'a/b/c' ).children?.should == false
23
+ end
24
+ end
25
+
26
+ describe '#num_children' do
27
+ it "retrieves complex values correctly" do
28
+ @ref.set( a:{b:{c:1, d:"hello"}} )
29
+ @ref.child(:a).num_children.should == 1
30
+ @ref.child('a/b').num_children.should == 2
31
+ @ref.child( 'a/b/c' ).num_children.should == 0
32
+ end
33
+ end
34
+
35
+ describe '#val' do
36
+ it "retrieves simple value correctly" do
37
+ @ref.set( a:1 )
38
+ @ref.child( :a ).val.to_i.should == 1
39
+ end
40
+
41
+ it "retrieves complex values correctly" do
42
+ @ref.set( a:{b:{c:1, d:"hello"}} )
43
+ @ref.child( :a ).val.should == {b:{c:1, d:"hello"}}
44
+ end
45
+ end
46
+
47
+ describe '#child' do
48
+ it "creates a child ref correctly" do
49
+ child_ref = @ref.child( 'fred')
50
+ child_ref.name.should == 'fred'
51
+ child_ref.parent.to_s.should == ENV['fb_url']
52
+ end
53
+
54
+ it "creates a deep child ref correctly" do
55
+ child_ref = @ref.child( 'fred/blee/duh')
56
+ child_ref.name.should == 'duh'
57
+ child_ref.parent.to_s.should == ENV['fb_url'] + '/fred/blee'
58
+ end
59
+ end
60
+
61
+ describe '#name' do
62
+ it "identifies / as the root name correctly" do
63
+ @ref.name.should be_nil
64
+ end
65
+
66
+ it "identifies a url name correctly" do
67
+ ref = Basilik::Load.new( ENV['fb_url'] + '/fred/blee' )
68
+ ref.name.should == 'blee'
69
+ end
70
+ end
71
+
72
+ describe '#root' do
73
+ it "identifies / as root correctly" do
74
+ @ref.root.should == @ref
75
+ end
76
+
77
+ it "identifies non root url correctly" do
78
+ ref = Basilik::Load.new( ENV['fb_url'] + '/fred/blee' )
79
+ ref.root.to_s.should == ENV['fb_url']
80
+ end
81
+ end
82
+
83
+ describe '#root?' do
84
+ it "identifies root correctly" do
85
+ @ref.should be_root
86
+ end
87
+
88
+ it "identifies / as root correctly" do
89
+ ref = Basilik::Load.new( ENV['fb_url'] + '/' )
90
+ ref.should be_root
91
+ end
92
+
93
+ it "identifies non root correctly" do
94
+ ref = Basilik::Load.new( ENV['fb_url'] + '/fred' )
95
+ ref.should_not be_root
96
+ end
97
+ end
98
+
99
+ describe '#parent' do
100
+ it "identifies parent from root correctly" do
101
+ @ref.parent.should be_nil
102
+ end
103
+
104
+ it "identifies a parent correctly" do
105
+ ref = Basilik::Load.new( ENV['fb_url'] + '/fred/blee' )
106
+ ref.parent.to_s.should == ENV['fb_url'] + '/fred'
107
+ end
108
+ end
109
+ end