verso 0.0.2 → 0.0.3

Sign up to get free protection for your applications and to get access to all the features.
Files changed (63) hide show
  1. data/.gitignore +2 -0
  2. data/README.md +10 -22
  3. data/lib/verso.rb +10 -2
  4. data/lib/verso/base.rb +50 -0
  5. data/lib/verso/cluster.rb +55 -25
  6. data/lib/verso/cluster_list.rb +15 -10
  7. data/lib/verso/correlation_list.rb +30 -40
  8. data/lib/verso/course.rb +106 -43
  9. data/lib/verso/course_list.rb +57 -21
  10. data/lib/verso/credential.rb +94 -20
  11. data/lib/verso/credential_list.rb +30 -15
  12. data/lib/verso/duty_area.rb +23 -10
  13. data/lib/verso/edition_list.rb +18 -10
  14. data/lib/verso/emphasis.rb +26 -12
  15. data/lib/verso/emphasis_list.rb +18 -10
  16. data/lib/verso/examination_list.rb +28 -12
  17. data/lib/verso/extra.rb +34 -32
  18. data/lib/verso/extras_list.rb +28 -12
  19. data/lib/verso/frontmatter.rb +43 -11
  20. data/lib/verso/hash.rb +19 -0
  21. data/lib/verso/http_gettable.rb +31 -0
  22. data/lib/verso/occupation.rb +36 -14
  23. data/lib/verso/occupation_data.rb +35 -14
  24. data/lib/verso/occupation_list.rb +23 -20
  25. data/lib/verso/pathway.rb +32 -14
  26. data/lib/verso/program_area.rb +42 -21
  27. data/lib/verso/program_area_list.rb +15 -11
  28. data/lib/verso/standard.rb +45 -23
  29. data/lib/verso/standards_list.rb +41 -30
  30. data/lib/verso/task.rb +52 -17
  31. data/lib/verso/task_list.rb +40 -17
  32. data/lib/verso/version.rb +1 -1
  33. data/spec/cluster_list_spec.rb +78 -5
  34. data/spec/cluster_spec.rb +106 -9
  35. data/spec/correlation_list_spec.rb +108 -50
  36. data/spec/course_list_spec.rb +112 -23
  37. data/spec/course_spec.rb +321 -127
  38. data/spec/credential_list_spec.rb +83 -52
  39. data/spec/credential_spec.rb +358 -19
  40. data/spec/duty_area_spec.rb +47 -17
  41. data/spec/edition_list_spec.rb +90 -4
  42. data/spec/emphasis_list_spec.rb +75 -11
  43. data/spec/emphasis_spec.rb +37 -21
  44. data/spec/examination_list_spec.rb +146 -20
  45. data/spec/extra_spec.rb +61 -22
  46. data/spec/extras_list_spec.rb +80 -17
  47. data/spec/frontmatter_spec.rb +141 -6
  48. data/spec/hash_spec.rb +49 -0
  49. data/spec/occupation_data_spec.rb +31 -13
  50. data/spec/occupation_list_spec.rb +88 -15
  51. data/spec/occupation_spec.rb +72 -28
  52. data/spec/pathway_spec.rb +47 -27
  53. data/spec/program_area_list_spec.rb +78 -4
  54. data/spec/program_area_spec.rb +70 -22
  55. data/spec/standard_spec.rb +94 -36
  56. data/spec/standards_list_spec.rb +130 -36
  57. data/spec/task_list_spec.rb +160 -51
  58. data/spec/task_spec.rb +120 -33
  59. data/verso.gemspec +3 -1
  60. metadata +41 -17
  61. data/lib/verso/http_get.rb +0 -9
  62. data/lib/verso/sol_correlation_list.rb +0 -53
  63. data/spec/sol_correlation_list_spec.rb +0 -74
@@ -1,38 +1,40 @@
1
1
  module Verso
2
- class Extra
3
- include HTTPGet
4
-
5
- def initialize(raw_extra)
6
- @raw_extra = raw_extra
7
- end
8
-
9
- def code
10
- @raw_extra["code"]
11
- end
12
-
13
- def edition
14
- @raw_extra["edition"]
15
- end
16
-
17
-
18
- def name
19
- @raw_extra["name"]
20
- end
21
-
22
-
23
- def title
24
- @raw_extra["title"]
2
+ # Extra resource
3
+ #
4
+ # An extra (or related material) associated with a Course framework in
5
+ # Verso.
6
+ #
7
+ # @see http://api.cteresource.org/docs/courses/course/extras/extra
8
+ #
9
+ # @!attribute [r] code
10
+ # @return [String] Course code
11
+ # @!attribute [r] edition
12
+ # @return [String] Edition year
13
+ # @!attribute [r] name
14
+ # @return [String] Extra name, used as an identifier and not to
15
+ # be confused with its title, found by {Verso::ExtrasList}
16
+ # @!attribute [r] description
17
+ # @return [String] HTML-formatted Extra description
18
+ # @!attribute [r] title
19
+ # @return [String] Extra title
20
+ #
21
+ # @overload initialize(attrs={})
22
+ # @note Certain attributes are required for instantiation.
23
+ # @option attrs [String] :code Course code *Required*
24
+ # @option attrs [String] :edition Edition year *Required*
25
+ # @option attrs [String] :name Extra name *Required*
26
+ class Extra < Verso::Base
27
+ include HTTPGettable
28
+ attr_reader :code, :description, :edition, :name, :title
29
+
30
+ private
31
+
32
+ def path
33
+ "/courses/#{code},#{edition}/extras/#{name}"
25
34
  end
26
35
 
27
- def description
28
- if @raw_extra["description"].nil?
29
- @raw_extra.merge!(
30
- JSON.parse(
31
- http_get("/courses/#{code},#{edition}/extras/#{name}")
32
- )["extra"]
33
- )
34
- end
35
- @raw_extra["description"]
36
+ def fetch
37
+ super[:extra]
36
38
  end
37
39
  end
38
40
  end
@@ -1,22 +1,38 @@
1
1
  module Verso
2
- class ExtrasList
3
- attr_reader :code, :edition
2
+ # Extras List resource
3
+ #
4
+ # A collection of {Verso::Extra} associated with a {Course} framework.
5
+ #
6
+ # @see http://api.cteresource.org/docs/courses/course/extras
7
+ #
8
+ # @example Get extras for 2012 edition of course 6320
9
+ # extras = Verso::ExtrasList.new(:code => "6320", :edition => "2012")
10
+ #
11
+ # @!attribute [r] code
12
+ # @return [String] Course code
13
+ # @!attribute [r] edition
14
+ # @return [String] Edition year
15
+ #
16
+ # @overload initialize(attrs={})
17
+ # @note Certain attributes are required for instantiation.
18
+ # @option attrs [String] :code Course code *Required*
19
+ # @option attrs [String] :edition Edition year *Required*
20
+ class ExtrasList < Verso::Base
4
21
  include Enumerable
5
- include HTTPGet
22
+ include HTTPGettable
23
+ extend Forwardable
24
+ attr_reader :code, :edition
25
+ def_delegators :extras, :[], :each, :empty?, :last, :length
6
26
 
7
- def initialize(opts)
8
- @code, @edition = opts[:code], opts[:edition]
9
- end
27
+ private
10
28
 
11
29
  def extras
12
- @extras ||= JSON.parse(
13
- http_get("/courses/#{code},#{edition}/extras/")
14
- )["extras"].
15
- collect { |e| Extra.new(e.merge("code" => code, "edition" => edition)) }
30
+ @extras ||= get_attr(:extras).
31
+ collect { |e| Extra.new(e.merge(:code => code, :edition => edition)) }
16
32
  end
17
33
 
18
- def each &block
19
- extras.each &block
34
+ def path
35
+ "/courses/#{code},#{edition}/extras/"
20
36
  end
21
37
  end
22
38
  end
@@ -1,18 +1,50 @@
1
1
  module Verso
2
- class Frontmatter
3
- include HTTPGet
2
+ # Frontmatter resource
3
+ #
4
+ # Frontmatter for {Verso::Course} framework.
5
+ #
6
+ # @see http://api.cteresource.org/docs/courses/course/frontmatter
7
+ #
8
+ # @!attribute [r] acknowledgments_text
9
+ # @return [String] HTML-formatted acknowledgments
10
+ # @!attribute [r] attribution_block
11
+ # @return [String] HTML-formatted attribution
12
+ # @!attribute [r] code
13
+ # @return [String] Course code
14
+ # @!attribute [r] copyright_year
15
+ # @return [String] Copyright year
16
+ # @!attribute [r] developed_by
17
+ # @return [String] HTML-formatted developed-by text
18
+ # @!attribute [r] developed_for
19
+ # @return [String] HTML-formatted developed-for text
20
+ # @!attribute [r] edition
21
+ # @return [String] Edition year
22
+ # @!attribute [r] foreword_text
23
+ # @return [String] HTML-formatted foreword
24
+ # @!attribute [r] introduction_text
25
+ # @return [String] HTML-formatted introduction_text
26
+ # @!attribute [r] notice_block
27
+ # @return [String] HTML formatted notice_block
28
+ #
29
+ # @overload initialize(attrs={})
30
+ # @note Any attributes may be set upon instantiation, using Options Hash.
31
+ # The following are required:
32
+ # @option attrs [String] :code Course code *Required*
33
+ # @option attrs [String] :edition Edition year *Required*
34
+ class Frontmatter < Verso::Base
35
+ include HTTPGettable
36
+ attr_reader :acknowledgments_text, :attribution_block, :code,
37
+ :copyright_year, :developed_by, :developed_for, :edition,
38
+ :foreword_text, :introduction_text, :notice_block
4
39
 
5
- def initialize(opts)
6
- @raw_frontmatter = opts
40
+ private
41
+
42
+ def fetch
43
+ super[:frontmatter]
7
44
  end
8
45
 
9
- def method_missing(mname)
10
- if !@raw_frontmatter.has_key?(mname.to_s)
11
- @raw_frontmatter = JSON.parse(
12
- http_get("/courses/#{@raw_frontmatter['code']},#{@raw_frontmatter['edition']}/frontmatter")
13
- )["frontmatter"]
14
- end
15
- @raw_frontmatter[mname.to_s]
46
+ def path
47
+ "/courses/#{code},#{edition}/frontmatter"
16
48
  end
17
49
  end
18
50
  end
@@ -0,0 +1,19 @@
1
+ class Hash
2
+ def symbolize_nested_keys!
3
+ keys.each do |key|
4
+ self[(key.to_sym rescue key) || key] = if self[key].is_a?(Hash)
5
+ delete(key).symbolize_nested_keys!
6
+ elsif self[key].is_a?(Array)
7
+ delete(key).collect do |i|
8
+ if i.is_a?(Hash)
9
+ i.symbolize_nested_keys!
10
+ end
11
+ i
12
+ end
13
+ else
14
+ delete(key)
15
+ end
16
+ end
17
+ self
18
+ end
19
+ end
@@ -0,0 +1,31 @@
1
+ module Verso
2
+ # Mixin for classes derived from {Verso::Base}. Provides the capability to
3
+ # fetch the JSON resource to update attributes of the class.
4
+ #
5
+ # @abstract Implement a path method that gives the relative path of the
6
+ # corresponding JSON resource.
7
+ module HTTPGettable
8
+ protected
9
+
10
+ # Get an attribute from the attributes store {#attrs}. Fetch and merge
11
+ # first if the attribute store doesn't have the key.
12
+ #
13
+ # @param [Symbol] attr The attribute key
14
+ # @return [Object] The attribute
15
+ def get_attr(attr)
16
+ attrs.merge!(fetch) unless attrs.has_key?(attr)
17
+ super attr
18
+ end
19
+
20
+ # HTTP GET the JSON resource
21
+ # @return [String] JSON resource
22
+ def http_get
23
+ Net::HTTP.get('api.cteresource.org', path, 80)
24
+ end
25
+
26
+ # @return [Hash] Resource hash
27
+ def fetch
28
+ JSON.parse(http_get).symbolize_nested_keys!
29
+ end
30
+ end
31
+ end
@@ -1,25 +1,47 @@
1
1
  module Verso
2
- class Occupation
3
- include HTTPGet
2
+ # Occupation resource
3
+ #
4
+ # An occupation is contained by a Pathway
5
+ #
6
+ # @see http://api.cteresource.org/docs/occupations/occupation
7
+ # @see http://www.careertech.org/career-clusters/clusters-occupations.html
8
+ #
9
+ # @!attribute [r] description
10
+ # @return [String] HTML-formatted Occupation description
11
+ # @!attribute [r] id
12
+ # @return [Fixnum] Occupation ID
13
+ # @!attribute [r] preparations
14
+ # @return [Array] Collection of preparation titles (strings)
15
+ # @!attribute [r] title
16
+ # @return [String] Occupation title
17
+ #
18
+ # @overload initialize(attrs={})
19
+ # @note Any attributes may be set upon instantiation, using Options Hash.
20
+ # The following are required:
21
+ # @option attrs [Fixnum] :id Occupation ID *Required*
22
+ class Occupation < Verso::Base
23
+ include HTTPGettable
24
+ attr_reader :description, :id, :preparations, :title
4
25
 
5
- def initialize(raw_occupation)
6
- @raw_occupation = raw_occupation
26
+ # @return [Verso::Pathway] Pathway that is the parent of this occupation
27
+ def pathway
28
+ @pathway ||= Pathway.new(get_attr(:pathway))
7
29
  end
8
30
 
9
- def method_missing(mname)
10
- if @raw_occupation[mname.to_s].nil?
11
- @raw_occupation = JSON.parse(http_get("/occupations/#{id}"))["occupation"]
12
- end
13
- @raw_occupation[mname.to_s]
31
+ # @return [Array] Collection of related {Verso::Course} objects
32
+ def related_courses
33
+ @related_courses ||= get_attr(:related_courses).
34
+ collect { |c| Course.new(c) }
14
35
  end
15
36
 
16
- def related_courses
17
- @related_courses ||= method_missing(:related_courses).
18
- collect { |c| Course.new(c) }
37
+ private
38
+
39
+ def fetch
40
+ super[:occupation]
19
41
  end
20
42
 
21
- def pathway
22
- @pathway ||= Pathway.new(method_missing(:pathway))
43
+ def path
44
+ "/occupations/#{id}"
23
45
  end
24
46
  end
25
47
  end
@@ -1,30 +1,51 @@
1
1
  module Verso
2
- class OccupationData
3
- def initialize(od)
4
- @raw_od = od
5
- end
6
-
2
+ # Occupation Data
3
+ #
4
+ # A list of occupations grouped by the Cluster/Pathway that contains them.
5
+ # Occupation Data is not an independent resource. It is a component of
6
+ # {Verso::OccupationList}, {Verso::Course}, and {Verso::Emphasis}.
7
+ #
8
+ # @see http://api.cteresource.org/docs/academics/emphasis
9
+ # @see http://api.cteresource.org/docs/courses/course
10
+ # @see http://api.cteresource.org/docs/occupations
11
+ #
12
+ # @note Because an OccupationData object is created for you by other
13
+ # resources you should never need to instantiate one yourself.
14
+ class OccupationData < Verso::Base
15
+ # @return [Verso::Cluster] Cluster (grandparent of occupations)
7
16
  def cluster
8
- @cluster ||= Cluster.new(@raw_od["cluster"])
17
+ @cluster ||= Cluster.new(get_attr(:cluster))
9
18
  end
10
19
 
11
- def pathway
12
- @pathway ||= Pathway.new(@raw_od["pathway"])
20
+ # @return [Array] Collection of {Verso::Occupation} objects
21
+ def occupations
22
+ @occupations ||= get_attr(:occupations).collect { |o| Occupation.new(o) }
13
23
  end
14
24
 
15
- def occupations
16
- @occupations ||= @raw_od["occupations"].
17
- collect { |o| Occupation.new(o) }
25
+ # @return [Verso::Pathway] Pathway (parent of occupations)
26
+ def pathway
27
+ @pathway ||= Pathway.new(get_attr(:pathway))
18
28
  end
19
29
 
30
+ # Find OccupationData by cluster, pathway, and occupation slugs
31
+ #
32
+ # @example Find an OccupationData for an occupation
33
+ # od = Verso::OccupationData.find_by_slugs('finance', 'accounting', 'cost-analyst')
34
+ # od.occupations.count # => 1
35
+ # od.occupations.first.title # => 'Cost Analyst'
36
+ #
37
+ # @param [String] cslug Cluster slug
38
+ # @param [String] pslug Pathway slug
39
+ # @param [String] slug Occupation slug
40
+ # @return [Verso::OccupationData,nil] Containing only one Occupation
20
41
  def self.find_by_slugs(cslug, pslug, slug)
21
42
  cluster = ClusterList.new.find { |c| c.title.parameterize == cslug }
22
43
  pathway = cluster.pathways.find { |p| p.title.parameterize == pslug }
23
44
  occupation = pathway.occupations.find { |o| o.title.parameterize == slug }
24
45
  OccupationData.new(
25
- { "cluster" => { "title" => cluster.title },
26
- "pathway" => { "title" => pathway.title },
27
- "occupations" => [{ "title" => occupation.title }] }
46
+ { :cluster => { :title => cluster.title },
47
+ :pathway => { :title => pathway.title },
48
+ :occupations => [{ :title => occupation.title }] }
28
49
  )
29
50
  end
30
51
  end
@@ -1,32 +1,35 @@
1
1
  module Verso
2
- class OccupationList
2
+ # Occupation list resource
3
+ #
4
+ # Search {Verso::Occupation} objects using free text. Result is a collection
5
+ # of {Verso::OccupationData} objects containing relevant {Verso::Occupation}
6
+ # objects.
7
+ #
8
+ # @see http://api.cteresource.org/docs/occupations
9
+ #
10
+ # @example Search by free text
11
+ # ods = Verso::OccupationList.new(:text => "golf") # => golf-related ODs
12
+ #
13
+ # @overload initialize(attrs={})
14
+ # @option attrs [String] :text Free text
15
+ class OccupationList < Verso::Base
3
16
  include Enumerable
4
- include HTTPGet
17
+ include HTTPGettable
18
+ extend Forwardable
19
+ def_delegators :occupations, :[], :each, :empty?, :last, :length
5
20
 
6
- def initialize(raw_query={})
7
- @q_uri = Addressable::URI.new(
8
- :path => '/occupations',
9
- :query_values => raw_query
10
- )
11
- end
21
+ private
12
22
 
13
23
  def occupations
14
- @occupations ||= JSON.
15
- parse(http_get(@q_uri.request_uri))["occupation_data"].
24
+ return [] if attrs[:text].to_s.empty?
25
+ @occupations ||= get_attr(:occupation_data).
16
26
  collect { |o| OccupationData.new(o) }.
17
27
  sort_by { |o| [o.cluster.title, o.pathway.title] }
18
28
  end
19
29
 
20
- def each &block
21
- occupations.each &block
22
- end
23
-
24
- def last
25
- occupations[-1]
26
- end
27
-
28
- def empty?
29
- occupations.empty?
30
+ def path
31
+ @path ||= Addressable::URI.new(:path => '/occupations',
32
+ :query_values => attrs).request_uri
30
33
  end
31
34
  end
32
35
  end
@@ -1,25 +1,43 @@
1
1
  module Verso
2
- class Pathway
3
- include HTTPGet
2
+ # Pathway Resource
3
+ #
4
+ # @see http://api.cteresource.org/docs/pathways/pathway
5
+ # @see http://www.careertech.org/career-clusters/clusters-occupations.html
6
+ #
7
+ # @!attribute [r] description
8
+ # @return [String] HTML-formatted Pathway description
9
+ # @!attribute [r] id
10
+ # @return [Fixnum] Pathway id
11
+ # @!attribute [r] title
12
+ # @return [String] Pathway title
13
+ #
14
+ # @overload initialize(attrs={})
15
+ # @note Any attributes may be set upon instantiation, using Options Hash.
16
+ # The following are required:
17
+ # @option attrs [Fixnum] :id Pathway id *Required*
18
+ class Pathway < Verso::Base
19
+ include HTTPGettable
20
+ attr_reader :description, :id, :title
4
21
 
5
- def initialize(raw_pathway)
6
- @raw_pathway = raw_pathway
7
- end
8
-
9
- def method_missing(mname)
10
- if @raw_pathway[mname.to_s].nil?
11
- @raw_pathway = JSON.parse(http_get("/pathways/#{id}"))["pathway"]
12
- end
13
- @raw_pathway[mname.to_s]
22
+ # @return [Verso::Cluster] Parent Cluster
23
+ def cluster
24
+ @cluster ||= Cluster.new(get_attr(:cluster))
14
25
  end
15
26
 
27
+ # @return [Array] Children {Verso::Occupation} objects
16
28
  def occupations
17
- @occupations ||= method_missing(:occupations).
29
+ @occupations ||= get_attr(:occupations).
18
30
  collect { |o| Occupation.new(o) }
19
31
  end
20
32
 
21
- def cluster
22
- @cluster ||= Cluster.new(method_missing(:cluster))
33
+ private
34
+
35
+ def fetch
36
+ super[:pathway]
37
+ end
38
+
39
+ def path
40
+ "/pathways/#{id}"
23
41
  end
24
42
  end
25
43
  end