boned 0.2.0

Sign up to get free protection for your applications and to get access to all the features.
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
+