chawk 0.1.15 → 0.1.16
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.
- checksums.yaml +4 -4
- data/.gitignore +1 -0
- data/.pryrc +10 -0
- data/.travis.yml +0 -2
- data/CHANGES +5 -0
- data/chawk.gemspec +5 -6
- data/lib/chawk.rb +48 -35
- data/lib/chawk/migration.rb +92 -0
- data/lib/chawk/version.rb +1 -1
- data/lib/models.rb +145 -80
- data/test/lib/addr_test.rb +23 -16
- data/test/lib/failed_node_test.rb +18 -0
- data/test/lib/paddr_test.rb +128 -110
- data/test/lib/vaddr_test.rb +160 -160
- data/test/schema.rb +60 -0
- data/test/test_helper.rb +13 -9
- metadata +34 -18
- data/lib/addr.rb +0 -108
- data/lib/paddr.rb +0 -59
- data/lib/store.rb +0 -124
- data/lib/vaddr.rb +0 -23
checksums.yaml
CHANGED
@@ -1,7 +1,7 @@
|
|
1
1
|
---
|
2
2
|
SHA1:
|
3
|
-
metadata.gz:
|
4
|
-
data.tar.gz:
|
3
|
+
metadata.gz: 8e4af3f3f47d72453ac226d2f5645f8288c38843
|
4
|
+
data.tar.gz: 4c5cdc1405311d4b03e67540f0fd55c1eb4997af
|
5
5
|
SHA512:
|
6
|
-
metadata.gz:
|
7
|
-
data.tar.gz:
|
6
|
+
metadata.gz: e276cefee3bc31f2bd0c3e1fd668a67336935abde8f17624f901d672e85fc03f22b61777bc9f4d166d1bcf6fcaeb451fe54702de8b166e0ffc722e78991cb696
|
7
|
+
data.tar.gz: c373b9799df4768f20851298ce7a083cfe16174a24b73e29769b48920e034273ff91a5df75509ca6f941c20cd232be6bb079e1eb9652ed214646f6fea490499f
|
data/.gitignore
CHANGED
data/.pryrc
ADDED
data/.travis.yml
CHANGED
data/CHANGES
CHANGED
data/chawk.gemspec
CHANGED
@@ -18,15 +18,14 @@ Gem::Specification.new do |spec|
|
|
18
18
|
spec.test_files = spec.files.grep(%r{^(test|spec|features)/})
|
19
19
|
spec.require_paths = ["lib"]
|
20
20
|
|
21
|
-
|
22
|
-
spec.add_runtime_dependency "data_mapper", "1.2.0"
|
21
|
+
spec.add_runtime_dependency('activerecord',"4.0.4")
|
23
22
|
|
24
23
|
spec.add_development_dependency "bundler", "~> 1.5"
|
25
24
|
spec.add_development_dependency "rake"
|
26
|
-
spec.add_development_dependency('minitest', '5.3.
|
25
|
+
spec.add_development_dependency('minitest', '5.3.1')
|
27
26
|
spec.add_development_dependency('rack-test', "0.6.2")
|
28
27
|
spec.add_development_dependency('json', "1.8.1")
|
29
|
-
spec.add_development_dependency('simplecov')
|
30
|
-
spec.add_development_dependency
|
31
|
-
|
28
|
+
spec.add_development_dependency('simplecov', "0.8.2")
|
29
|
+
spec.add_development_dependency('pg', "0.17.1")
|
30
|
+
spec.add_development_dependency('sqlite3', "1.3.9")
|
32
31
|
end
|
data/lib/chawk.rb
CHANGED
@@ -1,35 +1,65 @@
|
|
1
1
|
require "chawk/version"
|
2
|
-
require 'data_mapper'
|
3
2
|
require 'quantizer'
|
4
3
|
require 'models'
|
5
|
-
require 'addr'
|
6
4
|
|
7
5
|
# Chawk is a gem for storing and retrieving time seris data.
|
8
6
|
module Chawk
|
7
|
+
def self.check_node_security(agent,node)
|
9
8
|
|
10
|
-
|
11
|
-
|
9
|
+
if node.public_read
|
10
|
+
return node
|
11
|
+
end
|
12
|
+
|
13
|
+
rel = node.relations.where(agent_id:agent.id).first
|
14
|
+
|
15
|
+
if (rel && (rel.read || rel.admin))
|
16
|
+
return node
|
17
|
+
else
|
18
|
+
raise SecurityError,"You do not have permission to access this node. #{agent} #{rel}"
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
def self.find_or_create_node(agent,key)
|
23
|
+
#TODO also accept regex-tested string
|
24
|
+
raise(ArgumentError,"Key must be a string.") unless key.is_a?(String)
|
25
|
+
|
26
|
+
node = Chawk::Models::Node.where(key:key).first
|
27
|
+
if node
|
28
|
+
node = check_node_security(agent,node)
|
29
|
+
else
|
30
|
+
node = Chawk::Models::Node.create(key:key) if node.nil?
|
31
|
+
node.relations.create(agent:agent,node:node,admin:true,read:true,write:true)
|
32
|
+
return node
|
33
|
+
end
|
34
|
+
end
|
12
35
|
|
13
36
|
# @param agent [Chawk::Agent] the agent whose permission will be used for this request
|
14
|
-
# @param
|
37
|
+
# @param key [String] the string address this addr can be found in the database.
|
15
38
|
# @return [Chawk::Addr]
|
16
|
-
# The primary method for retrieving an Addr. If a
|
39
|
+
# The primary method for retrieving an Addr. If a key does not exist, it will be created
|
17
40
|
# and the current agent will be set as an admin for it.
|
18
|
-
def self.addr(agent,
|
19
|
-
|
20
|
-
|
41
|
+
def self.addr(agent,key)
|
42
|
+
|
43
|
+
unless key =~ /^[\w\:\$\!\@\*\[\]\~\(\)]+$/
|
44
|
+
raise ArgumentError, "Key can only contain [A-Za-z0-9_:$!@*[]~()] (#{key})"
|
21
45
|
end
|
22
46
|
|
23
47
|
unless agent.is_a?(Chawk::Models::Agent)
|
24
48
|
raise ArgumentError, 'Agent must be a Chawk::Models::Agent instance'
|
25
49
|
end
|
26
50
|
|
27
|
-
unless
|
28
|
-
raise ArgumentError, '
|
51
|
+
unless key.is_a?(String)
|
52
|
+
raise ArgumentError, 'key must be a string.'
|
29
53
|
end
|
30
54
|
|
31
|
-
|
55
|
+
node = find_or_create_node(agent,key)
|
56
|
+
|
57
|
+
unless node
|
58
|
+
raise ArgumentError,"No node was returned."
|
59
|
+
end
|
32
60
|
|
61
|
+
node.agent = agent
|
62
|
+
return node
|
33
63
|
end
|
34
64
|
|
35
65
|
# @param agent [Chawk::Agent] the agent whose permission will be used for this request
|
@@ -40,34 +70,17 @@ module Chawk
|
|
40
70
|
data.keys.each do |key|
|
41
71
|
dset = data[key]
|
42
72
|
daddr = addr(agent,key)
|
43
|
-
daddr.
|
73
|
+
daddr.add_points dset
|
44
74
|
end
|
45
75
|
end
|
46
76
|
|
47
77
|
|
48
78
|
# Deletes all data in the database. Very dangerous. Backup often!
|
49
79
|
def self.clear_all_data!
|
50
|
-
|
51
|
-
|
52
|
-
|
53
|
-
|
54
|
-
Chawk::Models::
|
55
|
-
Chawk::Models::Relation.all.destroy!
|
56
|
-
Chawk::Models::AgentTag.all.destroy!
|
57
|
-
Chawk::Models::Tag.all.destroy!
|
58
|
-
Chawk::Models::Node.all.destroy!
|
59
|
-
Chawk::Models::Point.all.destroy!
|
60
|
-
Chawk::Models::Value.all.destroy!
|
61
|
-
end
|
62
|
-
|
63
|
-
|
64
|
-
# @param database_url [String]
|
65
|
-
# Startup routine for Chawk, requires a database URL in DataMapper's standard format.
|
66
|
-
def self.setup(database_url)
|
67
|
-
@@ready = true
|
68
|
-
adapter = DataMapper.setup(:default, database_url)
|
69
|
-
DataMapper::Model.raise_on_save_failure = true
|
70
|
-
DataMapper.finalize
|
71
|
-
nil
|
80
|
+
Chawk::Models::Agent.destroy_all
|
81
|
+
Chawk::Models::Relation.destroy_all
|
82
|
+
Chawk::Models::Node.destroy_all
|
83
|
+
Chawk::Models::Point.destroy_all
|
84
|
+
Chawk::Models::Value.destroy_all
|
72
85
|
end
|
73
86
|
end
|
@@ -0,0 +1,92 @@
|
|
1
|
+
## ~~~- Created via -~~~
|
2
|
+
|
3
|
+
# ActiveRecord::Base.establish_connection ENV["TEST_DATABASE_URL"]
|
4
|
+
# File.open(schema_file, "w") do |file|
|
5
|
+
# ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
|
6
|
+
# end
|
7
|
+
|
8
|
+
# create_table "chawk_g_users", force: true do |t|
|
9
|
+
# t.string "google_id", limit: 120
|
10
|
+
# t.string "google_email", limit: 120
|
11
|
+
# t.string "email", limit: 120
|
12
|
+
# t.string "handle", limit: 30
|
13
|
+
# t.string "name", limit: 40
|
14
|
+
# t.string "family_name", limit: 20
|
15
|
+
# t.text "image"
|
16
|
+
# t.text "secret"
|
17
|
+
# t.string "api_key", limit: 200
|
18
|
+
# t.integer "reset_token"
|
19
|
+
# t.integer "access_token"
|
20
|
+
# t.integer "agent_id"
|
21
|
+
# end
|
22
|
+
# add_index "chawk_g_users", ["agent_id"], name: "index_chawk_g_users_agent", using: :btree
|
23
|
+
|
24
|
+
#require 'active_record'
|
25
|
+
#ActiveRecord::Base.logger = Logger.new(STDOUT)
|
26
|
+
#ActiveRecord::Base.establish_connection ENV["TEST_DATABASE_URL"]
|
27
|
+
|
28
|
+
ActiveRecord::Migration.verbose = false
|
29
|
+
class CreateChawkBase < ActiveRecord::Migration
|
30
|
+
def up
|
31
|
+
create_table "chawk_agents", force: true do |t|
|
32
|
+
t.integer "foreign_id"
|
33
|
+
t.string "name", limit: 200
|
34
|
+
t.timestamps
|
35
|
+
end
|
36
|
+
|
37
|
+
create_table "chawk_nodes", force: true do |t|
|
38
|
+
t.string "key", limit: 150
|
39
|
+
t.text "decription"
|
40
|
+
t.boolean "public_read", default: false
|
41
|
+
t.boolean "public_write", default: false
|
42
|
+
end
|
43
|
+
|
44
|
+
create_table "chawk_points", force: true do |t|
|
45
|
+
t.float "observed_at"
|
46
|
+
t.datetime "recorded_at"
|
47
|
+
t.text "meta"
|
48
|
+
t.integer "value"
|
49
|
+
t.integer "node_id", null: false
|
50
|
+
end
|
51
|
+
|
52
|
+
add_index "chawk_points", ["node_id"], name: "index_chawk_points_node", using: :btree
|
53
|
+
|
54
|
+
create_table "chawk_values", force: true do |t|
|
55
|
+
t.float "observed_at"
|
56
|
+
t.datetime "recorded_at"
|
57
|
+
t.text "meta"
|
58
|
+
t.text "value"
|
59
|
+
t.integer "node_id", null: false
|
60
|
+
end
|
61
|
+
|
62
|
+
add_index "chawk_values", ["node_id"], name: "index_chawk_values_node", using: :btree
|
63
|
+
|
64
|
+
create_table "chawk_relations", force: true do |t|
|
65
|
+
t.boolean "admin", default: false
|
66
|
+
t.boolean "read", default: false
|
67
|
+
t.boolean "write", default: false
|
68
|
+
t.integer "agent_id", null: false
|
69
|
+
t.integer "node_id", null: false
|
70
|
+
end
|
71
|
+
|
72
|
+
add_index "chawk_relations", ["agent_id"], name: "index_chawk_relations_agent", using: :btree
|
73
|
+
add_index "chawk_relations", ["node_id"], name: "index_chawk_relations_node", using: :btree
|
74
|
+
end
|
75
|
+
|
76
|
+
def down
|
77
|
+
drop_table "chawk_agents"
|
78
|
+
drop_table "chawk_nodes"
|
79
|
+
drop_table "chawk_points"
|
80
|
+
drop_table "chawk_values"
|
81
|
+
drop_table "chawk_relations"
|
82
|
+
end
|
83
|
+
|
84
|
+
end
|
85
|
+
|
86
|
+
#CreateChawkBase.migrate :down
|
87
|
+
CreateChawkBase.migrate :up
|
88
|
+
|
89
|
+
File.open('./test/schema.rb', "w") do |file|
|
90
|
+
ActiveRecord::SchemaDumper.dump(ActiveRecord::Base.connection, file)
|
91
|
+
end
|
92
|
+
|
data/lib/chawk/version.rb
CHANGED
data/lib/models.rb
CHANGED
@@ -1,102 +1,167 @@
|
|
1
|
-
require '
|
2
|
-
require 'dm-aggregates'
|
1
|
+
require 'active_record'
|
3
2
|
module Chawk
|
4
|
-
# Models used in Chawk.
|
3
|
+
# Models used in Chawk. ActiveRecord classes.
|
5
4
|
module Models
|
6
5
|
|
7
|
-
# Base stored item, imported into other DataMapper classes.
|
8
|
-
module Datum
|
9
|
-
|
10
|
-
protected
|
11
|
-
|
12
|
-
def self.included(base)
|
13
|
-
base.class_eval do
|
14
|
-
include DataMapper::Resource
|
15
|
-
before :create, :set_timestamp
|
16
|
-
|
17
|
-
property :id, DataMapper::Property::Serial
|
18
|
-
property :observed_at, DataMapper::Property::Float
|
19
|
-
property :recorded_at, DataMapper::Property::DateTime
|
20
|
-
property :meta, DataMapper::Property::Text
|
21
|
-
|
22
|
-
belongs_to :node
|
23
|
-
belongs_to :agent
|
24
|
-
|
25
|
-
def set_timestamp
|
26
|
-
attribute_set(:recorded_at, DateTime.now )
|
27
|
-
end
|
28
|
-
|
29
|
-
end
|
30
|
-
end
|
31
|
-
|
32
|
-
end
|
33
|
-
|
34
|
-
# Agent DataMapper classes.
|
35
6
|
# Contains a foreign_id for use as a proxy to another table.
|
36
|
-
class Agent
|
37
|
-
|
38
|
-
|
39
|
-
|
40
|
-
|
41
|
-
has n, :tags
|
42
|
-
has n, :agent_tags
|
43
|
-
has n, :relations
|
7
|
+
class Agent < ActiveRecord::Base
|
8
|
+
self.table_name_prefix = "chawk_"
|
9
|
+
has_many :tags
|
10
|
+
has_many :agent_tags
|
11
|
+
has_many :relations
|
44
12
|
end
|
45
13
|
|
46
|
-
# Agent Relation
|
47
|
-
class Relation
|
48
|
-
|
49
|
-
property :id, Serial
|
50
|
-
property :admin, Boolean, default:false
|
51
|
-
property :read, Boolean, default:false
|
52
|
-
property :write, Boolean, default:false
|
14
|
+
# Agent Relation classes, with permission flags.
|
15
|
+
class Relation < ActiveRecord::Base
|
16
|
+
self.table_name_prefix = "chawk_"
|
53
17
|
belongs_to :agent
|
54
18
|
belongs_to :node
|
55
19
|
end
|
56
20
|
|
57
|
-
#
|
58
|
-
|
59
|
-
|
60
|
-
|
61
|
-
|
62
|
-
property :name, String, length:100
|
63
|
-
property :description, Text
|
64
|
-
property :managed, Boolean, default:false
|
21
|
+
# The Node, where most Chawk:Addr information is persisted..
|
22
|
+
class Node < ActiveRecord::Base
|
23
|
+
attr_accessor :agent
|
24
|
+
after_initialize :init
|
25
|
+
self.table_name_prefix = "chawk_"
|
65
26
|
belongs_to :agent
|
66
|
-
|
27
|
+
has_many :points
|
28
|
+
has_many :values
|
29
|
+
has_many :relations
|
30
|
+
|
31
|
+
def init
|
32
|
+
@agent = nil
|
33
|
+
end
|
34
|
+
|
35
|
+
def _insert_point(val,ts,options={})
|
36
|
+
values = {value:val,observed_at:ts.to_f}
|
37
|
+
values[:meta] = options[:meta] if options[:meta]
|
38
|
+
self.points.create(values)
|
39
|
+
end
|
40
|
+
|
41
|
+
def _insert_point_hash(item,ts,options)
|
42
|
+
if item['v'] && item['v'].is_a?(Integer)
|
43
|
+
if item['t']
|
44
|
+
_insert_point item['v'],item['t'], options
|
45
|
+
else
|
46
|
+
_insert_point item['v'],ts, options
|
47
|
+
end
|
48
|
+
else
|
49
|
+
raise ArgumentError, "Hash must have 'v' key set to proper type.. #{item.inspect}"
|
50
|
+
end
|
51
|
+
end
|
67
52
|
|
68
|
-
|
69
|
-
|
70
|
-
|
71
|
-
|
72
|
-
|
73
|
-
|
74
|
-
|
75
|
-
|
53
|
+
def _insert_point_array(item,options)
|
54
|
+
if item.length == 2 && item[0].is_a?(Integer)
|
55
|
+
_insert_point item[0],item[1], options
|
56
|
+
else
|
57
|
+
raise ArgumentError, "Array Items must be in [value,timestamp] format. #{item.inspect}"
|
58
|
+
end
|
59
|
+
end
|
60
|
+
|
61
|
+
def point_recognizer(item, dt, options={})
|
62
|
+
case
|
63
|
+
when item.is_a?(Integer)
|
64
|
+
_insert_point item,dt, options
|
65
|
+
when item.is_a?(Array)
|
66
|
+
_insert_point_array(item, options)
|
67
|
+
when item.is_a?(Hash)
|
68
|
+
_insert_point_hash(item,dt,options)
|
69
|
+
else
|
70
|
+
raise ArgumentError, "Can't recognize format of data item. #{item.inspect}"
|
71
|
+
end
|
72
|
+
end
|
73
|
+
|
74
|
+
# @param args [Object, Array of Objects]
|
75
|
+
# @param options [Hash] You can also pass in :meta and :timestamp
|
76
|
+
# Add an item or an array of items (one at a time) to the datastore.
|
77
|
+
def add_points(args,options={})
|
78
|
+
options[:observed_at] ? dt = options[:observed_at] : dt = Time.now
|
79
|
+
if args.is_a?(Array)
|
80
|
+
args.each do |arg|
|
81
|
+
point_recognizer(arg, dt, options)
|
82
|
+
end
|
83
|
+
else
|
84
|
+
point_recognizer(args, dt, options)
|
85
|
+
end
|
86
|
+
end
|
87
|
+
|
88
|
+
def increment(value=1, options={})
|
89
|
+
if value.is_a?(Integer)
|
90
|
+
last = self.points.last
|
91
|
+
add_points last.value + value,options
|
92
|
+
else
|
93
|
+
raise ArgumentError, "Value must be an Integer"
|
94
|
+
end
|
95
|
+
end
|
96
|
+
|
97
|
+
def decrement(value=1, options={})
|
98
|
+
if value.is_a?(Integer)
|
99
|
+
increment (-1) * value, options
|
100
|
+
else
|
101
|
+
raise ArgumentError, "Value must be an Integer"
|
102
|
+
end
|
103
|
+
end
|
104
|
+
|
105
|
+
def max()
|
106
|
+
points.maximum('value') || 0
|
107
|
+
end
|
108
|
+
|
109
|
+
def min()
|
110
|
+
points.minimum('value') || 0
|
111
|
+
end
|
112
|
+
|
113
|
+
# Returns items whose observed_at times fit within from a range.
|
114
|
+
# @param dt_from [Time::Time] The start time.
|
115
|
+
# @param dt_to [Time::Time] The end time.
|
116
|
+
# @return [Array of Objects]
|
117
|
+
def points_range(dt_from, dt_to,options={})
|
118
|
+
vals = points.where("observed_at >= :dt_from AND observed_at <= :dt_to",{dt_from:dt_from.to_f,dt_to:dt_to.to_f}, limit:1000,order:"observed_at asc, id asc")
|
119
|
+
return vals
|
120
|
+
end
|
121
|
+
|
122
|
+
# Returns items whose observed_at times fit within from a range ending now.
|
123
|
+
# @param dt_from [Time::Time] The start time.
|
124
|
+
# @return [Array of Objects]
|
125
|
+
def points_since(dt_from)
|
126
|
+
self.points_range(dt_from,Time.now)
|
127
|
+
end
|
128
|
+
|
129
|
+
# Sets public read flag for this address
|
130
|
+
# @param value [Boolean] true if public reading is allowed, false if it is not.
|
131
|
+
def set_public_read(value)
|
132
|
+
value = value ? true : false
|
133
|
+
self.public_read = value
|
134
|
+
save
|
135
|
+
end
|
136
|
+
|
137
|
+
# Sets permissions flag for this address, for a specific agent. The existing Chawk::Relationship will be destroyed and
|
138
|
+
# a new one created as specified. Write access is not yet checked.
|
139
|
+
# @param agent [Chawk::Agent] the agent to give permission.
|
140
|
+
# @param read [Boolean] true/false can the agent read this address.
|
141
|
+
# @param write [Boolean] true/false can the agent write this address. (Read acces is required to write.)
|
142
|
+
# @param admin [Boolean] does the agent have ownership/adnim rights for this address. (Read and write are granted if admin is as well.)
|
143
|
+
def set_permissions(agent,read=false,write=false,admin=false)
|
144
|
+
relations.where(agent_id:agent.id).destroy_all
|
145
|
+
if read || write || admin
|
146
|
+
vals = {agent:agent,read:(read ? true : false),write:(write ? true : false),admin:(admin ? true : false)}
|
147
|
+
relations.create(vals)
|
148
|
+
end
|
149
|
+
nil
|
150
|
+
end
|
76
151
|
|
77
|
-
# The Node, where most Chawk:Addr information is persisted..
|
78
|
-
class Node
|
79
|
-
include DataMapper::Resource
|
80
|
-
property :id, Serial
|
81
|
-
property :address, String, length:150
|
82
|
-
property :public_read, Boolean, default:false
|
83
|
-
property :public_write, Boolean, default:false
|
84
|
-
|
85
|
-
has n, :points
|
86
|
-
has n, :values
|
87
|
-
has n, :relations
|
88
152
|
end
|
89
153
|
|
90
154
|
# The Node, where most Chawk point information is persisted..
|
91
|
-
class Point
|
92
|
-
|
93
|
-
|
155
|
+
class Point < ActiveRecord::Base
|
156
|
+
self.table_name_prefix = "chawk_"
|
157
|
+
belongs_to :node
|
94
158
|
end
|
95
159
|
|
96
160
|
# The Node, where most Chawk value information is persisted..
|
97
|
-
class Value
|
98
|
-
|
99
|
-
|
161
|
+
class Value < ActiveRecord::Base
|
162
|
+
self.table_name_prefix = "chawk_"
|
163
|
+
belongs_to :node
|
100
164
|
end
|
165
|
+
|
101
166
|
end
|
102
167
|
end
|