quintype 0.0.2 → 0.0.3

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.
checksums.yaml CHANGED
@@ -1,7 +1,7 @@
1
1
  ---
2
2
  SHA1:
3
- metadata.gz: 0b6eaa40ec0c1992393a079355cd4ef4e4a0345f
4
- data.tar.gz: 3fd24dbc3782ead2833bfbd7b84d98fbd272cf12
3
+ metadata.gz: fefb62987388a3665a201c7b5516bb4b16207319
4
+ data.tar.gz: f2f0d1d2a058b4f884b9fd9848bc836d31022bdb
5
5
  SHA512:
6
- metadata.gz: 1be78480a44db61e2067c9b3b779df58a7cc4063ccc864ed2f7a93ad6fed41f93b9a5dd472191c39d5320054de1dd917875316e5f13ba6ebb5aece20ed86b5aa
7
- data.tar.gz: 4ad1e0588190e7b54929e5d49fe2b3bcc829251736b7dd9332b65574dc8c698f00ca7b0a088b0f2f087a94d46ac055e8e5c5a9f903a6042b745ef624f0d2005b
6
+ metadata.gz: 6c1d743109ee6717b5d3ba473c93601387d0ff9f3b91085cdef31c81b0f9e8a5eda044b3defd55450b1d278a726c56656c2bf62c358927a51b7eb76e6c70b590
7
+ data.tar.gz: 676ec05fe4c62ca5a8ac93e1a54de6c9cd721ee1496b514f1c4cb8dd7056d53c843f864a1dcd8853d0eee0a03afa27e2dab138f66ef32f6b14cc3c13dd7f6151
data/.gitignore ADDED
@@ -0,0 +1,16 @@
1
+ # See https://help.github.com/articles/ignoring-files for more about ignoring files.
2
+ #
3
+ # If you find yourself ignoring temporary files generated by your text editor
4
+ # or operating system, you probably want to add a global ignore instead:
5
+ # git config --global core.excludesfile '~/.gitignore_global'
6
+
7
+ # Ignore bundler config.
8
+ /.bundle
9
+
10
+ # Ignore all logfiles and tempfiles.
11
+ /log/*
12
+ !/log/.keep
13
+ /tmp
14
+ .DS_Store
15
+ .sass-cache/
16
+ .byebug_history
data/.rspec ADDED
@@ -0,0 +1,2 @@
1
+ --color
2
+ --require spec_helper
data/Gemfile ADDED
@@ -0,0 +1,17 @@
1
+ source "https://rubygems.org"
2
+
3
+ gemspec
4
+
5
+ group :development do
6
+ gem 'rspec', '~> 3.4'
7
+ end
8
+
9
+ group :development, :test do
10
+ gem 'pry'
11
+ gem 'byebug'
12
+ end
13
+
14
+ group :test do
15
+ gem 'vcr'
16
+ gem 'webmock'
17
+ end
data/Gemfile.lock ADDED
@@ -0,0 +1,68 @@
1
+ PATH
2
+ remote: .
3
+ specs:
4
+ quintype (0.0.0)
5
+ activesupport (~> 4.2)
6
+ faraday (~> 0.9)
7
+
8
+ GEM
9
+ remote: https://rubygems.org/
10
+ specs:
11
+ activesupport (4.2.5.1)
12
+ i18n (~> 0.7)
13
+ json (~> 1.7, >= 1.7.7)
14
+ minitest (~> 5.1)
15
+ thread_safe (~> 0.3, >= 0.3.4)
16
+ tzinfo (~> 1.1)
17
+ addressable (2.4.0)
18
+ byebug (8.2.2)
19
+ coderay (1.1.1)
20
+ crack (0.4.3)
21
+ safe_yaml (~> 1.0.0)
22
+ diff-lcs (1.2.5)
23
+ faraday (0.9.2)
24
+ multipart-post (>= 1.2, < 3)
25
+ hashdiff (0.3.0)
26
+ i18n (0.7.0)
27
+ json (1.8.3)
28
+ method_source (0.8.2)
29
+ minitest (5.8.4)
30
+ multipart-post (2.0.0)
31
+ pry (0.10.3)
32
+ coderay (~> 1.1.0)
33
+ method_source (~> 0.8.1)
34
+ slop (~> 3.4)
35
+ rspec (3.4.0)
36
+ rspec-core (~> 3.4.0)
37
+ rspec-expectations (~> 3.4.0)
38
+ rspec-mocks (~> 3.4.0)
39
+ rspec-core (3.4.3)
40
+ rspec-support (~> 3.4.0)
41
+ rspec-expectations (3.4.0)
42
+ diff-lcs (>= 1.2.0, < 2.0)
43
+ rspec-support (~> 3.4.0)
44
+ rspec-mocks (3.4.1)
45
+ diff-lcs (>= 1.2.0, < 2.0)
46
+ rspec-support (~> 3.4.0)
47
+ rspec-support (3.4.1)
48
+ safe_yaml (1.0.4)
49
+ slop (3.6.0)
50
+ thread_safe (0.3.5)
51
+ tzinfo (1.2.2)
52
+ thread_safe (~> 0.1)
53
+ vcr (3.0.1)
54
+ webmock (1.24.2)
55
+ addressable (>= 2.3.6)
56
+ crack (>= 0.3.2)
57
+ hashdiff
58
+
59
+ PLATFORMS
60
+ ruby
61
+
62
+ DEPENDENCIES
63
+ byebug
64
+ pry
65
+ quintype!
66
+ rspec (~> 3.4)
67
+ vcr
68
+ webmock
@@ -0,0 +1,224 @@
1
+ require 'faraday'
2
+ require 'json'
3
+ require 'active_support/all'
4
+
5
+ # API models
6
+ require_relative './api/story'
7
+ require_relative './api/stack'
8
+ require_relative './api/url'
9
+
10
+ class API
11
+ class << self
12
+ def establish_connection(host, conn = Faraday.new(url: host))
13
+ @@host = host
14
+ @@api_base = host + '/api/'
15
+ @@conn = conn
16
+ end
17
+
18
+ def conn
19
+ @@conn
20
+ end
21
+
22
+ def bulk_post(params)
23
+ _post("bulk", params)
24
+ end
25
+
26
+ def config
27
+ _get("config")
28
+ end
29
+
30
+ def story(story_id)
31
+ _get("stories/#{story_id}")
32
+ end
33
+
34
+ def story_by_slug(slug, params = {})
35
+ _get("stories-by-slug", params.merge({ slug: slug }))
36
+ end
37
+
38
+ def related_stories(story_id, section, fields = [])
39
+ _get("related-stories?", {
40
+ "story-id" => story_id,
41
+ section: section,
42
+ fields: make_fields(fields)
43
+ })
44
+ end
45
+
46
+ def stories(params, options = {})
47
+ url = options[:facets] ? "stories-with-facets" : "stories"
48
+ _get(url, params)
49
+ end
50
+
51
+ def comments_and_likes(story_ids)
52
+ if story_ids.present?
53
+ _get("comments-and-votes/" + story_ids.join('|'))
54
+ end
55
+ end
56
+
57
+ def videos
58
+ _get("stories-by-template", {
59
+ template: "video",
60
+ limit: 12,
61
+ fields: "hero-image-s3-key,hero-image-metadata,hero-image-caption,headline,slug"
62
+ })
63
+ end
64
+
65
+ def search_story_collection(name, options)
66
+ _get("story-collection", {
67
+ name: name,
68
+ type: "search",
69
+ fields: "author-name,hero-image-s3-key,hero-image-metadata,hero-image-caption,headline,slug,sections,metadata"
70
+ }.merge(options))
71
+ end
72
+
73
+ def story_collection(options)
74
+ _get("story-collection", options)
75
+ end
76
+
77
+ def story_collection_by_tag(options)
78
+ _get("story-collection/find-by-tag", options)
79
+ end
80
+
81
+ def post_comment(story_content_id, text, parent_comment_id=nil, session_cookie)
82
+ hash = {
83
+ "story-content-id" => story_content_id,
84
+ "text" => text
85
+ }
86
+ hash.merge!("parent-comment-id" => parent_comment_id.to_i) if parent_comment_id
87
+ _post("comment", hash, session_cookie)
88
+ end
89
+
90
+ def invite_users(emails, from_email, from_name)
91
+ params = { emails: emails }
92
+ params['from-email'] = from_email if from_email.present?
93
+ params['from-name'] = from_name if from_name.present?
94
+
95
+ _post("emails/invite", params)
96
+ end
97
+
98
+ def contact_publisher(params)
99
+ _post("emails/contact", params)
100
+ end
101
+
102
+ def unsubscribe_publisher(params)
103
+ _post("emails/unsubscribe", params)
104
+ end
105
+
106
+ def authors(params)
107
+ _get("authors", params)
108
+ end
109
+
110
+ def author_profile(author_id)
111
+ _get("author/#{author_id}")
112
+ end
113
+
114
+ def search(options)
115
+ _get("search", options)
116
+ end
117
+
118
+ def subscribe(member, profile, payment)
119
+ _post("subscribe", {
120
+ member: member,
121
+ profile: profile,
122
+ payment: payment
123
+ })
124
+ end
125
+
126
+ def unsubscribe(options)
127
+ _post("unsubscribe", { options: options })
128
+ end
129
+
130
+ def save_member_metadata(metadata)
131
+ _post("member/metadata", { metadata: metadata })
132
+ end
133
+
134
+ def check_email(email)
135
+ _get("member/check", { email: email })
136
+ end
137
+
138
+ def signup_member(member)
139
+ _post("member", member)
140
+ end
141
+
142
+ def login_member(auth)
143
+ _post("member/login", auth)
144
+ end
145
+
146
+ def login(provider, data)
147
+ user, headers = _post_returning_headers("login/#{provider}", data)
148
+ user['auth_token'] = headers['X-QT-AUTH']
149
+ user['member'].merge(user.except('member'))
150
+ end
151
+
152
+ def logout
153
+ _get("logout")
154
+ end
155
+
156
+ def forgot_password(member)
157
+ _post("member/forgot-password", member)
158
+ end
159
+
160
+ def reset_password(params)
161
+ _post("member/password", params)
162
+ end
163
+
164
+ def vote_on_story (data)
165
+ _post("stories/#{data[:story_id]}/votes", data)
166
+ end
167
+
168
+ def votes_on_story (options = {})
169
+ _get("stories/#{story_id}/votes", options)
170
+ end
171
+
172
+ private
173
+
174
+ def _post(url_path, body, session_cookie=nil)
175
+ body, headers = _post_returning_headers(url_path, body, session_cookie)
176
+
177
+ body
178
+ end
179
+
180
+ def _post_returning_headers(url_path, body, session_cookie=nil)
181
+ response = @@conn.post(@@api_base + url_path) do |request|
182
+ request.headers['Content-Type'] = 'application/json'
183
+ request.headers['X-QT-AUTH'] = session_cookie if session_cookie
184
+
185
+ request.body = body.to_json
186
+ end
187
+
188
+ if response.body.present?
189
+ body = case body = JSON.parse(response.body)
190
+ when Array
191
+ body.map { |i| keywordize(i) }
192
+ when Object
193
+ keywordize body
194
+ end
195
+
196
+ [body, response.headers]
197
+ end
198
+ end
199
+
200
+ def _get(url_path, *args)
201
+ response = @@conn.get(@@api_base + url_path, *args)
202
+ return nil if response.status >= 400
203
+
204
+ if response.body.present?
205
+ body = JSON.parse(response.body)
206
+
207
+ case body
208
+ when Array
209
+ body.map { |i| keywordize(i) }
210
+ when Object
211
+ keywordize body
212
+ end
213
+ end
214
+ end
215
+
216
+ def keywordize(obj)
217
+ obj.deep_transform_keys { |k| k.gsub('-', '_') }
218
+ end
219
+
220
+ def make_fields(arr)
221
+ arr.join(',') if arr.present?
222
+ end
223
+ end
224
+ end
@@ -0,0 +1,42 @@
1
+ class API
2
+ class Author
3
+ attr_reader :author
4
+ class << self
5
+ def wrap_all(authors)
6
+ authors ||= []
7
+ authors.is_a?(Array) ?
8
+ authors.map { |a| wrap(a) } :
9
+ wrap(authors)
10
+ end
11
+
12
+ def wrap(author)
13
+ new(author) if author
14
+ end
15
+
16
+ def where(params)
17
+ if params['ids'].kind_of? Array
18
+ params['ids'] = params['ids'].join ","
19
+ end
20
+ authors = API.authors(params)
21
+ wrap_all(authors)
22
+ end
23
+
24
+ def find(params)
25
+ if authors = API.authors({ids: params}).presence
26
+ author = authors.first
27
+ wrap(author)
28
+ end
29
+ end
30
+ end
31
+
32
+ def initialize(author)
33
+ @author = author
34
+ end
35
+
36
+ def to_h(config={})
37
+ author
38
+ end
39
+
40
+ private
41
+ end
42
+ end
@@ -0,0 +1,18 @@
1
+ class API
2
+ class Stack
3
+ class << self
4
+ def all
5
+ API.config['layout']['stacks']
6
+ end
7
+ #TODO filter by stacks
8
+ def with_stories(params={}, config={})
9
+ stories_with_stacks = API::Story.find_by_stacks(all, params)
10
+ stacks = all.map do |stack|
11
+ stories = stories_with_stacks[stack['story_group'].gsub('-', '_')]
12
+ stack['stories'] = stories
13
+ stack
14
+ end
15
+ end
16
+ end
17
+ end
18
+ end
@@ -0,0 +1,119 @@
1
+ require_relative './story/reading_time'
2
+
3
+ class API
4
+ class Story
5
+ attr_reader :story
6
+ include ReadingTime
7
+ class << self
8
+ def wrap_all(stories)
9
+ stories ||= []
10
+ stories.is_a?(Array) ?
11
+ stories.map { |s| wrap(s) } :
12
+ wrap(stories)
13
+ end
14
+
15
+ def wrap(story)
16
+ new(story) if story
17
+ end
18
+
19
+ def where(params, opts={})
20
+ stories = API.stories(params, opts)
21
+ wrap_all(stories)
22
+ end
23
+
24
+ def find(params, opts={})
25
+ if stories = API.stories(params, opts).presence
26
+ story = stories.first
27
+ wrap(story)
28
+ end
29
+ end
30
+
31
+ def find_by_stacks(stacks, options={})
32
+ if stacks.present?
33
+ requests = stacks.inject({}) do |hash, stack|
34
+ options.reject! {|k,v| k == 'section' }
35
+ hash[stack['story_group']] = { 'story_group' => stack['story_group'] }.merge(options)
36
+ hash
37
+ end
38
+
39
+ stories = find_in_bulk(requests)
40
+ stories
41
+ end
42
+ end
43
+
44
+ def find_in_bulk(params)
45
+ if params.present?
46
+ params = params.inject({}) do |hash, param|
47
+ hash[param.first] = param.last.merge(_type: 'stories')
48
+ hash
49
+ end
50
+ response = API.bulk_post(requests: params)
51
+ response['results']
52
+ else
53
+ []
54
+ end
55
+ end
56
+
57
+ def find_by_slug(slug, params = {})
58
+ if story = API.story_by_slug(slug, params).presence
59
+ wrap(story['story'])
60
+ end
61
+ end
62
+
63
+ def all_video_stories
64
+ stories = API.videos
65
+ wrap_all(stories['stories'])
66
+ end
67
+
68
+ def all
69
+ stories = API.stories({})
70
+ wrap_all(stories)
71
+ end
72
+ end
73
+
74
+ def initialize(story)
75
+ @story = story
76
+ end
77
+
78
+ def cards
79
+ @cards = story['cards'] || []
80
+ end
81
+
82
+ def to_h(config={})
83
+ hash = story.merge({
84
+ 'url' => URL.story(story),
85
+ 'time_in_minutes' => time_in_minutes,
86
+ 'tags' => add_urls_to_tags
87
+ })
88
+ if config.present?
89
+ hash.merge!({ 'sections' => add_display_names_to_sections(config),
90
+ 'canonical_url' => URL.story_canonical(config['root_url'], story)
91
+ })
92
+ end
93
+ hash
94
+ end
95
+
96
+ private
97
+ def add_display_names_to_sections(config)
98
+ return story unless story['sections'].present?
99
+
100
+ sections = story['sections'].map do |section|
101
+ display_section = config['sections'].find { |s| s['id'] == section['id'] } || {}
102
+
103
+ display_name = display_section['display_name'] || display_section['name'] || section['name']
104
+
105
+ section.merge({ 'display_name' => display_name })
106
+ end
107
+ end
108
+
109
+ def add_urls_to_tags
110
+ if story['tags'].present?
111
+ tags = story['tags'].map do |tag|
112
+ tag.merge('url' => URL.topic(tag['name']))
113
+ end
114
+ else
115
+ story['tags']
116
+ end
117
+ end
118
+ end
119
+ end