percolate 0.9.4

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.
@@ -0,0 +1,7 @@
1
+ ---
2
+ SHA256:
3
+ metadata.gz: 2b8382f1bf97ccb41b8ef91a2072f5693d7c5b94ef9c5dee541b788b622683d3
4
+ data.tar.gz: 13845c16938b31f54a1e9e3e28b01bed59f7817342743e4b00a7594c2dd85a32
5
+ SHA512:
6
+ metadata.gz: 2284c4ca4102771ec8664ebfa0c980bf416df8425274418fb1d1af8ffe34293d50de1640da86def3ce954037118bad5038018c4916042faf77b4bba3b8bc4275
7
+ data.tar.gz: 807c807e84365ffdb38f53fe1dd4569e2f03a1a4017be2312e760bb6eb24ba6e6a1d763ba3333aab758574ff75a4fd89969e3485259dfeff0fdd3d32509bee9d
@@ -0,0 +1,18 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright 2014 Roy Liu
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
6
+ # use this file except in compliance with the License. You may obtain a copy of
7
+ # the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ # License for the specific language governing permissions and limitations under
15
+ # the License.
16
+
17
+ require "percolate/percolator"
18
+ require "percolate/version"
@@ -0,0 +1,89 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright 2014 Roy Liu
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
6
+ # use this file except in compliance with the License. You may obtain a copy of
7
+ # the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ # License for the specific language governing permissions and limitations under
15
+ # the License.
16
+
17
+ require "active_support/inflector"
18
+
19
+ module Percolate
20
+ module Adapter
21
+ # A base class to build off of.
22
+ class BaseAdapter
23
+ # The constructor.
24
+ #
25
+ # @param data_source [Object] the data source.
26
+ def initialize(data_source = nil)
27
+ @data_source = data_source
28
+ end
29
+
30
+ # Loads entities in an adapter-specific way.
31
+ #
32
+ # @return [Hash] the loaded entities.
33
+ def load_entities
34
+ {}
35
+ end
36
+
37
+ # Loads a facet.
38
+ #
39
+ # @param context [String] the lookup context.
40
+ # @param facet_name [Symbol] the facet name.
41
+ #
42
+ # @return [Object] the loaded facet.
43
+ def load_facet(context, facet_name)
44
+ create_facet(facet_name)
45
+ end
46
+
47
+ # Creates a facet from the given name.
48
+ #
49
+ # @param facet_name [Symbol] the facet name.
50
+ #
51
+ # @return [Object] the facet.
52
+ def create_facet(facet_name)
53
+ const_str = ActiveSupport::Inflector.camelize(facet_name) + "Facet"
54
+
55
+ begin
56
+ require "percolate/facet/#{facet_name}_facet"
57
+ rescue LoadError
58
+ # Do nothing. Give the benefit of the doubt if the file doesn't exist.
59
+ end if !Facet.const_defined?(const_str)
60
+
61
+ Facet.const_get(const_str).new
62
+ end
63
+
64
+ # Configures a facet according to the given attribute hash.
65
+ #
66
+ # @param facet [Object] the facet.
67
+ # @param attr_hash [Hash] the attribute hash.
68
+ #
69
+ # @return [Object] the facet.
70
+ def configure_facet(facet, attr_hash)
71
+ attr_hash.each_pair do |attr, value|
72
+ facet.send((attr + "=").to_sym, value)
73
+ end
74
+
75
+ facet
76
+ end
77
+
78
+ # If the given method isn't found, check for a setter of the same name.
79
+ def method_missing(sym, *args, &block)
80
+ if sym[-1] != "="
81
+ sym_set = (sym.to_s + "=").to_sym
82
+ return send(sym_set, *args, &block) if respond_to?(sym_set)
83
+ end
84
+
85
+ super
86
+ end
87
+ end
88
+ end
89
+ end
@@ -0,0 +1,84 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright 2014 Roy Liu
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
6
+ # use this file except in compliance with the License. You may obtain a copy of
7
+ # the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ # License for the specific language governing permissions and limitations under
15
+ # the License.
16
+
17
+ require "active_support/inflector"
18
+
19
+ require "percolate/adapter/base_adapter"
20
+ require "percolate/util"
21
+
22
+ module Percolate
23
+ module Adapter
24
+ # An adapter for loading from Chef data bags.
25
+ class ChefDataBagAdapter < BaseAdapter
26
+ attr_writer :entities_data_bag
27
+
28
+ def initialize(data_source)
29
+ super
30
+
31
+ @entities_data_bag = "entities"
32
+ end
33
+
34
+ def load_entities
35
+ begin
36
+ @data_source.data_bag(@entities_data_bag).reduce({}) do |current, item_name|
37
+ Percolate::Util.merge_attributes(
38
+ current,
39
+ @data_source.data_bag_item(@entities_data_bag, item_name).raw_data["entities"] || {}
40
+ )
41
+ end
42
+ rescue Net::HTTPServerException => e
43
+ # Reraise the exception if the status code isn't 404 Not Found.
44
+ if e.response.code != "404"
45
+ raise
46
+ end
47
+
48
+ nil
49
+ end
50
+ end
51
+
52
+ def load_facet(context, name)
53
+ name = name.to_s
54
+
55
+ facets = begin
56
+ @data_source.data_bag(context).map do |item_name|
57
+ facets_hash = @data_source.data_bag_item(context, item_name).raw_data["facets"]
58
+
59
+ facet_hash = facets_hash[name] || {}
60
+ facet_type = facet_hash.fetch("type", name)
61
+ facet_attrs = facet_hash.fetch("attrs", {})
62
+
63
+ configure_facet(create_facet(facet_type), facet_attrs)
64
+ end
65
+ rescue Net::HTTPServerException => e
66
+ # Reraise the exception if the status code isn't 404 Not Found.
67
+ if e.response.code != "404"
68
+ raise
69
+ end
70
+
71
+ []
72
+ end
73
+
74
+ if facets.size > 0
75
+ facets[1...facets.size].reduce(facets[0]) do |current, other|
76
+ current.merge(other)
77
+ end
78
+ else
79
+ nil
80
+ end
81
+ end
82
+ end
83
+ end
84
+ end
@@ -0,0 +1,48 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright 2014 Roy Liu
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
6
+ # use this file except in compliance with the License. You may obtain a copy of
7
+ # the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ # License for the specific language governing permissions and limitations under
15
+ # the License.
16
+
17
+ require "percolate/adapter/base_adapter"
18
+
19
+ module Percolate
20
+ module Adapter
21
+ # An adapter for exposing a fixed attribute `Hash`.
22
+ class FixtureAdapter < BaseAdapter
23
+ def initialize(data_source)
24
+ super
25
+ end
26
+
27
+ def load_entities
28
+ @data_source["entities"]
29
+ end
30
+
31
+ def load_facet(context, name)
32
+ name = name.to_s
33
+
34
+ facets_hash = @data_source["contexts"][context]["facets"]
35
+
36
+ if facets_hash.include?(name)
37
+ facet_hash = facets_hash[name]
38
+ facet_type = facet_hash.fetch("type", name)
39
+ facet_attrs = facet_hash.fetch("attrs", {})
40
+
41
+ configure_facet(create_facet(facet_type), facet_attrs)
42
+ else
43
+ nil
44
+ end
45
+ end
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,40 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright 2014 Roy Liu
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
6
+ # use this file except in compliance with the License. You may obtain a copy of
7
+ # the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ # License for the specific language governing permissions and limitations under
15
+ # the License.
16
+
17
+ module Percolate
18
+ module Facet
19
+ # A base class to build off of.
20
+ class BaseFacet
21
+ # Finds entity information from the given input.
22
+ #
23
+ # @param args [Array] the argument splat.
24
+ #
25
+ # @return [String] the entity name.
26
+ def find(*args)
27
+ nil
28
+ end
29
+
30
+ # Merges the given facet with this one.
31
+ #
32
+ # @param other [BaseFacet] the other facet.
33
+ #
34
+ # @return [BaseFacet] this facet.
35
+ def merge(other)
36
+ BaseFacet.new
37
+ end
38
+ end
39
+ end
40
+ end
@@ -0,0 +1,50 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright 2014 Roy Liu
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
6
+ # use this file except in compliance with the License. You may obtain a copy of
7
+ # the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ # License for the specific language governing permissions and limitations under
15
+ # the License.
16
+
17
+ require "percolate/facet/base_facet"
18
+
19
+ module Percolate
20
+ module Facet
21
+ # A facet for looking up entities based on a fixed attribute `Hash`.
22
+ class FixtureFacet < BaseFacet
23
+ attr_accessor :fixtures
24
+
25
+ def initialize
26
+ @fixtures = {}
27
+ end
28
+
29
+ # Gets the fixtures.
30
+ #
31
+ # @return [Hash] the fixtures.
32
+ def fixtures
33
+ @fixtures
34
+ end
35
+
36
+ def find(key)
37
+ @fixtures[key]
38
+ end
39
+
40
+ def merge(other)
41
+ raise ArgumentError, "Please provide another #{self.class}" if !other.is_a?(FixtureFacet)
42
+
43
+ merged = FixtureFacet.new
44
+ merged.fixtures = @fixtures.merge(other.fixtures)
45
+
46
+ merged
47
+ end
48
+ end
49
+ end
50
+ end
@@ -0,0 +1,56 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright 2014 Roy Liu
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
6
+ # use this file except in compliance with the License. You may obtain a copy of
7
+ # the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ # License for the specific language governing permissions and limitations under
15
+ # the License.
16
+
17
+ require "percolate/facet/base_facet"
18
+
19
+ module Percolate
20
+ module Facet
21
+ # A facet for looking up entities based on hostname.
22
+ class HostnameFacet < BaseFacet
23
+ attr_accessor :hostnames, :domains, :organizations
24
+
25
+ def initialize
26
+ @hostnames = {}
27
+ @domains = {}
28
+ @organizations = {}
29
+ end
30
+
31
+ def find(hostname)
32
+ return @hostnames[hostname] if @hostnames.include?(hostname)
33
+
34
+ comps = hostname.split(".", -1)
35
+ domain = comps[1...3].join(".")
36
+
37
+ return @domains[domain] if @domains.include?(domain)
38
+
39
+ organization = comps[1]
40
+
41
+ @organizations.fetch(organization, organization)
42
+ end
43
+
44
+ def merge(other)
45
+ raise ArgumentError, "Please provide another #{self.class}" if !other.is_a?(HostnameFacet)
46
+
47
+ merged = HostnameFacet.new
48
+ merged.hostnames = @hostnames.merge(other.hostnames)
49
+ merged.domains = @domains.merge(other.domains)
50
+ merged.organizations = @organizations.merge(other.organizations)
51
+
52
+ merged
53
+ end
54
+ end
55
+ end
56
+ end
@@ -0,0 +1,213 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright 2014 Roy Liu
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
6
+ # use this file except in compliance with the License. You may obtain a copy of
7
+ # the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ # License for the specific language governing permissions and limitations under
15
+ # the License.
16
+
17
+ require "set"
18
+
19
+ if Gem::Version.new(RUBY_VERSION.dup) < Gem::Version.new("2.0.0")
20
+ require "backports/2.0.0/array"
21
+ end
22
+
23
+ require "percolate/facet/base_facet"
24
+
25
+ module Percolate
26
+ module Facet
27
+ # A facet for looking up entities based on collections of tags.
28
+ class TagFacet < BaseFacet
29
+ attr_reader :poset_root
30
+
31
+ def initialize(poset_root = nil)
32
+ @poset_root = poset_root
33
+ end
34
+
35
+ # Sets the tag lookup rules.
36
+ #
37
+ # @param rules_hash [Hash] the lookup rules.
38
+ def rules=(rules_hash)
39
+ @poset_root = TagPoset.new
40
+
41
+ rules_hash.each do |rule_hash|
42
+ @poset_root.insert(rule_hash["tags"], rule_hash["value"])
43
+ end
44
+
45
+ rules_hash
46
+ end
47
+
48
+ def find(tags)
49
+ @poset_root.matches(tags).sort
50
+ end
51
+
52
+ def merge(other)
53
+ raise ArgumentError, "Please provide another #{self.class}" if !other.is_a?(TagFacet)
54
+
55
+ TagFacet.new(@poset_root.merge(other.poset_root))
56
+ end
57
+
58
+ # A data structure representing the partial order induced on collections of tags.
59
+ class TagPoset
60
+ attr_reader :tags, :value, :supersets
61
+
62
+ # The constructor.
63
+ #
64
+ # @param tags [Array] the tags.
65
+ # @param value [Object] the associated value.
66
+ # @param supersets [Array] the {TagPoset}s that contain tag supersets.
67
+ def initialize(tags = [], value = nil, supersets = [])
68
+ @tags = tags
69
+ @value = value
70
+ @supersets = supersets
71
+ end
72
+
73
+ # Inserts the given collection of tags and its associated value into the partial order.
74
+ #
75
+ # @param tags [Array] the tags.
76
+ # @param value [Object] the associated value.
77
+ # @param visited [Set] the visited {TagPoset}s so far, memoized by their tags.
78
+ def insert(tags, value, visited = Set.new)
79
+ tags = tags.sort
80
+
81
+ # We got an exact match. Override the value and return.
82
+ if @tags == tags
83
+ @value = value
84
+
85
+ return value
86
+ end
87
+
88
+ n_supersets = 0
89
+ subset_indices = []
90
+
91
+ @supersets.each_with_index do |superset, i|
92
+ if superset.tags - tags == []
93
+ superset.insert(tags, value, visited) if !visited.add?(superset.tags).nil?
94
+ n_supersets = n_supersets + 1
95
+ elsif tags - superset.tags == []
96
+ subset_indices.push(i)
97
+ end
98
+ end
99
+
100
+ # We visited a superset; there's no more work to be done in this frame.
101
+ if n_supersets > 0
102
+ return value
103
+ end
104
+
105
+ if !subset_indices.empty?
106
+ # The tags are a subset of at least one superset; insert them in between this poset and the superset(s).
107
+ tp = TagPoset.new(tags, value, subset_indices.map { |i| @supersets[i] })
108
+
109
+ subset_indices.each { |i| @supersets[i] = nil }
110
+ @supersets = @supersets.select { |item| !item.nil? }.to_a
111
+
112
+ else
113
+ # The tags are not a subset of any superset; insert them separately.
114
+ tp = TagPoset.new(tags, value, transitives(tags))
115
+ end
116
+
117
+ # Insert the new poset with binary search for stability.
118
+ insertion_index = (0...@supersets.size).bsearch { |i| (@supersets[i].tags <=> tags) >= 0 } || @supersets.size
119
+ @supersets.insert(insertion_index, tp)
120
+
121
+ value
122
+ end
123
+
124
+ # Finds transitive supersets that contain the given collection of tags.
125
+ #
126
+ # @param remainder [Array] the remaining tags to look for.
127
+ # @param visited [Set] the visited {TagPoset}s so far, memoized by their tags.
128
+ #
129
+ # @return [Array] the transitive supersets.
130
+ def transitives(remainder, visited = Set.new)
131
+ transitives = []
132
+
133
+ @supersets.each do |superset|
134
+ r_remainder = remainder - superset.tags
135
+
136
+ if r_remainder != []
137
+ r_transitives = superset.transitives(r_remainder, visited)
138
+
139
+ r_transitives = r_transitives.select do |r_superset|
140
+ transitives.select do |superset|
141
+ superset.tags - r_superset.tags == []
142
+ end.empty?
143
+ end.to_a
144
+
145
+ transitives = transitives.select do |superset|
146
+ r_transitives.select do |r_superset|
147
+ r_superset.tags - superset.tags == []
148
+ end.empty?
149
+ end.to_a
150
+
151
+ transitives.concat(r_transitives)
152
+ else
153
+ transitives.push(superset)
154
+ end if !visited.add?(superset.tags).nil?
155
+ end
156
+
157
+ transitives
158
+ end
159
+
160
+ # Calculates the best matches for the given collection of tags.
161
+ #
162
+ # @param tags [Array] the tags.
163
+ # @param visited [Set] the visited {TagPoset}s so far, memoized by their tags.
164
+ #
165
+ # @return [Array] the values associated with best matches.
166
+ def matches(tags, visited = Set.new)
167
+ tags = tags.sort
168
+
169
+ matches = []
170
+ n_supersets = 0
171
+
172
+ @supersets.each do |superset|
173
+ if superset.tags - tags == []
174
+ matches.concat(superset.matches(tags, visited)) if !visited.add?(superset.tags).nil?
175
+ n_supersets = n_supersets + 1
176
+ end
177
+ end
178
+
179
+ # We didn't visit a superset; push on the associated value as a best match.
180
+ if n_supersets == 0 && !@value.nil?
181
+ matches.push(@value)
182
+ end
183
+
184
+ matches
185
+ end
186
+
187
+ # Merges the given {TagPoset} with this one.
188
+ #
189
+ # @param other [TagPoset] the other {TagPoset}.
190
+ #
191
+ # @return [TagPoset] the merged result.
192
+ def merge(other)
193
+ TagPoset.new.merge!(self).merge!(other)
194
+ end
195
+
196
+ # Mutatively merges the given {TagPoset} with this one.
197
+ #
198
+ # @param other [TagPoset] the other {TagPoset}.
199
+ #
200
+ # @return [TagPoset] `self`.
201
+ def merge!(other, visited = Set.new)
202
+ insert(other.tags, other.value)
203
+
204
+ other.supersets.each do |superset|
205
+ merge!(superset, visited) if !visited.add?(superset.tags).nil?
206
+ end
207
+
208
+ self
209
+ end
210
+ end
211
+ end
212
+ end
213
+ end
@@ -0,0 +1,124 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright 2014 Roy Liu
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
6
+ # use this file except in compliance with the License. You may obtain a copy of
7
+ # the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ # License for the specific language governing permissions and limitations under
15
+ # the License.
16
+
17
+ require "active_support/inflector"
18
+
19
+ module Percolate
20
+ # Creates a {Percolator} from the given adapter name and data source.
21
+ #
22
+ # @param adapter_name [Symbol] the adapter name.
23
+ # @param data_source [Object] the data source.
24
+ # @param block [Proc] the configuration block.
25
+ #
26
+ # @return [Percolator] the {Percolator}.
27
+ def self.create(adapter_name, data_source = nil, &block)
28
+ const_str = ActiveSupport::Inflector.camelize(adapter_name) + "Adapter"
29
+
30
+ begin
31
+ require "percolate/adapter/#{adapter_name}_adapter"
32
+ rescue LoadError
33
+ # Do nothing. Give the benefit of the doubt if the file doesn't exist.
34
+ end if !Adapter.const_defined?(const_str)
35
+
36
+ adapter = Adapter.const_get(const_str).new(data_source)
37
+ percolator = Percolator.new(adapter)
38
+ percolator.load(&block)
39
+ percolator
40
+ end
41
+
42
+ # The class that percolates information from entities through facets to the user.
43
+ class Percolator
44
+ attr_reader :adapter, :entities
45
+
46
+ # The constructor.
47
+ #
48
+ # @param adapter [Object] the adapter to a data source.
49
+ def initialize(adapter)
50
+ @adapter = adapter
51
+ @facet_cache = {}
52
+ end
53
+
54
+ # Configures and loads the underlying adapter's entities.
55
+ #
56
+ # @param block [Proc] the configuration block.
57
+ def load(&block)
58
+ @adapter.instance_eval(&block) if !block.nil?
59
+ @entities = @adapter.load_entities
60
+
61
+ nil
62
+ end
63
+
64
+ # Finds an entity or entities.
65
+ #
66
+ # @param context [String] the lookup context.
67
+ # @param facet_name [Symbol] the facet name.
68
+ # @param args [Array] the argument splat passed to the facet.
69
+ #
70
+ # @return [Object] the retrieved entity or entities.
71
+ def find(context, facet_name, *args)
72
+ facet = find_facet(context, facet_name)
73
+
74
+ if !facet
75
+ return nil
76
+ end
77
+
78
+ case result = facet.find(*args)
79
+ when Array
80
+ result.map { |item| @entities[item] }
81
+ when String
82
+ @entities[result]
83
+ when NilClass
84
+ nil
85
+ else
86
+ raise "Bad facet return type #{result.class.name.dump}"
87
+ end
88
+ end
89
+
90
+ # Finds a facet.
91
+ #
92
+ # @param context [String] the lookup context.
93
+ # @param facet_name [Symbol] the facet name.
94
+ #
95
+ # @return [Object] the facet.
96
+ def find_facet(context, facet_name)
97
+ cache_key = [context, facet_name]
98
+
99
+ if @facet_cache.include?(cache_key)
100
+ facet = @facet_cache[cache_key]
101
+ else
102
+ begin
103
+ require "percolate/facet/#{facet_name}_facet"
104
+ rescue LoadError
105
+ # Do nothing. Give the benefit of the doubt if the file doesn't exist.
106
+ end
107
+
108
+ if facet = @adapter.load_facet(context, facet_name)
109
+ @facet_cache[cache_key] = facet
110
+ end
111
+ end
112
+
113
+ facet
114
+ end
115
+ end
116
+
117
+ # The namespace for adapters.
118
+ module Adapter
119
+ end
120
+
121
+ # The namespace for facets.
122
+ module Facet
123
+ end
124
+ end
@@ -0,0 +1,48 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright 2014 Roy Liu
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
6
+ # use this file except in compliance with the License. You may obtain a copy of
7
+ # the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ # License for the specific language governing permissions and limitations under
15
+ # the License.
16
+
17
+ require "active_support/inflector"
18
+
19
+ module Percolate
20
+ # Contains utility methods.
21
+ module Util
22
+ # Merges the given attributes, which can take the form of nested `Hash`es, `Array`s, and `String`s. If there is a
23
+ # conflict, the right hand side wins.
24
+ def self.merge_attributes(lhs, rhs)
25
+ if lhs.is_a?(Hash) && rhs.is_a?(Hash)
26
+ res = {}
27
+
28
+ lhs.each_pair do |key, value|
29
+ if rhs.include?(key)
30
+ res[key] = merge_attributes(lhs[key], rhs[key])
31
+ else
32
+ res[key] = value
33
+ end
34
+ end
35
+
36
+ rhs.each_pair do |key, value|
37
+ res[key] = value if !res.include?(key)
38
+ end
39
+ elsif lhs.is_a?(Array) && rhs.is_a?(Array)
40
+ res = lhs + rhs
41
+ else
42
+ res = rhs
43
+ end
44
+
45
+ res
46
+ end
47
+ end
48
+ end
@@ -0,0 +1,34 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright 2014 Roy Liu
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
6
+ # use this file except in compliance with the License. You may obtain a copy of
7
+ # the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ # License for the specific language governing permissions and limitations under
15
+ # the License.
16
+
17
+ module Percolate
18
+ # A module containing the gem version information.
19
+ module Version
20
+ # The major version.
21
+ MAJOR = 0
22
+
23
+ # The minor version.
24
+ MINOR = 9
25
+
26
+ # The patch version.
27
+ PATCH = 4
28
+
29
+ # Gets the String representation of the gem version.
30
+ def self.to_s
31
+ "#{MAJOR}.#{MINOR}.#{PATCH}"
32
+ end
33
+ end
34
+ end
@@ -0,0 +1,28 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright 2014 Roy Liu
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
6
+ # use this file except in compliance with the License. You may obtain a copy of
7
+ # the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ # License for the specific language governing permissions and limitations under
15
+ # the License.
16
+
17
+ require "percolate/adapter/base_adapter"
18
+
19
+ require "spec_helper"
20
+
21
+ describe Percolate do
22
+ it "finds adapters by the naming convention" do
23
+ percolator = Percolate.create(:base)
24
+
25
+ expect(percolator).to be_an_instance_of Percolate::Percolator
26
+ expect(percolator.adapter).to be_an_instance_of Percolate::Adapter::BaseAdapter
27
+ end
28
+ end
@@ -0,0 +1,20 @@
1
+ # -*- coding: utf-8 -*-
2
+ #
3
+ # Copyright 2014 Roy Liu
4
+ #
5
+ # Licensed under the Apache License, Version 2.0 (the "License"); you may not
6
+ # use this file except in compliance with the License. You may obtain a copy of
7
+ # the License at
8
+ #
9
+ # http://www.apache.org/licenses/LICENSE-2.0
10
+ #
11
+ # Unless required by applicable law or agreed to in writing, software
12
+ # distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
13
+ # WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
14
+ # License for the specific language governing permissions and limitations under
15
+ # the License.
16
+
17
+ require "chefspec"
18
+ require "chefspec/librarian"
19
+
20
+ require "percolate"
metadata ADDED
@@ -0,0 +1,76 @@
1
+ --- !ruby/object:Gem::Specification
2
+ name: percolate
3
+ version: !ruby/object:Gem::Version
4
+ version: 0.9.4
5
+ platform: ruby
6
+ authors:
7
+ - Roy Liu
8
+ autorequire:
9
+ bindir: bin
10
+ cert_chain: []
11
+ date: 2019-03-29 00:00:00.000000000 Z
12
+ dependencies:
13
+ - !ruby/object:Gem::Dependency
14
+ name: activesupport
15
+ requirement: !ruby/object:Gem::Requirement
16
+ requirements:
17
+ - - ">="
18
+ - !ruby/object:Gem::Version
19
+ version: 4.0.2
20
+ type: :runtime
21
+ prerelease: false
22
+ version_requirements: !ruby/object:Gem::Requirement
23
+ requirements:
24
+ - - ">="
25
+ - !ruby/object:Gem::Version
26
+ version: 4.0.2
27
+ description: Percolate is a library for organizing and distributing configuration
28
+ settings. It contains adapters for frameworks like Chef, with which the user can
29
+ take full advantage of a declarative syntax for Chef data bags and avoid the antipattern
30
+ of representing initialization state with node attributes.
31
+ email:
32
+ - carsomyr@gmail.com
33
+ executables: []
34
+ extensions: []
35
+ extra_rdoc_files: []
36
+ files:
37
+ - lib/percolate.rb
38
+ - lib/percolate/adapter/base_adapter.rb
39
+ - lib/percolate/adapter/chef_data_bag_adapter.rb
40
+ - lib/percolate/adapter/fixture_adapter.rb
41
+ - lib/percolate/facet/base_facet.rb
42
+ - lib/percolate/facet/fixture_facet.rb
43
+ - lib/percolate/facet/hostname_facet.rb
44
+ - lib/percolate/facet/tag_facet.rb
45
+ - lib/percolate/percolator.rb
46
+ - lib/percolate/util.rb
47
+ - lib/percolate/version.rb
48
+ - spec/percolate_spec.rb
49
+ - spec/spec_helper.rb
50
+ homepage: https://github.com/carsomyr/percolate
51
+ licenses:
52
+ - Apache-2.0
53
+ metadata: {}
54
+ post_install_message:
55
+ rdoc_options: []
56
+ require_paths:
57
+ - lib
58
+ required_ruby_version: !ruby/object:Gem::Requirement
59
+ requirements:
60
+ - - ">="
61
+ - !ruby/object:Gem::Version
62
+ version: '0'
63
+ required_rubygems_version: !ruby/object:Gem::Requirement
64
+ requirements:
65
+ - - ">="
66
+ - !ruby/object:Gem::Version
67
+ version: '0'
68
+ requirements: []
69
+ rubyforge_project:
70
+ rubygems_version: 2.7.3
71
+ signing_key:
72
+ specification_version: 4
73
+ summary: Percolate is a library for organizing and distributing configuration settings
74
+ test_files:
75
+ - spec/spec_helper.rb
76
+ - spec/percolate_spec.rb