boned 0.2.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.
data/CHANGES.txt ADDED
@@ -0,0 +1,5 @@
1
+ BONED, CHANGES
2
+
3
+ #### 0.1.0 (2009-12-13) ###############################
4
+
5
+ Initial release
data/LICENSE.txt ADDED
@@ -0,0 +1,19 @@
1
+ Copyright (c) 2009 Solutious Inc, Delano Mandelbaum
2
+
3
+ Permission is hereby granted, free of charge, to any person obtaining a copy
4
+ of this software and associated documentation files (the "Software"), to deal
5
+ in the Software without restriction, including without limitation the rights
6
+ to use, copy, modify, merge, publish, distribute, sublicense, and/or sell
7
+ copies of the Software, and to permit persons to whom the Software is
8
+ furnished to do so, subject to the following conditions:
9
+
10
+ The above copyright notice and this permission notice shall be included in
11
+ all copies or substantial portions of the Software.
12
+
13
+ THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR
14
+ IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY,
15
+ FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE
16
+ AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER
17
+ LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM,
18
+ OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN
19
+ THE SOFTWARE.
data/README.md ADDED
@@ -0,0 +1,32 @@
1
+ ## Bone - 0.2 ##
2
+
3
+ **Bones it!**
4
+
5
+
6
+ == Running
7
+
8
+ $ redis-server config/redis-server.conf
9
+ $ boned start
10
+
11
+
12
+ == Installation
13
+
14
+ $ sudo gem install boned
15
+
16
+
17
+ == More Information
18
+
19
+
20
+ == Credits
21
+
22
+ * Delano Mandelbaum (http://solutious.com)
23
+
24
+
25
+ == Thanks
26
+
27
+ * Kalin Harvey for the early feedback.
28
+
29
+
30
+ == License
31
+
32
+ See LICENSE.txt
data/Rakefile ADDED
@@ -0,0 +1,55 @@
1
+
2
+ require 'rake/clean'
3
+ require 'rake/gempackagetask'
4
+ require 'hanna/rdoctask'
5
+ require 'rake/testtask'
6
+ require 'shoulda/tasks'
7
+ require 'rake/runtest'
8
+ require 'fileutils'
9
+ include FileUtils
10
+
11
+ task :default => :test
12
+
13
+
14
+ # PACKAGE =============================================================
15
+
16
+ name = "boned"
17
+ load "#{name}.gemspec"
18
+
19
+ version = @spec.version
20
+
21
+ Rake::GemPackageTask.new(@spec) do |p|
22
+ p.need_tar = true if RUBY_PLATFORM !~ /mswin/
23
+ end
24
+
25
+ task :test do
26
+ puts "Success!"
27
+ end
28
+
29
+ task :install => [ :rdoc, :package ] do
30
+ sh %{sudo gem install pkg/#{name}-#{version}.gem}
31
+ end
32
+
33
+ task :uninstall => [ :clean ] do
34
+ sh %{sudo gem uninstall #{name}}
35
+ end
36
+
37
+
38
+
39
+ Rake::RDocTask.new do |t|
40
+ t.rdoc_dir = 'doc'
41
+ t.title = @spec.summary
42
+ t.options << '--line-numbers' << '-A cattr_accessor=object'
43
+ t.options << '--charset' << 'utf-8'
44
+ t.rdoc_files.include('LICENSE.txt')
45
+ t.rdoc_files.include('README.md')
46
+ t.rdoc_files.include('CHANGES.txt')
47
+ #t.rdoc_files.include('Rudyfile') # why is the formatting f'd?
48
+ t.rdoc_files.include('bin/*')
49
+ t.rdoc_files.include('lib/**/*.rb')
50
+ end
51
+
52
+ CLEAN.include [ 'pkg', '*.gem', '.config', 'doc', 'coverage*' ]
53
+
54
+
55
+
data/bin/boned ADDED
@@ -0,0 +1,72 @@
1
+ #!/usr/bin/ruby
2
+
3
+ # = Boned
4
+ #
5
+ #
6
+ # Usage:
7
+ #
8
+ # $ boned -h
9
+ # $ boned [-d] start-master
10
+ #
11
+ #--
12
+
13
+ # Put our local lib in first place
14
+ BASE_PATH = File.expand_path File.join(File.dirname(__FILE__), '..')
15
+ lib_dir = File.join(BASE_PATH, 'lib')
16
+ $:.unshift lib_dir
17
+ #$:.unshift '/Users/delano/Projects/opensource/drydock/lib'
18
+
19
+ require 'drydock'
20
+ require 'boned'
21
+ require 'boned/cli'
22
+
23
+ # Command-line interface for bin/stella
24
+ class Boned::CLI::Definition
25
+ extend Drydock
26
+
27
+ debug :off
28
+
29
+ default :info
30
+
31
+ global :V, :version, "Display version" do
32
+ puts "Boned: #{Boned::VERSION} (api: #{Boned::APIVERSION})"
33
+ exit
34
+ end
35
+
36
+ global :R, :rackup, String, "Specify a rackup file"
37
+ global :e, :environment, String, "Rack environment"
38
+ global :v, :verbose, "Increase output" do; 1 end
39
+ global :d, :daemon, "Run as a daemon"
40
+ global :p, :port, Integer, "Port to run on"
41
+ global :D, :debug do
42
+ Drydock.debug true
43
+ Boned.enable_debug
44
+ end
45
+
46
+ command :start => Boned::CLI
47
+ command :stop => Boned::CLI
48
+ command :info => Boned::CLI
49
+
50
+ end
51
+
52
+
53
+ begin
54
+ Drydock.run!(ARGV, STDIN) if Drydock.run? && !Drydock.has_run?
55
+ rescue Boned::Problem => ex
56
+ STDERR.puts ex.message
57
+ STDERR.puts ex.backtrace if Drydock.debug?
58
+ exit 1
59
+ rescue Drydock::ArgError, Drydock::OptError => ex
60
+ STDERR.puts ex.message
61
+ STDERR.puts ex.usage
62
+ rescue Drydock::InvalidArgument => ex
63
+ STDERR.puts ex.message
64
+ rescue Drydock::UnknownCommand => ex
65
+ STDERR.puts "Unknown command: %s" % ex.name
66
+ rescue Interrupt
67
+ puts $/, "Exiting... "
68
+ exit 1
69
+ rescue => ex
70
+ STDERR.puts "ERROR (#{ex.class.to_s}): #{ex.message}"
71
+ STDERR.puts ex.backtrace if Drydock.debug?
72
+ end
data/boned.gemspec ADDED
@@ -0,0 +1,45 @@
1
+ @spec = Gem::Specification.new do |s|
2
+ s.name = "boned"
3
+ s.rubyforge_project = 'boned'
4
+ s.version = "0.2.0"
5
+ s.summary = "Get Bones"
6
+ s.description = s.summary
7
+ s.author = "Delano Mandelbaum"
8
+ s.email = "delano@solutious.com"
9
+ s.homepage = ""
10
+
11
+ s.extra_rdoc_files = %w[README.md LICENSE.txt CHANGES.txt]
12
+ s.has_rdoc = true
13
+ s.rdoc_options = ["--line-numbers", "--title", s.summary, "--main", "README.rdoc"]
14
+ s.require_paths = %w[lib]
15
+
16
+ s.executables = %w[boned]
17
+
18
+ # = MANIFEST =
19
+ # git ls-files
20
+ s.files = %w(
21
+ LICENSE.txt
22
+ README.md
23
+ Rakefile
24
+ bin/boned
25
+ boned.gemspec
26
+ config/redis-default.yml
27
+ config/redis-server-default.conf
28
+ config/stella/api-set.rb
29
+ config/stella/api.rb
30
+ lib/boned.rb
31
+ lib/boned/api.rb
32
+ lib/boned/api/debug.rb
33
+ lib/boned/api/redis.rb
34
+ lib/boned/api/service.rb
35
+ lib/boned/cli.rb
36
+ lib/boned/models.rb
37
+ lib/boned/models/bone.rb
38
+ lib/boned/server.rb
39
+ public/index.html
40
+ try/10_bone_model.rb
41
+ views/redisviewer/keys.erb
42
+ )
43
+
44
+
45
+ end
@@ -0,0 +1,14 @@
1
+ :development:
2
+ # :password: e89da64b29cf622e7c0fa545b537d7
3
+ :host: localhost
4
+ :port: "6379"
5
+ :db: 1
6
+ :timeout: 5
7
+ :thread_safe: true
8
+ :production:
9
+ # :password: e89da64b29cf622e7c0fa545b537d7
10
+ :host: localhost
11
+ :port: "6379"
12
+ :db: 0
13
+ :timeout: 5
14
+ :thread_safe: true
@@ -0,0 +1,171 @@
1
+ # Redis configuration file example
2
+
3
+ # By default Redis does not run as a daemon. Use 'yes' if you need it.
4
+ # Note that Redis will write a pid file in /var/run/redis.pid when daemonized.
5
+ daemonize no
6
+
7
+ # When run as a daemon, Redis write a pid file in /var/run/redis.pid by default.
8
+ # You can specify a custom pid file location here.
9
+ pidfile /var/run/redis.pid
10
+
11
+ # Accept connections on the specified port, default is 6379
12
+ port 6379
13
+
14
+ # If you want you can bind a single interface, if the bind option is not
15
+ # specified all the interfaces will listen for connections.
16
+ #
17
+ # bind 127.0.0.1
18
+
19
+ # Close the connection after a client is idle for N seconds (0 to disable)
20
+ timeout 300
21
+
22
+ # Save the DB on disk:
23
+ #
24
+ # save <seconds> <changes>
25
+ #
26
+ # Will save the DB if both the given number of seconds and the given
27
+ # number of write operations against the DB occurred.
28
+ #
29
+ # In the example below the behaviour will be to save:
30
+ # after 900 sec (15 min) if at least 1 key changed
31
+ # after 300 sec (5 min) if at least 10 keys changed
32
+ # after 60 sec if at least 10000 keys changed
33
+ save 900 1
34
+ save 300 10
35
+ save 60 10000
36
+
37
+ # The filename where to dump the DB
38
+ dbfilename dump.rdb
39
+
40
+ # For default save/load DB in/from the working directory
41
+ # Note that you must specify a directory not a file name.
42
+ dir ./
43
+
44
+ # Set server verbosity to 'debug'
45
+ # it can be one of:
46
+ # debug (a lot of information, useful for development/testing)
47
+ # notice (moderately verbose, what you want in production probably)
48
+ # warning (only very important / critical messages are logged)
49
+ loglevel debug
50
+
51
+ # Specify the log file name. Also 'stdout' can be used to force
52
+ # the demon to log on the standard output. Note that if you use standard
53
+ # output for logging but daemonize, logs will be sent to /dev/null
54
+ logfile stdout
55
+
56
+ # Set the number of databases. The default database is DB 0, you can select
57
+ # a different one on a per-connection basis using SELECT <dbid> where
58
+ # dbid is a number between 0 and 'databases'-1
59
+ databases 16
60
+
61
+ ################################# REPLICATION #################################
62
+
63
+ # Master-Slave replication. Use slaveof to make a Redis instance a copy of
64
+ # another Redis server. Note that the configuration is local to the slave
65
+ # so for example it is possible to configure the slave to save the DB with a
66
+ # different interval, or to listen to another port, and so on.
67
+
68
+ # slaveof <masterip> <masterport>
69
+
70
+ ################################## SECURITY ###################################
71
+
72
+ # Require clients to issue AUTH <PASSWORD> before processing any other
73
+ # commands. This might be useful in environments in which you do not trust
74
+ # others with access to the host running redis-server.
75
+ #
76
+ # This should stay commented out for backward compatibility and because most
77
+ # people do not need auth (e.g. they run their own servers).
78
+
79
+ # requirepass foobared
80
+
81
+ ################################### LIMITS ####################################
82
+
83
+ # Set the max number of connected clients at the same time. By default there
84
+ # is no limit, and it's up to the number of file descriptors the Redis process
85
+ # is able to open. The special value '0' means no limts.
86
+ # Once the limit is reached Redis will close all the new connections sending
87
+ # an error 'max number of clients reached'.
88
+
89
+ # maxclients 128
90
+
91
+ # Don't use more memory than the specified amount of bytes.
92
+ # When the memory limit is reached Redis will try to remove keys with an
93
+ # EXPIRE set. It will try to start freeing keys that are going to expire
94
+ # in little time and preserve keys with a longer time to live.
95
+ # Redis will also try to remove objects from free lists if possible.
96
+ #
97
+ # If all this fails, Redis will start to reply with errors to commands
98
+ # that will use more memory, like SET, LPUSH, and so on, and will continue
99
+ # to reply to most read-only commands like GET.
100
+ #
101
+ # WARNING: maxmemory can be a good idea mainly if you want to use Redis as a
102
+ # 'state' server or cache, not as a real DB. When Redis is used as a real
103
+ # database the memory usage will grow over the weeks, it will be obvious if
104
+ # it is going to use too much memory in the long run, and you'll have the time
105
+ # to upgrade. With maxmemory after the limit is reached you'll start to get
106
+ # errors for write operations, and this may even lead to DB inconsistency.
107
+
108
+ # maxmemory <bytes>
109
+
110
+ ############################## APPEND ONLY MODE ###############################
111
+
112
+ # By default Redis asynchronously dumps the dataset on disk. If you can live
113
+ # with the idea that the latest records will be lost if something like a crash
114
+ # happens this is the preferred way to run Redis. If instead you care a lot
115
+ # about your data and don't want to that a single record can get lost you should
116
+ # enable the append only mode: when this mode is enabled Redis will append
117
+ # every write operation received in the file appendonly.log. This file will
118
+ # be read on startup in order to rebuild the full dataset in memory.
119
+ #
120
+ # Note that you can have both the async dumps and the append only file if you
121
+ # like (you have to comment the "save" statements above to disable the dumps).
122
+ # Still if append only mode is enabled Redis will load the data from the
123
+ # log file at startup ignoring the dump.rdb file.
124
+ #
125
+ # The name of the append only file is "appendonly.log"
126
+
127
+ appendonly no
128
+
129
+ # The fsync() call tells the Operating System to actually write data on disk
130
+ # instead to wait for more data in the output buffer. Some OS will really flush
131
+ # data on disk, some other OS will just try to do it ASAP.
132
+ #
133
+ # Redis supports three different modes:
134
+ #
135
+ # no: don't fsync, just let the OS flush the data when it wants. Faster.
136
+ # always: fsync after every write to the append only log . Slow, Safest.
137
+ # everysec: fsync only if one second passed since the last fsync. Compromise.
138
+ #
139
+ # The default is "always" that's the safer of the options. It's up to you to
140
+ # understand if you can relax this to "everysec" that will fsync every second
141
+ # or to "no" that will let the operating system flush the output buffer when
142
+ # it want, for better performances (but if you can live with the idea of
143
+ # some data loss consider the default persistence mode that's snapshotting).
144
+
145
+ appendfsync always
146
+ # appendfsync everysec
147
+ # appendfsync no
148
+
149
+ ############################### ADVANCED CONFIG ###############################
150
+
151
+ # Glue small output buffers together in order to send small replies in a
152
+ # single TCP packet. Uses a bit more CPU but most of the times it is a win
153
+ # in terms of number of queries per second. Use 'yes' if unsure.
154
+ glueoutputbuf yes
155
+
156
+ # Use object sharing. Can save a lot of memory if you have many common
157
+ # string in your dataset, but performs lookups against the shared objects
158
+ # pool so it uses more CPU and can be a bit slower. Usually it's a good
159
+ # idea.
160
+ #
161
+ # When object sharing is enabled (shareobjects yes) you can use
162
+ # shareobjectspoolsize to control the size of the pool used in order to try
163
+ # object sharing. A bigger pool size will lead to better sharing capabilities.
164
+ # In general you want this value to be at least the double of the number of
165
+ # very common strings you have in your dataset.
166
+ #
167
+ # WARNING: object sharing is experimental, don't enable this feature
168
+ # in production before of Redis 1.0-stable. Still please try this feature in
169
+ # your development environment so that we can test it better.
170
+ shareobjects no
171
+ shareobjectspoolsize 1024
@@ -0,0 +1,32 @@
1
+
2
+
3
+ usecase "Set" do
4
+ set :key => :simple
5
+ set :token => '1901484c41b8752d61863e323c743abe2c6e90f8841bbdfa789e1d70bc6f4899'
6
+
7
+ get "/v1/set/:key" do
8
+ set :value => (rand*100000).to_i
9
+ param :token => resource(:token)
10
+ param :value => resource(:value)
11
+ end
12
+
13
+ get "/v1/get/:key" do
14
+ param :token => resource(:token)
15
+ response 200 do
16
+ p [resource(:value), body.to_i]
17
+ end
18
+ end
19
+
20
+ get "/v1/set/:key" do
21
+ param :key => rand(1000).to_i
22
+ set :value => (rand*100000).to_i
23
+ param :token => resource(:token)
24
+ param :value => resource(:value)
25
+ end
26
+
27
+ xpost "/v1/set/:key" do
28
+ param :key => :file
29
+ body file('/tmp/file')
30
+ end
31
+
32
+ end
@@ -0,0 +1,51 @@
1
+
2
+
3
+ usecase do
4
+ set :token => '1901484c41b8752d61863e323c743abe2c6e90f8841bbdfa789e1d70bc6f4899'
5
+ set :key => :simple
6
+
7
+ post "/v1/set/:key" do
8
+ set :value => (rand*100000).to_i
9
+ param :token => resource(:token)
10
+ param :value => resource(:value)
11
+ end
12
+
13
+ get "/v1/get/:key" do
14
+ param :token => resource(:token)
15
+ response 200 do
16
+ p [resource(:value), body.to_i]
17
+ end
18
+ end
19
+
20
+ post "/v1/set/:key" do
21
+ param :key => 'rand(1000).to_i'
22
+ set :value => (rand*100000).to_i
23
+ param :token => resource(:token)
24
+ param :value => resource(:value)
25
+ end
26
+
27
+ xpost "/v1/set/:key" do
28
+ param :key => :file
29
+ body file('/tmp/file')
30
+ end
31
+
32
+ get "/v1/keys/:key" do
33
+ param :token => resource(:token)
34
+ end
35
+
36
+ get "/v1/keys" do
37
+ param :token => resource(:token)
38
+ end
39
+
40
+ delete "/v1/del/:key" do
41
+ header :'X-BONE-TOKEN' => resource(:token)
42
+ end
43
+
44
+ get "/v1/get/:key" do
45
+ param :token => resource(:token)
46
+ response 404 do
47
+ # This is the correct response so don't fail
48
+ end
49
+ end
50
+
51
+ end
@@ -0,0 +1,21 @@
1
+
2
+ class Boned::API::Debug < Boned::API
3
+ set :public => 'public/debug/'
4
+ set :views => 'views/debug/'
5
+
6
+ not_found do
7
+ 'not found'
8
+ end
9
+
10
+ get '/env/?' do
11
+ content_type 'text/plain'
12
+ env.to_yaml
13
+ end
14
+
15
+ get '/slideshow' do
16
+ content_type 'text/html'
17
+ erb :slideshow
18
+ end
19
+ end
20
+
21
+
@@ -0,0 +1,44 @@
1
+
2
+
3
+ class Boned::API::RedisViewer < Boned::API
4
+
5
+ set :public => 'public/'
6
+ set :views => 'views/redisviewer/'
7
+
8
+ before do
9
+ content_type 'text/html'
10
+ end
11
+
12
+ get '/list/:name' do
13
+ Boned.redis.lrange(params[:name], 0, -1).to_yaml
14
+ end
15
+
16
+ get '/smembers/:name' do
17
+ Boned.redis.smembers(params[:name]).to_yaml
18
+ end
19
+
20
+ get '/opts' do
21
+ Boned.redis_opts.to_yaml
22
+ end
23
+
24
+ get '/get/:name' do
25
+ '%s=%s' % [params[:name], Boned.redis.get(params[:name])]
26
+ end
27
+
28
+ get '/:key' do
29
+ @keys = Boned.redis.keys("*#{params[:key]}*")
30
+ erb :keys
31
+ end
32
+
33
+ get '/?' do
34
+ @keys = Boned.redis.keys("*")
35
+ erb :keys
36
+ end
37
+
38
+
39
+ helpers do
40
+ def key_kind(key)
41
+ Boned.redis.type key
42
+ end
43
+ end
44
+ end
@@ -0,0 +1,86 @@
1
+ require 'pp'
2
+
3
+ # /get/bashrc?path=/Users/delano/&env=development&role=fe&token=1901484c41b8752d61863e323c743abe2c6e90f8841bbdfa789e1d70bc6f4899
4
+ # /set/bashrc?value=1000&path=/Users/delano/&env=development&role=fe&token=1901484c41b8752d61863e323c743abe2c6e90f8841bbdfa789e1d70bc6f4899
5
+ #
6
+ class Boned::API::Service < Boned::API
7
+ set :public => 'public/'
8
+ set :views => 'views/'
9
+
10
+ error do
11
+ "Bad bone rising"
12
+ end
13
+
14
+ get '/' do
15
+ 'Throw me a fricken bone'
16
+ end
17
+
18
+ get '/rev/?' do
19
+ Boned::VERSION.inspect
20
+ end
21
+
22
+ get '/get/:key/?' do
23
+ carefully do
24
+ assert_params :key
25
+ assert_exists current_token, "No token"
26
+ assert_sha256 current_token
27
+ bone = Bone.get current_token, params[:key], params
28
+ bone.value
29
+ end
30
+ end
31
+
32
+ post '/set/:key/?' do
33
+ carefully do
34
+ assert_params :key, :value
35
+ assert_exists current_token, "No token"
36
+ assert_sha256 current_token
37
+ bone = Bone.new current_token, params[:key], params[:value], params
38
+ bone.save
39
+ end
40
+ params[:value]
41
+ end
42
+
43
+ delete "/del/:key/?" do
44
+ carefully do
45
+ assert_params :key
46
+ assert_exists current_token, "No token"
47
+ assert_sha256 current_token
48
+ bone = Bone.del current_token, params[:key], params
49
+ bone.value
50
+ end
51
+ end
52
+
53
+ get '/keys/:key' do
54
+ carefully do
55
+ assert_params :key
56
+ assert_exists current_token, "No token"
57
+ assert_sha256 current_token
58
+ keys = Bone.keys current_token, params[:key]
59
+ keys.join($/)
60
+ end
61
+ end
62
+
63
+ get '/keys/?' do
64
+ carefully do
65
+ assert_exists current_token, "No token"
66
+ assert_sha256 current_token
67
+ keys = Bone.keys current_token, '*'
68
+ keys.join($/)
69
+ end
70
+ end
71
+
72
+ #post '/set/:key/?' do
73
+ # carefully do
74
+ # assert_params :key
75
+ # assert_sha256 current_token
76
+ # #assert_exists current_token
77
+ # #key, filter, path = params.values_at :key, :filter, :path
78
+ # #Boned::Object.set current_token, key
79
+ # pp request
80
+ # pp env['rack.input'].read
81
+ # end
82
+ #end
83
+
84
+ end
85
+
86
+
data/lib/boned/api.rb ADDED
@@ -0,0 +1,98 @@
1
+
2
+ require 'boned'
3
+
4
+
5
+ class Boned::API < Sinatra::Base
6
+
7
+ set :public => 'public/'
8
+ set :views => 'views/'
9
+ set :static => true
10
+
11
+ before do
12
+ puts ENV['RACK_ENV']
13
+ end
14
+
15
+ configure :development do
16
+ before do
17
+ Boned.enable_debug
18
+ Boned.ld ' --> ' << env['REQUEST_URI']
19
+ content_type 'text/plain'
20
+ end
21
+ end
22
+
23
+ configure :production do
24
+ Boned.disable_debug
25
+ before do
26
+ content_type 'application/json'
27
+ end
28
+ end
29
+
30
+ helpers do
31
+ def carefully(ret='', &blk)
32
+ begin
33
+ ret = blk.call
34
+ rescue Boned::BadBone => ex
35
+ return error(404, ex.message)
36
+ rescue => ex
37
+ Boned.ld "#{current_token}:#{params[:key]}", ex.message
38
+ Boned.ld ex.backtrace
39
+ return error(400, "Bad bone rising")
40
+ end
41
+ ret
42
+ end
43
+
44
+ def current_token() @env['HTTP_X_BONE_TOKEN'] || params[:token] end
45
+ def current_sig() @env['HTTP_X_BONE_SIGNATURE'] || params[:sig] end
46
+
47
+ def uri(*path)
48
+ [root_path, path].flatten.join('/')
49
+ end
50
+ def root_path
51
+ env['SCRIPT_NAME']
52
+ end
53
+
54
+ # +names+ One or more a required parameter names (Symbol)
55
+ def assert_params(*names)
56
+ names.each do |n|
57
+ if params[n].nil? || params[n].empty?
58
+ return error(400, "Missing param: %s" % n)
59
+ end
60
+ end
61
+ end
62
+ alias_method :assert_param, :assert_params
63
+
64
+ def assert_exists(val, msg)
65
+ return error(400, msg) if val.nil? ||
66
+ (val.respond_to?(:empty?) && val.empty?)
67
+ end
68
+
69
+ def assert_true(val, msg)
70
+ return error(400, msg) if val == true
71
+ end
72
+
73
+ def assert_sha1(val)
74
+ return error(400, "#{val} is not a sha1 digest") unless is_sha1?(val)
75
+ end
76
+
77
+ def assert_sha256(val)
78
+ return error(400, "#{val} is not a sha256 digest") unless is_sha256?(val)
79
+ end
80
+
81
+ def is_sha1?(val)
82
+ val.match(/\A[0-9a-f]{40}\z/)
83
+ end
84
+ def is_sha256?(val)
85
+ val.match(/\A[0-9a-f]{64}\z/)
86
+ end
87
+
88
+ end
89
+ end
90
+
91
+ class Boned::API::Stub < Boned::API
92
+ get '/' do
93
+ content_type 'text/plain'
94
+ "Do you want to get bones?"
95
+ end
96
+ end
97
+
98
+ require 'boned/api/service'
data/lib/boned/cli.rb ADDED
@@ -0,0 +1,58 @@
1
+ require 'boned/server'
2
+ require 'pp'
3
+
4
+ class Boned::CLI < Drydock::Command
5
+ attr_accessor :exit_code
6
+
7
+ def init
8
+ Boned.load_config Dir.pwd, :development
9
+ Boned.connect
10
+ end
11
+
12
+ def start
13
+ if Boned.service_available?('127.0.0.1', server_opts[:port])
14
+ raise Boned::Server::ServerRunning, server_opts[:port]
15
+ end
16
+ Boned::Controllers::Controller.new(server_opts).start
17
+ end
18
+
19
+ def stop
20
+ if not Boned.service_available?('127.0.0.1', server_opts[:port])
21
+ raise Boned::Server::ServerNotRunning, server_opts[:port]
22
+ end
23
+ Boned::Controllers::Controller.new(server_opts).stop
24
+ end
25
+
26
+ def info
27
+ require 'yaml'
28
+ if Boned.service_available?('127.0.0.1', server_opts[:port])
29
+ puts "boned is running on port #{server_opts[:port]}"
30
+ else
31
+ puts "No boned"
32
+ end
33
+ puts "Options:", server_opts.to_yaml if @global.verbose > 0
34
+ end
35
+
36
+ private
37
+
38
+ def server_opts
39
+ port = @global.port || Boned::Server::DEFAULT_PORT
40
+ config = @global.rackup || File.join(Dir.pwd, "config.ru")
41
+ @server_opts ||= {
42
+ :chdir => Dir.pwd,
43
+ :environment => @global.environment || 'development',
44
+ :address => '0.0.0.0',
45
+ :port => port,
46
+ :timeout => 30,
47
+ :log => "log/boned-#{port}.log",
48
+ :pid => "tmp/pids/boned-#{port}.pid",
49
+ :max_conns => Thin::Server::DEFAULT_MAXIMUM_CONNECTIONS,
50
+ :max_persistent_conns => Thin::Server::DEFAULT_MAXIMUM_PERSISTENT_CONNECTIONS,
51
+ :require => [],
52
+ :wait => Thin::Controllers::Cluster::DEFAULT_WAIT_TIME,
53
+ :backend => "Boned::Server",
54
+ :rackup => config,
55
+ :daemonize => @global.daemon || false
56
+ }
57
+ end
58
+ end
@@ -0,0 +1,113 @@
1
+
2
+ # Bone:a462b9ebda71f16cb1567ba8704695ae8dba9999:simple
3
+ # Bone:a462b9ebda71f16cb1567ba8704695ae8dba9999:us-east-1a-dev-fe:simple
4
+ # Bone:a462b9ebda71f16cb1567ba8704695ae8dba9999:dev-fe:simple
5
+ # Bone:a462b9ebda71f16cb1567ba8704695ae8dba9999:us-east-1a:simple
6
+
7
+ class Bone < Boned::Model
8
+
9
+ primarykey :boneid
10
+
11
+ field :token
12
+ field :name
13
+ field :value
14
+ field :prop
15
+
16
+ def initialize(token, name, value, prop={})
17
+ @prop ||= {}
18
+ prop ||= {}
19
+ prop.each_pair { |n,v| @prop[n.to_sym] ||= v }
20
+ @token, @name = token, name
21
+ @new = refresh!.nil?
22
+ @value = value unless value.nil? # use the new value if provided
23
+ @prop[:created] = Time.now.utc.to_i if new? || @prop[:created].nil?
24
+ # resolve string/symbol ambiguity for properties
25
+ def @prop.[](k) super(k.to_s.to_sym) || super(k.to_s) end
26
+ def @prop.[]=(k,v) super(k.to_s.to_sym,v) end
27
+ end
28
+
29
+ def [](k)
30
+ self.respond_to?(k) ? self.send : self.prop[k]
31
+ end
32
+
33
+ def new?() @new == true end
34
+
35
+ def file?
36
+ !prop[:file].nil?
37
+ end
38
+
39
+ def region() prop[:region] end
40
+ def env() prop[:env] end
41
+ def role() prop[:role] end
42
+ def num() prop[:num] end
43
+ def path() prop[:path] end
44
+
45
+ def boneid
46
+ loc = [region, env, role, num].compact.join('-')
47
+ parts = loc.empty? ? [token] : [token, loc]
48
+ parts << path.gsub(/\A\//, '').tr('/', '-') unless path.nil? # TODO: support windows
49
+ parts << name
50
+ parts.collect { |p| p.to_s.tr(':', '-') }.join(':')
51
+ end
52
+
53
+ def key(*parts)
54
+ parts.unshift boneid
55
+ self.class.key *parts
56
+ end
57
+
58
+ def save
59
+ redis.set(key, value)
60
+ prop[:modified] = Time.now.utc.to_i
61
+ redis.set(key(:prop), prop.to_json)
62
+ end
63
+
64
+ def refresh!
65
+ Boned.ld "REFRESH: #{key}"
66
+ @value = redis.get key
67
+ prop = redis.get key(:prop)
68
+ prop &&= JSON.parse(prop) rescue {}
69
+ # Merge the stored props with the current ones. Enforce symbols!
70
+ prop.each_pair { |n,v| @prop[n.to_sym] ||= v }
71
+ Boned.ld " -> #{@value} #{@prop.inspect}"
72
+ self
73
+ rescue
74
+ nil
75
+ end
76
+
77
+ def destroy!
78
+ Boned.ld "DESTROY: #{key}"
79
+ redis.del key
80
+ redis.del key(:prop)
81
+ end
82
+
83
+ class << self
84
+
85
+ def del(token, name, opts={})
86
+ bone = get(token, name, opts)
87
+ bone.destroy!
88
+ bone
89
+ end
90
+
91
+ def get(token, name, opts={})
92
+ bone = Bone.new token, name, nil, opts
93
+ raise Boned::BadBone, bone.name if bone.value.nil?
94
+ bone
95
+ end
96
+
97
+ def keys(token, k=nil, opts={})
98
+ search = key(token) << '*'
99
+ search << k unless k.nil?
100
+ Boned.ld "SEARCHING: #{search}"
101
+ dirty = redis.keys search # contains :prop when no keyname specified
102
+ clean = []
103
+ dirty.each { |k|
104
+ next if k.match(/prop\z/);
105
+ p k
106
+ clean << k.split(':')[4..-1].join(':')
107
+ }
108
+ clean
109
+ end
110
+
111
+ end
112
+
113
+ end
@@ -0,0 +1,34 @@
1
+
2
+
3
+ module Boned
4
+ class Model < Storable
5
+ def self.key(*el)
6
+ raise "#{self}: nil keypart: #{el.inspect}" if el.size != el.compact.size
7
+ a = "v1::#{self}"
8
+ a << ':' << el.join(':') unless el.empty?
9
+ a
10
+ end
11
+ def self.redis
12
+ Boned.redis
13
+ end
14
+ def self.primarykey(v=nil)
15
+ unless v.nil?
16
+ @primarykey = v
17
+ class_eval do
18
+ def primarykey() send(self.class.primarykey) end
19
+ end
20
+ end
21
+ @primarykey
22
+ end
23
+ def redis
24
+ self.class.redis
25
+ end
26
+ def save
27
+ redis.sadd(self.class.key(kind, :all), self.primarykey) &&
28
+ redis.set(key(:created), self.time.to_i) &&
29
+ redis.set(key(:object), to_json)
30
+ end
31
+ end
32
+ end
33
+
34
+ Boned.require_glob 'boned', 'models', '*.rb'
@@ -0,0 +1,67 @@
1
+ require 'thin'
2
+ require 'logging'
3
+ require 'forwardable'
4
+
5
+ module Boned
6
+ class Server < Thin::Backends::Base
7
+ DEFAULT_PORT = 6043.freeze
8
+
9
+ class ServerRunning < Boned::Problem
10
+ def message() "Server already running on port: #{super}" end
11
+ end
12
+
13
+ class ServerNotRunning < Boned::Problem
14
+ def message() "Server not running on port: #{super}" end
15
+ end
16
+
17
+ class << self
18
+ end
19
+
20
+ # Address and port on which the server is listening for connections.
21
+ attr_accessor :host, :port
22
+
23
+ def initialize(host, port, options)
24
+ @host = host
25
+ @port = port
26
+ super()
27
+ end
28
+
29
+ # Connect the server
30
+ def connect
31
+ @signature = EventMachine.start_server(@host, @port, Thin::Connection, &method(:initialize_connection))
32
+ rescue => ex
33
+ puts ex.message
34
+ puts ex.backtrace if Boned.debug
35
+ stop!
36
+ end
37
+
38
+ # Stops the server
39
+ def disconnect
40
+ EventMachine.stop_server(@signature)
41
+ end
42
+
43
+ def to_s
44
+ "#{@host}:#{@port}"
45
+ end
46
+
47
+ end
48
+ module Controllers
49
+ class Controller < Thin::Controllers::Controller
50
+ end
51
+ class Service < Thin::Controllers::Service
52
+ end
53
+ class Cluster < Thin::Controllers::Cluster
54
+ end
55
+ end
56
+ end
57
+
58
+
59
+
60
+
61
+ #controller = case
62
+ #when cluster? then Thin::Controllers::Cluster.new(options)
63
+ #when service? then Thin::Controllers::Service.new(options)
64
+ #else Thin::Controllers::Controller.new(options)
65
+ #end
66
+
67
+
data/lib/boned.rb ADDED
@@ -0,0 +1,139 @@
1
+ require 'rack/auth/digest/md5'
2
+
3
+ require 'redis'
4
+ require 'redis/namespace'
5
+ require 'storable'
6
+ require 'attic'
7
+ require 'gibbler/aliases'
8
+ require 'sysinfo'
9
+ require 'socket'
10
+
11
+ unless defined?(BONED_HOME)
12
+ BONED_HOME = File.expand_path(File.join(File.dirname(__FILE__), '..') )
13
+ end
14
+
15
+ module Boned
16
+ APIVERSION = 'v1'.freeze unless defined?(APIVERSION)
17
+ module VERSION
18
+ MAJOR = 0
19
+ MINOR = 2
20
+ TINY = 0
21
+ PATCH = '001'
22
+ def self.inspect; [to_s, PATCH].join('.'); end
23
+ def self.to_s; [MAJOR, MINOR, TINY].join('.'); end
24
+ def self.to_f; self.to_s.to_f; end
25
+ def self.patch; PATCH; end
26
+ end
27
+
28
+ class Problem < RuntimeError; end
29
+ class BadBone < Problem
30
+ def message() "No such bone: #{super}" end
31
+ end
32
+
33
+ @debug = false
34
+ @conf = nil
35
+ @redis = nil
36
+ @sysinfo = nil
37
+ class << self
38
+ attr_accessor :debug
39
+ attr_reader :conf
40
+ attr_accessor :redis
41
+ def debug?() @debug == true end
42
+ def enable_debug() @debug = true end
43
+ def disable_debug() @debug = false end
44
+ def sysinfo
45
+ @sysinfo = SysInfo.new.freeze if @sysinfo.nil?
46
+ @sysinfo
47
+ end
48
+ end
49
+
50
+ # Connect to Redis and Mongo.
51
+ def self.connect(conf=@conf)
52
+ @redis = Redis.new conf[:redis]
53
+ end
54
+
55
+ # Loads the yaml config files from config/
56
+ # * +base+ path where config dir lives
57
+ # * +env one of: :development, :production
58
+ # Returns a Hash: conf[:mongo][:host], ...
59
+ def self.load_config(base=Dir.pwd, env=:development)
60
+ @conf = {}
61
+ [:redis].each do |n|
62
+ Boned.ld "LOADING CONFIG: #{n}"
63
+ tmp = YAML.load_file(File.join(base, "config", "#{n}.yml"))
64
+ @conf[n] = tmp[ env.to_sym ]
65
+ end
66
+ @conf
67
+ end
68
+
69
+ # <tt>require</tt> a library from the vendor directory.
70
+ # The vendor directory should be organized such
71
+ # that +name+ and +version+ can be used to create
72
+ # the path to the library.
73
+ #
74
+ # e.g.
75
+ #
76
+ # vendor/httpclient-2.1.5.2/httpclient
77
+ #
78
+ def self.require_vendor(name, version)
79
+ path = File.join(BONED_HOME, 'vendor', "#{name}-#{version}", 'lib')
80
+ $:.unshift path
81
+ Boned.ld "REQUIRE VENDOR: ", path
82
+ require name
83
+ end
84
+
85
+ def self.require_glob(*path)
86
+ path = [BONED_HOME, 'lib', path].flatten
87
+ libs = Dir.glob(File.join(*path))
88
+ Boned.ld "REQUIRE GLOB: ", libs
89
+ libs.each do |lib|
90
+ next if lib == __FILE__
91
+ require lib if File.file? lib
92
+ end
93
+ end
94
+
95
+ # Checks whether something is listening to a socket.
96
+ # * +host+ A hostname
97
+ # * +port+ The port to check
98
+ # * +wait+ The number of seconds to wait for before timing out.
99
+ #
100
+ # Returns true if +host+ allows a socket connection on +port+.
101
+ # Returns false if one of the following exceptions is raised:
102
+ # Errno::EAFNOSUPPORT, Errno::ECONNREFUSED, SocketError, Timeout::Error
103
+ #
104
+ def self.service_available?(host, port, wait=3)
105
+ if Boned.sysinfo.vm == :java
106
+ begin
107
+ iadd = Java::InetSocketAddress.new host, port
108
+ socket = Java::Socket.new
109
+ socket.connect iadd, wait * 1000 # milliseconds
110
+ success = !socket.isClosed && socket.isConnected
111
+ rescue NativeException => ex
112
+ puts ex.message, ex.backtrace if Boned.debug?
113
+ false
114
+ end
115
+ else
116
+ begin
117
+ status = Timeout::timeout(wait) do
118
+ socket = Socket.new( Socket::AF_INET, Socket::SOCK_STREAM, 0 )
119
+ sockaddr = Socket.pack_sockaddr_in( port, host )
120
+ socket.connect( sockaddr )
121
+ end
122
+ true
123
+ rescue Errno::EAFNOSUPPORT, Errno::ECONNREFUSED, SocketError, Timeout::Error => ex
124
+ puts ex.class, ex.message, ex.backtrace if Boned.debug?
125
+ false
126
+ end
127
+ end
128
+ end
129
+
130
+
131
+ def self.ld(*msg)
132
+ return unless Boned.debug
133
+ prefix = "D(#{Thread.current.object_id}): "
134
+ puts "#{prefix}" << msg.join("#{$/}#{prefix}")
135
+ end
136
+
137
+ end
138
+
139
+ require 'boned/models'
data/public/index.html ADDED
@@ -0,0 +1,4 @@
1
+ <html>
2
+ <body>
3
+ </body>
4
+ </html>
@@ -0,0 +1,21 @@
1
+ # ruby -rubygems -Ilib try/10_bone_model.rb
2
+ require 'boned'
3
+ Boned.load_config
4
+ Boned.connect
5
+ Boned.enable_debug
6
+
7
+ token = Digest::SHA256.hexdigest('1901484c41b8752d61863e323c743')
8
+ key = 'simple'
9
+
10
+ puts Bone.keys(token)
11
+
12
+ __END__
13
+ opts = { :env => 'dev', :region => 'us-east-1a', :num => '01', :role => 'fe', :path => Dir.pwd }
14
+ bone = Bone.new token, key, rand(100000).to_i, opts
15
+ #puts "BONE: " << bone.inspect, $/
16
+ bone.save
17
+ #puts "KEYS: " << Bone.keys( token, key).inspect, $/
18
+
19
+ bone = Bone.get( token, key, opts)
20
+ puts "GET: " << bone.inspect, $/
21
+ puts bone.to_json
@@ -0,0 +1,12 @@
1
+ <% @keys.each do |key| %>
2
+ <div class="key">
3
+ <% kind = key_kind(key) %>
4
+ <% if kind == 'string' %>
5
+ <a href="<%= root_path %>/get/<%= key %>"><%= key %></a> <%= (kind) %>
6
+ <% elsif kind == 'set' %>
7
+ <a href="<%= root_path %>/smembers/<%= key %>"><%= key %></a> <%= (kind) %>
8
+ <% elsif kind == 'list' %>
9
+ <a href="<%= root_path %>/list/<%= key %>"><%= key %></a> <%= (kind) %>
10
+ <% end %>
11
+ </div>
12
+ <% end %>
metadata ADDED
@@ -0,0 +1,82 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: boned
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.2.0
5
+ platform: ruby
6
+ authors:
7
+ - Delano Mandelbaum
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+
12
+ date: 2009-12-13 00:00:00 -05:00
13
+ default_executable:
14
+ dependencies: []
15
+
16
+ description: Get Bones
17
+ email: delano@solutious.com
18
+ executables:
19
+ - boned
20
+ extensions: []
21
+
22
+ extra_rdoc_files:
23
+ - README.md
24
+ - LICENSE.txt
25
+ - CHANGES.txt
26
+ files:
27
+ - LICENSE.txt
28
+ - README.md
29
+ - Rakefile
30
+ - bin/boned
31
+ - boned.gemspec
32
+ - config/redis-default.yml
33
+ - config/redis-server-default.conf
34
+ - config/stella/api-set.rb
35
+ - config/stella/api.rb
36
+ - lib/boned.rb
37
+ - lib/boned/api.rb
38
+ - lib/boned/api/debug.rb
39
+ - lib/boned/api/redis.rb
40
+ - lib/boned/api/service.rb
41
+ - lib/boned/cli.rb
42
+ - lib/boned/models.rb
43
+ - lib/boned/models/bone.rb
44
+ - lib/boned/server.rb
45
+ - public/index.html
46
+ - try/10_bone_model.rb
47
+ - views/redisviewer/keys.erb
48
+ - CHANGES.txt
49
+ has_rdoc: true
50
+ homepage: ""
51
+ licenses: []
52
+
53
+ post_install_message:
54
+ rdoc_options:
55
+ - --line-numbers
56
+ - --title
57
+ - Get Bones
58
+ - --main
59
+ - README.rdoc
60
+ require_paths:
61
+ - lib
62
+ required_ruby_version: !ruby/object:Gem::Requirement
63
+ requirements:
64
+ - - ">="
65
+ - !ruby/object:Gem::Version
66
+ version: "0"
67
+ version:
68
+ required_rubygems_version: !ruby/object:Gem::Requirement
69
+ requirements:
70
+ - - ">="
71
+ - !ruby/object:Gem::Version
72
+ version: "0"
73
+ version:
74
+ requirements: []
75
+
76
+ rubyforge_project: boned
77
+ rubygems_version: 1.3.5
78
+ signing_key:
79
+ specification_version: 3
80
+ summary: Get Bones
81
+ test_files: []
82
+