tinkit 0.0.0
Sign up to get free protection for your applications and to get access to all the features.
- data/LICENSE +176 -0
- data/README +11 -0
- data/Rakefile +75 -0
- data/lib/glue_envs/couchrest/couchrest_attachment_handler.rb +260 -0
- data/lib/glue_envs/couchrest/couchrest_files_mgr.rb +198 -0
- data/lib/glue_envs/couchrest_glue_env.rb +536 -0
- data/lib/glue_envs/files_mgr_base.rb +51 -0
- data/lib/glue_envs/filesystem/filesystem_files_mgr.rb +187 -0
- data/lib/glue_envs/filesystem_glue_env.rb +395 -0
- data/lib/glue_envs/mysql/mysql_files_mgr.rb +175 -0
- data/lib/glue_envs/mysql_glue_env.rb +428 -0
- data/lib/glue_envs/sdb_s3/sdb_s3_files_mgr.rb +314 -0
- data/lib/glue_envs/sdb_s3_glue_env.rb +248 -0
- data/lib/helpers/camel.rb +21 -0
- data/lib/helpers/filesystem_helpers.rb +27 -0
- data/lib/helpers/hash_helpers.rb +74 -0
- data/lib/helpers/log_helper.rb +34 -0
- data/lib/helpers/mime_types_new.rb +126 -0
- data/lib/helpers/old_more_open_struct.rb +28 -0
- data/lib/helpers/require_helper.rb +45 -0
- data/lib/helpers/tk_escape.rb +17 -0
- data/lib/midas/bufs_data_structure.rb +84 -0
- data/lib/midas/node_element_operations.rb +264 -0
- data/lib/tinkit.rb +38 -0
- data/lib/tinkit_base_node.rb +733 -0
- data/lib/tinkit_node_factory.rb +47 -0
- data/spec/couchrest_files_mgr_spec.rb +551 -0
- data/spec/couchrest_glue_spec.rb +246 -0
- data/spec/filesystem_files_mgr_spec.rb +236 -0
- data/spec/filesystem_glue_spec.rb +243 -0
- data/spec/filesystem_helpers_spec.rb +42 -0
- data/spec/helpers/bufs_node_builder.rb +17 -0
- data/spec/helpers/bufs_sample_dataset.rb +160 -0
- data/spec/helpers/bufs_test_environments.rb +81 -0
- data/spec/helpers/tmp_view_cleaner.rb +15 -0
- data/spec/lib_helpers/tk_escape_spec.rb +45 -0
- data/spec/mysql_files_mgr_spec.rb +250 -0
- data/spec/mysql_glue_spec.rb +214 -0
- data/spec/node_element_operations_spec.rb +392 -0
- data/spec/sdb_s3_files_mgr_spec/sdb_s3_files_mgr_spec1.rb +82 -0
- data/spec/sdb_s3_files_mgr_spec/sdb_s3_files_mgr_spec2.rb +68 -0
- data/spec/sdb_s3_files_mgr_spec/sdb_s3_files_mgr_spec3.rb +80 -0
- data/spec/sdb_s3_files_mgr_spec/sdb_s3_files_mgr_spec4.rb +110 -0
- data/spec/sdb_s3_files_mgr_spec/sdb_s3_files_mgr_spec5.rb +84 -0
- data/spec/sdb_s3_files_mgr_spec/sdb_s3_files_mgr_spec6.rb +83 -0
- data/spec/sdb_s3_files_mgr_spec/sdb_s3_files_mgr_spec7.rb +101 -0
- data/spec/sdb_s3_files_mgr_spec/sdb_s3_files_mgr_spec8.rb +92 -0
- data/spec/sdb_s3_files_mgr_spec/sdb_s3_files_mgr_spec_all.rb +266 -0
- data/spec/sdb_s3_glue_spec.rb +230 -0
- data/spec/tinkit_node_factory_spec.rb +1108 -0
- metadata +114 -0
@@ -0,0 +1,21 @@
|
|
1
|
+
#copy of Rails camelize and underscore (almost)
|
2
|
+
|
3
|
+
module Camel
|
4
|
+
def self.ize(lower_case_and_underscored_word, first_letter_in_uppercase = true)
|
5
|
+
if first_letter_in_uppercase
|
6
|
+
lower_case_and_underscored_word.to_s.gsub(/\/(.?)/) { "::" + $1.upcase }.gsub(/(^|_)(.)/) { $2.upcase }
|
7
|
+
else
|
8
|
+
lower_case_and_underscored_word.first + camelize(lower_case_and_underscored_word)[1..-1]
|
9
|
+
end
|
10
|
+
end
|
11
|
+
|
12
|
+
def self.score(camel_cased_word)
|
13
|
+
word = camel_cased_word.to_s.dup
|
14
|
+
word.gsub!(/::/, '_') #except I changed '/' to '_'
|
15
|
+
word.gsub!(/([A-Z]+)([A-Z][a-z])/,'\1_\2')
|
16
|
+
word.gsub!(/([a-z\d])([A-Z])/,'\1_\2')
|
17
|
+
word.tr!("-", "_")
|
18
|
+
word.downcase!
|
19
|
+
word
|
20
|
+
end
|
21
|
+
end
|
@@ -0,0 +1,27 @@
|
|
1
|
+
|
2
|
+
class DirFilter
|
3
|
+
def initialize(ignore_list)
|
4
|
+
@ignore_list = [ignore_list].flatten
|
5
|
+
@ignore_list << /^\./ #ignore dot files
|
6
|
+
end
|
7
|
+
|
8
|
+
|
9
|
+
def filter_entries(path)
|
10
|
+
wkg_entries = Dir.entries(path)
|
11
|
+
#remove dot files
|
12
|
+
wkg_entires = wkg_entries.delete_if{|entry| in_ignore_list?(entry) }
|
13
|
+
end
|
14
|
+
|
15
|
+
private
|
16
|
+
def in_ignore_list?(entry)
|
17
|
+
in_ignore = @ignore_list.map{|list| true if entry =~ list}
|
18
|
+
in_ignore.compact.first #nil if nothing in ignore
|
19
|
+
end
|
20
|
+
end
|
21
|
+
|
22
|
+
class FileNames
|
23
|
+
def flatten_problem_chars(filenames)
|
24
|
+
#via http://stackoverflow.com/questions/2270635/invalid-chars-filter-for-file-folder-name-ruby
|
25
|
+
file_names.map! { |f| f.gsub(/[\x00\/\\:\*\?\"<>\|]/, '_') }
|
26
|
+
end
|
27
|
+
end
|
@@ -0,0 +1,74 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
#from Facets via Hashery
|
4
|
+
class Hash
|
5
|
+
|
6
|
+
|
7
|
+
|
8
|
+
def rekey!(*args, &block)
|
9
|
+
# for backward comptability (TODO: DEPRECATE).
|
10
|
+
block = args.pop.to_sym.to_proc if args.size == 1
|
11
|
+
if args.empty?
|
12
|
+
block = lambda{|k| k.to_sym} unless block
|
13
|
+
keys.each do |k|
|
14
|
+
nk = block[k]
|
15
|
+
self[nk]=delete(k) if nk
|
16
|
+
end
|
17
|
+
else
|
18
|
+
raise ArgumentError, "3 for 2" if block
|
19
|
+
to, from = *args
|
20
|
+
self[to] = self.delete(from) if self.has_key?(from)
|
21
|
+
end
|
22
|
+
self
|
23
|
+
end
|
24
|
+
|
25
|
+
def rekey(*args, &block)
|
26
|
+
dup.rekey!(*args, &block)
|
27
|
+
end
|
28
|
+
|
29
|
+
public :rekey, :rekey!
|
30
|
+
end
|
31
|
+
|
32
|
+
module HashKeys
|
33
|
+
def self.str_to_sym(a_hash)
|
34
|
+
raise "#{a_hash.class.name} must respond to inject" unless a_hash.respond_to? :inject
|
35
|
+
a_hash.inject({}){|memo,(k,v)| memo[k.to_sym] = v; memo}
|
36
|
+
end
|
37
|
+
|
38
|
+
def self.sym_to_str(a_hash) #inverse of above
|
39
|
+
raise "#{a_hash.class.name} must respond to inject" unless a_hash.respond_to? :inject
|
40
|
+
a_hash.inject({}){|memo,(k,v)| memo["#{k}"] = v; memo}
|
41
|
+
end
|
42
|
+
end
|
43
|
+
|
44
|
+
module HashOps
|
45
|
+
def self.remove_hash(other_hash)
|
46
|
+
delete_if { |k,v| other_hash[k] == v }
|
47
|
+
end
|
48
|
+
end
|
49
|
+
|
50
|
+
class MoreOpenStruct < OpenStruct
|
51
|
+
def _to_hash
|
52
|
+
h = @table
|
53
|
+
#handles nested structures
|
54
|
+
h.each do |k,v|
|
55
|
+
if v.class == MoreOpenStruct
|
56
|
+
h[k] = v._to_hash
|
57
|
+
end
|
58
|
+
end
|
59
|
+
return h
|
60
|
+
end
|
61
|
+
|
62
|
+
def _table
|
63
|
+
@table #table is the hash structure used in OpenStruct
|
64
|
+
end
|
65
|
+
|
66
|
+
def _manual_set(hash)
|
67
|
+
if hash && (hash.class == Hash)
|
68
|
+
for k,v in hash
|
69
|
+
@table[k.to_sym] = v
|
70
|
+
new_ostruct_member(k)
|
71
|
+
end
|
72
|
+
end
|
73
|
+
end
|
74
|
+
end
|
@@ -0,0 +1,34 @@
|
|
1
|
+
require 'log4r'
|
2
|
+
|
3
|
+
#Set Logger
|
4
|
+
#TODO: Create spec
|
5
|
+
module TinkitLog
|
6
|
+
include Log4r
|
7
|
+
|
8
|
+
class << self; attr_accessor :default_level, :fatal_log, :log_output end
|
9
|
+
TinkitLog.default_level = :info
|
10
|
+
TinkitLog.log_output = 'stdout'
|
11
|
+
TinkitLog.fatal_log = Logger.new('fatal_log')
|
12
|
+
TinkitLog.fatal_log.level = FATAL
|
13
|
+
TinkitLog.fatal_log.outputters = Outputter[TinkitLog.log_output]
|
14
|
+
|
15
|
+
@@log_levels = { :debug => DEBUG,
|
16
|
+
:info => INFO,
|
17
|
+
:warn => WARN,
|
18
|
+
:error => ERROR,
|
19
|
+
:fatal => FATAL
|
20
|
+
}
|
21
|
+
|
22
|
+
def self.set(name, level=TinkitLog.default_level, out=Outputter.stdout)
|
23
|
+
log = Logger[name] || Logger.new(name)
|
24
|
+
log.outputters = out
|
25
|
+
log.level = @@log_levels[level]
|
26
|
+
log.trace = true if log.level <= DEBUG
|
27
|
+
log
|
28
|
+
end
|
29
|
+
|
30
|
+
def self.log_raise(error_msg, exc_type= RuntimeError)
|
31
|
+
self.fatal_log.fatal("#{__LINE__} #{error_msg}")
|
32
|
+
raise exc_type, error_msg
|
33
|
+
end
|
34
|
+
end
|
@@ -0,0 +1,126 @@
|
|
1
|
+
#Copyright (C) 2010 David Martin
|
2
|
+
#
|
3
|
+
#David Martin mailto:dmarti21@gmail.com
|
4
|
+
|
5
|
+
|
6
|
+
require 'mime/types'
|
7
|
+
|
8
|
+
#This class will include the Office 2007 extension types when looking up MIME types.
|
9
|
+
class MimeNew
|
10
|
+
|
11
|
+
DefaultUnknownContentType = "application/octet-stream"
|
12
|
+
#Returns the mime type of a file
|
13
|
+
# MimeNew.for_ofc_x('a_new_word_doc.docx')
|
14
|
+
# #=> "application/vnd.openxmlformats-officedocument.wordprocessingml.document"
|
15
|
+
def self.for_ofc_x(fname)
|
16
|
+
cont_type = nil
|
17
|
+
old_ext = File.extname(fname)
|
18
|
+
cont_type =case old_ext
|
19
|
+
#New Office Formats
|
20
|
+
when '.docx'
|
21
|
+
["application/vnd.openxmlformats-officedocument.wordprocessingml.document"]
|
22
|
+
when '.dotx'
|
23
|
+
["application/vnd.openxmlformats-officedocument.wordprocessingml.template"]
|
24
|
+
when '.pptx'
|
25
|
+
["application/vnd.openxmlformats-officedocument.presentationml.presentation"]
|
26
|
+
when '.ppsx'
|
27
|
+
["application/vnd.openxmlformats-officedocument.presentationml.slideshow"]
|
28
|
+
when '.potx'
|
29
|
+
["application/vnd.openxmlformats-officedocument.presentationml.template"]
|
30
|
+
when '.xlsx'
|
31
|
+
["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"]
|
32
|
+
when '.xltx'
|
33
|
+
["application/vnd.openxmlformats-officedocument.spreadsheetml.template"]
|
34
|
+
else
|
35
|
+
self.other_content_types(fname)
|
36
|
+
end#case
|
37
|
+
cont_type = [cont_type].flatten.first
|
38
|
+
#puts "Content Type returned: #{cont_type.inspect}"
|
39
|
+
return cont_type
|
40
|
+
end# def
|
41
|
+
|
42
|
+
def self.just_ofc_x(ext)
|
43
|
+
cont_type = case File.extname(fname)
|
44
|
+
#New Office Formats
|
45
|
+
when '.docx'
|
46
|
+
["application/vnd.openxmlformats-officedocument.wordprocessingml.document"]
|
47
|
+
when '.dotx'
|
48
|
+
["application/vnd.openxmlformats-officedocument.wordprocessingml.template"]
|
49
|
+
when '.pptx'
|
50
|
+
["application/vnd.openxmlformats-officedocument.presentationml.presentation"]
|
51
|
+
when '.ppsx'
|
52
|
+
["application/vnd.openxmlformats-officedocument.presentationml.slideshow"]
|
53
|
+
when '.potx'
|
54
|
+
["application/vnd.openxmlformats-officedocument.presentationml.template"]
|
55
|
+
when '.xlsx'
|
56
|
+
["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"]
|
57
|
+
when '.xltx'
|
58
|
+
["application/vnd.openxmlformats-officedocument.spreadsheetml.template"]
|
59
|
+
end
|
60
|
+
end
|
61
|
+
|
62
|
+
def self.other_content_types(fname)
|
63
|
+
std_type = MIME::Types.type_for(fname).first
|
64
|
+
rtn = if std_type
|
65
|
+
std_type.content_type
|
66
|
+
else
|
67
|
+
DefaultUnknownContentType
|
68
|
+
end
|
69
|
+
return rtn
|
70
|
+
end#def
|
71
|
+
end
|
72
|
+
|
73
|
+
|
74
|
+
#duck punching RestClient's duck punch for bufs because libraries are using MIME::Types directly
|
75
|
+
=begin
|
76
|
+
module MIME
|
77
|
+
class Types
|
78
|
+
|
79
|
+
# Return the first found content-type for a value considered as an extension or the value itself
|
80
|
+
def type_for_extension ext
|
81
|
+
puts "Duck Punch ext: #{ext.inspect}"
|
82
|
+
candidates = @extension_index[ext]
|
83
|
+
puts "Duck Punch candidates: #{candidates.inspect}"
|
84
|
+
candidates.empty? ? ext : candidates[0].content_type
|
85
|
+
end
|
86
|
+
end
|
87
|
+
end
|
88
|
+
=end
|
89
|
+
=begin
|
90
|
+
class MIME::Types
|
91
|
+
def type_for(filename, platform = false)
|
92
|
+
puts "Duck Punched MIME::Types"
|
93
|
+
puts "--filename: #{filename.inspect}"
|
94
|
+
|
95
|
+
ext = filename.chomp.downcase.gsub(/.*\./o, '')
|
96
|
+
list = @extension_index[ext]
|
97
|
+
list.delete_if { |e| not e.platform? } if platform
|
98
|
+
list
|
99
|
+
|
100
|
+
new_ext = File.extname(filename)
|
101
|
+
cont_type =case new_ext
|
102
|
+
#New Office Formats
|
103
|
+
when '.docx'
|
104
|
+
["application/vnd.openxmlformats-officedocument.wordprocessingml.document"]
|
105
|
+
when '.dotx'
|
106
|
+
["application/vnd.openxmlformats-officedocument.wordprocessingml.template"]
|
107
|
+
when '.pptx'
|
108
|
+
["application/vnd.openxmlformats-officedocument.presentationml.presentation"]
|
109
|
+
when '.ppsx'
|
110
|
+
["application/vnd.openxmlformats-officedocument.presentationml.slideshow"]
|
111
|
+
when '.potx'
|
112
|
+
["application/vnd.openxmlformats-officedocument.presentationml.template"]
|
113
|
+
when '.xlsx'
|
114
|
+
["application/vnd.openxmlformats-officedocument.spreadsheetml.sheet"]
|
115
|
+
when '.xltx'
|
116
|
+
["application/vnd.openxmlformats-officedocument.spreadsheetml.template"]
|
117
|
+
end
|
118
|
+
|
119
|
+
puts "Duck New Cont Type: #{cont_type.inspect}"
|
120
|
+
(list + cont_type).compact! if cont_type
|
121
|
+
puts "Duck Mime Extension: #{ext.inspect}"
|
122
|
+
puts "Duck Content Types: #{ list.map{|m| m.content_type} }"
|
123
|
+
list
|
124
|
+
end
|
125
|
+
end
|
126
|
+
=end
|
@@ -0,0 +1,28 @@
|
|
1
|
+
require 'ostruct'
|
2
|
+
|
3
|
+
class MoreOpenStruct < OpenStruct
|
4
|
+
def _to_hash
|
5
|
+
h = @table
|
6
|
+
#handles nested structures
|
7
|
+
h.each do |k,v|
|
8
|
+
if v.class == MoreOpenStruct
|
9
|
+
h[k] = v._to_hash
|
10
|
+
end
|
11
|
+
end
|
12
|
+
return h
|
13
|
+
end
|
14
|
+
|
15
|
+
def _table
|
16
|
+
@table #table is the hash structure used in OpenStruct
|
17
|
+
end
|
18
|
+
|
19
|
+
def _manual_set(hash)
|
20
|
+
if hash && (hash.class == Hash)
|
21
|
+
for k,v in hash
|
22
|
+
@table[k.to_sym] = v
|
23
|
+
new_ostruct_member(k)
|
24
|
+
end
|
25
|
+
end
|
26
|
+
end
|
27
|
+
end
|
28
|
+
|
@@ -0,0 +1,45 @@
|
|
1
|
+
#Tinkit Library Locations
|
2
|
+
module Tinkit
|
3
|
+
@@top = File.join(File.dirname(__FILE__), '../..') #main bufs directory
|
4
|
+
@@lib = File.join(@@top, 'lib/')
|
5
|
+
@@helpers = File.join(@@top, 'lib/helpers/')
|
6
|
+
@@moabs = File.join(@@lib, 'moabs')
|
7
|
+
@@midas = File.join(@@lib, 'midas')
|
8
|
+
@@glue = File.join(@@lib, 'glue_envs')
|
9
|
+
@@specs = File.join(@@top, 'spec')
|
10
|
+
@@spec_helpers = File.join(@@specs, 'helpers')
|
11
|
+
@@fixtures = File.join(@@top, 'bufs_fixtures')
|
12
|
+
|
13
|
+
def self.lib(req_file)
|
14
|
+
File.expand_path(File.join(@@lib, req_file))
|
15
|
+
end
|
16
|
+
|
17
|
+
def self.helpers(req_file)
|
18
|
+
File.expand_path(File.join(@@helpers, req_file))
|
19
|
+
end
|
20
|
+
|
21
|
+
def self.spec(req_file)
|
22
|
+
File.expand_path(File.join(@@specs, req_file))
|
23
|
+
end
|
24
|
+
|
25
|
+
def self.spec_helpers(req_file)
|
26
|
+
File.expand_path(File.join(@@spec_helpers, req_file))
|
27
|
+
end
|
28
|
+
|
29
|
+
def self.fixtures(req_file)
|
30
|
+
File.expand_path(File.join(@@fixtures, req_file))
|
31
|
+
end
|
32
|
+
|
33
|
+
def self.moabs(req_file)
|
34
|
+
File.expand_path(File.join(@@moabs, req_file))
|
35
|
+
end
|
36
|
+
|
37
|
+
def self.midas(req_file)
|
38
|
+
File.expand_path(File.join(@@midas, req_file))
|
39
|
+
end
|
40
|
+
|
41
|
+
def self.glue(req_file)
|
42
|
+
File.expand_path(File.join(@@glue, req_file))
|
43
|
+
end
|
44
|
+
end
|
45
|
+
|
@@ -0,0 +1,17 @@
|
|
1
|
+
require 'cgi' #Can replace with url_escape if performance is an issue
|
2
|
+
|
3
|
+
class TkEscape
|
4
|
+
def self.escape(str)
|
5
|
+
esc_str = str.gsub(/([^a-zA-Z0-9_.-]+)/n, '_')
|
6
|
+
#str.gsub!('+', ' ')
|
7
|
+
#str = CGI.escape(str)
|
8
|
+
#str.gsub!('%2B', '+')
|
9
|
+
return esc_str
|
10
|
+
end
|
11
|
+
|
12
|
+
#TODO: Continue using cgi or create unescape specific to Tinkit?
|
13
|
+
def self.unescape(str)
|
14
|
+
return CGI.unescape(str)
|
15
|
+
end
|
16
|
+
end
|
17
|
+
|
@@ -0,0 +1,84 @@
|
|
1
|
+
module DataStructureModels
|
2
|
+
module Tinkit
|
3
|
+
#Required Keys on instantiation
|
4
|
+
RequiredInstanceKeys = [:my_category]
|
5
|
+
RequiredSaveKeys = [:my_category] #duplicative?
|
6
|
+
NodeKey = :my_category #TODO look at supporting multiple node keys
|
7
|
+
end
|
8
|
+
end
|
9
|
+
|
10
|
+
=begin
|
11
|
+
module NodeElementOperations
|
12
|
+
#TODO the hash inside the proc is confusing (the curly braces) update to better readability
|
13
|
+
MyCategoryAddOp = lambda {|this,other| Hash[:update_this => this] } #my cat is not allowed to change
|
14
|
+
MyCategorySubtractOp = lambda{ |this, other| Hash[:update_this => this] } #TODO use this to delete a node?
|
15
|
+
MyCategoryOps = {:add => MyCategoryAddOp, :subtract => MyCategorySubtractOp}
|
16
|
+
ParentCategoryAddOp = lambda {|this,other|
|
17
|
+
this = this || []
|
18
|
+
other = other || []
|
19
|
+
this = this + [other].flatten
|
20
|
+
this.uniq!; this.compact!
|
21
|
+
Hash[:update_this => this]
|
22
|
+
}
|
23
|
+
ParentCategorySubtractOp = lambda {|this,other|
|
24
|
+
this = [this] || []
|
25
|
+
other = [other] || []
|
26
|
+
this.flatten!
|
27
|
+
other.flatten!
|
28
|
+
this -= other
|
29
|
+
this.uniq!
|
30
|
+
this.compact!
|
31
|
+
Hash[:update_this => this]
|
32
|
+
}
|
33
|
+
ParentCategoryOps = {:add => ParentCategoryAddOp, :subtract => ParentCategorySubtractOp}
|
34
|
+
LinkAddOp = lambda {|this, other|
|
35
|
+
this = this || {} #investigate why its passed as nil (probably hasn't been built yet
|
36
|
+
other = other || {}
|
37
|
+
srcs = other.keys
|
38
|
+
srcs.each {|s| if this[s]
|
39
|
+
#this[s] = [ other[s] ].flatten
|
40
|
+
this[s] = other[s]
|
41
|
+
else
|
42
|
+
#this[s] = [ other[s] ].flatten
|
43
|
+
this[s] = other[s]
|
44
|
+
end }
|
45
|
+
#this[s].uniq!
|
46
|
+
#this[s].compact! }
|
47
|
+
Hash[:update_this => this] }
|
48
|
+
#if link_name is used besides other, then all link_names would need to be unique, so we use other
|
49
|
+
LinkSubtractOp = lambda {|this, other| this = this || {}
|
50
|
+
#Hacked together needs thought out (and TESTED!!)
|
51
|
+
other = other || {}
|
52
|
+
puts "This / Other: #{this.inspect} / #{other.inspect}"
|
53
|
+
#srcs = [other].flatten
|
54
|
+
other.keys.each { |s|
|
55
|
+
#other[s].each {|olnk| this[s].delete(olnk) if this[s]}
|
56
|
+
puts "delete #{other[s].inspect} from #{this[s].inspect}"
|
57
|
+
#this[s].delete(other[s]) if this[s]
|
58
|
+
this.delete(s)
|
59
|
+
#this.delete(s) if (this[s].nil? || this[s].empty?)
|
60
|
+
}
|
61
|
+
Hash[:update_this => this]
|
62
|
+
}
|
63
|
+
#think if this is what you want, returning a single uri if only one exists, while an array if more than one?
|
64
|
+
#I think so since it's *almost* an error case if more than one url exists for a name, but I'm not sure this is the best approach
|
65
|
+
LinkGetOp = lambda {|this, link_name|
|
66
|
+
this_ary = this.to_a
|
67
|
+
rtn_val = nil
|
68
|
+
rtn_val = if this_ary.flatten.include? link_name
|
69
|
+
srcs = []
|
70
|
+
this_ary.each { |s, ls| srcs << s if ls.include? link_name }
|
71
|
+
rtn_val = {:return_value => srcs, :update_this => this } if srcs.size > 1
|
72
|
+
rtn_val = {:return_value => srcs.first, :update_this => this } if srcs.size == 1
|
73
|
+
rtn_val
|
74
|
+
else
|
75
|
+
rtn_val = {:return_value => nil, :update_this => this}
|
76
|
+
end
|
77
|
+
rtn_val
|
78
|
+
}
|
79
|
+
|
80
|
+
LinkOps = {:add => LinkAddOp, :subtract => LinkSubtractOp, :get => LinkGetOp}
|
81
|
+
|
82
|
+
Ops = {:my_category => MyCategoryOps, :parent_categories => ParentCategoryOps, :links => LinkOps}
|
83
|
+
end
|
84
|
+
=end
|