Class: SafetyPin::Node

Inherits:
Object
  • Object
show all
Defined in:
lib/safety_pin/node.rb

Instance Attribute Summary collapse

Class Method Summary collapse

Instance Method Summary collapse

Constructor Details

#initialize(j_node) ⇒ Node

Returns a new instance of Node.



95
96
97
# File 'lib/safety_pin/node.rb', line 95

def initialize(j_node)
  @j_node = j_node
end

Instance Attribute Details

#j_nodeObject (readonly)

Returns the value of attribute j_node.



9
10
11
# File 'lib/safety_pin/node.rb', line 9

def j_node
  @j_node
end

Class Method Details

.build(node_blueprint_or_path_string) ⇒ Object



31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
# File 'lib/safety_pin/node.rb', line 31

def self.build(node_blueprint_or_path_string)      
  if node_blueprint_or_path_string.respond_to?(:primary_type, :properties)
    node_blueprint = node_blueprint_or_path_string
  else
    node_blueprint = NodeBlueprint.new(path: node_blueprint_or_path_string)
  end

  raise NodeError.new("NodeBlueprint is nil") if node_blueprint.nil?
  raise NodeError.new("NodeBlueprint has non-absolute path") unless node_blueprint.path.to_s.start_with?("/")
  raise NodeError.new("Node already exists at path: #{node_blueprint.path}") if Node.exists?(node_blueprint.path)
  
  rel_path_to_root_node = node_blueprint.path.to_s[1..-1]
  node = self.new(session.root_node.add_node(rel_path_to_root_node, node_blueprint.primary_type))
  node.properties = node_blueprint.properties

  node
rescue javax.jcr.PathNotFoundException => e
  raise NodeError.new("Cannot add a new node to a non-existing parent at #{node_blueprint.path}")
end

.create(node_blueprint_or_path_string) ⇒ Object



60
61
62
63
64
# File 'lib/safety_pin/node.rb', line 60

def self.create(node_blueprint_or_path_string)
  node = self.build(node_blueprint_or_path_string)
  node.save
  node
end

.create_or_update(node_blueprint_or_node_blueprints) ⇒ Object



84
85
86
87
88
89
90
91
92
93
# File 'lib/safety_pin/node.rb', line 84

def self.create_or_update(node_blueprint_or_node_blueprints)
  node_blueprints = Array(node_blueprint_or_node_blueprints)
  node_blueprints.map do |node_blueprint|
    if exists?(node_blueprint.path)
      update(node_blueprint)
    else
      create(node_blueprint)
    end
  end
end

.create_parents(path) ⇒ Object



66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
# File 'lib/safety_pin/node.rb', line 66

def self.create_parents(path)
  intermediate_paths = []

  current_intermediate_path = Pathname(path)
  while(current_intermediate_path.to_s != "/")
    current_intermediate_path = current_intermediate_path.parent
    intermediate_paths.push(current_intermediate_path)
  end
  
  results = intermediate_paths.reverse.map do |intermediate_path|
    create(NodeBlueprint.new(:path => intermediate_path.to_s)) unless exists?(intermediate_path)
  end

  session.save

  results
end

.exists?(path) ⇒ Boolean

Returns:

  • (Boolean)


23
24
25
# File 'lib/safety_pin/node.rb', line 23

def self.exists?(path)
  find(path) != nil
end

.find(path) ⇒ Object



11
12
13
14
15
16
# File 'lib/safety_pin/node.rb', line 11

def self.find(path)
  raise ArgumentError unless path.to_s.start_with?("/")
  Node.new(session.get_node(path.to_s))
rescue javax.jcr.PathNotFoundException
  nil
end

.find_or_create(path, primary_type = nil) ⇒ Object



18
19
20
21
# File 'lib/safety_pin/node.rb', line 18

def self.find_or_create(path, primary_type = nil)
  node_blueprint = NodeBlueprint.new(:path => path.to_s, :primary_type => primary_type)
  find(path) || create(node_blueprint)
end

.sessionObject



27
28
29
# File 'lib/safety_pin/node.rb', line 27

def self.session
  JCR.session
end

.update(node_blueprint) ⇒ Object



51
52
53
54
55
56
57
58
# File 'lib/safety_pin/node.rb', line 51

def self.update(node_blueprint)
  node = find(node_blueprint.path)
  # raise NodeError.new("Cannot retrieve node for update -- might not exist") if node.nil?
  node.properties = node_blueprint.properties
  node.primary_type = node_blueprint.primary_type
  node.save
  node
end

Instance Method Details

#==(other_node) ⇒ Object



107
108
109
110
111
# File 'lib/safety_pin/node.rb', line 107

def ==(other_node)
  return false if other_node.nil?
  return false unless other_node.respond_to?(:path)
  self.path == other_node.path
end

#[](name) ⇒ Object



262
263
264
# File 'lib/safety_pin/node.rb', line 262

def [](name)
  read_attribute(name)
end

#[]=(name, value) ⇒ Object



266
267
268
# File 'lib/safety_pin/node.rb', line 266

def []=(name, value)
  write_attribute(name, value)
end

#add_mixin(mixin_name) ⇒ Object



361
362
363
# File 'lib/safety_pin/node.rb', line 361

def add_mixin(mixin_name)
  j_node.add_mixin(mixin_name)
end

#build(name, node_blueprint = nil) ⇒ Object



387
388
389
# File 'lib/safety_pin/node.rb', line 387

def build(name, node_blueprint = nil)
  Node.build(node_blueprint_for(name, node_blueprint))
end

#changed?Boolean

Returns:

  • (Boolean)


270
271
272
# File 'lib/safety_pin/node.rb', line 270

def changed?
  j_node.modified?
end

#child(relative_path) ⇒ Object



130
131
132
133
134
135
# File 'lib/safety_pin/node.rb', line 130

def child(relative_path)
  child_j_node = j_node.get_node(relative_path.to_s)
  Node.new(child_j_node)
rescue javax.jcr.PathNotFoundException
  nil
end

#childrenObject



118
119
120
121
122
123
124
# File 'lib/safety_pin/node.rb', line 118

def children
  child_nodes = []
  j_node.get_nodes.each do |child_j_node|
    child_nodes << Node.new(child_j_node)
  end
  child_nodes
end

#convert_hash_to_node_blueprint(hash) ⇒ Object

Convert a hash (and it’s values recursively) to NodeBlueprints. This is a helper method, allowing a hash to be passed in to Node#properties= when only properties need to be set. One caveat: all node types will default to nt:unstructured.



334
335
336
337
338
339
340
341
# File 'lib/safety_pin/node.rb', line 334

def convert_hash_to_node_blueprint(hash)
  hash.keys.each do |key|
    if hash[key].is_a? Hash
      hash[key] = convert_hash_to_node_blueprint(hash[key])
    end
  end
  NodeBlueprint.new(:path => :no_path, :properties => hash)
end

#create(name, node_blueprint = nil) ⇒ Object

Create and return a child node with a given name



383
384
385
# File 'lib/safety_pin/node.rb', line 383

def create(name, node_blueprint = nil)
  Node.create(node_blueprint_for(name, node_blueprint))
end

#descendantsObject



126
127
128
# File 'lib/safety_pin/node.rb', line 126

def descendants
  children.map {|child| [child, child.descendants] }.flatten
end

#destroyObject



347
348
349
350
351
352
353
354
355
# File 'lib/safety_pin/node.rb', line 347

def destroy
  path = self.path
  parent_j_node = j_node.parent
  j_node.remove
  parent_j_node.save
  # raise NodeError.new("Unable to destroy #{path} node") unless self.class.find(path).nil?
rescue javax.jcr.RepositoryException => e
  raise NodeError.new("Unable to destroy #{path} node: #{e.message}")
end

#find_or_create(name, primary_type = nil) ⇒ Object



377
378
379
380
# File 'lib/safety_pin/node.rb', line 377

def find_or_create(name, primary_type = nil)
  path = Pathname(self.path) + name
  self.class.find_or_create(path.to_s, primary_type)
end

#has_property(name) ⇒ Object



423
424
425
# File 'lib/safety_pin/node.rb', line 423

def has_property(name)
  properties.keys.include?(name)
end

#has_property?(name) ⇒ Boolean

Returns:

  • (Boolean)


202
203
204
# File 'lib/safety_pin/node.rb', line 202

def has_property?(name)
  j_node.has_property(name)
end

#inspectObject



427
428
429
# File 'lib/safety_pin/node.rb', line 427

def inspect
  "#<#{self.class} path=#{path}>"
end

#mixin_typesObject



357
358
359
# File 'lib/safety_pin/node.rb', line 357

def mixin_types
  j_node.mixin_node_types.map(&:name)
end

#nameObject



137
138
139
# File 'lib/safety_pin/node.rb', line 137

def name
  @name ||= j_node.name
end

#new?Boolean

Returns:

  • (Boolean)


274
275
276
# File 'lib/safety_pin/node.rb', line 274

def new?
  j_node.new?
end

#node_blueprint_for(name, node_blueprint = nil) ⇒ Object



391
392
393
394
395
396
397
398
399
400
# File 'lib/safety_pin/node.rb', line 391

def node_blueprint_for(name, node_blueprint = nil)
  path = Pathname(self.path) + name.to_s
  
  unless node_blueprint.nil?
    properties = node_blueprint.properties
    primary_type = node_blueprint.primary_type
  end
  
  NodeBlueprint.new(:path => path.to_s, :properties => properties, :primary_type => primary_type)
end

#parentObject

Raises:



113
114
115
116
# File 'lib/safety_pin/node.rb', line 113

def parent
  raise NodeError.new("Root node does not have parent") if path == "/"
  Node.new(j_node.parent)
end

#pathObject



99
100
101
# File 'lib/safety_pin/node.rb', line 99

def path
  @path ||= j_node.path
end

#primary_typeObject



369
370
371
# File 'lib/safety_pin/node.rb', line 369

def primary_type
  self["jcr:primaryType"]
end

#primary_type=(primary_type) ⇒ Object



373
374
375
# File 'lib/safety_pin/node.rb', line 373

def primary_type=(primary_type)
  j_node.set_primary_type(primary_type)
end

#propertiesObject



278
279
280
281
282
283
284
285
286
287
288
289
# File 'lib/safety_pin/node.rb', line 278

def properties
  props = {}
  prop_iter = j_node.properties
  while prop_iter.has_next
    prop = prop_iter.next_property        
    unless prop.definition.protected?
      prop_name = prop.name
      props[prop_name] = self[prop_name]
    end
  end
  props
end

#properties=(new_props) ⇒ Object



304
305
306
307
308
309
310
311
312
313
314
315
316
317
318
319
320
321
322
323
324
325
326
327
328
# File 'lib/safety_pin/node.rb', line 304

def properties=(new_props)
  property_names = (properties.keys + new_props.keys).uniq
  property_names.each do |name|
    # REFACTOR ME PLZ
    child_path = Pathname(path.to_s) + name.to_s
    if new_props[name].is_a? Hash
      new_props[name] = convert_hash_to_node_blueprint(new_props[name])
    end

    if new_props[name].respond_to?(:node_blueprint?) and new_props[name].node_blueprint?
      # Handle node blue prints
      node_blueprint = NodeBlueprint.new(:properties => new_props[name].properties, 
                                         :path => child_path.to_s, 
                                         :primary_type => new_props[name].primary_type)
      if Node.exists?(child_path)
        Node.update(node_blueprint)
      else
        Node.build(node_blueprint)
      end
    else
      # handle everything else
      self[name] = new_props[name]
    end
  end
end

#property_is_multi_valued?(property) ⇒ Boolean

Returns:

  • (Boolean)


153
154
155
156
157
158
# File 'lib/safety_pin/node.rb', line 153

def property_is_multi_valued?(property)
  property.values
  true
rescue javax.jcr.ValueFormatException
  false
end

#property_multi_valued?(name) ⇒ Boolean

Returns:

  • (Boolean)


198
199
200
# File 'lib/safety_pin/node.rb', line 198

def property_multi_valued?(name)
  properties.has_key?(name) and properties[name].is_a?(Array)
end

#property_single_valued?(name) ⇒ Boolean

Returns:

  • (Boolean)


194
195
196
# File 'lib/safety_pin/node.rb', line 194

def property_single_valued?(name)
  properties.has_key?(name) and !property_multi_valued?(name)
end

#protected_propertiesObject



291
292
293
294
295
296
297
298
299
300
301
302
# File 'lib/safety_pin/node.rb', line 291

def protected_properties
  props = {}
  prop_iter = j_node.properties
  while prop_iter.has_next
    prop = prop_iter.next_property        
    if prop.definition.protected?
      prop_name = prop.name
      props[prop_name] = self[prop_name]
    end
  end
  props
end

#read_attribute(name) ⇒ Object



141
142
143
144
145
146
147
148
149
150
151
# File 'lib/safety_pin/node.rb', line 141

def read_attribute(name)
  name = name.to_s
  property = j_node.get_property(name)
  if property_is_multi_valued?(property)
    retrieve_property_multi_value(property)
  else
    retrieve_property_value(property)
  end
rescue javax.jcr.PathNotFoundException
  raise NilPropertyError.new("#{name} property not found on node")
end

#reloadObject



258
259
260
# File 'lib/safety_pin/node.rb', line 258

def reload
  j_node.refresh(false)
end

#remove_mixin(mixin_name) ⇒ Object



365
366
367
# File 'lib/safety_pin/node.rb', line 365

def remove_mixin(mixin_name)
  j_node.remove_mixin(mixin_name)
end

#remove_property(name) ⇒ Object



206
207
208
# File 'lib/safety_pin/node.rb', line 206

def remove_property(name)
  j_node.get_property(name).remove if has_property?(name)
end

#replace_property(opts, &block) ⇒ Object



402
403
404
405
406
407
408
409
410
411
412
413
414
415
416
417
418
419
420
421
# File 'lib/safety_pin/node.rb', line 402

def replace_property(opts, &block)
  opts = {recursive: false}.merge(opts)
  name_pattern = opts.fetch(:name)
  target_pattern = opts.fetch(:target)
  replacement_block = block || lambda {|value| opts.fetch(:replacement) }

  modified = false
  properties.each do |name, value|
    if name.match(name_pattern) and value.match(target_pattern)
      self[name] = replacement_block.call(value)
      modified = true
    end
  end

  modified_nodes = []
  modified_nodes << self if modified
  modified_nodes << children.map {|child_node| child_node.replace_property(opts) } if opts.fetch(:recursive)

  modified_nodes.flatten
end

#retrieve_property_multi_value(property) ⇒ Object



160
161
162
# File 'lib/safety_pin/node.rb', line 160

def retrieve_property_multi_value(property)
  property.values.map {|value| retrieve_value(value) }
end

#retrieve_property_value(property) ⇒ Object



164
165
166
# File 'lib/safety_pin/node.rb', line 164

def retrieve_property_value(property)
  retrieve_value(property.value)
end

#retrieve_value(value) ⇒ Object



168
169
170
171
172
173
174
175
176
177
178
179
180
181
182
183
184
185
186
187
188
189
190
191
192
# File 'lib/safety_pin/node.rb', line 168

def retrieve_value(value)
  property_type = PropertyType.name_from_value(value.type)
  case property_type
  when "String"
    value.string
  when "Boolean"
    value.boolean
  when "Double"
    value.double
  when "Long"
    value.long
  when "Date"
    Time.at(value.date.time.time / 1000)
  when "Name"
    value.string # Not sure if these should be handled differently
  when "Binary"
    value.binary
  when "Decimal"
    value.decimal
  when "Path"
    Pathname(value.string)
  else
    raise PropertyTypeError.new("Unknown property type: #{property_type}")
  end
end

#saveObject



248
249
250
251
252
253
254
255
256
# File 'lib/safety_pin/node.rb', line 248

def save
  if new?
    j_node.parent.save
  else
    j_node.save
  end
  
  not changed?
end

#sessionObject



103
104
105
# File 'lib/safety_pin/node.rb', line 103

def session
  @session ||= JCR.session
end

#value_factoryObject



343
344
345
# File 'lib/safety_pin/node.rb', line 343

def value_factory
  session.value_factory
end

#write_attribute(name, value) ⇒ Object

Raises:



210
211
212
213
214
215
216
217
218
219
220
221
222
223
224
225
226
227
228
229
230
231
232
233
234
235
236
237
238
239
240
241
242
243
244
245
246
# File 'lib/safety_pin/node.rb', line 210

def write_attribute(name, value)
  raise PropertyError.new("Illegal operation: cannot change jcr:primaryType property") if name == "jcr:primaryType"
  name = name.to_s
  
  if value.nil?
    remove_property(name)
    return nil
  end

  # when going from multi to single value
  if property_multi_valued?(name) and !value.is_a?(Array)
    remove_property(name)
  end
  
  if value.is_a? Array
    remove_property(name)
    values = value
    val_fact = value_factory
    j_values = []
    values.each do |value|
      j_values << val_fact.create_value(value.to_java)
    end
    j_node.set_property(name, j_values.to_java(Java::JavaxJcr::Value))
  elsif value.is_a? Time or value.is_a? Date
    calendar_value = Calendar.instance
    calendar_value.set_time(value.to_java)
    j_node.set_property(name, calendar_value)
  elsif value.is_a?(Symbol) or value.is_a?(Pathname)
    j_node.set_property(name, value.to_s)
  else
    begin
      j_node.set_property(name, value)
    rescue NameError
      raise SafetyPin::PropertyTypeError.new("Property value type of #{value.class} is unsupported")
    end
  end
end