pillboxr 0.0.3 → 0.6.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.
@@ -0,0 +1,36 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Pillboxr
3
+ class Params < Array
4
+
5
+ def initialize(size = 0, obj = nil, module_name, &block)
6
+ @module_name = module_name
7
+ super(size, obj, &block)
8
+ end
9
+
10
+ def concatenate
11
+ self.collect(&:to_param).join
12
+ end
13
+
14
+ def all(&block)
15
+ @module_name.send(:complete, &block)
16
+ end
17
+
18
+ def limit
19
+ if self.any? { |param| param.respond_to?(:lower_limit)}
20
+ limit = self.select { |param| param.respond_to?(:lower_limit) }.first.lower_limit
21
+ return limit
22
+ else
23
+ return DEFAULT_LOWER_LIMIT
24
+ end
25
+ end
26
+
27
+ def respond_to_missing(method_name, include_private = false) # :nodoc:
28
+ @module_name.send(:respond_to_missing, method_name, include_private)
29
+ end
30
+
31
+ def method_missing(method_name, *args)
32
+ puts "Params method missing called with #{method_name}."
33
+ @module_name.send(:method_missing, method_name, *args)
34
+ end
35
+ end
36
+ end
@@ -0,0 +1,74 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require_relative 'attributes'
3
+
4
+ module Pillboxr
5
+ include Attributes
6
+ extend self
7
+ class Pill
8
+ Pillboxr.attributes.each do |k,v|
9
+ attr_accessor k.to_sym
10
+ alias_method v.to_sym, k.to_sym
11
+ alias_method (v.to_s + "=").to_sym, (k.to_s + "=").to_sym
12
+ end
13
+
14
+ def initialize(params_hash)
15
+ params_hash.each do |k,v|
16
+ self.send((k.downcase + "=").to_sym, v)
17
+ end
18
+ end
19
+
20
+ def color
21
+ Pillboxr::Attributes::COLOR_CODES[@color.to_sym]
22
+ end
23
+
24
+ def shape
25
+ Pillboxr::Attributes::SHAPE_CODES[@shape.to_sym]
26
+ end
27
+
28
+ def score
29
+ Integer(@score)
30
+ end
31
+
32
+ def score?
33
+ @score.to_i > 1 ? true : false
34
+ end
35
+
36
+ def image
37
+ @image.to_i == 1 ? true : false
38
+ end
39
+
40
+ def image?
41
+ @image.to_i == 1 ? true : false
42
+ end
43
+
44
+ def to_s
45
+ string = "#<Pillboxr::Pill:#{object_id} "
46
+ instance_variables.each do |ivar|
47
+ string << String(ivar)
48
+ string << " = "
49
+ string << (self.instance_variable_get(ivar) || "")
50
+ string << ", "
51
+ end unless instance_variables.empty?
52
+ string << ">"
53
+ end
54
+
55
+ def image_url(image_size = 'super_small')
56
+ unless image_id
57
+ return nil
58
+ end
59
+ case String(image_size)
60
+ when "super_small"; "http://pillbox.nlm.nih.gov/assets/super_small/#{image_id}ss.png"
61
+ when "small"; "http://pillbox.nlm.nih.gov/assets/small/#{image_id}sm.jpg"
62
+ when "medium"; "http://pillbox.nlm.nih.gov/assets/medium/#{image_id}md.jpg"
63
+ when "large"; "http://pillbox.nlm.nih.gov/assets/large/#{image_id}lg.jpg"
64
+ when "all"
65
+ ["http://pillbox.nlm.nih.gov/assets/super_small/#{image_id}ss.png",
66
+ "http://pillbox.nlm.nih.gov/assets/small/#{image_id}sm.jpg",
67
+ "http://pillbox.nlm.nih.gov/assets/medium/#{image_id}md.jpg",
68
+ "http://pillbox.nlm.nih.gov/assets/large/#{image_id}lg.jpg"]
69
+ end
70
+ end
71
+
72
+ alias_method :scored?, :score?
73
+ end
74
+ end
@@ -0,0 +1,59 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require 'httparty'
3
+ require_relative 'response'
4
+
5
+ module Pillboxr
6
+ class Request
7
+ include HTTParty
8
+ format :xml
9
+ base_uri BASE_URI
10
+ parser(Class.new(HTTParty::Parser) do
11
+ def parse
12
+ begin
13
+ body.gsub!(/^<disclaimer>.+<\/disclaimer>/, "")
14
+ body.gsub!(/\s\&\s/, ' and ')
15
+ super
16
+ rescue MultiXml::ParseError => e
17
+ if e.message == NO_RECORDS_ERROR_MESSAGE
18
+ result = {'Pills' => {'pill' => [], 'record_count' => 0 }}
19
+ return result
20
+ elsif e.message == API_KEY_ERROR_MESSAGE
21
+ raise "Invalid api_key.yml. Check format and try again."
22
+ else
23
+ raise
24
+ end
25
+ end
26
+ end
27
+ end)
28
+
29
+ attr_reader :full_path, :params, :api_response
30
+
31
+ def initialize(default_path = default_path, api_params)
32
+ @full_path = default_path + api_params.concatenate
33
+ @params = api_params
34
+ @api_response = Pillboxr::Response.new
35
+ @api_response.query = self
36
+ end
37
+
38
+ def perform
39
+ puts "path = #{default_path + params.concatenate}"
40
+ @api_response.body = self.class.get(full_path)
41
+ return self.api_response
42
+ end
43
+
44
+ private
45
+ def api_key
46
+ begin
47
+ @api_key ||= YAML.load_file(File.expand_path("api_key.yml"))
48
+ rescue Errno::ENOENT => e
49
+ raise e, "API key not found. You must create an api_key.yml file in the root directory of the project."
50
+ rescue TypeError => e
51
+ raise e, "api_key.yml does not contain an appropriate key."
52
+ end
53
+ end
54
+
55
+ def default_path
56
+ "/PHP/pillboxAPIService.php?key=#{api_key}"
57
+ end
58
+ end
59
+ end
@@ -0,0 +1,8 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Pillboxr
3
+ Response = Struct.new(:body, :query) do
4
+
5
+
6
+ end
7
+
8
+ end
@@ -0,0 +1,79 @@
1
+ # -*- encoding: utf-8 -*-
2
+ require_relative 'pages'
3
+
4
+ module Pillboxr
5
+ class Result
6
+
7
+ attr_accessor :record_count, :pages
8
+
9
+ def initialize(api_response, &block)
10
+ initial_page_number = Integer(api_response.query.params.limit / RECORDS_PER_PAGE )
11
+ @record_count = Integer(api_response.body['Pills']['record_count'])
12
+
13
+ puts "#{@record_count} records available. #{api_response.body['Pills']['pill'].size} records retrieved."
14
+
15
+ @pages = initialize_pages_array(api_response, initial_page_number)
16
+ @pages[initial_page_number].send(:pills=, self.class.parse_pills(api_response, @record_count))
17
+ api_response.query.params.clear
18
+ if block_given?
19
+ yield self
20
+ else
21
+ return self
22
+ end
23
+ end
24
+
25
+ def self.subsequent(api_response)
26
+ return parse_pills(api_response, RECORDS_PER_PAGE)
27
+ end
28
+
29
+ def self.parse_pills(api_response, record_count)
30
+ pills = []
31
+ if record_count == 1
32
+ pills << Pill.new(api_response.body['Pills']['pill'])
33
+ else
34
+ api_response.body['Pills']['pill'].each do |pill|
35
+ pills << Pill.new(pill)
36
+ end
37
+ end unless record_count == 0
38
+ return pills
39
+ end
40
+
41
+ def initialize_pages_array(api_response, initial_page_number)
42
+ unless record_count == 0
43
+ record_count.divmod(RECORDS_PER_PAGE).tap do |ary|
44
+ if ary[1] == 0
45
+ return Pages.new(ary[0]) do |i|
46
+ page_params = api_response.query.params.dup
47
+ page_params.delete_if { |param| param.respond_to?(:lower_limit)}
48
+ page_params << Attributes::Lowerlimit.new(i * RECORDS_PER_PAGE)
49
+ Page.new(i == initial_page_number, i == initial_page_number, i, [], page_params)
50
+ end
51
+ else
52
+ return Pages.new(ary[0] + 1) do |i|
53
+ page_params = api_response.query.params.dup
54
+ page_params.delete_if { |param| param.respond_to?(:lower_limit)}
55
+ page_params << Attributes::Lowerlimit.new(i * RECORDS_PER_PAGE)
56
+ Page.new(i == initial_page_number, i == initial_page_number, i, [], page_params)
57
+ end
58
+ end
59
+ end
60
+ end
61
+ return Pages.new(1) { |i| Page.new(true, true, i, [], api_response.query.params.dup)}
62
+ end
63
+
64
+ def inspect
65
+ string = "#<Pillboxr::Result:#{object_id} "
66
+ instance_variables.each do |ivar|
67
+ string << String(ivar)
68
+ string << " = "
69
+ string << (String(self.instance_variable_get(ivar)) || "")
70
+ string << ", " unless ivar == instance_variables.last
71
+ end unless instance_variables.empty?
72
+ string << ">"
73
+ return string
74
+ end
75
+
76
+ alias_method :to_s, :inspect
77
+ private :initialize_pages_array
78
+ end
79
+ end
@@ -0,0 +1,4 @@
1
+ # -*- encoding: utf-8 -*-
2
+ module Pillboxr
3
+ VERSION = "0.6.0"
4
+ end
@@ -1,6 +1,6 @@
1
1
  # -*- encoding: utf-8 -*-
2
2
  require File.expand_path('../lib/pillboxr', __FILE__)
3
- require File.expand_path("../lib/version", __FILE__)
3
+ require File.expand_path('../lib/pillboxr/version', __FILE__)
4
4
 
5
5
  Gem::Specification.new do |s|
6
6
  s.name = "pillboxr"
@@ -11,18 +11,29 @@ Gem::Specification.new do |s|
11
11
  s.homepage = "http://rubygems.org/gems/pillboxr"
12
12
  s.summary = "Access the NLM Pillbox API using ActiveResource."
13
13
  s.description = <<-END
14
- Pillboxr is a subclass of ActiveResource::Base that provides additional convenience methods and some parameter wrapping for querying the Pillbox API Service located at http://pillbox.nlm.nih.gov/PHP/pillboxAPIService.php
14
+ Pillboxr is a Ruby wrapper for the National Library of Medicine Pillbox API Service located at http://pillbox.nlm.nih.gov](http://pillbox.nlm.nih.gov).
15
+
16
+ The pillbox API provides information from the FDA about various prescription medications.
17
+
18
+ The current version of this library has two forms. The first (preferred) version does not depend on any gems except for `httparty`.
19
+ The second version depends upon `active_resource`.
20
+ This version of Pillboxr inherits from ActiveResource to perform its XML wrapping so ActiveResource 3.2.6 is a requirement for using the wrapper.
21
+
22
+ This deprecated version is not included in the distributed gem but the library code can be found at [https://github.com/kgautreaux/pillboxr](https://github.com/kgautreaux/pillboxr).
23
+
24
+ *Note:* This library is designed for use with Ruby 1.9.3 and above, and will not work with earlier versions of Ruby.
15
25
  END
16
26
 
17
- s.required_rubygems_version = ">= 1.3.6"
27
+ s.required_rubygems_version = ">= 1.8.6"
18
28
  s.rubyforge_project = "pillboxr"
19
-
20
- s.add_dependency 'activeresource', '> 2.3.5'
29
+
30
+ s.add_dependency 'httparty'
21
31
 
22
32
  s.add_development_dependency "bundler", ">= 1.0.0"
23
- s.add_development_dependency "activeresource", "> 2.3.5"
33
+ s.add_development_dependency 'webmock'
34
+ s.add_development_dependency 'vcr'
24
35
 
25
- s.files = `git ls-files`.split("\n")
36
+ s.files = `git ls-files`.split("\n").delete_if { |fn| fn.match(/active(_|)resource/) }
26
37
  s.executables = `git ls-files`.split("\n").map{|f| f =~ /^bin\/(.*)/ ? $1 : nil}.compact
27
38
  s.require_path = 'lib'
28
39
  end
@@ -0,0 +1,15 @@
1
+ require_relative 'test_helper'
2
+
3
+ class TestAttributes < MiniTest::Unit::TestCase
4
+ def setup
5
+ extend Pillboxr::Attributes
6
+ end
7
+
8
+ def test_fetching_an_attribute
9
+ assert_equal(:splcolor, attributes.fetch(:color))
10
+ end
11
+
12
+ def test_fetching_an_api_attribute
13
+ assert_equal(:color, api_attributes.fetch(:splcolor))
14
+ end
15
+ end
@@ -0,0 +1,28 @@
1
+ require_relative 'test_helper'
2
+
3
+ class TestParams < MiniTest::Unit::TestCase
4
+ def setup
5
+ @params = Pillboxr::Params.new(Pillboxr)
6
+ end
7
+
8
+ def test_creation_of_params
9
+ assert_equal([], @params)
10
+ end
11
+
12
+ def test_concatenate_method
13
+ 5.times do
14
+ @params << Pillboxr::Attributes::Lowerlimit.new(0)
15
+ end
16
+ assert_equal("&lower_limit=0&lower_limit=0&lower_limit=0&lower_limit=0&lower_limit=0", @params.concatenate)
17
+ end
18
+
19
+ def test_limit_method
20
+ assert_equal(Pillboxr::DEFAULT_LOWER_LIMIT, @params.limit)
21
+ @params << Pillboxr::Attributes::Lowerlimit.new(300)
22
+ assert_equal(300, @params.limit)
23
+ end
24
+
25
+ def test_method_missing
26
+
27
+ end
28
+ end
@@ -0,0 +1,20 @@
1
+ require_relative 'test_helper'
2
+
3
+ class TestPill < MiniTest::Unit::TestCase
4
+ def setup
5
+ @blue_pills = Pillboxr.color(:blue).image(true).all.pages.current.pills
6
+ @scored_pills = Pillboxr.score(3).all.pages.current.pills
7
+ end
8
+
9
+ def test_accessor_methods
10
+ @blue_pills.each do |pill|
11
+ assert_equal(:blue, pill.color)
12
+ assert_equal(true, pill.image?)
13
+ end
14
+
15
+ @scored_pills.each do |pill|
16
+ assert_equal(true, pill.scored?)
17
+ assert_equal(3, pill.score)
18
+ end
19
+ end
20
+ end
@@ -0,0 +1,167 @@
1
+ require_relative 'test_helper'
2
+ require 'vcr'
3
+
4
+
5
+ VCR.configure do |c|
6
+ c.cassette_library_dir = 'test/pillboxr/fixtures/vcr_cassettes'
7
+ c.hook_into :webmock
8
+ c.allow_http_connections_when_no_cassette = true
9
+ end
10
+
11
+ class TestPillboxr < MiniTest::Unit::TestCase
12
+ def setup
13
+ @num_round_shape_records = 11773
14
+ @num_blue_color_records = 2059
15
+ @num_image_records = 748
16
+ @num_blue_records_with_image = 69
17
+ @num_5_mm_records = 4724
18
+ @num_mylan_records = 753
19
+ @request_object = Pillboxr::Request.new(Pillboxr::Params.new([Pillboxr::Attributes::Shape.new(:round)]))
20
+ end
21
+
22
+ def test_api_key
23
+ assert_raises(NoMethodError) { Pillboxr::Request.api_key }
24
+ assert_raises(NoMethodError) do
25
+ @request_object.api_key
26
+ end
27
+ end
28
+
29
+ def test_returns_the_correct_default_path
30
+ assert_raises(NoMethodError) { @request_object.default_path }
31
+ assert_equal("/PHP/pillboxAPIService.php?key=#{@request_object.send(:api_key)}", @request_object.send(:default_path))
32
+ end
33
+
34
+ def test_returns_number_of_records
35
+ VCR.use_cassette(:round_shape) do
36
+ assert_equal(@num_round_shape_records, Pillboxr.with(:shape => :round).record_count)
37
+ end
38
+ end
39
+
40
+ def test_returns_no_records
41
+ VCR.use_cassette(:foo_shape) do
42
+ assert_equal(0, Pillboxr.with(:shape => :foo).record_count)
43
+ end
44
+ end
45
+
46
+ def test_all_valid_colors
47
+ VCR.use_cassette(:all_valid_colors) do
48
+ Pillboxr::Attributes::COLORS.keys.each do |color|
49
+ refute_equal([], Pillboxr.with(:color => color))
50
+ end
51
+ end
52
+ end
53
+
54
+ def test_all_valid_shapes
55
+ VCR.use_cassette(:all_valid_shapes) do
56
+ Pillboxr::Attributes::SHAPES.keys.each do |shape|
57
+ case shape
58
+ when :gear
59
+ assert_empty(Pillboxr.with(:shape => shape).pages.first.pills)
60
+ when :heptagon
61
+ assert_empty(Pillboxr.with(:shape => shape).pages.first.pills)
62
+ else
63
+ refute_empty(Pillboxr.with(:shape => shape).pages.first.pills, "shape is #{shape}")
64
+ end
65
+ end
66
+ end
67
+ end
68
+
69
+ def test_combination_hash
70
+ VCR.use_cassette(:combination_hash) do
71
+ assert_equal(@num_blue_records_with_image, Pillboxr.with({ :color => "blue", :image => true }).record_count)
72
+ end
73
+ end
74
+
75
+ def test_with_block_form
76
+ VCR.use_cassette(:with_block_form) do
77
+ @result = Pillboxr.with({:color => :blue}) do |r|
78
+ r.pages.each do |page|
79
+ page.get unless page.retrieved?
80
+ end
81
+ end
82
+ end
83
+ @result.pages.each { |page| assert page.retrieved? }
84
+ assert_equal(@num_blue_color_records, @result.pages.inject(0) { |sum, page| sum + page.pills.size })
85
+ end
86
+
87
+ def test_respond_to_missing
88
+ VCR.use_cassette(:respond_to_missing_shape) do
89
+ assert_equal(true, Pillboxr.respond_to?(:shape))
90
+ end
91
+ end
92
+
93
+ def test_method_missing_with_shape
94
+ VCR.use_cassette(:method_missing_shape) do
95
+ assert_equal(@num_round_shape_records, Pillboxr.shape(:round).all.record_count)
96
+ end
97
+ end
98
+
99
+ def test_method_missing_with_image
100
+ VCR.use_cassette(:method_missing_has_image) do
101
+ @result = Pillboxr.image(true).all
102
+ end
103
+
104
+ @result.pages.first.pills.each do |pill|
105
+ refute_nil(pill.image_url(:small))
106
+ end
107
+ assert_equal(@num_image_records, @result.record_count)
108
+ end
109
+
110
+ def test_method_missing_with_color
111
+ VCR.use_cassette(:method_missing_color) do
112
+ assert_equal(@num_blue_color_records, Pillboxr.color(:blue).all.record_count)
113
+ end
114
+ end
115
+
116
+ # def test_method_missing_with_imprint # Broken currently
117
+ # VCR.use_cassette(:method_missing_imprint) do
118
+ # assert_equal(@num_imprint_23_records, Pillboxr.imprint(23).all.record_count)
119
+ # end
120
+ # end
121
+
122
+ def test_method_missing_with_size
123
+ VCR.use_cassette(:method_missing_size, :allow_playback_repeats => true) do
124
+ assert_equal(@num_5_mm_records, Pillboxr.size(5).all.record_count)
125
+ assert_equal(@num_5_mm_records, Pillboxr.size("5").all.record_count)
126
+ end
127
+ end
128
+
129
+ def test_method_missing_with_author
130
+ VCR.use_cassette(:method_missing_author) do
131
+ assert_equal(@num_mylan_records, Pillboxr.author("Mylan Pharmaceuticals Inc.").all.record_count)
132
+ end
133
+ end
134
+
135
+ def test_method_missing_with_lower_limit
136
+ VCR.use_cassette(:method_missing_with_lower_limit, :allow_playback_repeats => true) do
137
+ assert_equal(201, Pillboxr.shape(:round).lower_limit(202).all.pages.current.pills.size)
138
+ end
139
+ end
140
+
141
+ def test_with_lower_limit
142
+ VCR.use_cassette(:with_lower_limit, :allow_playback_repeats => true) do
143
+ assert_equal(201, Pillboxr.with({ :shape => :round, :lower_limit => 202 }).pages.current.pills.size)
144
+ first = Pillboxr.with({ :shape => :round }).pages.current.pills
145
+ second = Pillboxr.with({ :shape => :round, :lower_limit => 202 }).pages.current.pills
146
+ first.each { |p| refute_includes(second, p)}
147
+ end
148
+ end
149
+
150
+ def test_method_chaining
151
+ VCR.use_cassette(:method_chaining) do
152
+ assert_equal(@num_blue_records_with_image, Pillboxr.color(:blue).image(true).all.record_count)
153
+ end
154
+ end
155
+
156
+ def test_method_missing_with_a_block
157
+ VCR.use_cassette(:method_missing_with_a_block) do
158
+ @result = Pillboxr.image(true).all do |r|
159
+ r.pages.each do |page|
160
+ page.get unless page.retrieved?
161
+ end
162
+ end
163
+ end
164
+ @result.pages.each { |page| assert page.retrieved? }
165
+ assert_equal(@num_image_records, @result.pages.inject(0) { |sum, page| sum + page.pills.size })
166
+ end
167
+ end