lib/leanback.rb in leanback-0.4.2 vs lib/leanback.rb in leanback-0.5.0
- old
+ new
@@ -1,614 +1,156 @@
require 'rest_client'
-require 'yajl'
-require 'erb'
+require 'json/pure'
+require 'active_support/all'
-class CouchdbException < RuntimeError
- attr :error
- def initialize(error)
- @error = error.values[0]
- end
-end
-
-module Couchdb
- def self.couch_error(e)
- raise e if e.is_a? OpenSSL::SSL::SSLError
- hash = Yajl::Parser.parse(e.response.to_s)
- raise CouchdbException.new(hash), "CouchDB: Error - #{hash.values[0]}. Reason - #{hash.values[1]}"
- end
-
- def self.salt
- o = [('a'..'z'),('A'..'Z')].map{|i| i.to_a}.flatten;
- salt = (0..50).map{ o[rand(o.length)] }.join;
- end
-
- #change non-admin user password
- def self.change_password(username, new_password,auth_session = "")
- salty = salt()
- password_sha = Digest::SHA1.hexdigest(new_password + salty)
- user_id = 'org.couchdb.user:' + username
- data = {"salt" => salty,"password_sha" => password_sha}
- doc = { :database => '_users', :doc_id => user_id, :data => data}
- update_doc doc,auth_session
- end
-
- #add a new user
- def self.add_user(user, auth_session="")
- o = [('a'..'z'),('A'..'Z')].map{|i| i.to_a}.flatten;
- salt = (0..50).map{ o[rand(o.length)] }.join;
- new_user = {:username => user[:username], :password => user[:password], :roles => user[:roles], :salt => salt}
- create_user(new_user,auth_session)
- end
-
- #create a new user
- def self.create_user(user,auth_session= "")
- password_sha = Digest::SHA1.hexdigest(user[:password] + user[:salt])
-
- user_hash = { :type => "user",
- :name => user[:username],
- :password_sha => password_sha,
- :salt => user[:salt],
- :roles => user[:roles]
- }
-
- str = Yajl::Encoder.encode(user_hash)
- set_address
- begin
- response = RestClient.put "#{@address}:#{@port}/_users/org.couchdb.user:#{URI.escape(user[:username])}", str,{:cookies => {"AuthSession" => auth_session}}
- hash = Yajl::Parser.parse(response.to_str)
- rescue => e
- couch_error(e)
- end
-
- end
-
- #add security object
- def self.set_security(db_name, data,auth_session="")
- security_data = Yajl::Encoder.encode(data)
- set_address
- begin
- response = RestClient.put "#{@address}:#{@port}/#{URI.escape(db_name)}/_security/",security_data, {:cookies => {"AuthSession" => auth_session}}
- hash = Yajl::Parser.parse(response.to_str)
- rescue => e
- couch_error(e)
- end
- end
-
- #get security object
- def self.get_security(db_name, auth_session="")
- set_address
- begin
- response = RestClient.get "#{@address}:#{@port}/#{URI.escape(db_name)}/_security/", {:cookies => {"AuthSession" => auth_session}}
- hash = Yajl::Parser.parse(response.to_str)
- rescue => e
- couch_error(e)
- end
- end
-
- #login to couchdb
- def self.login(username, password)
- set_address
- data = "name=#{username}&password=#{password}"
- begin
- response = RestClient.post "#{@address}:#{@port}/_session/", data, {:content_type => 'application/x-www-form-urlencoded'}
- response.cookies
- rescue => e
- couch_error(e)
+module Leanback
+ class InvalidDatabaseName < StandardError; end
+ class InvalidDocumentID < StandardError; end
+ class CouchdbException < StandardError
+ attr_reader :response
+ def initialize(response)
+ @response = response
end
end
-
- #couchdb configuration api
- def self.set_config(data,auth_session = "")
- section = data[:section]
- key = data[:key]
- value = data[:value]
- json_data = Yajl::Encoder.encode(value)
- set_address
- begin
- response = RestClient.put "#{@address}:#{@port}/_config/#{URI.escape(section)}/#{URI.escape(key)}",json_data, {:cookies => {"AuthSession" => auth_session}}
- hash = Yajl::Parser.parse(response.to_str)
- rescue => e
- couch_error(e)
+ class Couchdb
+ attr_reader :address
+ attr_reader :port
+ attr_reader :username
+ attr_reader :password
+ attr_reader :database
+ def initialize(args = {})
+ @database = args.fetch(:database, nil)
+ @address = args.fetch(:address, 'http://127.0.0.1')
+ @port = args.fetch(:port, '5984')
+ @username = args.fetch(:username, nil)
+ @password = args.fetch(:password, nil)
end
- end
-
- def self.delete_config(data,auth_session = "")
- section = data[:section]
- key = data[:key]
- set_address
- begin
- response = RestClient.delete "#{@address}:#{@port}/_config/#{URI.escape(section)}/#{URI.escape(key)}", {:cookies => {"AuthSession" => auth_session}}
- hash = Yajl::Parser.parse(response.to_str)
- rescue => e
- couch_error(e)
+ def create
+ api_request { RestClient.put "#{address_port}/#{db_uri}", content_type, cookies }
end
- end
-
-
- def self.get_config(data,auth_session = "")
- section = data[:section]
- key = data[:key]
- set_address
- begin
- response = RestClient.get "#{@address}:#{@port}/_config/#{URI.escape(section)}/#{URI.escape(key)}", {:cookies => {"AuthSession" => auth_session}}
- hash = Yajl::Parser.parse(response.to_str)
- rescue => e
- couch_error(e)
+ def delete
+ api_request { RestClient.delete "#{address_port}/#{db_uri}", cookies }
end
- end
-
- #create a document
- def self.create_doc( doc,auth_session = "")
- db_name = doc[:database]
- doc_id = doc[:doc_id]
- data = doc[:data]
- json_data = Yajl::Encoder.encode(data)
- set_address
- begin
- response = RestClient.put "#{@address}:#{@port}/#{URI.escape(db_name)}/#{URI.escape(doc_id)}",json_data, {:cookies => {"AuthSession" => auth_session}}
- hash = Yajl::Parser.parse(response.to_str)
- rescue => e
- couch_error(e)
+ def create_doc(doc_id, doc)
+ api_request { RestClient.put "#{address_port}/#{db_uri}/#{doc_uri(doc_id)}", generate_json(doc), cookies }
end
- end
-
- #edit a document
- def self.edit_doc(doc,auth_session = "")
- db_name = doc[:database]
- doc_id = doc[:doc_id]
- data = doc[:data]
- json_data = Yajl::Encoder.encode(data)
- set_address
- begin
- response = RestClient.put "#{@address}:#{@port}/#{URI.escape(db_name)}/#{URI.escape(doc_id)}", json_data, {:cookies => {"AuthSession" => auth_session}}
- hash = Yajl::Parser.parse(response.to_str)
- rescue => e
- couch_error(e)
+ def delete_doc(doc_id, rev)
+ api_request { RestClient.delete "#{address_port}/#{db_uri}/#{doc_uri(doc_id)}?rev=#{rev}", cookies }
end
- end
-
- #update a doc
- def self.update_doc(doc,auth_session = "")
- db_name = doc[:database]
- doc_id = doc[:doc_id]
- data = doc[:data]
- doc = {:database => db_name, :doc_id => doc_id}
- options = Couchdb.view doc,auth_session
- options = options.merge(data)
- doc = {:database => db_name, :doc_id => doc_id, :data => options}
- edit_doc doc,auth_session
- end
-
- #delete document
- def self.delete_doc(doc,auth_session = "")
- db_name = doc[:database]
- doc_id = doc[:doc_id]
- doc = {:database => db_name, :doc_id => doc_id}
- hash = Couchdb.view doc,auth_session
- doc = {:database => db_name, :doc_id => doc_id, :rev => hash["_rev"]}
- delete_rev(doc,auth_session)
- end
-
-
- #delete a doc by rev#
- def self.delete_rev(doc,auth_session = "")
- db_name = doc[:database]
- doc_id = doc[:doc_id]
- rev = doc[:rev]
- set_address
- begin
- response = RestClient.delete "#{@address}:#{@port}/#{URI.escape(db_name)}/#{URI.escape(doc_id)}?rev=#{rev}", {:cookies => {"AuthSession" => auth_session}}
- hash = Yajl::Parser.parse(response.to_str)
- rescue => e
- couch_error(e)
+ def delete_doc!(doc_id)
+ document = get_doc(doc_id)
+ delete_doc(document[:_id], document[:_rev])
end
- end
-
-
- #create a database if one with the same name doesn't already exist
- def self.create(database_name,auth_session = "")
- set_address
- begin
- response = RestClient.put "#{@address}:#{@port}/#{URI.escape(database_name)}", {:content_type => :json},{:cookies => {"AuthSession" => auth_session}}
- hash = Yajl::Parser.parse(response.to_str)
- rescue => e
- couch_error(e)
+ def get_doc(doc_id)
+ api_request { RestClient.get "#{address_port}/#{@database}/#{doc_id}", cookies }
end
- end
-
- #delete a database
- def self.delete(database_name,auth_session = "")
- set_address
- begin
- response = RestClient.delete "#{@address}:#{@port}/#{URI.escape(database_name)}", {:cookies => {"AuthSession" => auth_session}}
- hash = Yajl::Parser.parse(response.to_str)
- rescue => e
- couch_error(e)
- end
- end
-
- #return a list of all databases
- def self.all(auth_session = "")
- set_address
- begin
- response = RestClient.get "#{@address}:#{@port}/_all_dbs", {:cookies => {"AuthSession" => auth_session}}
- hash = Yajl::Parser.parse(response.to_str)
- rescue => e
- raise e
+ def update_doc(doc_id, data)
+ api_request { RestClient.put "#{address_port}/#{db_uri}/#{doc_uri(doc_id)}", generate_json(data), cookies }
end
- end
-
- ##view a document
- def self.view(doc,auth_session = "", options = {})
- set_address
- db_name = doc[:database]
- doc_id = doc[:doc_id]
- begin
- response = RestClient.get "#{@address}:#{@port}/#{db_name}/#{doc_id}",{:cookies => {"AuthSession" => auth_session}}
- hash = Yajl::Parser.parse(response.to_str, symbolize_keys(options))
- rescue => e
- couch_error(e)
- end
- end
-
- def self.get_params(options)
- params = ""
- if options.has_key?(:startkey)
- if options[:startkey].is_a? String
- params = 'startkey="' + options[:startkey] + '"'
- else
- params = 'startkey=' + options[:startkey].to_s # for complex keys
- end
+ def edit_doc!(doc_id, data)
+ document = get_doc(doc_id)
+ document_with_rev = document.merge(data)
+ update_doc(doc_id, document_with_rev)
end
- if options.has_key?(:endkey)
- if options[:endkey].is_a? String
- params = params + '&endkey="' + options[:endkey] + '"'
- else
- params = params + '&endkey=' + options[:endkey].to_s #for complex keys
- end
+ def view(design_doc_name, view_name, options = {})
+ api_request { RestClient.get "#{address_port}/#{db_uri}/#{URI.escape(design_doc_name)}/_view/#{URI.escape(view_name)}?#{options.to_query}", cookies }
end
-
- if options.has_key?(:limit)
- params = params + "&" + "limit=" + options[:limit].to_s
+ def where(hash, options = {})
+ search_term = hash.values
+ index = hash.keys.join("_")
+ new_options = options.merge({startkey: search_term.to_s, endkey: search_term.to_s})
+ view!("_design/#{index}_keys_finder", "find_by_keys_#{index}", new_options)
+ rescue CouchdbException => e
+ add_multiple_finder(hash.keys)
+ view!("_design/#{index}_keys_finder", "find_by_keys_#{index}", new_options)
end
-
- if options.has_key?(:skip)
- params = params + "&" + "skip=" + options[:skip].to_s
+ def security_object=(security_settings)
+ api_request { RestClient.put "#{address_port}/#{db_uri}/_security/", generate_json(security_settings), cookies }
end
-
- if options.has_key?(:descending)
- params = params + "&" + "descending=true"
+ def security_object
+ api_request { RestClient.get "#{address_port}/#{db_uri}/_security/", cookies }
end
-
- return params
- end
-
-
- #query a permanent view
- def self.find(doc,auth_session = "", key=nil, options = {})
- set_address
- db_name = doc[:database]
- design_doc_name = doc[:design_doc]
- view_name = doc[:view]
- params = get_params(options)
-
- begin
- if key == nil
- response = RestClient.get "#{@address}:#{@port}/#{db_name}/_design/#{design_doc_name}/_view/#{view_name}?#{URI.escape(params)}",{:cookies => {"AuthSession" => auth_session}}
- else
- key = URI.escape('?key="' + key + '"')
- response = RestClient.get "#{@address}:#{@port}/#{db_name}/_design/#{design_doc_name}/_view/#{view_name}#{key}&#{URI.escape(params)}" ,{:cookies => {"AuthSession" => auth_session}}
- end
- hash = Yajl::Parser.parse(response.to_str, symbolize_keys(options))
- data = []
- rows = hash["rows"] unless hash["rows"].nil?
- rows = hash[:rows] unless hash[:rows].nil?
- rows.each do |row|
- value = row["value"] unless row["value"].nil?
- value = row[:value] unless row[:value].nil?
- data << value
- end
- return data
- rescue => e
- couch_error(e)
- end
- end
-
- #create a design document with views
- def self.create_design(doc,auth_session = "")
- set_address
- db_name = doc[:database]
- design_doc_name = doc[:design_doc]
- json_doc_name = doc[:json_doc]
-
- begin
- #bind json doc to string
- message_template = ERB.new File.new(json_doc_name).read
- str = message_template.result(binding)
- rescue => e
- raise e
+ def set_config(section, key, value)
+ !!(RestClient.put "#{address_port}/_config/#{URI.escape(section)}/#{URI.escape(key)}", value, cookies)
end
-
- begin
- response = RestClient.put "#{@address}:#{@port}/#{db_name}/_design/#{design_doc_name}", str, {:cookies => {"AuthSession" => auth_session}}
- hash = Yajl::Parser.parse(response.to_str)
+ def get_config(section, key)
+ RestClient.get "#{address_port}/_config/#{URI.escape(section)}/#{URI.escape(key)}", cookies
rescue => e
- couch_error(e)
+ raise_error(e)
end
- end
-
- #Query view, create view on fly if it dosen't already exist
- def self.find_on_fly(doc,auth_session = "",key = nil, options = {})
- db_name = doc[:database]
- design_doc = doc[:design_doc]
- view = doc[:view]
- json_doc = doc[:json_doc]
- query_info = {:database => db_name, :design_doc => design_doc, :view => view}
- begin
- if( key == nil)
- docs = find(query_info,auth_session,key=nil,options)
- else
- docs = find(query_info,auth_session,key,options)
- end
- rescue CouchdbException => e
- document = { :database => db_name, :design_doc => design_doc, :json_doc => json_doc}
- create_design document,auth_session
- if( key == nil)
- docs = find(query_info,auth_session,key=nil,options)
- else
- docs = find(query_info,auth_session,key,options)
- end
- end
- return docs
- end
-
-
- #add a finder method to the database
- #this creates a find by key method
- def self.add_finder(options,auth_session = "")
- set_address
- db_name = options[:database]
- key = options[:key]
- design_doc_name = key + '_finder'
-
- view ='{
- "language" : "javascript",
- "views" :{
- "find_by_'+key+'" : {
- "map" : "function(doc){ if(doc.'+key+') emit(doc.'+key+',doc);}"
- }
- }
- }'
-
- begin
- response = RestClient.put "#{@address}:#{@port}/#{db_name}/_design/#{design_doc_name}", view, {:cookies => {"AuthSession" => auth_session}}
+ def delete_config(section, key)
+ !!(RestClient.delete "#{address_port}/_config/#{URI.escape(section)}/#{URI.escape(key)}", cookies)
rescue => e
- couch_error(e)
+ raise_error(e)
end
- end
-
- #add a multiple finder method to the database
- #this creates a find by keys method
- def self.add_multiple_finder(options,auth_session = "")
- set_address
- db_name = options[:database]
- keys = options[:keys]
- view_name = keys.join("_")
- key = keys.join(",doc.")
- condition = keys.join(" && doc.")
- design_doc_name = "#{view_name}_keys_finder"
-
- view = '{
- "language" : "javascript",
- "views" :{
- "find_by_keys_'+view_name+'" : {
- "map" : "function(doc){ if(doc.'+condition+') emit([doc.'+key+'],doc);}"
- }
- }
- }'
-
- begin
- response = RestClient.put "#{@address}:#{@port}/#{db_name}/_design/#{design_doc_name}", view, {:cookies => {"AuthSession" => auth_session}}
- rescue => e
- couch_error(e)
+ private
+ def view!(design_doc_name, view_name, options = {})
+ new_options = options.merge({include_docs: true})
+ result = view(design_doc_name, view_name, new_options)
+ rows = result[:rows]
+ rows.map { |row| row[:doc] }
end
- end
-
- #add a multiple counter method to the database
- #this creates a count by keys method
- def self.add_multiple_counter(options,auth_session = "")
- set_address
- db_name = options[:database]
- keys = options[:keys]
- view_name = keys.join("_")
- key = keys.join(",doc.")
- condition = keys.join(" && doc.")
- design_doc_name = "#{view_name}_keys_counter"
- view = '{
- "language" : "javascript",
- "views" :{
- "count_by_keys_'+view_name+'" : {
- "map" : "function(doc){ if(doc.'+condition+') emit([doc.'+key+'],null);}", "reduce": "_count"
- }
- }
- }'
-
- begin
- response = RestClient.put "#{@address}:#{@port}/#{db_name}/_design/#{design_doc_name}", view, {:cookies => {"AuthSession" => auth_session}}
- rescue => e
- couch_error(e)
+ def add_multiple_finder(keys)
+ view_name = keys.join("_")
+ condition = keys.join(" && doc.")
+ key = keys.join(",doc.")
+ design_doc_name = "#{view_name}_keys_finder"
+ design_doc = {
+ language: "javascript",
+ views: {
+ "find_by_keys_#{view_name}" => {
+ map: "function(doc){ if(doc.#{condition}) emit([doc.#{key}]);}"
+ }
+ }
+ }
+ create_doc "_design/#{design_doc_name}", design_doc
end
- end
-
- #add a counter method to the database
- #this creates a count method that counts documents by key
- def self.add_counter(options,auth_session = "")
- set_address
- db_name = options[:database]
- key = options[:key]
- design_doc_name = key + '_counter'
-
- view ='{
- "language" : "javascript",
- "views" :{
- "count_'+key+'" : {
- "map" : "function(doc){ if(doc.'+key+') emit(doc.'+key+',null);}", "reduce": "_count"
- }
- }
- }'
-
- begin
- response = RestClient.put "#{@address}:#{@port}/#{db_name}/_design/#{design_doc_name}", view, {:cookies => {"AuthSession" => auth_session}}
+ def api_request
+ response = yield
+ parse_json(response)
rescue => e
- couch_error(e)
+ raise_error(e)
end
- end
-
- #count by key
- def self.count(options,auth_session = "")
- set_address
- db_name = options[:database]
- index = options.keys[1].to_s
- search_term = options.values[1]
- design_doc_name = "#{index}_counter"
- view_name = "count_#{index}"
-
- begin
- view = { :database => db_name, :design_doc => design_doc_name, :view => view_name}
- docs = find view,auth_session,search_term
- rescue CouchdbException => e
- #add a counter index if one doesn't already exist in the database
- #then count_by_key
- add_counter({:database => db_name, :key => index},auth_session)
- docs = find view,auth_session,search_term
+ def doc_uri(doc_id)
+ error_message = "Invalid Document ID: #{doc_id}"
+ raise(Leanback::InvalidDocumentID.new(error_message), error_message) unless doc_id.is_a?(String)
+ URI.escape(doc_id)
end
- count = docs[0]
- return count.to_i
- end
-
- #count by keys
- #example:
- #hash = {:firstname =>'john', :gender => 'male' }
- #Couchdb.count_by_keys({:database => 'contacts', :keys => hash},auth_session)
- def self.count_by_keys(options,auth_session = "", params = {})
- set_address
- db_name = options[:database]
- keys = []
- search_term = []
- hash = options[:keys]
-
- hash.each do |k,v|
- keys << k
- search_term << v
+ def db_uri
+ error_message = "Invalid database name: #{@database}"
+ raise(Leanback::InvalidDatabaseName.new(error_message), error_message) unless @database.is_a?(String)
+ URI.escape(@database)
end
- index = keys.join("_")
- design_doc_name = "#{index}_keys_counter"
- view_name = "count_by_keys_#{index}"
- params[:startkey] = search_term
- params[:endkey] = search_term
-
- begin
- view = { :database => db_name, :design_doc => design_doc_name, :view => view_name}
- docs = find view,auth_session,key=nil,params
- rescue CouchdbException => e
- #add a finder/index if one doesn't already exist in the database
- #then find_by_keys
- add_multiple_counter({:database => db_name, :keys => keys},auth_session)
- docs = find view,auth_session,key=nil,params
- end
- count = docs[0]
- return count.to_i
- end
-
- #find by key
- def self.find_by(options,auth_session = "", params = {})
- set_address
- db_name = options[:database]
- index = options.keys[1].to_s
- search_term = options.values[1]
- design_doc_name = "#{index}_finder"
- view_name = "find_by_#{index}"
-
- begin
- view = { :database => db_name, :design_doc => design_doc_name, :view => view_name}
- docs = find view,auth_session,search_term,params
- rescue CouchdbException => e
- #add a finder/index if one doesn't already exist in the database
- #then find_by_key
- add_finder({:database => db_name, :key => index},auth_session)
- docs = find view,auth_session,search_term,params
+ def content_type
+ {content_type: :json}
end
- return docs
- end
-
- #find by keys
- #example:
- #hash = {:firstname =>'john', :gender => 'male' }
- #Couchdb.find_by_keys({:database => 'contacts', :keys => hash},auth_session)
- def self.find_by_keys(options,auth_session = "", params = {})
- set_address
- db_name = options[:database]
- keys = []
- search_term = []
- hash = options[:keys]
-
- hash.each do |k,v|
- keys << k
- search_term << v
+ def cookies
+ {cookies: {"AuthSession" => auth_session}}
end
- index = keys.join("_")
- design_doc_name = "#{index}_keys_finder"
- view_name = "find_by_keys_#{index}"
- params[:startkey] = search_term
- params[:endkey] = search_term
-
- begin
- view = { :database => db_name, :design_doc => design_doc_name, :view => view_name}
- docs = find view,auth_session,key=nil,params
- rescue CouchdbException => e
- #add a finder/index if one doesn't already exist in the database
- #then find_by_keys
- add_multiple_finder({:database => db_name, :keys => keys},auth_session)
- docs = find view,auth_session,key=nil,params
+ def address_port
+ "#{@address}:#{@port}"
end
- return docs
- end
-
- #return a list of all docs in the database
- def self.docs_from(database_name,auth_session = "")
- set_address
- begin
- response = RestClient.get "#{@address}:#{@port}/#{URI.escape(database_name)}/_all_docs?include_docs=true",{:cookies => {"AuthSession" => auth_session}}
- hash = Yajl::Parser.parse(response.to_str)
- rows = hash["rows"]
- count = 0
- rows.each do |row|
- rows[count] = row["doc"]
- count += 1
+ def parse_json(json_doc)
+ JSON.parse(json_doc, symbolize_names: true)
+ end
+ def generate_json(data)
+ JSON.generate(data)
+ end
+ def raise_error(exeception)
+ if exeception.respond_to?('response')
+ response = parse_json(exeception.response)
+ raise(CouchdbException.new(response), response)
+ else
+ raise(exeception)
end
- return rows
+ end
+ def auth_session
+ return "" if @username.nil? && @password.nil?
+ data = "name=#{@username}&password=#{@password}"
+ response = RestClient.post "#{@address}:#{@port}/_session/", data, {content_type: 'application/x-www-form-urlencoded'}
+ hash = response.cookies
+ hash["AuthSession"]
rescue => e
- couch_error(e)
- end
- end
-
- def self.symbolize_keys(options)
- return {symbolize_keys: true} if options[:symbolize_keys]
- return {}
- end
-
- private_class_method :symbolize_keys
-
- class << self
- attr_accessor :address
- attr_accessor :port
-
- def set_address()
- if @address == nil
- @address = 'http://127.0.0.1'
- end
- if @port == nil
- @port = '5984'
- end
+ raise_error(e)
end
end
end