arc_weld 0.2.5

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.
Files changed (57) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +12 -0
  3. data/.rspec +2 -0
  4. data/.travis.yml +4 -0
  5. data/Gemfile +3 -0
  6. data/Guardfile +58 -0
  7. data/LICENSE.txt +674 -0
  8. data/README.md +73 -0
  9. data/Rakefile +6 -0
  10. data/arc_weld.gemspec +34 -0
  11. data/bin/setup +7 -0
  12. data/bin/weld +8 -0
  13. data/lib/arc_weld/archive.rb +87 -0
  14. data/lib/arc_weld/cli/project.rb +116 -0
  15. data/lib/arc_weld/cli/readers/relation_csv.rb +41 -0
  16. data/lib/arc_weld/cli/readers/resource_csv.rb +64 -0
  17. data/lib/arc_weld/cli.rb +45 -0
  18. data/lib/arc_weld/helpers.rb +12 -0
  19. data/lib/arc_weld/reference.rb +51 -0
  20. data/lib/arc_weld/relationship.rb +96 -0
  21. data/lib/arc_weld/relationships/customer_of.rb +28 -0
  22. data/lib/arc_weld/relationships/has_alternate_interface.rb +27 -0
  23. data/lib/arc_weld/relationships/has_child.rb +42 -0
  24. data/lib/arc_weld/relationships/has_customer.rb +28 -0
  25. data/lib/arc_weld/relationships/has_location.rb +16 -0
  26. data/lib/arc_weld/relationships/in_category.rb +26 -0
  27. data/lib/arc_weld/relationships/in_network.rb +26 -0
  28. data/lib/arc_weld/relationships/in_zone.rb +41 -0
  29. data/lib/arc_weld/relationships/location_of.rb +28 -0
  30. data/lib/arc_weld/resource.rb +149 -0
  31. data/lib/arc_weld/resource_definitions.rb +7 -0
  32. data/lib/arc_weld/resources/asset.rb +33 -0
  33. data/lib/arc_weld/resources/asset_category.rb +40 -0
  34. data/lib/arc_weld/resources/customer.rb +16 -0
  35. data/lib/arc_weld/resources/group.rb +29 -0
  36. data/lib/arc_weld/resources/location.rb +18 -0
  37. data/lib/arc_weld/resources/network.rb +14 -0
  38. data/lib/arc_weld/resources/zone.rb +65 -0
  39. data/lib/arc_weld/templates/project-csv/.gitignore.tt +2 -0
  40. data/lib/arc_weld/templates/project-csv/Gemfile.tt +3 -0
  41. data/lib/arc_weld/templates/project-csv/Thorfile.tt +9 -0
  42. data/lib/arc_weld/templates/project-csv/in/assets.csv.tt +2 -0
  43. data/lib/arc_weld/templates/project-csv/in/categories.csv.tt +3 -0
  44. data/lib/arc_weld/templates/project-csv/in/customer_groups.csv.tt +3 -0
  45. data/lib/arc_weld/templates/project-csv/in/customers.csv.tt +3 -0
  46. data/lib/arc_weld/templates/project-csv/in/group_categories.csv.tt +3 -0
  47. data/lib/arc_weld/templates/project-csv/in/locations.csv.tt +6 -0
  48. data/lib/arc_weld/templates/project-csv/in/networks.csv.tt +3 -0
  49. data/lib/arc_weld/templates/project-csv/in/zone_locations.csv.tt +2 -0
  50. data/lib/arc_weld/templates/project-csv/in/zones.csv.tt +2 -0
  51. data/lib/arc_weld/templates/project-csv/lib/thor/README.txt.tt +2 -0
  52. data/lib/arc_weld/templates/project-csv/lib/thor/sample.thor.tt +7 -0
  53. data/lib/arc_weld/templates/project-csv/out/README-archive.txt.tt +5 -0
  54. data/lib/arc_weld/templates/project-csv/weld.json.tt +39 -0
  55. data/lib/arc_weld/version.rb +3 -0
  56. data/lib/arc_weld.rb +19 -0
  57. metadata +269 -0
data/README.md ADDED
@@ -0,0 +1,73 @@
1
+ # ArcWeld
2
+
3
+ [![Build Status](https://travis-ci.org/ryanbreed/arc_weld.svg?branch=master)](https://travis-ci.org/ryanbreed/arc_weld)
4
+
5
+ Toolkit for generating ArcSight resources. Useful for fine-grained control
6
+ over the asset model. Currently supports generating the following resource
7
+ types:
8
+
9
+ - Asset
10
+ - AssetCategory
11
+ - Customer
12
+ - Location
13
+ - Network
14
+ - Zone
15
+ - Groups of the above resources
16
+
17
+ Basic support classes included for building resource XML archives and reading
18
+ resources from CSV files. You can also model supported relationships between
19
+ the resources using the relationship extension modules. This lets you do things
20
+ like add a location to zones, add a network to a customer, categorize resources,
21
+ etc.
22
+
23
+
24
+
25
+ ## **WARNING**
26
+
27
+ You will completely trash your ESM if you do not understand *exactly* what
28
+ you are getting into with the archive tool. There's many reasons why not much
29
+ documentation exists on the subject. `$MANAGER_HOME/bin/arcsight archive -h`
30
+ to get started on that journey. Also, there is no guarantee that HP will
31
+ even continue to expose the archive tool interface. It could be deprecated
32
+ at any time in any release with no notice.
33
+
34
+ ## Installation
35
+
36
+ Add this line to your application's Gemfile:
37
+
38
+ ```ruby
39
+ gem 'arc_weld'
40
+ ```
41
+
42
+ And then execute:
43
+
44
+ $ bundle
45
+
46
+ Or install it yourself as:
47
+
48
+ $ gem install arc_weld
49
+
50
+ ## Usage
51
+
52
+ So far, the spec files contain the most complete description of how to use the
53
+ resource classes and relationship modules. There is a basic cli tool for
54
+ creating a csv-based model project and using that to generate the model xml.
55
+ Bear in mind that you can always break up a modeling effort into multiple smaller
56
+ projects and merge them in the target ESM if that makes more sense.
57
+
58
+ `ArcWeld::Resource` and `ArcWeld::Relationship` define the core DSL for describing
59
+ resources and relationships. The code for the resources and relationships themselves
60
+ are defined under `lib/arc_weld/resources` and `lib/arc_weld/relationships`.
61
+
62
+ Be advised - this is also an experiment for me, so some things could be better
63
+ organized. This will happen before I cut a 1.0.0.
64
+
65
+ ## Licensed under GPLv3
66
+
67
+ ## Development
68
+
69
+ To install this gem onto your local machine, run `bundle exec rake install`. To release a new version, update the version number in `version.rb`, and then run `bundle exec rake release`, which will create a git tag for the version, push git commits and tags, and push the `.gem` file to [rubygems.org](https://rubygems.org).
70
+
71
+ ## Contributing
72
+
73
+ Bug reports and pull requests are welcome on GitHub at https://github.com/ryanbreed/arc_weld.
data/Rakefile ADDED
@@ -0,0 +1,6 @@
1
+ require "bundler/gem_tasks"
2
+ require "rspec/core/rake_task"
3
+
4
+ RSpec::Core::RakeTask.new(:spec)
5
+
6
+ task :default => :spec
data/arc_weld.gemspec ADDED
@@ -0,0 +1,34 @@
1
+ # coding: utf-8
2
+ lib = File.expand_path('../lib', __FILE__)
3
+ $LOAD_PATH.unshift(lib) unless $LOAD_PATH.include?(lib)
4
+ require 'arc_weld/version'
5
+
6
+ Gem::Specification.new do |spec|
7
+ spec.name = 'arc_weld'
8
+ spec.version = ArcWeld::VERSION
9
+ spec.authors = ['Ryan Breed']
10
+ spec.email = ['opensource@breed.org']
11
+
12
+ spec.summary = 'Toolkit for building ArcSight resources'
13
+ spec.description = 'Toolkit for building ArcSight resources'
14
+ spec.homepage = 'https://github.com/ryanbreed/arc_weld'
15
+
16
+ spec.files = `git ls-files -z`.split("\x0").reject { |f| f.match(%r{^(test|spec|features)/}) }
17
+ spec.bindir = 'bin'
18
+ spec.executables = spec.files.grep(%r{^bin/}) { |f| File.basename(f) }
19
+ spec.require_paths = ['lib']
20
+
21
+ spec.add_dependency 'gyoku'
22
+ spec.add_dependency 'nokogiri'
23
+ spec.add_dependency 'thor'
24
+
25
+ spec.add_development_dependency 'pry'
26
+ spec.add_development_dependency 'pry-doc'
27
+ spec.add_development_dependency 'bundler', '~> 1.10'
28
+ spec.add_development_dependency 'rake', '~> 10.0'
29
+ spec.add_development_dependency 'rspec'
30
+ spec.add_development_dependency 'guard'
31
+ spec.add_development_dependency 'guard-rspec'
32
+ spec.add_development_dependency 'guard-bundler'
33
+ spec.add_development_dependency 'simplecov'
34
+ end
data/bin/setup ADDED
@@ -0,0 +1,7 @@
1
+ #!/bin/bash
2
+ set -euo pipefail
3
+ IFS=$'\n\t'
4
+
5
+ bundle install
6
+
7
+ # Do any other automated setup that you need to do here
data/bin/weld ADDED
@@ -0,0 +1,8 @@
1
+ #!/usr/bin/env ruby
2
+ $LOAD_PATH.push(File.join(File.expand_path(File.dirname(__FILE__)), '..','lib'))
3
+ require 'arc_weld'
4
+ if Dir.exist?(local_lib=File.join(Dir.pwd,'lib'))
5
+ Dir[File.join(local_lib,'*.rb')]. each {|local_lib| require local_lib }
6
+ end
7
+
8
+ ArcWeld::WeldCli.start(ARGV)
@@ -0,0 +1,87 @@
1
+ module ArcWeld
2
+ class Archive
3
+ attr_accessor :resources, :createTime, :dtd_location
4
+
5
+ BUILD_VERSION = '6.5.1.1952.1'
6
+ BUILD_TIME = '3-6-2015_17:2:26'
7
+ TIME_FORMAT = '%m-%d-%Y_%H:%M:%S'
8
+
9
+ PARSE_DEFAULT = Nokogiri::XML::ParseOptions::DEFAULT_XML
10
+ PARSE_VALIDATE = Nokogiri::XML::ParseOptions::DEFAULT_XML | Nokogiri::XML::ParseOptions::DTDLOAD
11
+
12
+ ARCHIVE_DTD = "../../schema/xml/archive/arcsight-archive.dtd"
13
+
14
+ def initialize(*args)
15
+ Hash[*args].each {|k,v| self.send(format('%s=',k),v)}
16
+ @resources ||= []
17
+ @createTime ||= Time.new
18
+ @dtd_location ||= ARCHIVE_DTD
19
+ self
20
+ end
21
+
22
+ def include_list
23
+ resources.map {|g| g.ref.render }.join('')
24
+ end
25
+
26
+ def add_item(item)
27
+ resources.push(item) unless resources.include?(item)
28
+ end
29
+
30
+ def add(*items)
31
+ items.each { |item| add_item(item) }
32
+ end
33
+
34
+ def dtd(location=ARCHIVE_DTD)
35
+ format('<!DOCTYPE archive SYSTEM "%s">', dtd_location)
36
+ end
37
+
38
+ def archive_creation_parameters
39
+ acp = {
40
+ ArchiveCreationParameters: {
41
+ :action => 'insert',
42
+ 'excludeReferenceIDs/' => '',
43
+ :format => 'default',
44
+ 'formatRoot/' => ''
45
+ }
46
+ }
47
+
48
+ if resources.empty?
49
+ acp[:ArchiveCreationParameters]['include/'] = ''
50
+ else
51
+ acp[:ArchiveCreationParameters][:include] = { :list! => include_list }
52
+ end
53
+
54
+ Gyoku.xml(acp, {key_converter: :none})
55
+ end
56
+
57
+ def create_time
58
+ @createTime.strftime(TIME_FORMAT)
59
+ end
60
+
61
+ def resource_content
62
+ resources.map {|res| res.render }.join('')
63
+ end
64
+
65
+ def to_h
66
+ archive_h = {
67
+ :archive! => archive_creation_parameters + resource_content,
68
+ :attributes! => {
69
+ :archive! => {
70
+ :buildVersion => BUILD_VERSION,
71
+ :buildTime => BUILD_TIME,
72
+ :createTime => create_time
73
+ }
74
+ }
75
+ }
76
+ end
77
+
78
+ def render
79
+ Gyoku.xml(to_h, {key_converter: :none} )
80
+ end
81
+
82
+ def xml(options=PARSE_DEFAULT)
83
+ Nokogiri.XML(dtd + render, nil, 'UTF-8', options)
84
+ end
85
+
86
+ end
87
+ end
@@ -0,0 +1,116 @@
1
+ module ArcWeld
2
+ module Cli
3
+ class Project
4
+ include ArcWeld::Helpers
5
+ attr_accessor :name, :input_dir, :output_dir, :resource_driver,
6
+ :resource_root, :resource_inputs, :relationships, :base_model
7
+
8
+ def initialize(config)
9
+ config.each {|k,v| self.send(format('%s=',k),v)}
10
+ self
11
+ end
12
+
13
+ def resource_class_for(resource_type)
14
+ ArcWeld.const_get(constantize(resource_type))
15
+ end
16
+
17
+ def resource_input_path(resource_type)
18
+ File.join(self.input_dir, resource_inputs.fetch(resource_type))
19
+ end
20
+
21
+ def resource_reader(resource_type)
22
+ klass = ArcWeld::Cli::ResourceReaders.const_get(constantize(resource_driver))
23
+ klass.new( path: resource_input_path(resource_type),
24
+ target_class: resource_class_for(resource_type) )
25
+ end
26
+
27
+ def reader
28
+ @readers ||= Hash[
29
+ resource_inputs.keys.map { |type| [type, resource_reader(type)]}
30
+ ]
31
+ end
32
+
33
+ def resources
34
+ @resources ||= Hash.new {|h,k| h[k]=Array.new }
35
+ end
36
+
37
+ def archive_resources(resource_type)
38
+ archive.resources.select {|r| r.resource_type == constantize(resource_type)}
39
+ end
40
+
41
+ def archive
42
+ @archive ||= Archive.new
43
+ end
44
+
45
+ def load_resources
46
+ reader.each do |type, type_reader|
47
+ res=type_reader.to_a
48
+ resources[type].push(*res)
49
+ end
50
+ end
51
+
52
+ def resource_group(type)
53
+ klass = ArcWeld.const_get(constantize(type))
54
+ group_name = resource_root[type]
55
+ ArcWeld::Group.new(
56
+ name: group_name,
57
+ externalID: format('weld_%s_root', type),
58
+ description: format('ArcWeld root %s group',
59
+ constantize(type)),
60
+ containedResourceType: klass.class_id,
61
+ parent_ref: klass.toplevel
62
+ )
63
+ end
64
+
65
+ def resource_roots
66
+ @roots ||= Hash[
67
+ resource_root.map do |type, group_name|
68
+ grp = resource_group(type)
69
+ resources['group'].push(grp)
70
+ [type, grp ]
71
+ end
72
+ ]
73
+ end
74
+
75
+ def zone_assets
76
+ resources['asset'].each do |asset|
77
+ asset.auto_zone(*resources['zone'])
78
+ end
79
+ end
80
+
81
+ def assign_model_relations
82
+ relationships.each do |rel|
83
+ klass = ArcWeld::Cli::RelationReaders.const_get(constantize(rel['driver']))
84
+ rel_reader = klass.new(
85
+ path: File.join(input_dir,rel['file']),
86
+ relationship_type: rel['type'] )
87
+ sources = resources[rel_reader.src_type]
88
+ dests = resources[rel_reader.dst_type]
89
+ rel_reader.relate(sources,dests)
90
+ end
91
+ end
92
+
93
+ def assign_resource_groups
94
+ resource_roots['zone'].add_children(*resources['zone'])
95
+
96
+ zone_assets
97
+
98
+ unzoned = resources['asset'].select {|a| a.in_zone.nil?}
99
+ resource_roots['asset'].add_children(*unzoned)
100
+
101
+ %w{ network customer location asset_category }.each do |type|
102
+ resource_roots[type].add_children(*resources[type])
103
+ end
104
+ end
105
+
106
+ def generate_archive
107
+ resource_roots
108
+ load_resources
109
+ assign_resource_groups
110
+ assign_model_relations
111
+ archive.add(*resources.values.flatten)
112
+ archive
113
+ end
114
+ end
115
+ end
116
+ end
@@ -0,0 +1,41 @@
1
+ module ArcWeld
2
+ module Cli
3
+ module RelationReaders
4
+ class Csv
5
+ include ArcWeld::Helpers
6
+ attr_accessor :path, :relationship_type,
7
+ :src_select, :dst_select,
8
+ :src_class, :dst_class
9
+
10
+ attr_reader :csv, :keys, :src_type, :dst_type
11
+
12
+ def initialize(*args)
13
+ Hash[*args].each {|k,v| self.send(format('%s=',k),v)}
14
+ @csv = CSV.open(path)
15
+ @keys = csv.readline
16
+ @src_type, @src_select, @dst_type, @dst_select = keys.flat_map do |k|
17
+ k.split(':')
18
+ end
19
+ @src_class=ArcWeld.const_get(constantize(src_type))
20
+ @dst_class=ArcWeld.const_get(constantize(dst_type))
21
+ self
22
+ end
23
+
24
+ def relate(sources,destinations)
25
+ csv.each do |selected_src, selected_dst|
26
+ src_instance=sources.find {|s| s.send(src_select)==selected_src}
27
+ dst_instance=destinations.find {|s| s.send(dst_select)==selected_dst}
28
+ if (src_instance.nil? || dst_instance.nil?)
29
+ STDERR.puts format('could not %s %s/%s',
30
+ relationship_type,
31
+ selected_src,
32
+ selected_dst)
33
+ else
34
+ src_instance.send(relationship_type,dst_instance)
35
+ end
36
+ end
37
+ end
38
+ end
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,64 @@
1
+ require 'csv'
2
+
3
+ module ArcWeld
4
+ module Cli
5
+ module ResourceReaders
6
+ class Csv
7
+ attr_accessor :path, :target_class, :key_proc
8
+ attr_reader :keys, :csv
9
+ DEFAULT_OPTIONS = {skip_lines: %r{\A[#;]}}
10
+
11
+ def initialize(path: nil, target_class: nil, options: DEFAULT_OPTIONS)
12
+ @path, @target_class = path, target_class
13
+ @csv = CSV.open(path, 'r', options)
14
+ @key_proc ||= {}
15
+ @keys = csv.readline.map { |k| k.to_sym }
16
+ end
17
+
18
+ def read_keys
19
+ keys - processed_keys
20
+ end
21
+
22
+ def processed_keys
23
+ key_proc.keys
24
+ end
25
+
26
+ def parse(array)
27
+ row_hash = Hash[keys.zip(array)]
28
+ instance_hash = row_hash.select {|k,v| read_keys.include?(k)}
29
+ new_instance = target_class.new(instance_hash)
30
+
31
+ if processed_keys.empty?
32
+ new_instance
33
+ else
34
+ processed_hash = row_hash.select {|k,v| read_keys.include?(k)==false }
35
+ processed_hash.each do |processed_key, processed_value|
36
+ key_proc.fetch(processed_key).call(processed_key,processed_value,new_instance)
37
+ end
38
+ new_instance
39
+ end
40
+ end
41
+
42
+ def not_at_start_record?
43
+ (csv.lineno!=1)
44
+ end
45
+
46
+ def each(rewind: not_at_start_record?)
47
+ reset_csv if rewind
48
+ csv.each {|arr| yield parse(arr) }
49
+ end
50
+
51
+ def to_a(rewind: not_at_start_record?)
52
+ reset_csv if rewind
53
+ csv.readlines.map {|arr| parse(arr)}
54
+ end
55
+
56
+ def reset_csv
57
+ csv.rewind
58
+ *trash = csv.readline
59
+ csv.lineno
60
+ end
61
+ end
62
+ end
63
+ end
64
+ end
@@ -0,0 +1,45 @@
1
+ require 'arc_weld'
2
+ require 'arc_weld/cli/project'
3
+ require 'arc_weld/cli/readers/relation_csv'
4
+ require 'arc_weld/cli/readers/resource_csv'
5
+ require 'openssl'
6
+ require 'json'
7
+ module ArcWeld
8
+ class WeldCli < Thor
9
+
10
+ include Thor::Actions
11
+ def self.source_root
12
+ @source_root ||= File.join( File.dirname( File.expand_path(__FILE__) ),
13
+ 'templates')
14
+ end
15
+
16
+ class_option :config,
17
+ type: :string,
18
+ default: 'weld.json',
19
+ desc: 'config file'
20
+
21
+ desc 'new PROJECT_NAME', 'generate new project'
22
+ option :dir,
23
+ type: :string,
24
+ default: Dir.pwd,
25
+ desc: 'create project directory here'
26
+ option :driver,
27
+ type: :string,
28
+ default: 'csv',
29
+ desc: 'driver for reading resources and relations'
30
+ def new(name)
31
+ self.destination_root = options[:dir]
32
+ @config = options.merge(name: name)
33
+ directory(format('project-%s',options[:driver]),name)
34
+ end
35
+
36
+ desc 'generate FILENAME', 'render entire model in FILENAME'
37
+ def generate(filename='model.xml')
38
+ config = JSON.parse(File.read(options[:config]))
39
+ project = ArcWeld::Cli::Project.new(config)
40
+ outfile = File.join(project.output_dir, filename)
41
+ archive = project.generate_archive
42
+ File.open(outfile,'w') {|f| f.puts archive.xml.to_s }
43
+ end
44
+ end
45
+ end
@@ -0,0 +1,12 @@
1
+ module ArcWeld
2
+ module Helpers
3
+ def constantize(literal)
4
+ (literal.to_s.split(%r{[ _]}).map &:capitalize).join('')
5
+ end
6
+
7
+ def uri_join(*parts)
8
+ pieces=parts.flat_map {|p| p.to_s.gsub(%r{\A/},'').gsub(%r{/\z},'')}
9
+ format('/%s', pieces.join('/'))
10
+ end
11
+ end
12
+ end
@@ -0,0 +1,51 @@
1
+ module ArcWeld
2
+ class Reference
3
+ attr_accessor :type, :uri, :id, :externalID
4
+
5
+ def initialize(*args)
6
+ Hash[*args].each {|k,v| self.send(format('%s=',k),v)}
7
+ @type ||= 'Group'
8
+ end
9
+
10
+ def ref
11
+ self
12
+ end
13
+
14
+ def ==(other)
15
+ [:type, :uri, :id, :externalID].all? do |attr|
16
+ self.send(attr) == other.send(attr)
17
+ end
18
+ end
19
+
20
+ def identity_hash
21
+ if identity=={}
22
+ {}
23
+ else
24
+ Hash[identity.keys.map {|k| format('@%s',k.to_s)}.zip(identity.values)]
25
+ end
26
+ end
27
+
28
+ def identity
29
+ if id!=nil
30
+ {:id => id}
31
+ elsif externalID!=nil
32
+ {:externalID => externalID}
33
+ else
34
+ {}
35
+ end
36
+ end
37
+
38
+ def to_h
39
+ {
40
+ 'ref/' => {
41
+ :@type => type,
42
+ :@uri => uri
43
+ }.merge(identity_hash)
44
+ }
45
+ end
46
+
47
+ def render
48
+ Gyoku.xml(to_h, key_converter: :none)
49
+ end
50
+ end
51
+ end
@@ -0,0 +1,96 @@
1
+ require 'arc_weld/relationships/customer_of'
2
+ require 'arc_weld/relationships/location_of'
3
+ require 'arc_weld/relationships/has_alternate_interface'
4
+ require 'arc_weld/relationships/has_child'
5
+ require 'arc_weld/relationships/has_customer'
6
+ require 'arc_weld/relationships/has_location'
7
+ require 'arc_weld/relationships/in_category'
8
+ require 'arc_weld/relationships/in_network'
9
+ require 'arc_weld/relationships/in_zone'
10
+
11
+ module ArcWeld
12
+ module Relationship
13
+
14
+ def self.included(klass)
15
+ klass.class_eval '@@RELATIONSHIPS = []'
16
+ klass.class_eval do
17
+ extend ClassMethods
18
+ end
19
+
20
+ end
21
+
22
+ def relationship_types
23
+ self.class.class_variable_get :@@RELATIONSHIPS
24
+ end
25
+
26
+ def relationship_hash
27
+ relationship_types.reduce({}) do |memo, key|
28
+ meth = format('%s_relationship',key)
29
+ if self.send(meth).nil?
30
+ memo
31
+ else
32
+ memo.merge(self.send(meth))
33
+ end
34
+ end
35
+ end
36
+
37
+ module ClassMethods
38
+ def has_relationship(*args)
39
+ #puts format('defining relationship on %s', self.name)
40
+ options=case args[-1]
41
+ when Hash
42
+ args.pop
43
+ else
44
+ Hash.new(false)
45
+ end
46
+
47
+ args.each do |sym|
48
+ register_relationship(sym,options[:multiple])
49
+ end
50
+ end
51
+
52
+ def class_relationship_types
53
+ class_variable_get :@@RELATIONSHIPS
54
+ end
55
+
56
+ def register_relationship(name, multiple=false)
57
+ unless class_relationship_types.include?(name)
58
+ class_relationship_types << name
59
+
60
+ if ArcWeld::Relationships.const_defined? constantize(name)
61
+ self.class_eval "include ArcWeld::Relationships::#{constantize(name)}"
62
+ end
63
+ # TODO: this is a horrible mess
64
+ unless self.method_defined? name
65
+ if multiple
66
+ self.class_eval do
67
+ define_method("#{name}") do
68
+ val = self.instance_variable_get("@#{name}")
69
+ if val.nil?
70
+ self.instance_variable_set("@#{name}",[])
71
+ end
72
+ self.instance_variable_get("@#{name}")
73
+ end
74
+ end
75
+ else
76
+ self.class_eval do
77
+ define_method("#{name}") do
78
+ self.instance_variable_get("@#{name}")
79
+ end
80
+ end
81
+ end
82
+ end
83
+ unless self.method_defined? "#{name}="
84
+ self.class_eval do
85
+ define_method("#{name}=") do |val|
86
+ self.instance_variable_set("@#{name}",val)
87
+ end
88
+ end
89
+ end
90
+
91
+
92
+ end
93
+ end
94
+ end
95
+ end
96
+ end
@@ -0,0 +1,28 @@
1
+ module ArcWeld
2
+ module Relationships
3
+ module CustomerOf
4
+ def add_customer_resource(res)
5
+ unless customer_of.include?(res)
6
+ customer_of.push(res)
7
+ res.add_customer(self)
8
+ end
9
+ end
10
+
11
+ def add_customer_resources(*resources)
12
+ resources.each { |res| add_customer_resource(res) }
13
+ end
14
+
15
+ def related_customer_of_references
16
+ (customer_of.map { |resource| resource.ref.render }).join
17
+ end
18
+
19
+ def customer_of_relationship
20
+ unless customer_of.empty?
21
+ { 'customerOf' => {
22
+ 'list!' => related_customer_of_references
23
+ }}
24
+ end
25
+ end
26
+ end
27
+ end
28
+ end