supplejack_client 1.0.1

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 (47) hide show
  1. checksums.yaml +7 -0
  2. data/.gitignore +27 -0
  3. data/.rspec +2 -0
  4. data/Gemfile +16 -0
  5. data/Guardfile +24 -0
  6. data/LICENSE.txt +674 -0
  7. data/README.md +153 -0
  8. data/Rakefile +9 -0
  9. data/lib/generators/locales/en.yml +11 -0
  10. data/lib/generators/supplejack/install_generator.rb +28 -0
  11. data/lib/generators/templates/README +19 -0
  12. data/lib/generators/templates/supplejack_client.rb +120 -0
  13. data/lib/supplejack/config.rb +116 -0
  14. data/lib/supplejack/controllers/helpers.rb +172 -0
  15. data/lib/supplejack/engine.rb +20 -0
  16. data/lib/supplejack/exceptions.rb +17 -0
  17. data/lib/supplejack/facet.rb +33 -0
  18. data/lib/supplejack/item.rb +94 -0
  19. data/lib/supplejack/item_relation.rb +73 -0
  20. data/lib/supplejack/log_subscriber.rb +58 -0
  21. data/lib/supplejack/paginated_collection.rb +61 -0
  22. data/lib/supplejack/record.rb +147 -0
  23. data/lib/supplejack/request.rb +95 -0
  24. data/lib/supplejack/search.rb +346 -0
  25. data/lib/supplejack/url_formats/item_hash.rb +208 -0
  26. data/lib/supplejack/user.rb +132 -0
  27. data/lib/supplejack/user_set.rb +349 -0
  28. data/lib/supplejack/user_set_relation.rb +143 -0
  29. data/lib/supplejack/util.rb +120 -0
  30. data/lib/supplejack/version.rb +10 -0
  31. data/lib/supplejack_client.rb +29 -0
  32. data/spec/spec_helper.rb +23 -0
  33. data/spec/supplejack/controllers/helpers_spec.rb +277 -0
  34. data/spec/supplejack/facet_spec.rb +44 -0
  35. data/spec/supplejack/item_relation_spec.rb +111 -0
  36. data/spec/supplejack/item_spec.rb +115 -0
  37. data/spec/supplejack/log_subscriber_spec.rb +40 -0
  38. data/spec/supplejack/paginated_collection_spec.rb +43 -0
  39. data/spec/supplejack/record_spec.rb +255 -0
  40. data/spec/supplejack/request_spec.rb +195 -0
  41. data/spec/supplejack/search_spec.rb +727 -0
  42. data/spec/supplejack/url_formats/item_hash_spec.rb +341 -0
  43. data/spec/supplejack/user_set_relation_spec.rb +149 -0
  44. data/spec/supplejack/user_set_spec.rb +465 -0
  45. data/spec/supplejack/user_spec.rb +159 -0
  46. data/supplejack_client.gemspec +30 -0
  47. metadata +159 -0
@@ -0,0 +1,172 @@
1
+ # The Supplejack Client code is Crown copyright (C) 2014, New Zealand Government,
2
+ # and is licensed under the GNU General Public License, version 3.
3
+ # See https://github.com/DigitalNZ/supplejack_client for details.
4
+ #
5
+ # Supplejack was created by DigitalNZ at the National Library of NZ
6
+ # and the Department of Internal Affairs. http://digitalnz.org/supplejack
7
+
8
+ require 'rails_autolink'
9
+
10
+ module Supplejack
11
+ module Controllers
12
+ module Helpers
13
+ extend ActiveSupport::Concern
14
+
15
+ def search(special_params=nil)
16
+ return @supplejack_search if @supplejack_search
17
+ klass = Supplejack.search_klass ? Supplejack.search_klass.classify.constantize : Supplejack::Search
18
+ @supplejack_search = klass.new(special_params || params[:search] || params)
19
+ end
20
+
21
+ # Displays a record attribute with a label and allows you to customize the
22
+ # HTML markup generated. It will not display anything if the value is blank or nil.
23
+ #
24
+ # @param [ Record ] record Class which includes the Supplejack::Record module
25
+ # @param [ Symbol ] attribute The name of the attribute to return
26
+ # @param [ Hash ] options Hash of options to customize the output,
27
+ # supported options: :label, :limit, :delimiter, :link_path, :tag
28
+ #
29
+ # @option options [ true, false ] :label Display the attribute name
30
+ # @option options [ String ] :label_tag HTML tag to surround label
31
+ # @option options [ String ] :label_class CSS class to apply to label_tag
32
+ # @option options [ Integer ] :limit Number of charachters to truncate or number of values (when multivalue field)
33
+ # @option options [ Integer ] :delimiter Used to separate multi value attributes
34
+ # @option options [ String ] :link_path The method name of a routes path when provided it will generate links for every value
35
+ # @option options [ Symbol ] :tag HTML tag to wrap the label and value
36
+ # @option options [ true, false, String ] :link When true will try to make the value into a link, When is a string it will try
37
+ # to find a {{value}} within the string and replace it with the value.
38
+ # @option options [ String ] :extra_html HTML which will be included inside the tag at the end.
39
+ # @option options [ Symbol ] :tag_class The class for the attribute tag
40
+ #
41
+ # @return [ String ] A HTML snippet with the attribute name and value
42
+ #
43
+ def attribute(record, attributes, options={})
44
+ options.reverse_merge!(:label => true, :label_inline => true, :limit => nil, :delimiter => ", ",
45
+ :link_path => false, :tag => Supplejack.attribute_tag, :label_tag => Supplejack.label_tag,
46
+ :label_class => Supplejack.label_class, :trans_key => nil, :link => false,
47
+ :extra_html => nil, :tag_class => nil)
48
+
49
+ value = []
50
+ attributes = [attributes] unless attributes.is_a?(Array)
51
+ attributes.each do |attribute|
52
+ if attribute.is_a?(String) && attribute.match(/\./)
53
+ object, attr = attribute.split(".")
54
+ v = record.try(object.to_sym).try(attr.to_sym) if object && attr
55
+ else
56
+ v = record.send(attribute)
57
+ end
58
+
59
+ if v.is_a?(Array)
60
+ value += v.compact
61
+ else
62
+ value << v if v.present?
63
+ end
64
+ end
65
+
66
+ value = value.first if value.size == 1
67
+ attribute = attributes.first
68
+
69
+ if value.is_a?(Array)
70
+ if options[:limit] && options[:limit].to_i > 0
71
+ value = value[0..(options[:limit].to_i-1)]
72
+ end
73
+
74
+ if options[:link_path]
75
+ value = value.map {|v| link_to(v, send(options[:link_path], {:i => {attribute => v}})) }
76
+ end
77
+
78
+ if options[:link]
79
+ value = value.map do |v|
80
+ attribute_link_replacement(v, options[:link])
81
+ end
82
+ end
83
+
84
+ value = value.join(options[:delimiter]).html_safe
85
+ value = truncate(value, :length => options[:limit]) if options[:limit].to_i > 20
86
+ value
87
+ else
88
+ if options[:limit] && options[:limit].to_i > 0
89
+ value = truncate(value, :length => options[:limit].to_i)
90
+ end
91
+
92
+ if options[:link]
93
+ value = attribute_link_replacement(value, options[:link])
94
+ end
95
+ end
96
+
97
+ content = ""
98
+ if options[:label]
99
+ if options[:trans_key].present?
100
+ translation = I18n.t(options[:trans_key], :default => attribute.to_s.capitalize) + ": "
101
+ else
102
+ i18n_class_name = record.class.to_s.tableize.downcase.gsub(/\//, "_")
103
+ translation = "#{I18n.t("#{i18n_class_name}.#{attribute}", :default => attribute.to_s.capitalize)}: "
104
+ end
105
+ content = content_tag(options[:label_tag], translation, :class => options[:label_class]).html_safe
106
+ content << "<br/>".html_safe unless options[:label_inline]
107
+ end
108
+
109
+ content << value.to_s
110
+ content << options[:extra_html] if options[:extra_html]
111
+ if value.present? and value != "Not specified"
112
+ options[:tag] ? content_tag(options[:tag], content.html_safe, :class => options[:tag_class]) : content.html_safe
113
+ end
114
+ end
115
+
116
+ # Displays the next and/or previous links based on the record and current search
117
+ #
118
+ # @params [ Dnz::Record ] The record object which has information about the next/previous record and pages.
119
+ # @params [ Hash ] options Hash of options to customize the output,
120
+ # supported options: :prev_class, :next_class, :prev_label, :next_label
121
+ #
122
+ # @option options [ String ] :prev_class The CSS class to use on the previous button
123
+ # @option options [ String ] :next_class The CSS class to use on the next button
124
+ # @option options [ String ] :wrapper_class The CSS class to use on the wrapping span
125
+ # @option options [ String ] :prev_label Any HTML to be put inside the previous button
126
+ # @option options [ String ] :next_label Any HTML to be put inside the next button
127
+ #
128
+ def next_previous_links(record, html_options={})
129
+ html_options.reverse_merge!({prev_class: "prev", next_class: "next", wrapper_class: 'nav', prev_label: nil, next_label: nil})
130
+
131
+ return "" unless params[:search]
132
+ links = "".html_safe
133
+
134
+ options = search.options
135
+
136
+ previous_label = html_options[:prev_label] ||= t('supplejack_client.previous', default: "Previous")
137
+ next_label = html_options[:next_label] ||= t('supplejack_client.next', default: "Next")
138
+ previous_label = previous_label.html_safe
139
+ next_label = next_label.html_safe
140
+
141
+ options[:path] = params[:search][:path].gsub(/(\W|\d)/, '') if params[:search] && params[:search][:path]
142
+
143
+ if record.previous_record
144
+ options[:page] = record.previous_page if record.previous_page.to_i > 1
145
+ links += link_to(raw(previous_label), record_path(record.previous_record, search: options), class: html_options[:prev_class]).html_safe
146
+ else
147
+ links += content_tag(:span, previous_label, class: html_options[:prev_class])
148
+ end
149
+
150
+ if record.next_record
151
+ options[:page] = record.next_page if record.next_page.to_i > 1
152
+ links += link_to(raw(next_label), record_path(record.next_record, search: options), class: html_options[:next_class]).html_safe
153
+ else
154
+ links += content_tag(:span, next_label, class: html_options[:next_class])
155
+ end
156
+
157
+ content_tag(:span, links, class: html_options[:wrapper_class])
158
+ end
159
+
160
+ def attribute_link_replacement(value, link_pattern)
161
+ if link_pattern.is_a?(String)
162
+ link_pattern = URI.decode(link_pattern)
163
+ url = link_pattern.gsub("{{value}}", value)
164
+ link_to(value, url)
165
+ else
166
+ auto_link value
167
+ end
168
+ end
169
+
170
+ end
171
+ end
172
+ end
@@ -0,0 +1,20 @@
1
+ # The Supplejack Client code is Crown copyright (C) 2014, New Zealand Government,
2
+ # and is licensed under the GNU General Public License, version 3.
3
+ # See https://github.com/DigitalNZ/supplejack_client for details.
4
+ #
5
+ # Supplejack was created by DigitalNZ at the National Library of NZ
6
+ # and the Department of Internal Affairs. http://digitalnz.org/supplejack
7
+
8
+ require 'supplejack_client'
9
+ require 'rails'
10
+
11
+ module Supplejack
12
+ class Engine < Rails::Engine
13
+
14
+ initializer "supplejack.helpers" do
15
+ ActionView::Base.send :include, Supplejack::Controllers::Helpers
16
+ ActionController::Base.send :include, Supplejack::Controllers::Helpers
17
+ end
18
+
19
+ end
20
+ end
@@ -0,0 +1,17 @@
1
+ # The Supplejack Client code is Crown copyright (C) 2014, New Zealand Government,
2
+ # and is licensed under the GNU General Public License, version 3.
3
+ # See https://github.com/DigitalNZ/supplejack_client for details.
4
+ #
5
+ # Supplejack was created by DigitalNZ at the National Library of NZ
6
+ # and the Department of Internal Affairs. http://digitalnz.org/supplejack
7
+
8
+ module Supplejack
9
+ class RecordNotFound < StandardError
10
+ end
11
+
12
+ class SetNotFound < StandardError
13
+ end
14
+
15
+ class MalformedRequest < StandardError
16
+ end
17
+ end
@@ -0,0 +1,33 @@
1
+ # The Supplejack Client code is Crown copyright (C) 2014, New Zealand Government,
2
+ # and is licensed under the GNU General Public License, version 3.
3
+ # See https://github.com/DigitalNZ/supplejack_client for details.
4
+ #
5
+ # Supplejack was created by DigitalNZ at the National Library of NZ
6
+ # and the Department of Internal Affairs. http://digitalnz.org/supplejack
7
+
8
+ module Supplejack
9
+ class Facet
10
+
11
+ attr_reader :name
12
+
13
+ def initialize(name, values)
14
+ @name = name
15
+ @values = values
16
+ end
17
+
18
+ def values(sort=nil)
19
+ sort = sort || Supplejack.facets_sort
20
+
21
+ array = case sort.try(:to_sym)
22
+ when :index
23
+ @values.sort_by {|k,v| k.to_s }
24
+ when :count
25
+ @values.sort_by {|k,v| -v.to_i }
26
+ else
27
+ @values.to_a
28
+ end
29
+
30
+ ActiveSupport::OrderedHash[array]
31
+ end
32
+ end
33
+ end
@@ -0,0 +1,94 @@
1
+ # The Supplejack Client code is Crown copyright (C) 2014, New Zealand Government,
2
+ # and is licensed under the GNU General Public License, version 3.
3
+ # See https://github.com/DigitalNZ/supplejack_client for details.
4
+ #
5
+ # Supplejack was created by DigitalNZ at the National Library of NZ
6
+ # and the Department of Internal Affairs. http://digitalnz.org/supplejack
7
+
8
+ module Supplejack
9
+
10
+ # The +Item+ class represents a SetItem on the Supplejack API
11
+ #
12
+ # An Item always belongs to a UserSet. In the API the SetItem class
13
+ # only has a record_id and position, but it gets augmented with some
14
+ # of the attributes of the Record that it represents.
15
+ #
16
+ # An Item object can have the following values:
17
+ # - record_id
18
+ # - position
19
+ #
20
+ # If more attributes of the Record are needed, they should be added to the SetItem::ATTRIBUTES
21
+ # array in the API SetItem model.
22
+ #
23
+ class Item
24
+ include Supplejack::Request
25
+
26
+ ATTRIBUTES = [:record_id, :date]
27
+
28
+ attr_reader *ATTRIBUTES
29
+ attr_reader :attributes, :user_set_id
30
+ attr_accessor :api_key, :errors, :position
31
+
32
+ def initialize(attributes={})
33
+ @attributes = attributes.try(:symbolize_keys) || {}
34
+ @user_set_id = @attributes[:user_set_id]
35
+ @api_key = @attributes[:api_key]
36
+ @position = @attributes[:position]
37
+
38
+ ATTRIBUTES.each do |attribute|
39
+ self.instance_variable_set("@#{attribute}", @attributes[attribute])
40
+ end
41
+ end
42
+
43
+ def attributes
44
+ Hash[ATTRIBUTES.map {|attr| [attr, self.send(attr)]}]
45
+ end
46
+
47
+ def id
48
+ self.record_id
49
+ end
50
+
51
+ # Executes a POST request to the API to persist the current Item
52
+ #
53
+ # @return [ true, false ] True if the API response was successful, false if not.
54
+ #
55
+ def save
56
+ begin
57
+ api_attributes = {record_id: self.record_id}
58
+ api_attributes[:position] = self.position if self.position.present?
59
+ post("/sets/#{self.user_set_id}/records", {api_key: self.api_key}, {record: api_attributes})
60
+ Rails.cache.delete("/users/#{self.api_key}/sets") if Supplejack.enable_caching
61
+ return true
62
+ rescue StandardError => e
63
+ self.errors = e.inspect
64
+ return false
65
+ end
66
+ end
67
+
68
+ # Executes a DELETE request to the API to remove the current Item
69
+ #
70
+ # @return [ true, false ] True if the API response was successful, false if not.
71
+ #
72
+ def destroy
73
+ begin
74
+ delete("/sets/#{self.user_set_id}/records/#{self.record_id}", {api_key: self.api_key})
75
+ Rails.cache.delete("/users/#{self.api_key}/sets") if Supplejack.enable_caching
76
+ return true
77
+ rescue StandardError => e
78
+ self.errors = e.inspect
79
+ return false
80
+ end
81
+ end
82
+
83
+ # Date getter to force the date attribute to be a Date object.
84
+ #
85
+ def date
86
+ @date = @date.first if @date.is_a?(Array)
87
+ Time.parse(@date) rescue nil if @date
88
+ end
89
+
90
+ def method_missing(symbol, *args, &block)
91
+ return nil
92
+ end
93
+ end
94
+ end
@@ -0,0 +1,73 @@
1
+ # The Supplejack Client code is Crown copyright (C) 2014, New Zealand Government,
2
+ # and is licensed under the GNU General Public License, version 3.
3
+ # See https://github.com/DigitalNZ/supplejack_client for details.
4
+ #
5
+ # Supplejack was created by DigitalNZ at the National Library of NZ
6
+ # and the Department of Internal Affairs. http://digitalnz.org/supplejack
7
+
8
+ module Supplejack
9
+
10
+ # The +ItemRelation+ class provides ActiveRecord like functionality to the
11
+ # relationship between a UserSet object and it's Item objects.
12
+ #
13
+ # @exmaple
14
+ # user_set = user.sets.first
15
+ #
16
+ # user_set.items.build({record_id: 1}) => Returns a new Item object linked to the UserSet
17
+ # user_set.items.find(1) => Return a Item object which belongs to the UserSet
18
+ #
19
+ class ItemRelation
20
+ include Supplejack::Request
21
+
22
+ attr_reader :user_set, :items
23
+
24
+ def initialize(user_set)
25
+ @user_set = user_set
26
+ items_array = user_set.attributes[:records] || []
27
+ @items = items_array.map { |hash| Supplejack::Item.new(hash.merge(user_set_id: user_set.id, api_key: user_set.api_key)) }
28
+ end
29
+
30
+ # Returns an Array with all items for the current UserSet
31
+ #
32
+ def all
33
+ @items
34
+ end
35
+
36
+ # Finds the item based on the record_id and returns it.
37
+ #
38
+ def find(record_id)
39
+ @items.detect {|i| i.record_id == record_id.to_i}
40
+ end
41
+
42
+ # Initializes a new Item with the provided attributes
43
+ #
44
+ # @return [ Supplejack::Item ] A new Supplejack::Item object
45
+ #
46
+ def build(attributes={})
47
+ attributes ||= {}
48
+ attributes[:user_set_id] = self.user_set.id
49
+ attributes[:api_key] = self.user_set.api_key
50
+ Supplejack::Item.new(attributes)
51
+ end
52
+
53
+ # Initializes and persists the Item to the API
54
+ #
55
+ # @return [ true, false ] True if the API response was successful, false if not.
56
+ #
57
+ def create(attributes={})
58
+ item = self.build(attributes)
59
+ item.save
60
+ end
61
+
62
+ # Any method missing on this class is delegated to the Item objects array
63
+ # so that the developer can easily execute any Array method on the ItemRelation
64
+ #
65
+ # @example
66
+ # user_set.items.each .... => Iterate through the Item objects array
67
+ # user_set.items.size => Get the size of the Item objects array
68
+ #
69
+ def method_missing(method, *args, &block)
70
+ @items.send(method, *args, &block)
71
+ end
72
+ end
73
+ end
@@ -0,0 +1,58 @@
1
+ # The Supplejack Client code is Crown copyright (C) 2014, New Zealand Government,
2
+ # and is licensed under the GNU General Public License, version 3.
3
+ # See https://github.com/DigitalNZ/supplejack_client for details.
4
+ #
5
+ # Supplejack was created by DigitalNZ at the National Library of NZ
6
+ # and the Department of Internal Affairs. http://digitalnz.org/supplejack
7
+
8
+ module Supplejack
9
+ class LogSubscriber < ActiveSupport::LogSubscriber
10
+
11
+ def log_request(duration, payload, solr_request_params={})
12
+ return unless Supplejack.enable_debugging
13
+
14
+ solr_request_params ||= {}
15
+ payload ||= {}
16
+ payload.reverse_merge!({:params => {}, :options => {}, :payload => {}})
17
+ method = payload[:method] || :get
18
+
19
+ name = '%s (%.1fms)' % ["Supplejack API #{api_environment}", duration]
20
+
21
+ parameters = payload[:params].map { |k, v| "#{k}: #{colorize(v, BOLD)}" }.join(', ')
22
+ body = payload[:payload].map { |k, v| "#{k}: #{colorize(v, BOLD)}" }.join(', ')
23
+ options = payload[:options].map { |k, v| "#{k}: #{colorize(v, BOLD)}" }.join(', ')
24
+ request = "#{method.to_s.upcase} path=#{payload[:path]} params={#{parameters}}, body={#{body}} options={#{options}}"
25
+
26
+ if payload[:exception]
27
+ info = "\n #{colorize('Exception', RED)} [ #{payload[:exception].join(', ')} ]"
28
+ else
29
+ info = ""
30
+ if solr_request_params.try(:any?)
31
+ solr_request_params = solr_request_params.map { |k, v| "#{k}: #{colorize(v, BOLD)}" }.join(', ')
32
+ info = "\n #{colorize('SOLR Request', YELLOW)} [ #{solr_request_params} ]"
33
+ end
34
+ end
35
+
36
+ debug " #{colorize(name, GREEN)} [ #{request} ] #{info}"
37
+ end
38
+
39
+ def colorize(text, color)
40
+ if text.is_a?(Hash)
41
+ "{#{text.map {|k, v| "#{k}: #{colorize(v, color)}" }.join(', ')}}"
42
+ elsif text.is_a?(Array)
43
+ "[#{text.map {|e| colorize(e, color) }.join(', ')}]"
44
+ else
45
+ "#{BOLD}#{color}#{text}#{CLEAR}"
46
+ end
47
+ end
48
+
49
+ def api_environment
50
+ case Supplejack.api_url
51
+ when "http://api.digitalnz.org" then "Prod"
52
+ when /(hippo.uat)|(api.uat)/ then "Staging"
53
+ else
54
+ "Dev"
55
+ end
56
+ end
57
+ end
58
+ end