lib/intranet/pictures/json_db_provider.rb in intranet-pictures-1.1.0 vs lib/intranet/pictures/json_db_provider.rb in intranet-pictures-2.0.0

- old
+ new

@@ -3,50 +3,47 @@ require 'json' require 'mimemagic' module Intranet module Pictures - # Provides pictures data and pictures groups listings from a database file in JSON format. + # Provides pictures data and pictures listings from a database file in JSON format. # # === Structure of the JSON database # See the example below. # # The title of the pictures gallery is indicated in +title+ # # Pictures are described individually as a hash in the +pictures+ array. The mandatory keys in # this hash are: - # * +id+ : unique identifier of the picture # * +uri+ : location of the picture, relative to the JSON file # * +title+ : description of the picture # * +datetime+ : date and time of the picture, as a string in ISO8601 format # * +height+ and +width+ : size in pixels of the picture # - # Pictures are meant to be grouped in various group types (event, city, region/country, ...). - # Each group type is defined as an entry in the +groups+ hash and consists of a set of groups, - # each of them described by a hash with the following keys: + # Additional keys may be added (for instance: event, city, region/country, ...); they will be + # used to group pictures sharing the same value of a given key. + # Some groups may be defined in the +groups+ hash to add a thumbnail picture and a brief text + # description for them. The possible keys in this hash are: # * +id+ : unique identifier of the group, mandatory - # * +title+ : human-readable group name, mandatory # * +brief+ : optional short text associated to the group name # * +uri+ : optional group thumbnail, relative to the JSON file # - # Each image is associated to at most one group in each group type by by a a "foreign key" - # constraint of the type *picture*.+group_type+ = *group*.+id+. # @example Structure of the JSON database (not all mandatory are present for readability) # { # "title": "gallery title", # "groups": { # "event": [ - # { "id": "party", "title": "...", "brief": "...", "uri": "party.jpg", ... }, + # { "id": "Party", "brief": "...", "uri": "party.jpg", ... }, # { ... } # ], # "city": [ - # { "id": "houston", "title": "...", "uri": "houston.png", ... }, + # { "id": "Houston", "uri": "houston.png", ... }, # { ... } # ] # }, # "pictures": [ - # { "uri": "dir/pic0.jpg", "datetime": "...", "event": "party", "city": "houston", ... }, + # { "uri": "dir/pic0.jpg", "datetime": "...", "event": "Party", "city": "Houston", ... }, # { ... } # ] # } class JsonDbProvider # Initializes a new pictures data provider. @@ -64,51 +61,20 @@ def title load_json @json.fetch('title').to_s end - # Returns the list of available group types. - # @return [Array<String>] The available group types. - # @raise KeyError If no group type is defined in JSON file. - def group_types - load_json - @json.fetch('groups').keys - end - - # Returns the list of the groups satisfying the following conditions: - # * the group is of the given +type+, and - # * at least one picture belonging to that group matches the +selector+. - # Results are returned ordered by *group*.+sort_by+ in ascending order if +asc+, and in - # descending order otherwise. - # @param type [String] The group type. - # @param selector [Hash<String,String>] The pictures selector, interpreted as a logical AND - # combination of all key/value pairs provided. - # @param sort_by [String] The group field to sort the results by, or nil if results should be - # returned without particular sorting. - # @param asc [Boolean] True to sort returned groups in ascending order, False to sort in - # descending order. - # @return [Array<Hash{'id'=>String, 'title'=>String, ...}>] The selected groups. - # @raise KeyError If JSON file is malformed, if +type+ does not match an existing group type, - # or if +sort_by+ is not an existing group field. - def list_groups(type, selector = {}, sort_by = nil, asc = true) - load_json - groups = select_groups(type, selector).map { |g| g.except('uri') } - groups.sort_by! { |g| g.fetch(sort_by) } unless sort_by.nil? - groups.reverse! unless asc - groups - end - # Returns the list of the pictures matching +selector+. - # Results are returned ordered by *picture*.+sort_by+ in ascending order if +asc+, and in - # descending order otherwise. + # Results are returned ordered by +sort_by+ in ascending order if +asc+, and in descending + # order otherwise. # @param selector [Hash<String,String>] The pictures selector, interpreted as a logical AND # combination of all key/value pairs provided. # @param sort_by [String] The picture field to sort the results by, or nil if results should # be returned without particular sorting. - # @param asc [Boolean] True to sort returned pictures in ascending order, False to sort in - # descending order. - # @return [Array<Hash{'id'=>String, 'title'=>String, 'datetime'=>String, 'height'=>Integer, + # @param asc [Boolean] True to sort returned pictures in ascending order, False to sort them + # in descending order. + # @return [Array<Hash{'title'=>String, 'datetime'=>String, 'height'=>Integer, # 'width'=>Integer, ...}>] The selected pictures. # @raise KeyError If JSON file is malformed, or if +sort_by+ is not an existing picture field. def list_pictures(selector = {}, sort_by = nil, asc = true) load_json pics = select_pictures(selector).map { |p| p.except('uri') } @@ -120,41 +86,52 @@ # Returns the picture file matching +selector+. # @param selector [Hash<String,String>] The picture selector, which should return exactly one # picture. # @return [String, Blob] The MIME type of the picture, and the picture file content. # @raise KeyError If JSON file is malformed, if selector does not match exactly one picture, - # or if the *picture*.+uri+ does not refer to an existing image file. + # or if the picture +uri+ does not refer to an existing image file. def picture(selector = {}) load_json pic = select_pictures(selector) raise KeyError unless pic.size == 1 path = File.join(@json_dir, pic.first.fetch('uri')) - open_image_file(path) + read_image_file(path) end - # Returns the thumbnail picture of the group satisfying the following conditions: - # * the group is of the given +type+, and - # * at least one picture belonging to that group matches the +selector+. - # @param type [String] The group type. - # @param selector [Hash<String,String>] The pictures selector, interpreted as a logical AND - # combination of all key/value pairs provided. The - # selector should return exactly one group. + # Returns the thumbnail picture of the group whose type is +key+ and named +value+. + # @param key [String] The group type. + # @param value [String] The group name. # @return [String, Blob] The MIME type of the picture, and the picture file content. Nil may # be returned if no thumbnail is available for that group. - # @raise KeyError If JSON file is malformed, if selector does not match exactly one group, - # or if the *group*.+uri+ does not refer to an existing image file. - def group_thumbnail(type, selector = {}) + # @raise KeyError If JSON file is malformed, if key/value do not designate exactly one group, + # or if the group +uri+ does not refer to an existing image file. + def group_thumbnail(key, value) load_json - group = select_groups(type, selector) + group = select_group(key, value) raise KeyError unless group.size == 1 return nil if group.first['uri'].nil? path = File.join(@json_dir, group.first.fetch('uri')) - open_image_file(path) + read_image_file(path) end + # Returns the brief text of the group whose type is +key+ and named +value+. + # @param key [String] The group type. + # @param value [String] The group name. + # @return [String] The brief text of the group, or an empty string if no brief is available. + # @raise KeyError If JSON file is malformed or if key/value do not designate exactly one + # group. + def group_brief(key, value) + load_json + group = select_group(key, value) + raise KeyError unless group.size == 1 + return '' if group.first['brief'].nil? + + group.first.fetch('brief') + end + private # (Re)load the JSON file if modified on disk def load_json mtime = File.mtime(@json_file) @@ -162,11 +139,11 @@ @json = JSON.parse(File.read(@json_file)) @json_time = mtime end - def open_image_file(path) + def read_image_file(path) mime_type = MimeMagic.by_path(path).type raise KeyError unless mime_type.start_with?('image/') [MimeMagic.by_path(path).type, File.read(path)] rescue Errno::ENOENT @@ -177,22 +154,15 @@ selector.all? { |k, v| picture.fetch(k) == v } rescue KeyError false end - def select_groups(type, selector) - return @json.fetch('groups').fetch(type) if selector.empty? # optimization - - groups_id = select_pictures_having(type, selector).map { |p| p.fetch(type) }.uniq.compact - @json.fetch('groups').fetch(type).select { |g| groups_id.include?(g.fetch('id')) } - end - def select_pictures(selector) @json.fetch('pictures').select { |p| picture_match?(p, selector) } end - def select_pictures_having(group_type, selector) - @json.fetch('pictures').select { |p| p.key?(group_type) && picture_match?(p, selector) } + def select_group(type, id) + @json.fetch('groups').fetch(type).select { |g| g.fetch('id') == id } end end end end