locabulary 0.3.1 → 0.5.0

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 399c1a9f7cf3a7365116f94bd18cefa4c5ccd240
4
- data.tar.gz: 9399cbc0fcd7b6a8d8b1839b79225e3061bd5fb5
3
+ metadata.gz: 4c1e85110e906091033981d644e5a147c001eab2
4
+ data.tar.gz: 1cf448e30e0d19469afafee922ac3f3a5d01f025
5
5
  SHA512:
6
- metadata.gz: 74c9bb11679cea1e9c2831a995607426db1494028a5b147171e24233b11fe1378221769bb210c3f8f1e6331960502cdbf24b61f6167f3b3836da31baeffe312b
7
- data.tar.gz: e1a7b89c4e1583ecbb29e2b0137fa3422d9b33ec4e2ec3afd6ac2f228f48a11afecc5ffcb4daa32d334196e73ca213b44957c79f02aa9402eac2505840823b3c
6
+ metadata.gz: 105285fc4f39faa1f49d78a5245a67fbb975c31656f62f430e831d256a0f6adb1810936695a068c015ce16674e5ffa503d8f3745624a97265515e94453201772
7
+ data.tar.gz: d0337a83c1248bb28b87494350e59770ddf9834480b4284a39bbb57289d9af24c3389fb831407a258b7d3709b72ce7e9e21ee6d633f0a2a2309c91ec49b306d3
@@ -0,0 +1,4 @@
1
+ speedups:
2
+ each_with_index_vs_while: false
3
+ exclude_paths:
4
+ - 'vendor/**/*.rb'
data/.hound.yml CHANGED
@@ -21,6 +21,7 @@ AllCops:
21
21
  - 'tmp/**/*'
22
22
  - 'bin/**/*'
23
23
  - 'lib/milan/registry.rb'
24
+ - 'script/*'
24
25
  TargetRubyVersion: 2.2
25
26
  Rails:
26
27
  Enabled: false
File without changes
data/Gemfile CHANGED
@@ -2,6 +2,3 @@ source 'https://rubygems.org'
2
2
 
3
3
  # Specify your gem's dependencies in vocabulary.gemspec
4
4
  gemspec
5
-
6
- gem 'dry-validation', github: 'dry-rb/dry-validation'
7
- gem 'dry-types', github: 'dry-rb/dry-types'
data/README.md CHANGED
@@ -10,3 +10,7 @@
10
10
  An extraction of limited localized vocabulary for Sipity and CurateND.
11
11
  This controlled vocabulary has a limited shelf-life as we explore other more
12
12
  robust options.
13
+
14
+ ## Testing
15
+
16
+ The full test suite is run via `bundle exec rake`.
data/Rakefile CHANGED
@@ -21,6 +21,16 @@ namespace :commitment do
21
21
  task :configure_test_for_code_coverage do
22
22
  ENV['COVERAGE'] = 'true'
23
23
  end
24
+ desc "Check for code that can go faster"
25
+ task :fasterer do
26
+ require 'fasterer/file_traverser'
27
+ file_traverser = Fasterer::FileTraverser.new(nil)
28
+ file_traverser.traverse
29
+ if file_traverser.offenses_found?
30
+ $stderr.puts "You can make the code go faster, see above. You can add exceptions in .fasterer.yml"
31
+ abort
32
+ end
33
+ end
24
34
  task :code_coverage do
25
35
  require 'json'
26
36
  $stdout.puts "Checking code_coverage"
@@ -38,5 +48,5 @@ namespace :commitment do
38
48
  end
39
49
  end
40
50
 
41
- task(default: [:rubocop, 'commitment:configure_test_for_code_coverage', :spec, 'commitment:code_coverage'])
51
+ task(default: [:rubocop, 'commitment:fasterer', 'commitment:configure_test_for_code_coverage', :spec, 'commitment:code_coverage'])
42
52
  task(release: :default)
@@ -6,12 +6,14 @@
6
6
  "term_label": "University of Notre Dame",
7
7
  "classification": "University",
8
8
  "homepage": "http://www.nd.edu/",
9
- "activated_on": "2015-07-22"
9
+ "activated_on": "2015-07-22",
10
+ "default_presentation_sequence": 1
10
11
  },
11
12
  {
12
13
  "predicate_name": "administrative_units",
13
14
  "term_label": "University of Notre Dame::School of Architecture",
14
15
  "classification": "College",
16
+ "default_presentation_sequence": 1,
15
17
  "homepage": "http://architecture.nd.edu/",
16
18
  "activated_on": "2015-07-22"
17
19
  },
@@ -20,6 +22,7 @@
20
22
  "term_label": "University of Notre Dame::College of Arts and Letters",
21
23
  "grouping": "The Humanities",
22
24
  "classification": "College",
25
+ "default_presentation_sequence": 2,
23
26
  "homepage": "http://al.nd.edu/",
24
27
  "activated_on": "2015-07-22"
25
28
  },
@@ -196,6 +199,7 @@
196
199
  "predicate_name": "administrative_units",
197
200
  "term_label": "University of Notre Dame::Mendoza College of Business",
198
201
  "classification": "College",
202
+ "default_presentation_sequence": 3,
199
203
  "homepage": "http://mendoza.nd.edu/",
200
204
  "activated_on": "2015-07-22"
201
205
  },
@@ -238,6 +242,7 @@
238
242
  "predicate_name": "administrative_units",
239
243
  "term_label": "University of Notre Dame::College of Engineering",
240
244
  "classification": "College",
245
+ "default_presentation_sequence": 4,
241
246
  "homepage": "http://engineering.nd.edu/",
242
247
  "activated_on": "2015-07-22"
243
248
  },
@@ -287,6 +292,7 @@
287
292
  "predicate_name": "administrative_units",
288
293
  "term_label": "University of Notre Dame::College of Science",
289
294
  "classification": "College",
295
+ "default_presentation_sequence": 5,
290
296
  "homepage": "http://science.nd.edu/",
291
297
  "activated_on": "2015-07-22"
292
298
  },
@@ -343,6 +349,7 @@
343
349
  "predicate_name": "administrative_units",
344
350
  "term_label": "University of Notre Dame::First Year of Studies",
345
351
  "classification": "College",
352
+ "default_presentation_sequence": 6,
346
353
  "homepage": "http://firstyear.nd.edu/",
347
354
  "activated_on": "2015-07-22"
348
355
  },
@@ -350,6 +357,7 @@
350
357
  "predicate_name": "administrative_units",
351
358
  "term_label": "University of Notre Dame::Graduate School",
352
359
  "classification": "College",
360
+ "default_presentation_sequence": 7,
353
361
  "homepage": "http://graduateschool.nd.edu/",
354
362
  "activated_on": "2015-07-22"
355
363
  },
@@ -357,6 +365,7 @@
357
365
  "predicate_name": "administrative_units",
358
366
  "term_label": "University of Notre Dame::Law School",
359
367
  "classification": "College",
368
+ "default_presentation_sequence": 8,
360
369
  "homepage": "http://law.nd.edu/",
361
370
  "activated_on": "2015-07-22"
362
371
  },
@@ -364,6 +373,7 @@
364
373
  "predicate_name": "administrative_units",
365
374
  "term_label": "University of Notre Dame::Reserve Officers Training Corps",
366
375
  "classification": "College",
376
+ "default_presentation_sequence": 9,
367
377
  "activated_on": "2015-07-22"
368
378
  },
369
379
  {
@@ -398,12 +408,13 @@
398
408
  "predicate_name": "administrative_units",
399
409
  "term_label": "University of Notre Dame::Hesburgh Libraries",
400
410
  "classification": "College",
411
+ "default_presentation_sequence": 10,
401
412
  "homepage": "http://library.nd.edu/",
402
413
  "activated_on": "2015-07-22"
403
414
  },
404
415
  {
405
416
  "predicate_name": "administrative_units",
406
- "term_label": "University of Notre Dame::Hesburgh Libraries::General Collection",
417
+ "term_label": "University of Notre Dame::Hesburgh Libraries::General",
407
418
  "classification": "Department",
408
419
  "activated_on": "2015-07-22"
409
420
  },
@@ -425,6 +436,7 @@
425
436
  "predicate_name": "administrative_units",
426
437
  "term_label": "University of Notre Dame::Centers and Institutes",
427
438
  "classification": "CenterOrInstitute",
439
+ "default_presentation_sequence": 11,
428
440
  "activated_on": "2015-07-22"
429
441
  },
430
442
  {
@@ -876,6 +888,19 @@
876
888
  "affiliation": "University of Notre Dame::College of Engineering",
877
889
  "homepage": "http://wireless.nd.edu/",
878
890
  "activated_on": "2015-07-22"
891
+ },
892
+ {
893
+ "predicate_name": "administrative_units",
894
+ "term_label": "Catholic Organizations",
895
+ "classification": "OrganizationalGroup",
896
+ "activated_on": "2015-07-22"
897
+ },
898
+ {
899
+ "predicate_name": "administrative_units",
900
+ "term_label": "Catholic Organizations::Catholic Research Resources Alliance",
901
+ "classification": "Organization",
902
+ "homepage": "http://www.catholicresearch.net/",
903
+ "activated_on": "2015-07-22"
879
904
  }
880
905
  ]
881
906
  }
@@ -2,27 +2,27 @@
2
2
  "predicate_name": "college",
3
3
  "values": [{
4
4
  "term_label": "School of Architecture",
5
- "default_presentation_sequence": 60,
5
+ "default_presentation_sequence": 10,
6
+ "activated_on": "2015-07-22"
7
+ },{
8
+ "term_label": "College of Arts and Letters",
9
+ "default_presentation_sequence": 20,
6
10
  "activated_on": "2015-07-22"
7
11
  },{
8
12
  "term_label": "Mendoza College of Business",
9
- "default_presentation_sequence": 50,
13
+ "default_presentation_sequence": 30,
10
14
  "activated_on": "2015-07-22"
11
15
  },{
12
16
  "term_label": "College of Engineering",
13
- "default_presentation_sequence": 20,
17
+ "default_presentation_sequence": 40,
14
18
  "activated_on": "2015-07-22"
15
19
  },{
16
20
  "term_label": "College of Science",
17
- "default_presentation_sequence": 30,
18
- "activated_on": "2015-07-22"
19
- },{
20
- "term_label": "College of Arts and Letters",
21
- "default_presentation_sequence": 10,
21
+ "default_presentation_sequence": 50,
22
22
  "activated_on": "2015-07-22"
23
23
  },{
24
- "term_label": "First Year Studies",
25
- "default_presentation_sequence": 40,
24
+ "term_label": "First Year of Studies",
25
+ "default_presentation_sequence": 60,
26
26
  "activated_on": "2015-12-14"
27
27
  }]
28
28
  }
@@ -0,0 +1,24 @@
1
+ {
2
+ "predicate_name": "spec",
3
+ "values": [
4
+ {
5
+ "predicate_name": "spec",
6
+ "term_label": "Active Item",
7
+ "default_presentation_sequence": 3,
8
+ "activated_on": "2015-07-22"
9
+ },
10
+ {
11
+ "predicate_name": "spec",
12
+ "term_label": "Deactive Item",
13
+ "default_presentation_sequence": 1,
14
+ "activated_on": "2015-07-22",
15
+ "deactivated_on": "2016-07-01"
16
+ },
17
+ {
18
+ "predicate_name": "spec",
19
+ "term_label": "Alternate Item",
20
+ "default_presentation_sequence": 2,
21
+ "activated_on": "2015-07-22"
22
+ }
23
+ ]
24
+ }
@@ -13,7 +13,7 @@
13
13
  "default_presentation_sequence": 3,
14
14
  "activated_on": "2015-01-01"
15
15
  },{
16
- "term_label": "Junior",
16
+ "term_label": "Senior",
17
17
  "default_presentation_sequence": 4,
18
18
  "activated_on": "2015-01-01"
19
19
  },{
@@ -1,125 +1,117 @@
1
1
  require 'date'
2
2
  require 'json'
3
3
  require 'locabulary/exceptions'
4
- require 'locabulary/items'
4
+ require 'locabulary/item'
5
+ require 'locabulary/commands/build_ordered_hierarchical_tree_command'
6
+ require 'locabulary/commands/active_items_for_command'
7
+ require 'locabulary/commands/active_hierarchical_roots_command'
5
8
 
6
9
  # @since 0.1.0
7
10
  module Locabulary
8
- DATA_DIRECTORY = File.expand_path("../../data/", __FILE__).freeze
9
-
10
- module_function
11
+ # @api private
12
+ # @since 0.5.0
13
+ #
14
+ # Responsible for building a hierarchical tree from faceted items, and ordering the nodes as per the presentation sequence for the
15
+ # associated predicate_name.
16
+ #
17
+ # @param [Hash] options
18
+ # @option options [String] :predicate_name
19
+ # @option options [Array<#hits, #value>] :faceted_items
20
+ # @option options [String] :faceted_item_hierarchy_delimiter
21
+ # @return Array[<FacetWrapperForItem>]
22
+ #
23
+ # @see Locabulary::Commands::BuildOrderedHierarchicalTree
24
+ def self.build_ordered_hierarchical_tree(options = {})
25
+ Commands::BuildOrderedHierarchicalTreeCommand.call(options)
26
+ end
11
27
 
12
28
  # @api public
13
29
  # @since 0.1.0
14
30
  #
15
- # @note A concession about the as_of; This is not a live query. The data has a
16
- # low churn rate. And while the date is important, I'm not as concerned
17
- # about the local controlled vocabulary exposing a date that has expired.
18
- # When we next deploy the server changes, the deactivated will go away.
19
- def active_items_for(options = {})
20
- predicate_name = options.fetch(:predicate_name)
21
- as_of = options.fetch(:as_of) { Date.today }
22
- builder = Items.builder_for(predicate_name: predicate_name)
23
- active_cache[predicate_name] ||= begin
24
- collector = []
25
- with_active_extraction_for(predicate_name, as_of) do |data|
26
- collector << builder.call(data.merge('predicate_name' => predicate_name))
27
- end
28
- collector.sort
29
- end
31
+ # Responsible for extracting a non-hierarchical sorted array of Locabulary::Items::Base objects for the given predicate_name.
32
+ #
33
+ # @param [Hash] options
34
+ # @option options [String] :predicate_name
35
+ # @option options [Date] :as_of (Date.today)
36
+ # @return Array[<Locabulary::Items::Base>]
37
+ #
38
+ # @see Locabulary::Commands::ActiveItemsForCommand
39
+ def self.active_items_for(options = {})
40
+ Commands::ActiveItemsForCommand.call(options)
30
41
  end
31
42
 
32
43
  # @api public
33
- # @since 0.2.0
34
- def active_hierarchical_root(options = {})
35
- predicate_name = options.fetch(:predicate_name)
36
- as_of = options.fetch(:as_of) { Date.today }
37
- builder = Items.builder_for(predicate_name: predicate_name)
38
- active_hierarchical_root_cache[predicate_name] ||= begin
39
- items = []
40
- hierarchy_graph_keys = {}
41
- top_level_slugs = Set.new
42
- with_active_extraction_for(predicate_name, as_of) do |data|
43
- item = builder.call(data.merge('predicate_name' => predicate_name))
44
- items << item
45
- top_level_slugs << item.root_slug
46
- hierarchy_graph_keys[item.term_label] = item
47
- end
48
- associate_parents_and_childrens_for(hierarchy_graph_keys, items, predicate_name)
49
- raise Exceptions::TooManyHierarchicalRootsError.new(predicate_name, top_level_slugs.to_a) if top_level_slugs.size > 1
50
- hierarchy_graph_keys.fetch(top_level_slugs.first)
51
- end
52
- end
53
-
54
- def associate_parents_and_childrens_for(hierarchy_graph_keys, items, predicate_name)
55
- items.each do |item|
56
- begin
57
- hierarchy_graph_keys.fetch(item.parent_term_label).add_child(item) unless item.parent_slugs.empty?
58
- rescue KeyError => error
59
- raise Exceptions::MissingHierarchicalParentError.new(predicate_name, error)
60
- end
61
- end
44
+ # @since 0.4.0
45
+ #
46
+ # Responsible for transforming the flat data for the given :predicate_name
47
+ # into a hierarchy.
48
+ #
49
+ # @param [Hash] options
50
+ # @option options [String] :predicate_name
51
+ # @option options [Date] :as_of (Date.today)
52
+ # @return [Array<Locabulary::Items::Base>] - the root nodes
53
+ def self.active_hierarchical_roots(options = {})
54
+ Commands::ActiveHierarchicalRootsCommand.call(options)
62
55
  end
63
- private :associate_parents_and_childrens_for
64
56
 
65
- def with_active_extraction_for(predicate_name, as_of)
66
- filename = filename_for_predicate_name(predicate_name: predicate_name)
67
- json = JSON.parse(File.read(filename))
68
- json.fetch('values').each do |data|
69
- yield(data) if data_is_active?(data, as_of)
57
+ # @api public
58
+ # @since 0.5.0
59
+ # @param options [Hash]
60
+ # @option options [String] :predicate_name
61
+ # @option options [String] :term_label
62
+ # @option options [Date] :as_of (Date.today)
63
+ # @raise [Locabulary::Exceptions::ItemNotFoundError] if unable to find label for predicate_name
64
+ # @return [Locabulary::Items::Base]
65
+ def self.item_for(options = {})
66
+ predicate_name = options.fetch(:predicate_name)
67
+ term_label = options.fetch(:term_label)
68
+ as_of = options.fetch(:as_of) { Date.today }
69
+ item = nil
70
+ Utility.with_extraction_for(predicate_name) do |data|
71
+ next unless data.fetch('term_label') == term_label
72
+ item = Item.build(data.merge('predicate_name' => predicate_name))
73
+ break if Utility.data_is_active?(data, as_of)
70
74
  end
75
+ return item unless item.nil?
76
+ raise Locabulary::Exceptions::ItemNotFoundError.new(predicate_name, term_label)
71
77
  end
72
- private :with_active_extraction_for
73
-
74
- def data_is_active?(data, as_of)
75
- activated_on = Date.parse(data.fetch('activated_on'))
76
- return false unless activated_on < as_of
77
- deactivated_on_value = data.fetch('deactivated_on', nil)
78
- return true if deactivated_on_value.nil?
79
- deactivated_on = Date.parse(deactivated_on_value)
80
- return false unless deactivated_on >= as_of
81
- true
82
- end
83
- private :data_is_active?
84
78
 
85
79
  # @api public
86
80
  # @since 0.1.0
87
- def active_label_for_uri(options = {})
88
- predicate_name = options.fetch(:predicate_name)
81
+ #
82
+ # @param [Hash] options
83
+ # @option options [String] :predicate_name
84
+ # @option options [String] :term_uri
85
+ # @option options [String] :as_of (Date.today)
86
+ #
87
+ # @return [String] a label or URI
88
+ #
89
+ # @see Locabulary.active_items_for
90
+ def self.active_label_for_uri(options = {})
89
91
  term_uri = options.fetch(:term_uri)
90
- object = active_items_for(predicate_name: predicate_name).detect { |obj| obj.term_uri == term_uri }
92
+ object = active_items_for(options).detect { |obj| obj.term_uri == term_uri }
91
93
  return object.term_label if object
92
94
  term_uri
93
95
  end
94
96
 
95
97
  # @api public
96
98
  # @since 0.1.0
97
- def active_labels_for(options = {})
98
- predicate_name = options.fetch(:predicate_name)
99
- active_items_for(predicate_name: predicate_name).map(&:term_label)
100
- end
101
-
102
- # @api private
103
- def filename_for_predicate_name(options = {})
104
- predicate_name = options.fetch(:predicate_name)
105
- filename = File.join(DATA_DIRECTORY, "#{File.basename(predicate_name)}.json")
106
- return filename if File.exist?(filename)
107
- raise Locabulary::Exceptions::RuntimeError, "Unable to find predicate_name: #{predicate_name}"
108
- end
109
-
110
- # @api private
111
- def active_cache
112
- @active_cache ||= {}
113
- end
114
-
115
- # @api private
116
- def active_hierarchical_root_cache
117
- @active_hierarchical_root_cache ||= {}
99
+ #
100
+ # Return an Array of term labels for the given :predicate_name
101
+ #
102
+ # @param [Hash] options
103
+ # @option options [String] :predicate_name
104
+ # @option options [String] :as_of (Date.today)
105
+ # @return [Array<String>] an array of Locabuarly::Items::Base#term_label
106
+ #
107
+ # @see Locabulary.active_items_for
108
+ def self.active_labels_for(options = {})
109
+ active_items_for(options).map(&:term_label)
118
110
  end
119
111
 
120
112
  # @api private
121
- def reset_active_cache!
122
- @active_cache = nil
123
- @active_hierarchical_root_cache = nil
113
+ def self.reset_active_cache!
114
+ Commands::ActiveItemsForCommand.reset_cache!
115
+ Commands::ActiveHierarchicalRootsCommand.reset_cache!
124
116
  end
125
117
  end