supplejack_client 1.0.1

Sign up to get free protection for your applications and to get access to all the features.
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