arc_weld 0.2.5

Sign up to get free protection for your applications and to get access to all the features.
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