percolate 0.9.4

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