tinkit 0.0.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/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
|