merriam_webster_plus 0.1.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.
Files changed (49) hide show
  1. checksums.yaml +7 -0
  2. data/.DS_Store +0 -0
  3. data/.byebug_history +256 -0
  4. data/.github/workflows/main.yml +21 -0
  5. data/.gitignore +9 -0
  6. data/.rubocop.yml +10 -0
  7. data/Gemfile +6 -0
  8. data/Gemfile.lock +68 -0
  9. data/LICENSE.txt +21 -0
  10. data/README.md +93 -0
  11. data/Rakefile +18 -0
  12. data/bin/console +15 -0
  13. data/bin/mwp +4 -0
  14. data/bin/setup +8 -0
  15. data/fixtures/.DS_Store +0 -0
  16. data/fixtures/vcr_cassettes/.DS_Store +0 -0
  17. data/fixtures/vcr_cassettes/empty_api_entries.yml +37 -0
  18. data/fixtures/vcr_cassettes/heteronym_entries.yml +277 -0
  19. data/fixtures/vcr_cassettes/invalid_api_entries.yml +41 -0
  20. data/fixtures/vcr_cassettes/invalid_dictionary_plus_lookup.yml +79 -0
  21. data/fixtures/vcr_cassettes/invalid_endpoint_api_entries.yml +132 -0
  22. data/fixtures/vcr_cassettes/main_entries_dictionary.yml +210 -0
  23. data/fixtures/vcr_cassettes/main_entries_thesaurus.yml +70 -0
  24. data/fixtures/vcr_cassettes/main_entry_dictionary.yml +210 -0
  25. data/fixtures/vcr_cassettes/main_entry_thesaurus.yml +70 -0
  26. data/fixtures/vcr_cassettes/non_heteronym_entries.yml +157 -0
  27. data/fixtures/vcr_cassettes/valid_api_entries.yml +210 -0
  28. data/fixtures/vcr_cassettes/valid_dictionary_plus_lookup.yml +277 -0
  29. data/fixtures/vcr_cassettes/word_medley_primary_entry_population.yml +1384 -0
  30. data/fixtures/vcr_cassettes/word_not_in_dictionary_primary_entry.yml +141 -0
  31. data/fixtures/vcr_cassettes/word_not_in_thesaurus_primary_entry.yml +144 -0
  32. data/lib/.DS_Store +0 -0
  33. data/lib/merriam_webster_plus/.DS_Store +0 -0
  34. data/lib/merriam_webster_plus/client.rb +88 -0
  35. data/lib/merriam_webster_plus/dictionary_entries.rb +41 -0
  36. data/lib/merriam_webster_plus/dictionary_entry.rb +124 -0
  37. data/lib/merriam_webster_plus/dictionary_plus.rb +46 -0
  38. data/lib/merriam_webster_plus/dictionary_sense.rb +57 -0
  39. data/lib/merriam_webster_plus/entry_audio_merger.rb +55 -0
  40. data/lib/merriam_webster_plus/populator.rb +31 -0
  41. data/lib/merriam_webster_plus/primary_entry_finder.rb +41 -0
  42. data/lib/merriam_webster_plus/thesaurus_entries.rb +13 -0
  43. data/lib/merriam_webster_plus/thesaurus_entry.rb +12 -0
  44. data/lib/merriam_webster_plus/thesaurus_sense.rb +47 -0
  45. data/lib/merriam_webster_plus/version.rb +5 -0
  46. data/lib/merriam_webster_plus.rb +9 -0
  47. data/merriam_webster_plus.gemspec +35 -0
  48. data/test_out.csv +11 -0
  49. metadata +219 -0
@@ -0,0 +1,141 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://www.dictionaryapi.com/api/v3/references/thesaurus/json/pardoned?key=<COLLEGIATE_THESAURUS_API_KEY>
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept-Encoding:
11
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
12
+ Accept:
13
+ - "*/*"
14
+ User-Agent:
15
+ - Ruby
16
+ response:
17
+ status:
18
+ code: 200
19
+ message: OK
20
+ headers:
21
+ Date:
22
+ - Tue, 21 Jun 2022 23:24:58 GMT
23
+ Content-Type:
24
+ - application/json; charset=utf-8
25
+ Content-Length:
26
+ - '1270'
27
+ Connection:
28
+ - keep-alive
29
+ Server:
30
+ - Apache
31
+ Cache-Control:
32
+ - max-age=86400, public
33
+ Pragma:
34
+ - cache
35
+ Vary:
36
+ - Accept-Encoding
37
+ Access-Control-Allow-Origin:
38
+ - "*"
39
+ body:
40
+ encoding: ASCII-8BIT
41
+ string: '[{"meta":{"id":"pardoned","uuid":"2ac7ae9c-5b1c-4f5a-9ff5-89ed65cd08ce","src":"coll_thes","section":"alpha","stems":["pardoned"],"syns":[["forgave"],["blinked
42
+ (at)","brushed (aside or off)","condoned","discounted","disregarded","excused","forgave","glossed
43
+ (over)","glozed (over)","ignored","overlooked","overpassed","papered over","passed
44
+ over","remitted","shrugged off","whitewashed","winked (at)"]],"ants":[["resented"]],"offensive":false},"hwi":{"hw":"pardoned"},"fl":"verb","sls":["past
45
+ tense of {d_link|pardon|pardon}"],"def":[{"sseq":[[["sense",{"sn":"1","dt":[["text","to
46
+ cease to have feelings of anger or bitterness toward "],["vis",[{"t":"he eventually
47
+ {it}pardoned{\/it} his sister after she apologized"}]]],"syn_list":[[{"wd":"forgave"}]],"rel_list":[[{"wd":"absolved"},{"wd":"acquitted"},{"wd":"cleared"},{"wd":"exculpated"},{"wd":"exonerated"},{"wd":"vindicated"}],[{"wd":"remitted"},{"wd":"shrived","wvrs":[{"wvl":"or","wva":"shrove"}]}],[{"wd":"condoned"},{"wd":"disregarded"},{"wd":"excused"},{"wd":"ignored"},{"wd":"passed
48
+ over"},{"wd":"shrugged off"}],[{"wd":"discharged"},{"wd":"liberated"},{"wd":"redeemed"},{"wd":"released"},{"wd":"unburdened"}]],"near_list":[[{"wd":"abhorred"},{"wd":"abominated"},{"wd":"despised"},{"wd":"detested"},{"wd":"disliked"},{"wd":"execrated"},{"wd":"hated"},{"wd":"loathed"}],[{"wd":"avenged"},{"wd":"redressed"},{"wd":"requited"},{"wd":"retaliated"},{"wd":"revenged"}],[{"wd":"disciplined"},{"wd":"penalized"},{"wd":"punished"}]],"ant_list":[[{"wd":"resented"}]]}]],[["sense",{"sn":"2","dt":[["text","to
49
+ dismiss as of little importance "],["vis",[{"t":"I\u0027m willing to {it}pardon{\/it}
50
+ a little sloppiness of dress in such a kind and loving person"}]]],"syn_list":[[{"wd":"blinked
51
+ (at)"},{"wd":"brushed (aside or off)"},{"wd":"condoned"},{"wd":"discounted"},{"wd":"disregarded"},{"wd":"excused"},{"wd":"forgave"},{"wd":"glossed
52
+ (over)"},{"wd":"glozed (over)"},{"wd":"ignored"},{"wd":"overlooked"},{"wd":"overpassed"},{"wd":"papered
53
+ over"},{"wd":"passed over"},{"wd":"remitted"},{"wd":"shrugged off"},{"wd":"whitewashed"},{"wd":"winked
54
+ (at)"}]],"rel_list":[[{"wd":"explained"},{"wd":"justified"},{"wd":"rationalized"}],[{"wd":"absolved"},{"wd":"acquitted"},{"wd":"cleared"},{"wd":"exculpated"},{"wd":"exonerated"},{"wd":"vindicated"}],[{"wd":"waived"},{"wd":"waved
55
+ (aside or off)"}]],"phrase_list":[[{"wd":"closed one\u0027s eyes to"},{"wd":"forgave
56
+ and forgot"}]],"near_list":[[{"wd":"heeded"},{"wd":"marked"},{"wd":"minded"},{"wd":"noted"},{"wd":"objected
57
+ (to)"}]]}]]]}],"shortdef":["to cease to have feelings of anger or bitterness
58
+ toward","to dismiss as of little importance"]},{"meta":{"id":"pardon","uuid":"a55237df-c637-48d1-b7ab-81eddc3dcb55","src":"coll_thes","section":"alpha","target":{"tuuid":"7e15693b-2866-457f-a3bb-d200dd996e5c","tsrc":"collegiate"},"stems":["pardon","pardoned","pardoning","pardons"],"syns":[["forgive"],["blink
59
+ (at)","brush (aside or off)","condone","discount","disregard","excuse","forgive","gloss
60
+ (over)","gloze (over)","ignore","overlook","overpass","paper over","pass over","remit","shrug
61
+ off","whitewash","wink (at)"]],"ants":[["resent"]],"offensive":false},"hwi":{"hw":"pardon"},"fl":"verb","def":[{"sseq":[[["sense",{"sn":"1","dt":[["text","to
62
+ cease to have feelings of anger or bitterness toward "],["vis",[{"t":"he eventually
63
+ {it}pardoned{\/it} his sister after she apologized"}]]],"syn_list":[[{"wd":"forgive"}]],"rel_list":[[{"wd":"absolve"},{"wd":"acquit"},{"wd":"clear"},{"wd":"exculpate"},{"wd":"exonerate"},{"wd":"vindicate"}],[{"wd":"remit"},{"wd":"shrive"}],[{"wd":"condone"},{"wd":"disregard"},{"wd":"excuse"},{"wd":"ignore"},{"wd":"pass
64
+ over"},{"wd":"shrug off"}],[{"wd":"discharge"},{"wd":"liberate"},{"wd":"redeem"},{"wd":"release"},{"wd":"unburden"}]],"near_list":[[{"wd":"abhor"},{"wd":"abominate"},{"wd":"despise"},{"wd":"detest"},{"wd":"dislike"},{"wd":"execrate"},{"wd":"hate"},{"wd":"loathe"}],[{"wd":"avenge"},{"wd":"redress"},{"wd":"requite"},{"wd":"retaliate"},{"wd":"revenge"}],[{"wd":"discipline"},{"wd":"penalize"},{"wd":"punish"}]],"ant_list":[[{"wd":"resent"}]]}]],[["sense",{"sn":"2","dt":[["text","to
65
+ dismiss as of little importance "],["vis",[{"t":"I\u0027m willing to {it}pardon{\/it}
66
+ a little sloppiness of dress in such a kind and loving person"}]]],"syn_list":[[{"wd":"blink
67
+ (at)"},{"wd":"brush (aside or off)"},{"wd":"condone"},{"wd":"discount"},{"wd":"disregard"},{"wd":"excuse"},{"wd":"forgive"},{"wd":"gloss
68
+ (over)"},{"wd":"gloze (over)"},{"wd":"ignore"},{"wd":"overlook"},{"wd":"overpass"},{"wd":"paper
69
+ over"},{"wd":"pass over"},{"wd":"remit"},{"wd":"shrug off"},{"wd":"whitewash"},{"wd":"wink
70
+ (at)"}]],"rel_list":[[{"wd":"explain"},{"wd":"justify"},{"wd":"rationalize"}],[{"wd":"absolve"},{"wd":"acquit"},{"wd":"clear"},{"wd":"exculpate"},{"wd":"exonerate"},{"wd":"vindicate"}],[{"wd":"waive"},{"wd":"wave
71
+ (aside or off)"}]],"phrase_list":[[{"wd":"close one\u0027s eyes to"},{"wd":"forgive
72
+ and forget"}]],"near_list":[[{"wd":"heed"},{"wd":"mark"},{"wd":"mind"},{"wd":"note"},{"wd":"object
73
+ (to)"}]]}]]]}],"shortdef":["to cease to have feelings of anger or bitterness
74
+ toward","to dismiss as of little importance"]}]'
75
+ recorded_at: Tue, 21 Jun 2022 23:24:58 GMT
76
+ - request:
77
+ method: get
78
+ uri: https://www.dictionaryapi.com/api/v3/references/collegiate/json/pardoned?key=<COLLEGIATE_DICTIONARY_API_KEY>
79
+ body:
80
+ encoding: US-ASCII
81
+ string: ''
82
+ headers:
83
+ Accept-Encoding:
84
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
85
+ Accept:
86
+ - "*/*"
87
+ User-Agent:
88
+ - Ruby
89
+ response:
90
+ status:
91
+ code: 200
92
+ message: OK
93
+ headers:
94
+ Date:
95
+ - Tue, 21 Jun 2022 23:24:58 GMT
96
+ Content-Type:
97
+ - application/json; charset=utf-8
98
+ Content-Length:
99
+ - '1085'
100
+ Connection:
101
+ - keep-alive
102
+ Server:
103
+ - Apache
104
+ Cache-Control:
105
+ - max-age=86400, public
106
+ Pragma:
107
+ - cache
108
+ Vary:
109
+ - Accept-Encoding
110
+ Access-Control-Allow-Origin:
111
+ - "*"
112
+ body:
113
+ encoding: ASCII-8BIT
114
+ string: '[{"meta":{"id":"pardon:2","uuid":"7e15693b-2866-457f-a3bb-d200dd996e5c","sort":"160068700","src":"collegiate","section":"alpha","stems":["pardon","pardoned","pardoning","pardons"],"offensive":false},"hom":2,"hwi":{"hw":"pardon"},"fl":"verb","ins":[{"if":"par*doned"},{"if":"par*don*ing","prs":[{"mw":"\u02c8p\u00e4rd-ni\u014b","sound":{"audio":"pardon02","ref":"c","stat":"1"}},{"mw":"\u02c8p\u00e4r-d\u1d4an-i\u014b"}]}],"def":[{"vd":"transitive
115
+ verb","sseq":[[["sense",{"sn":"1 a","dt":[["text","{bc}to absolve from the
116
+ consequences of a fault or crime"]]}],["sense",{"sn":"b","dt":[["text","{bc}to
117
+ allow (an offense) to pass without punishment {bc}{sx|forgive||}"]]}],["sense",{"sn":"c","dt":[["text","{bc}to
118
+ relieve of a penalty improperly assessed"]]}]],[["sense",{"sn":"2","dt":[["text","{bc}{sx|tolerate||}"]]}]]]}],"syns":[{"pl":"synonyms","pt":[["text","{sc}excuse{\/sc}
119
+ {sc}condone{\/sc} {sc}pardon{\/sc} {sc}forgive{\/sc} mean to exact neither
120
+ punishment nor redress. {sc}excuse{\/sc} may refer to specific acts especially
121
+ in social or conventional situations or the person responsible for these.
122
+ "],["vis",[{"t":"{it}excuse{\/it} an interruption"},{"t":"{it}excused{\/it}
123
+ them for interrupting"}]],["text"," Often the term implies extenuating circumstances.
124
+ "],["vis",[{"t":"injustice {it}excuses{\/it} strong responses"}]],["text","
125
+ {sc}condone{\/sc} implies that one overlooks without censure behavior (such
126
+ as dishonesty or violence) that involves a serious breach of a moral, ethical,
127
+ or legal code, and the term may refer to the behavior or to the agent responsible
128
+ for it. "],["vis",[{"t":"a society that {it}condones{\/it} alcohol but not
129
+ narcotics"}]],["text"," {sc}pardon{\/sc} implies that one remits a penalty
130
+ due for an admitted or established offense. "],["vis",[{"t":"{it}pardon{\/it}
131
+ a criminal"}]],["text"," {sc}forgive{\/sc} implies that one gives up all claim
132
+ to requital and to resentment or vengeful feelings. "],["vis",[{"t":"could
133
+ not {it}forgive{\/it} their rudeness"}]]]}],"et":[["text","Middle English,
134
+ from Anglo-French {it}parduner{\/it}, from Late Latin {it}perdonare{\/it}
135
+ to grant freely, from Latin {it}per-{\/it} thoroughly + {it}donare{\/it} to
136
+ give {ma}{mat|parboil|}, {mat|donation|}{\/ma}"]],"date":"15th century{ds||1|a|}","shortdef":["to
137
+ absolve from the consequences of a fault or crime","to allow (an offense)
138
+ to pass without punishment : forgive","to relieve of a penalty improperly
139
+ assessed"]}]'
140
+ recorded_at: Tue, 21 Jun 2022 23:24:58 GMT
141
+ recorded_with: VCR 6.1.0
@@ -0,0 +1,144 @@
1
+ ---
2
+ http_interactions:
3
+ - request:
4
+ method: get
5
+ uri: https://www.dictionaryapi.com/api/v3/references/thesaurus/json/salmon?key=<COLLEGIATE_THESAURUS_API_KEY>
6
+ body:
7
+ encoding: US-ASCII
8
+ string: ''
9
+ headers:
10
+ Accept-Encoding:
11
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
12
+ Accept:
13
+ - "*/*"
14
+ User-Agent:
15
+ - Ruby
16
+ response:
17
+ status:
18
+ code: 200
19
+ message: OK
20
+ headers:
21
+ Date:
22
+ - Tue, 21 Jun 2022 23:24:57 GMT
23
+ Content-Type:
24
+ - application/json; charset=utf-8
25
+ Content-Length:
26
+ - '113'
27
+ Connection:
28
+ - keep-alive
29
+ Server:
30
+ - Apache
31
+ Cache-Control:
32
+ - max-age=86400, public
33
+ Pragma:
34
+ - cache
35
+ Vary:
36
+ - Accept-Encoding
37
+ Access-Control-Allow-Origin:
38
+ - "*"
39
+ body:
40
+ encoding: ASCII-8BIT
41
+ string: '["salon","saloon","saloons","daemon","daimon","gammon","sallow","salons","salvo","salvor","salvos","sermon","summon","psalmody","salesman","salesmen","almoner","calming","salamis","salient"]'
42
+ recorded_at: Tue, 21 Jun 2022 23:24:57 GMT
43
+ - request:
44
+ method: get
45
+ uri: https://www.dictionaryapi.com/api/v3/references/collegiate/json/salmon?key=<COLLEGIATE_DICTIONARY_API_KEY>
46
+ body:
47
+ encoding: US-ASCII
48
+ string: ''
49
+ headers:
50
+ Accept-Encoding:
51
+ - gzip;q=1.0,deflate;q=0.6,identity;q=0.3
52
+ Accept:
53
+ - "*/*"
54
+ User-Agent:
55
+ - Ruby
56
+ response:
57
+ status:
58
+ code: 200
59
+ message: OK
60
+ headers:
61
+ Date:
62
+ - Tue, 21 Jun 2022 23:24:57 GMT
63
+ Content-Type:
64
+ - application/json; charset=utf-8
65
+ Content-Length:
66
+ - '1941'
67
+ Connection:
68
+ - keep-alive
69
+ Server:
70
+ - Apache
71
+ Cache-Control:
72
+ - max-age=86400, public
73
+ Pragma:
74
+ - cache
75
+ Vary:
76
+ - Accept-Encoding
77
+ Access-Control-Allow-Origin:
78
+ - "*"
79
+ body:
80
+ encoding: ASCII-8BIT
81
+ string: '[{"meta":{"id":"salmon","uuid":"cb7ebb03-74af-4bd2-8191-5d88cee9dcbf","sort":"190315000","src":"collegiate","section":"alpha","stems":["Atlantic
82
+ salmon","salmon","salmons"],"offensive":false},"hwi":{"hw":"salm*on","prs":[{"mw":"\u02c8sa-m\u0259n","sound":{"audio":"salmon01","ref":"c","stat":"1"}}]},"fl":"noun","ins":[{"il":"plural","if":"salmon"},{"il":"also","if":"salm*ons"}],"def":[{"sseq":[[["sense",{"sn":"1
83
+ a","dt":[["text","{bc}a large anadromous {a_link|salmonid} fish ({it}Salmo
84
+ salar{\/it}) of the North Atlantic noted as a game and food fish "],["ca",{"intro":"called
85
+ also","cats":[{"cat":"Atlantic salmon"}]}]]}],["sense",{"sn":"b","dt":[["text","{bc}any
86
+ of various anadromous salmonid fishes other than the salmon"]],"sdsense":{"sd":"especially","dt":[["text","{bc}{sx|pacific
87
+ salmon||}"]]}}],["sense",{"sn":"c","dt":[["text","{bc}a fish (such as a barramundi)
88
+ resembling a salmon"]]}]],[["sense",{"sn":"2","dt":[["text","{bc}the variable
89
+ color of salmon\u0027s flesh averaging a strong yellowish pink"]]}]]]}],"et":[["text","Middle
90
+ English {it}samon{\/it}, from Anglo-French {it}salmon, samon{\/it}, from Latin
91
+ {it}salmon-, salmo{\/it}"]],"date":"13th century{ds||1|a|}","shortdef":["a
92
+ large anadromous salmonid fish (Salmo salar) of the North Atlantic noted as
93
+ a game and food fish \u2014called also Atlantic salmon","any of various anadromous
94
+ salmonid fishes other than the salmon; especially : pacific salmon","a fish
95
+ (such as a barramundi) resembling a salmon"]},{"meta":{"id":"Salmon:g","uuid":"a5f856eb-b391-47c0-a661-6436e1d1581a","sort":"282519000","src":"collegiate","section":"geog","stems":["Salmon"],"offensive":false},"hwi":{"hw":"Salm*on","prs":[{"mw":"\u02c8sa-m\u0259n","sound":{"audio":"ggsalm01","ref":"c","stat":"1"}}]},"fl":"geographical
96
+ name","def":[{"sseq":[[["sense",{"dt":[["text","river 420 miles (676 kilometers)
97
+ long in central Idaho flowing into the Snake River"]]}]]]}],"shortdef":["river
98
+ 420 miles (676 kilometers) long in central Idaho flowing into the Snake River"]},{"meta":{"id":"salmon
99
+ pink","uuid":"0cd33845-1c8d-446b-8908-a83389918e7e","sort":"190321000","src":"collegiate","section":"alpha","stems":["salmon
100
+ pink","salmon pinks"],"offensive":false},"hwi":{"hw":"salmon pink"},"fl":"noun","def":[{"sseq":[[["sense",{"dt":[["text","{bc}a
101
+ strong yellowish pink"]]}]]]}],"date":"1882","shortdef":["a strong yellowish
102
+ pink"]},{"meta":{"id":"Salmon River Mountains:g","uuid":"2f208a45-3e68-48b1-b0b7-53cc0b7b483a","sort":"282520000","src":"collegiate","section":"geog","stems":["Salmon
103
+ River Mountains"],"offensive":false},"hwi":{"hw":"Salmon River Mountains"},"fl":"geographical
104
+ name","def":[{"sseq":[[["sense",{"dt":[["text","mountains in central Idaho
105
+ with many peaks over 9000 feet (2743 meters) high"]]}]]]}],"shortdef":["mountains
106
+ in central Idaho with many peaks over 9000 feet (2743 meters) high"]},{"meta":{"id":"Atlantic
107
+ salmon","uuid":"12ee4593-c04b-4ecc-9a25-806df767ad7e","sort":"014523000","src":"collegiate","section":"alpha","stems":["Atlantic
108
+ salmon","Atlantic salmons"],"offensive":false},"hwi":{"hw":"Atlantic salmon"},"fl":"noun","def":[{"sseq":[[["sense",{"dt":[["text","{bc}{sx|salmon||1a}"]]}]]]}],"date":"1813","shortdef":["salmon"]},{"meta":{"id":"black
109
+ salmon","uuid":"0a75d3f5-b816-430a-ab01-da53aa2fb41a","sort":"020261750","src":"collegiate","section":"alpha","stems":["black
110
+ salmon","black salmons"],"offensive":false},"hwi":{"hw":"black salmon"},"fl":"noun","def":[{"sseq":[[["sense",{"dt":[["text","{bc}{sx|chinook
111
+ salmon||}"]]}]]]}],"date":"1873","shortdef":["chinook salmon"]},{"meta":{"id":"Chinook
112
+ salmon","uuid":"1fa80af4-81c1-4a08-9f0b-578454e0eabe","sort":"032660000","src":"collegiate","section":"alpha","stems":["Chinook","Chinook
113
+ salmon","Chinooks","chinook","chinook jargon","chinook salmon","chinook salmons","chinookan","chinooks","king
114
+ salmon"],"offensive":false},"hwi":{"hw":"Chinook salmon"},"vrs":[{"vl":"or
115
+ less commonly","va":"chinook salmon"},{"vl":"or","va":"Chinook"},{"vl":"or","va":"chinook"}],"fl":"noun","def":[{"sseq":[[["sense",{"dt":[["text","{bc}a
116
+ large, usually red-fleshed, commercially important salmon ({it}Oncorhynchus
117
+ tshawytscha{\/it}) of the northern Pacific Ocean that is typically bluish-green
118
+ above with silvery sides, has a dark mouth with black gums, and attains an
119
+ average weight of about 30 to 40 pounds (13.5 to 18 kilograms) but may sometimes
120
+ exceed 120 pounds (54 kilograms) "],["ca",{"intro":"called also","cats":[{"cat":"black
121
+ salmon"},{"cat":"king salmon"}]}]]}]]]}],"art":{"artid":"chin_sa","capt":"Chinook
122
+ salmon"},"date":"1851","shortdef":["a large, usually red-fleshed, commercially
123
+ important salmon (Oncorhynchus tshawytscha) of the northern Pacific Ocean
124
+ that is typically bluish-green above with silvery sides, has a dark mouth
125
+ with black gums, and attains an average weight of about 30 to 40 pounds (13.5
126
+ to 18 kilograms) but may sometimes exceed 120 pounds (54 kilograms) \u2014called
127
+ also black salmon, king salmon"]},{"meta":{"id":"chum salmon","uuid":"a0d372d5-dd32-419f-b79f-5160e5f296c0","sort":"033065000","src":"collegiate","section":"alpha","stems":["chum","chum
128
+ salmon","chum salmons","chums","chumship","chumships"],"offensive":false},"hwi":{"hw":"chum
129
+ salmon"},"vrs":[{"vl":"or less commonly","va":"chum"}],"fl":"noun","def":[{"sseq":[[["sense",{"dt":[["text","{bc}a
130
+ metallic bluish green salmon ({it}Oncorhynchus keta{\/it}) of the northern
131
+ Pacific Ocean and Arctic Ocean that may reach a length of about 3.5 feet (1
132
+ meter) but is typically smaller "],["snote",[["t","Upon entering fresh water
133
+ to spawn, the male chum salmon develops large, sharp teeth and becomes dark
134
+ green to brown with reddish-purple vertical stripes."]]]]}]]]}],"et":[["text","{et_link|chum:5|chum:5}"]],"date":"1907","shortdef":["a
135
+ metallic bluish green salmon (Oncorhynchus keta) of the northern Pacific Ocean
136
+ and Arctic Ocean that may reach a length of about 3.5 feet (1 meter) but is
137
+ typically smaller"]},{"meta":{"id":"jack salmon","uuid":"d3d51ea4-f587-418d-a756-feb55f6795dc","sort":"100005100","src":"collegiate","section":"alpha","stems":["jack
138
+ salmon","jack salmons"],"offensive":false},"hwi":{"hw":"jack salmon"},"fl":"noun","def":[{"sseq":[[["sense",{"sn":"1","dt":[["text","{bc}{sx|walleye||3}"]]}]],[["sense",{"sn":"2
139
+ a","dt":[["text","{bc}{sx|grilse||}"]]}],["sense",{"sn":"b","dt":[["text","{bc}{sx|coho||}"]]}]]]}],"date":"1850{ds||1||}","shortdef":["walleye","grilse","coho"]},{"meta":{"id":"king
140
+ salmon","uuid":"9c8b42d9-a92d-4900-ae11-25f5bd6898cd","sort":"110049200","src":"collegiate","section":"alpha","stems":["king
141
+ salmon","king salmons"],"offensive":false},"hwi":{"hw":"king salmon"},"fl":"noun","def":[{"sseq":[[["sense",{"dt":[["text","{bc}{sx|chinook
142
+ salmon||}"]]}]]]}],"date":"1871","shortdef":["chinook salmon"]}]'
143
+ recorded_at: Tue, 21 Jun 2022 23:24:57 GMT
144
+ recorded_with: VCR 6.1.0
data/lib/.DS_Store ADDED
Binary file
Binary file
@@ -0,0 +1,88 @@
1
+ require "json"
2
+ require "httparty"
3
+
4
+ module MerriamWebsterPlus
5
+ class Client
6
+ MerriamWebsterAPIError = Class.new(StandardError)
7
+ BadRequestError = Class.new(MerriamWebsterAPIError)
8
+ UnauthorizedError = Class.new(MerriamWebsterAPIError)
9
+ ForbiddenError = Class.new(MerriamWebsterAPIError)
10
+ NotFoundError = Class.new(MerriamWebsterAPIError)
11
+ UnprocessableEntityError = Class.new(MerriamWebsterAPIError)
12
+ ApiError = Class.new(MerriamWebsterAPIError)
13
+
14
+ HTTP_OK_CODE = 200
15
+
16
+ HTTP_BAD_REQUEST_CODE = 400
17
+ HTTP_UNAUTHORIZED_CODE = 401
18
+ HTTP_FORBIDDEN_CODE = 403
19
+ HTTP_NOT_FOUND_CODE = 404
20
+ HTTP_UNPROCESSABLE_ENTITY_CODE = 429
21
+
22
+ COLLEGIATE_DICTIONARY_ENDPOINT = "https://www.dictionaryapi.com/api/v3/references/collegiate/json/"
23
+ COLLEGIATE_THESAURUS_ENDPOINT = "https://www.dictionaryapi.com/api/v3/references/thesaurus/json/"
24
+
25
+ attr_reader :word, :title
26
+
27
+ def initialize(word:, title:)
28
+ @word = railsy_parameterize(word)
29
+ @title = title
30
+ end
31
+
32
+ def api_entries
33
+ parsed_response
34
+ end
35
+
36
+ private
37
+
38
+ def response
39
+ @response ||= HTTParty.get("#{endpoint}#{word}?key=#{api_key}")
40
+ end
41
+
42
+ def parsed_response
43
+ return JSON.parse(response.body, symbolize_names: true) if response_successful?
44
+
45
+ raise error_class, "code: #{response.code}, message: #{response.message}, response: #{response.body}"
46
+ end
47
+
48
+ def error_class
49
+ case response.code
50
+ when HTTP_BAD_REQUEST_CODE
51
+ BadRequestError
52
+ when HTTP_UNAUTHORIZED_CODE
53
+ UnauthorizedError
54
+ when HTTP_FORBIDDEN_CODE
55
+ ForbiddenError
56
+ when HTTP_NOT_FOUND_CODE
57
+ NotFoundError
58
+ when HTTP_UNPROCESSABLE_ENTITY_CODE
59
+ UnprocessableEntityError
60
+ else
61
+ ApiError
62
+ end
63
+ end
64
+
65
+ def response_successful?
66
+ (response.code == HTTP_OK_CODE) && valid_json?(response.body)
67
+ end
68
+
69
+ def valid_json?(json)
70
+ JSON.parse(json)
71
+ true
72
+ rescue JSON::ParserError => e
73
+ false
74
+ end
75
+
76
+ def endpoint
77
+ MerriamWebsterPlus::Client.const_get("#{title.upcase}_ENDPOINT")
78
+ end
79
+
80
+ def api_key
81
+ ENV["#{title.upcase}_API_KEY"]
82
+ end
83
+
84
+ def railsy_parameterize(string)
85
+ string.gsub(/[^a-z0-9\-_]+/i, "_").downcase
86
+ end
87
+ end
88
+ end
@@ -0,0 +1,41 @@
1
+ require "merriam_webster_plus/client"
2
+ require "merriam_webster_plus/dictionary_entry"
3
+
4
+ module MerriamWebsterPlus
5
+ class DictionaryEntries
6
+ attr_reader :word, :api_entries, :entry_parser
7
+
8
+ def initialize(word:)
9
+ @word = word
10
+ title_post_initialize
11
+ end
12
+
13
+ def main_entries
14
+ main_entries = []
15
+ api_entries.each do |api_entry|
16
+ main_entry = main_entry(api_entry)
17
+ (main_entries << main_entry) if word_equals_entry_headword?(main_entry)
18
+ end
19
+ main_entries
20
+ end
21
+
22
+ private
23
+
24
+ def title_post_initialize
25
+ @api_entries = MerriamWebsterPlus::Client.new(word: word, title: :collegiate_dictionary).api_entries
26
+ @entry_parser = DictionaryEntry
27
+ end
28
+
29
+ def main_entry(api_entry)
30
+ entry_parser.new(api_entry: api_entry).main_entry if api_entry_valid?(api_entry)
31
+ end
32
+
33
+ def api_entry_valid?(api_entry)
34
+ (api_entry.is_a? Hash)
35
+ end
36
+
37
+ def word_equals_entry_headword?(entry)
38
+ entry && (word.downcase == entry[:word].downcase)
39
+ end
40
+ end
41
+ end
@@ -0,0 +1,124 @@
1
+ require "merriam_webster_plus/dictionary_sense"
2
+
3
+ module MerriamWebsterPlus
4
+ class DictionaryEntry
5
+ AUDIO_MP3_ENDPOINT = "https://media.merriam-webster.com/audio/prons/en/us/mp3/"
6
+
7
+ attr_reader :api_entry, :sense_parser
8
+
9
+ def initialize(api_entry:)
10
+ @api_entry = api_entry
11
+ title_post_initialize
12
+ end
13
+
14
+ def main_entry
15
+ {
16
+ word: headword,
17
+ class: word_class,
18
+ audio: audio,
19
+ senses: senses,
20
+ uuid: uuid,
21
+ target_uuid: target_uuid
22
+ }
23
+ end
24
+
25
+ private
26
+
27
+ def title_post_initialize
28
+ @sense_parser = DictionarySense
29
+ end
30
+
31
+ def headword
32
+ remove_non_letters(headword_information&.dig(:hw))
33
+ end
34
+
35
+ def word_class
36
+ functional_label
37
+ end
38
+
39
+ def audio
40
+ ("#{AUDIO_MP3_ENDPOINT}#{audio_subdirectory}/#{audio_filename}.mp3" if audio_filename)
41
+ end
42
+
43
+ def uuid
44
+ entry_metadata[:uuid]
45
+ end
46
+
47
+ def target_uuid
48
+ entry_metadata&.dig(:target, :tuuid)
49
+ end
50
+
51
+ def entry_metadata
52
+ api_entry[:meta]
53
+ end
54
+
55
+ def headword_information
56
+ api_entry[:hwi]
57
+ end
58
+
59
+ def functional_label
60
+ api_entry[:fl]
61
+ end
62
+
63
+ def definition_section
64
+ api_entry[:def]
65
+ end
66
+
67
+ def short_definition
68
+ api_entry[:shortdef]
69
+ end
70
+
71
+ def senses
72
+ return unless definition_section
73
+
74
+ senses = []
75
+ sense_sequence.each_with_index do |sense_group, index|
76
+ senses << primary_sense(sense_group, index)
77
+ end
78
+ senses.first(3)
79
+ end
80
+
81
+ def primary_sense(sense_group, index)
82
+ ["#{index + 1}".to_i,
83
+ sense_parser.new(definition: definition(index), sense_group: sense_group).primary_sense]
84
+ end
85
+
86
+ def definition(index_num)
87
+ short_definition&.dig(index_num)&.chomp(": such as")&.chomp(":")
88
+ end
89
+
90
+ def sense_sequence
91
+ return parenthesized_sense_sequence if parenthesized_sense_sequence?
92
+
93
+ definition_section&.dig(0, :sseq)
94
+ end
95
+
96
+ def parenthesized_sense_sequence
97
+ definition_section&.dig(0, :sseq, 0, 0, 1)
98
+ end
99
+
100
+ def parenthesized_sense_sequence?
101
+ definition_section&.dig(0, :sseq, 0, 0, 0) == "pseq"
102
+ end
103
+
104
+ def audio_filename
105
+ headword_information&.dig(:prs, 0, :sound, :audio)
106
+ end
107
+
108
+ def audio_subdirectory
109
+ if audio_filename.start_with?("bix")
110
+ "bix"
111
+ elsif audio_filename.start_with?("gg")
112
+ "gg"
113
+ elsif audio_filename.chr.match?((/[^a-z ]/i))
114
+ "number"
115
+ else
116
+ audio_filename.chr
117
+ end
118
+ end
119
+
120
+ def remove_non_letters(string)
121
+ string&.gsub(/[^a-z ]/i, "")
122
+ end
123
+ end
124
+ end
@@ -0,0 +1,46 @@
1
+ require "merriam_webster_plus/dictionary_entries"
2
+ require "merriam_webster_plus/thesaurus_entries"
3
+ require "merriam_webster_plus/entry_audio_merger"
4
+ require "merriam_webster_plus/primary_entry_finder"
5
+
6
+ module MerriamWebsterPlus
7
+ class DictionaryPlus
8
+ attr_reader :word, :thesaurus_entries, :dictionary_entries, :entry_audio_merger, :primary_entry_finder
9
+
10
+ def initialize(word:)
11
+ @word = word
12
+ @thesaurus_entries = MerriamWebsterPlus::ThesaurusEntries.new(word: word).main_entries
13
+ @dictionary_entries = MerriamWebsterPlus::DictionaryEntries.new(word: word).main_entries
14
+ @entry_audio_merger = MerriamWebsterPlus::EntryAudioMerger
15
+ @primary_entry_finder = PrimaryEntryFinder
16
+ end
17
+
18
+ def primary_entry
19
+ primary_entry_with_audio if entries.any?
20
+ end
21
+
22
+ def entries
23
+ thesaurus_entries.any? ? thesaurus_entries_with_audio : dictionary_entries_with_audio
24
+ end
25
+
26
+ private
27
+
28
+ def thesaurus_entries_with_audio
29
+ entry_audio_merger.new(dst_entries: thesaurus_entries, src_entries: dictionary_entries).merged_entries
30
+ end
31
+
32
+ def dictionary_entries_with_audio
33
+ entry_audio_merger.new(dst_entries: dictionary_entries, src_entries: dictionary_entries).merged_entries
34
+ end
35
+
36
+ def primary_entry_with_audio
37
+ primary_entry_finder.new(primary_src_entries: thesaurus_entries_with_audio,
38
+ secondary_src_entries: dictionary_entries_with_audio,
39
+ tertiary_src_entries: entries).primary_entry
40
+ end
41
+
42
+ def railsy_blank(object)
43
+ object.nil? || object.empty?
44
+ end
45
+ end
46
+ end
@@ -0,0 +1,57 @@
1
+ module MerriamWebsterPlus
2
+ class DictionarySense
3
+ attr_reader :definition, :sense_group
4
+
5
+ def initialize(definition:, sense_group:)
6
+ @definition = definition
7
+ @sense_group = sense_group
8
+ end
9
+
10
+ def primary_sense
11
+ {
12
+ definition: definition,
13
+ example: example_sentence
14
+ }.compact
15
+ end
16
+
17
+ private
18
+
19
+ def sense
20
+ sense_group&.select { |sense| sense[0] == "sense" }&.first&.dig(1)
21
+ end
22
+
23
+ def example_sentence
24
+ verbal_illustration unless lacking_letters?(verbal_illustration)
25
+ end
26
+
27
+ def defining_text_section
28
+ sense&.dig(:dt)
29
+ end
30
+
31
+ def defining_text_section_usage_notes
32
+ defining_text_section&.select { |section| section[0] == "uns" }&.dig(0, 1, 0)
33
+ end
34
+
35
+ def defining_text_section_supplemental_information_notes
36
+ defining_text_section&.select { |section| section[0] == "snote" }&.dig(0, 1)
37
+ end
38
+
39
+ def verbal_illustration_section
40
+ select_verbal_illustration_section(defining_text_section) ||
41
+ select_verbal_illustration_section(defining_text_section_usage_notes) ||
42
+ select_verbal_illustration_section(defining_text_section_supplemental_information_notes)
43
+ end
44
+
45
+ def select_verbal_illustration_section(containing_section)
46
+ containing_section&.select { |section| section[0] == "vis" }&.last
47
+ end
48
+
49
+ def verbal_illustration
50
+ verbal_illustration_section&.dig(1, 0, :t)&.gsub(/\{.*?\}/, "")&.strip
51
+ end
52
+
53
+ def lacking_letters?(string)
54
+ string.nil? || string.to_s[/\p{L}/].nil?
55
+ end
56
+ end
57
+ end